Block layer patches:

- Add qemu-storage-daemon (still experimental)
 - rbd: Add support for ceph namespaces
 - Fix bdrv_reopen() with backing file in different AioContext
 - qcow2: Fix read-write reopen with persistent dirty bitmaps
 - qcow2: Fix alloc_cluster_abort() for pre-existing clusters
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJeYoR/AAoJEH8JsnLIjy/W3VcP/jOCxj+dMiQO7/+ywFvPYg+8
 Mux0nDD4vvvduldOObxvFPT+ufid03MTIX+V27gGqsXeh+GhZja5w+uLYK8H9fy8
 swviWEBGgZJb2q5RXVUMtFfnViNzO3NyWNC8vD2E8lJmEwPDyXeL31mJZCjSnSZ1
 /9tJaztN/+8H2Ra26HBj2OnqsMb1CYPS0vmGLdJ34Bn2BpLSmmeUdalxOTeOHRP3
 KlmJYLk7GwVfz98YKVtet71/WfBsU5s7h6Dq/ZQkHoqwk4LNQAVcv/qbiPoIhm5I
 pqERbQlk/3le8jh5M8rmdE1P1LVLRfW0CDlkFNeTt45XBd3lfk396i+14dZvWMa0
 m1egpCUcCGS6GcxJvVOPnsqyAZzTjW/EW7NJkeKlOl6ljzSmbVyfcke7DlUua1SG
 xwe9zrR4Ru8LC/JRDJtapTPnWWfa/63BV3dACokjaS3ix+OA3gLmzwoszErYIYfM
 MGxkfog/rRmb69s61tGUwuSEDEnlAQpiZ+r+yEo9mMtULCFlwKH0GlvsuwNcpHd/
 XffT7omg/Phdrtffp8Vs0SQ4a3A+ILGotqY+LKRhVtP06i84SNTohhmQANBjSJo8
 qeZA4sHvwxGxunllb8fF7r3QZ8sIbN3dckfHze3MCtA4gpHs5ZM/x9A//J/R7bEC
 OFx4nuk2ME9SSCTWeM3W
 =32U7
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches:

- Add qemu-storage-daemon (still experimental)
- rbd: Add support for ceph namespaces
- Fix bdrv_reopen() with backing file in different AioContext
- qcow2: Fix read-write reopen with persistent dirty bitmaps
- qcow2: Fix alloc_cluster_abort() for pre-existing clusters

# gpg: Signature made Fri 06 Mar 2020 17:12:31 GMT
# gpg:                using RSA key 7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (29 commits)
  block: bdrv_reopen() with backing file in different AioContext
  iotests: Refactor blockdev-reopen test for iothreads
  block/rbd: Add support for ceph namespaces
  qemu-storage-daemon: Add --monitor option
  monitor: Add allow_hmp parameter to monitor_init()
  hmp: Fail gracefully if chardev is already in use
  qmp: Fail gracefully if chardev is already in use
  monitor: Create QAPIfied monitor_init()
  qapi: Create 'pragma' module
  stubs: Update monitor stubs for qemu-storage-daemon
  qemu-storage-daemon: Add --chardev option
  qemu-storage-daemon: Add main loop
  qemu-storage-daemon: Add --export option
  blockdev-nbd: Boxed argument type for nbd-server-add
  qemu-storage-daemon: Add --nbd-server option
  qemu-storage-daemon: Add --object option
  qapi: Flatten object-add
  qemu-storage-daemon: Add --blockdev option
  block: Move sysemu QMP commands to QAPI block module
  block: Move common QMP commands to block-core QAPI module
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2020-03-06 17:15:35 +00:00
commit 67f17e23ba
52 changed files with 2170 additions and 1319 deletions

View File

@ -128,7 +128,28 @@ GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
GENERATED_QAPI_FILES += qapi/qapi-doc.texi
# The following list considers only the storage daemon main module. All other
# modules are currently shared with the main schema, so we don't actually
# generate additional files.
GENERATED_STORAGE_DAEMON_QAPI_FILES = storage-daemon/qapi/qapi-commands.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-commands.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-doc.texi
generated-files-y += $(GENERATED_QAPI_FILES)
generated-files-y += $(GENERATED_STORAGE_DAEMON_QAPI_FILES)
generated-files-y += trace/generated-tcg-tracers.h
@ -450,6 +471,8 @@ dummy := $(call unnest-vars,, \
qga-vss-dll-obj-y \
block-obj-y \
block-obj-m \
storage-daemon-obj-y \
storage-daemon-obj-m \
crypto-obj-y \
qom-obj-y \
io-obj-y \
@ -482,6 +505,7 @@ TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(T
SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
$(SOFTMMU_ALL_RULES): $(authz-obj-y)
$(SOFTMMU_ALL_RULES): $(block-obj-y)
$(SOFTMMU_ALL_RULES): $(storage-daemon-obj-y)
$(SOFTMMU_ALL_RULES): $(chardev-obj-y)
$(SOFTMMU_ALL_RULES): $(crypto-obj-y)
$(SOFTMMU_ALL_RULES): $(io-obj-y)
@ -586,6 +610,7 @@ qemu-img.o: qemu-img-cmds.h
qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(chardev-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS)
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
@ -647,6 +672,17 @@ qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
"GEN","$(@:%-timestamp=%)")
@>$@
qapi-modules-storage-daemon = \
$(SRC_PATH)/storage-daemon/qapi/qapi-schema.json \
$(QAPI_MODULES_STORAGE_DAEMON:%=$(SRC_PATH)/qapi/%.json)
$(GENERATED_STORAGE_DAEMON_QAPI_FILES): storage-daemon/qapi/qapi-gen-timestamp ;
storage-daemon/qapi/qapi-gen-timestamp: $(qapi-modules-storage-daemon) $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
-o "storage-daemon/qapi" $<, \
"GEN","$(@:%-timestamp=%)")
@>$@
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h qga-qapi-init-commands.h)
$(qga-obj-y): $(QGALIB_GEN)
@ -745,6 +781,7 @@ clean: recurse-clean
rm -f trace/generated-tracers-dtrace.h*
rm -f $(foreach f,$(generated-files-y),$(f) $(f)-timestamp)
rm -f qapi-gen-timestamp
rm -f storage-daemon/qapi/qapi-gen-timestamp
rm -rf qga/qapi-generated
rm -f config-all-devices.mak

View File

@ -27,6 +27,15 @@ io-obj-y = io/
endif # CONFIG_SOFTMMU or CONFIG_TOOLS
#######################################################################
# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are
# used for system emulation, too, but specified separately there)
storage-daemon-obj-y = block/ monitor/ qapi/ qom/ storage-daemon/
storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o job-qmp.o
storage-daemon-obj-$(CONFIG_WIN32) += os-win32.o
storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o
######################################################################
# Target independent part of system emulation. The long term path is to
# suppress *all* target specific code in case of system emulation, i.e. a

44
block.c
View File

@ -600,7 +600,7 @@ static int bdrv_create_file_fallback(const char *filename, BlockDriver *drv,
QemuOpts *opts, Error **errp)
{
BlockBackend *blk;
QDict *options = qdict_new();
QDict *options;
int64_t size = 0;
char *buf = NULL;
PreallocMode prealloc;
@ -623,6 +623,7 @@ static int bdrv_create_file_fallback(const char *filename, BlockDriver *drv,
return -ENOTSUP;
}
options = qdict_new();
qdict_put_str(options, "driver", drv->format_name);
blk = blk_new_open(filename, NULL, options,
@ -3694,6 +3695,15 @@ cleanup_perm:
}
}
}
if (ret == 0) {
QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
BlockDriverState *bs = bs_entry->state.bs;
if (bs->drv->bdrv_reopen_commit_post)
bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
}
}
cleanup:
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
if (ret) {
@ -3777,6 +3787,29 @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
*shared = cumulative_shared_perms;
}
static bool bdrv_reopen_can_attach(BlockDriverState *parent,
BdrvChild *child,
BlockDriverState *new_child,
Error **errp)
{
AioContext *parent_ctx = bdrv_get_aio_context(parent);
AioContext *child_ctx = bdrv_get_aio_context(new_child);
GSList *ignore;
bool ret;
ignore = g_slist_prepend(NULL, child);
ret = bdrv_can_set_aio_context(new_child, parent_ctx, &ignore, NULL);
g_slist_free(ignore);
if (ret) {
return ret;
}
ignore = g_slist_prepend(NULL, child);
ret = bdrv_can_set_aio_context(parent, child_ctx, &ignore, errp);
g_slist_free(ignore);
return ret;
}
/*
* Take a BDRVReopenState and check if the value of 'backing' in the
* reopen_state->options QDict is valid or not.
@ -3828,14 +3861,11 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
}
/*
* TODO: before removing the x- prefix from x-blockdev-reopen we
* should move the new backing file into the right AioContext
* instead of returning an error.
* Check AioContext compatibility so that the bdrv_set_backing_hd() call in
* bdrv_reopen_commit() won't fail.
*/
if (new_backing_bs) {
if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
error_setg(errp, "Cannot use a new backing file "
"with a different AioContext");
if (!bdrv_reopen_can_attach(bs, bs->backing, new_backing_bs, errp)) {
return -EINVAL;
}
}

View File

@ -46,7 +46,9 @@ block-obj-y += aio_task.o
block-obj-y += backup-top.o
block-obj-y += filter-compress.o
common-obj-y += stream.o
block-obj-y += stream.o
common-obj-y += qapi-sysemu.o
nfs.o-libs := $(LIBNFS_LIBS)
iscsi.o-cflags := $(LIBISCSI_CFLAGS)

590
block/qapi-sysemu.c Normal file
View File

@ -0,0 +1,590 @@
/*
* QMP command handlers specific to the system emulators
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-block.h"
#include "qapi/qmp/qdict.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id,
Error **errp)
{
BlockBackend *blk;
if (!blk_name == !qdev_id) {
error_setg(errp, "Need exactly one of 'device' and 'id'");
return NULL;
}
if (qdev_id) {
blk = blk_by_qdev_id(qdev_id, errp);
} else {
blk = blk_by_name(blk_name);
if (blk == NULL) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", blk_name);
}
}
return blk;
}
/*
* Attempt to open the tray of @device.
* If @force, ignore its tray lock.
* Else, if the tray is locked, don't open it, but ask the guest to open it.
* On error, store an error through @errp and return -errno.
* If @device does not exist, return -ENODEV.
* If it has no removable media, return -ENOTSUP.
* If it has no tray, return -ENOSYS.
* If the guest was asked to open the tray, return -EINPROGRESS.
* Else, return 0.
*/
static int do_open_tray(const char *blk_name, const char *qdev_id,
bool force, Error **errp)
{
BlockBackend *blk;
const char *device = qdev_id ?: blk_name;
bool locked;
blk = qmp_get_blk(blk_name, qdev_id, errp);
if (!blk) {
return -ENODEV;
}
if (!blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device '%s' is not removable", device);
return -ENOTSUP;
}
if (!blk_dev_has_tray(blk)) {
error_setg(errp, "Device '%s' does not have a tray", device);
return -ENOSYS;
}
if (blk_dev_is_tray_open(blk)) {
return 0;
}
locked = blk_dev_is_medium_locked(blk);
if (locked) {
blk_dev_eject_request(blk, force);
}
if (!locked || force) {
blk_dev_change_media_cb(blk, false, &error_abort);
}
if (locked && !force) {
error_setg(errp, "Device '%s' is locked and force was not specified, "
"wait for tray to open and try again", device);
return -EINPROGRESS;
}
return 0;
}
void qmp_blockdev_open_tray(bool has_device, const char *device,
bool has_id, const char *id,
bool has_force, bool force,
Error **errp)
{
Error *local_err = NULL;
int rc;
if (!has_force) {
force = false;
}
rc = do_open_tray(has_device ? device : NULL,
has_id ? id : NULL,
force, &local_err);
if (rc && rc != -ENOSYS && rc != -EINPROGRESS) {
error_propagate(errp, local_err);
return;
}
error_free(local_err);
}
void qmp_blockdev_close_tray(bool has_device, const char *device,
bool has_id, const char *id,
Error **errp)
{
BlockBackend *blk;
Error *local_err = NULL;
device = has_device ? device : NULL;
id = has_id ? id : NULL;
blk = qmp_get_blk(device, id, errp);
if (!blk) {
return;
}
if (!blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device '%s' is not removable", device ?: id);
return;
}
if (!blk_dev_has_tray(blk)) {
/* Ignore this command on tray-less devices */
return;
}
if (!blk_dev_is_tray_open(blk)) {
return;
}
blk_dev_change_media_cb(blk, true, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
}
static void blockdev_remove_medium(bool has_device, const char *device,
bool has_id, const char *id, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
AioContext *aio_context;
bool has_attached_device;
device = has_device ? device : NULL;
id = has_id ? id : NULL;
blk = qmp_get_blk(device, id, errp);
if (!blk) {
return;
}
/* For BBs without a device, we can exchange the BDS tree at will */
has_attached_device = blk_get_attached_dev(blk);
if (has_attached_device && !blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device '%s' is not removable", device ?: id);
return;
}
if (has_attached_device && blk_dev_has_tray(blk) &&
!blk_dev_is_tray_open(blk))
{
error_setg(errp, "Tray of device '%s' is not open", device ?: id);
return;
}
bs = blk_bs(blk);
if (!bs) {
return;
}
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
goto out;
}
blk_remove_bs(blk);
if (!blk_dev_has_tray(blk)) {
/* For tray-less devices, blockdev-open-tray is a no-op (or may not be
* called at all); therefore, the medium needs to be ejected here.
* Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
* value passed here (i.e. false). */
blk_dev_change_media_cb(blk, false, &error_abort);
}
out:
aio_context_release(aio_context);
}
void qmp_blockdev_remove_medium(const char *id, Error **errp)
{
blockdev_remove_medium(false, NULL, true, id, errp);
}
static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
BlockDriverState *bs, Error **errp)
{
Error *local_err = NULL;
bool has_device;
int ret;
/* For BBs without a device, we can exchange the BDS tree at will */
has_device = blk_get_attached_dev(blk);
if (has_device && !blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device is not removable");
return;
}
if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
error_setg(errp, "Tray of the device is not open");
return;
}
if (blk_bs(blk)) {
error_setg(errp, "There already is a medium in the device");
return;
}
ret = blk_insert_bs(blk, bs, errp);
if (ret < 0) {
return;
}
if (!blk_dev_has_tray(blk)) {
/* For tray-less devices, blockdev-close-tray is a no-op (or may not be
* called at all); therefore, the medium needs to be pushed into the
* slot here.
* Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
* value passed here (i.e. true). */
blk_dev_change_media_cb(blk, true, &local_err);
if (local_err) {
error_propagate(errp, local_err);
blk_remove_bs(blk);
return;
}
}
}
static void blockdev_insert_medium(bool has_device, const char *device,
bool has_id, const char *id,
const char *node_name, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
blk = qmp_get_blk(has_device ? device : NULL,
has_id ? id : NULL,
errp);
if (!blk) {
return;
}
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Node '%s' not found", node_name);
return;
}
if (bdrv_has_blk(bs)) {
error_setg(errp, "Node '%s' is already in use", node_name);
return;
}
qmp_blockdev_insert_anon_medium(blk, bs, errp);
}
void qmp_blockdev_insert_medium(const char *id, const char *node_name,
Error **errp)
{
blockdev_insert_medium(false, NULL, true, id, node_name, errp);
}
void qmp_blockdev_change_medium(bool has_device, const char *device,
bool has_id, const char *id,
const char *filename,
bool has_format, const char *format,
bool has_read_only,
BlockdevChangeReadOnlyMode read_only,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *medium_bs = NULL;
int bdrv_flags;
bool detect_zeroes;
int rc;
QDict *options = NULL;
Error *err = NULL;
blk = qmp_get_blk(has_device ? device : NULL,
has_id ? id : NULL,
errp);
if (!blk) {
goto fail;
}
if (blk_bs(blk)) {
blk_update_root_state(blk);
}
bdrv_flags = blk_get_open_flags_from_root_state(blk);
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
if (!has_read_only) {
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
}
switch (read_only) {
case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
break;
case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
bdrv_flags &= ~BDRV_O_RDWR;
break;
case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
bdrv_flags |= BDRV_O_RDWR;
break;
default:
abort();
}
options = qdict_new();
detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
if (has_format) {
qdict_put_str(options, "driver", format);
}
medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
if (!medium_bs) {
goto fail;
}
rc = do_open_tray(has_device ? device : NULL,
has_id ? id : NULL,
false, &err);
if (rc && rc != -ENOSYS) {
error_propagate(errp, err);
goto fail;
}
error_free(err);
err = NULL;
blockdev_remove_medium(has_device, device, has_id, id, &err);
if (err) {
error_propagate(errp, err);
goto fail;
}
qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
if (err) {
error_propagate(errp, err);
goto fail;
}
qmp_blockdev_close_tray(has_device, device, has_id, id, errp);
fail:
/* If the medium has been inserted, the device has its own reference, so
* ours must be relinquished; and if it has not been inserted successfully,
* the reference must be relinquished anyway */
bdrv_unref(medium_bs);
}
void qmp_eject(bool has_device, const char *device,
bool has_id, const char *id,
bool has_force, bool force, Error **errp)
{
Error *local_err = NULL;
int rc;
if (!has_force) {
force = false;
}
rc = do_open_tray(has_device ? device : NULL,
has_id ? id : NULL,
force, &local_err);
if (rc && rc != -ENOSYS) {
error_propagate(errp, local_err);
return;
}
error_free(local_err);
blockdev_remove_medium(has_device, device, has_id, id, errp);
}
/* throttling disk I/O limits */
void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
{
ThrottleConfig cfg;
BlockDriverState *bs;
BlockBackend *blk;
AioContext *aio_context;
blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
arg->has_id ? arg->id : NULL,
errp);
if (!blk) {
return;
}
aio_context = blk_get_aio_context(blk);
aio_context_acquire(aio_context);
bs = blk_bs(blk);
if (!bs) {
error_setg(errp, "Device has no medium");
goto out;
}
throttle_config_init(&cfg);
cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd;
cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd;
cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
if (arg->has_bps_max) {
cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
}
if (arg->has_bps_rd_max) {
cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
}
if (arg->has_bps_wr_max) {
cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
}
if (arg->has_iops_max) {
cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
}
if (arg->has_iops_rd_max) {
cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
}
if (arg->has_iops_wr_max) {
cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
}
if (arg->has_bps_max_length) {
cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
}
if (arg->has_bps_rd_max_length) {
cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
}
if (arg->has_bps_wr_max_length) {
cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
}
if (arg->has_iops_max_length) {
cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
}
if (arg->has_iops_rd_max_length) {
cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
}
if (arg->has_iops_wr_max_length) {
cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
}
if (arg->has_iops_size) {
cfg.op_size = arg->iops_size;
}
if (!throttle_is_valid(&cfg, errp)) {
goto out;
}
if (throttle_enabled(&cfg)) {
/* Enable I/O limits if they're not enabled yet, otherwise
* just update the throttling group. */
if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
blk_io_limits_enable(blk,
arg->has_group ? arg->group :
arg->has_device ? arg->device :
arg->id);
} else if (arg->has_group) {
blk_io_limits_update_group(blk, arg->group);
}
/* Set the new throttling configuration */
blk_set_io_limits(blk, &cfg);
} else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
/* If all throttling settings are set to 0, disable I/O limits */
blk_io_limits_disable(blk);
}
out:
aio_context_release(aio_context);
}
void qmp_block_latency_histogram_set(
const char *id,
bool has_boundaries, uint64List *boundaries,
bool has_boundaries_read, uint64List *boundaries_read,
bool has_boundaries_write, uint64List *boundaries_write,
bool has_boundaries_flush, uint64List *boundaries_flush,
Error **errp)
{
BlockBackend *blk = qmp_get_blk(NULL, id, errp);
BlockAcctStats *stats;
int ret;
if (!blk) {
return;
}
stats = blk_get_stats(blk);
if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
!has_boundaries_flush)
{
block_latency_histograms_clear(stats);
return;
}
if (has_boundaries || has_boundaries_read) {
ret = block_latency_histogram_set(
stats, BLOCK_ACCT_READ,
has_boundaries_read ? boundaries_read : boundaries);
if (ret) {
error_setg(errp, "Device '%s' set read boundaries fail", id);
return;
}
}
if (has_boundaries || has_boundaries_write) {
ret = block_latency_histogram_set(
stats, BLOCK_ACCT_WRITE,
has_boundaries_write ? boundaries_write : boundaries);
if (ret) {
error_setg(errp, "Device '%s' set write boundaries fail", id);
return;
}
}
if (has_boundaries || has_boundaries_flush) {
ret = block_latency_histogram_set(
stats, BLOCK_ACCT_FLUSH,
has_boundaries_flush ? boundaries_flush : boundaries);
if (ret) {
error_setg(errp, "Device '%s' set flush boundaries fail", id);
return;
}
}
}

View File

@ -1026,7 +1026,7 @@ err:
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
{
BDRVQcow2State *s = bs->opaque;
if (!has_data_file(bs)) {
if (!has_data_file(bs) && !m->keep_old_clusters) {
qcow2_free_clusters(bs, m->alloc_offset,
m->nb_clusters << s->cluster_bits,
QCOW2_DISCARD_NEVER);

View File

@ -1884,6 +1884,11 @@ fail:
static void qcow2_reopen_commit(BDRVReopenState *state)
{
qcow2_update_options_commit(state->bs, state->opaque);
g_free(state->opaque);
}
static void qcow2_reopen_commit_post(BDRVReopenState *state)
{
if (state->flags & BDRV_O_RDWR) {
Error *local_err = NULL;
@ -1898,7 +1903,6 @@ static void qcow2_reopen_commit(BDRVReopenState *state)
bdrv_get_node_name(state->bs));
}
}
g_free(state->opaque);
}
static void qcow2_reopen_abort(BDRVReopenState *state)
@ -5534,6 +5538,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_close = qcow2_close,
.bdrv_reopen_prepare = qcow2_reopen_prepare,
.bdrv_reopen_commit = qcow2_reopen_commit,
.bdrv_reopen_commit_post = qcow2_reopen_commit_post,
.bdrv_reopen_abort = qcow2_reopen_abort,
.bdrv_join_options = qcow2_join_options,
.bdrv_child_perm = bdrv_format_default_perms,

View File

@ -104,6 +104,7 @@ typedef struct BDRVRBDState {
rbd_image_t image;
char *image_name;
char *snap;
char *namespace;
uint64_t image_size;
} BDRVRBDState;
@ -152,7 +153,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
const char *start;
char *p, *buf;
QList *keypairs = NULL;
char *found_str;
char *found_str, *image_name;
if (!strstart(filename, "rbd:", &start)) {
error_setg(errp, "File name must start with 'rbd:'");
@ -171,18 +172,24 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
qdict_put_str(options, "pool", found_str);
if (strchr(p, '@')) {
found_str = qemu_rbd_next_tok(p, '@', &p);
qemu_rbd_unescape(found_str);
qdict_put_str(options, "image", found_str);
image_name = qemu_rbd_next_tok(p, '@', &p);
found_str = qemu_rbd_next_tok(p, ':', &p);
qemu_rbd_unescape(found_str);
qdict_put_str(options, "snapshot", found_str);
} else {
found_str = qemu_rbd_next_tok(p, ':', &p);
qemu_rbd_unescape(found_str);
qdict_put_str(options, "image", found_str);
image_name = qemu_rbd_next_tok(p, ':', &p);
}
/* Check for namespace in the image_name */
if (strchr(image_name, '/')) {
found_str = qemu_rbd_next_tok(image_name, '/', &image_name);
qemu_rbd_unescape(found_str);
qdict_put_str(options, "namespace", found_str);
} else {
qdict_put_str(options, "namespace", "");
}
qemu_rbd_unescape(image_name);
qdict_put_str(options, "image", image_name);
if (!p) {
goto done;
}
@ -343,6 +350,11 @@ static QemuOptsList runtime_opts = {
.type = QEMU_OPT_STRING,
.help = "Rados pool name",
},
{
.name = "namespace",
.type = QEMU_OPT_STRING,
.help = "Rados namespace name in the pool",
},
{
.name = "image",
.type = QEMU_OPT_STRING,
@ -467,13 +479,14 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
* schema, but when they come from -drive, they're all QString.
*/
loc = rbd_opts->location;
loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
loc->has_conf = !!loc->conf;
loc->user = g_strdup(qdict_get_try_str(options, "user"));
loc->has_user = !!loc->user;
loc->image = g_strdup(qdict_get_try_str(options, "image"));
keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
loc->has_conf = !!loc->conf;
loc->user = g_strdup(qdict_get_try_str(options, "user"));
loc->has_user = !!loc->user;
loc->q_namespace = g_strdup(qdict_get_try_str(options, "namespace"));
loc->image = g_strdup(qdict_get_try_str(options, "image"));
keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp);
if (ret < 0) {
@ -648,6 +661,11 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
error_setg_errno(errp, -r, "error opening pool %s", opts->pool);
goto failed_shutdown;
}
/*
* Set the namespace after opening the io context on the pool,
* if nspace == NULL or if nspace == "", it is just as we did nothing
*/
rados_ioctx_set_namespace(*io_ctx, opts->q_namespace);
return 0;

View File

@ -132,6 +132,11 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
nbd_server = NULL;
}
void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
{
nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, errp);
}
void qmp_nbd_server_start(SocketAddressLegacy *addr,
bool has_tls_creds, const char *tls_creds,
bool has_tls_authz, const char *tls_authz,
@ -143,10 +148,7 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr,
qapi_free_SocketAddress(addr_flat);
}
void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
bool has_description, const char *description,
bool has_writable, bool writable,
bool has_bitmap, const char *bitmap, Error **errp)
void qmp_nbd_server_add(BlockExportNbd *arg, Error **errp)
{
BlockDriverState *bs = NULL;
BlockBackend *on_eject_blk;
@ -159,28 +161,28 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
return;
}
if (!has_name) {
name = device;
if (!arg->has_name) {
arg->name = arg->device;
}
if (strlen(name) > NBD_MAX_STRING_SIZE) {
error_setg(errp, "export name '%s' too long", name);
if (strlen(arg->name) > NBD_MAX_STRING_SIZE) {
error_setg(errp, "export name '%s' too long", arg->name);
return;
}
if (has_description && strlen(description) > NBD_MAX_STRING_SIZE) {
error_setg(errp, "description '%s' too long", description);
if (arg->description && strlen(arg->description) > NBD_MAX_STRING_SIZE) {
error_setg(errp, "description '%s' too long", arg->description);
return;
}
if (nbd_export_find(name)) {
error_setg(errp, "NBD server already has export named '%s'", name);
if (nbd_export_find(arg->name)) {
error_setg(errp, "NBD server already has export named '%s'", arg->name);
return;
}
on_eject_blk = blk_by_name(device);
on_eject_blk = blk_by_name(arg->device);
bs = bdrv_lookup_bs(device, device, errp);
bs = bdrv_lookup_bs(arg->device, arg->device, errp);
if (!bs) {
return;
}
@ -194,15 +196,15 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
goto out;
}
if (!has_writable) {
writable = false;
if (!arg->has_writable) {
arg->writable = false;
}
if (bdrv_is_read_only(bs)) {
writable = false;
arg->writable = false;
}
exp = nbd_export_new(bs, 0, len, name, description, bitmap,
!writable, !writable,
exp = nbd_export_new(bs, 0, len, arg->name, arg->description, arg->bitmap,
!arg->writable, !arg->writable,
NULL, false, on_eject_blk, errp);
if (!exp) {
goto out;

View File

@ -67,14 +67,6 @@
static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
static int do_open_tray(const char *blk_name, const char *qdev_id,
bool force, Error **errp);
static void blockdev_remove_medium(bool has_device, const char *device,
bool has_id, const char *id, Error **errp);
static void blockdev_insert_medium(bool has_device, const char *device,
bool has_id, const char *id,
const char *node_name, Error **errp);
static const char *const if_name[IF_COUNT] = {
[IF_NONE] = "none",
[IF_IDE] = "ide",
@ -1047,29 +1039,6 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp)
return bs;
}
static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id,
Error **errp)
{
BlockBackend *blk;
if (!blk_name == !qdev_id) {
error_setg(errp, "Need exactly one of 'device' and 'id'");
return NULL;
}
if (qdev_id) {
blk = blk_by_qdev_id(qdev_id, errp);
} else {
blk = blk_by_name(blk_name);
if (blk == NULL) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", blk_name);
}
}
return blk;
}
void hmp_commit(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
@ -2508,29 +2477,6 @@ exit:
job_txn_unref(block_job_txn);
}
void qmp_eject(bool has_device, const char *device,
bool has_id, const char *id,
bool has_force, bool force, Error **errp)
{
Error *local_err = NULL;
int rc;
if (!has_force) {
force = false;
}
rc = do_open_tray(has_device ? device : NULL,
has_id ? id : NULL,
force, &local_err);
if (rc && rc != -ENOSYS) {
error_propagate(errp, local_err);
return;
}
error_free(local_err);
blockdev_remove_medium(has_device, device, has_id, id, errp);
}
void qmp_block_passwd(bool has_device, const char *device,
bool has_node_name, const char *node_name,
const char *password, Error **errp)
@ -2539,455 +2485,6 @@ void qmp_block_passwd(bool has_device, const char *device,
"Setting block passwords directly is no longer supported");
}
/*
* Attempt to open the tray of @device.
* If @force, ignore its tray lock.
* Else, if the tray is locked, don't open it, but ask the guest to open it.
* On error, store an error through @errp and return -errno.
* If @device does not exist, return -ENODEV.
* If it has no removable media, return -ENOTSUP.
* If it has no tray, return -ENOSYS.
* If the guest was asked to open the tray, return -EINPROGRESS.
* Else, return 0.
*/
static int do_open_tray(const char *blk_name, const char *qdev_id,
bool force, Error **errp)
{
BlockBackend *blk;
const char *device = qdev_id ?: blk_name;
bool locked;
blk = qmp_get_blk(blk_name, qdev_id, errp);
if (!blk) {
return -ENODEV;
}
if (!blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device '%s' is not removable", device);
return -ENOTSUP;
}
if (!blk_dev_has_tray(blk)) {
error_setg(errp, "Device '%s' does not have a tray", device);
return -ENOSYS;
}
if (blk_dev_is_tray_open(blk)) {
return 0;
}
locked = blk_dev_is_medium_locked(blk);
if (locked) {
blk_dev_eject_request(blk, force);
}
if (!locked || force) {
blk_dev_change_media_cb(blk, false, &error_abort);
}
if (locked && !force) {
error_setg(errp, "Device '%s' is locked and force was not specified, "
"wait for tray to open and try again", device);
return -EINPROGRESS;
}
return 0;
}
void qmp_blockdev_open_tray(bool has_device, const char *device,
bool has_id, const char *id,
bool has_force, bool force,
Error **errp)
{
Error *local_err = NULL;
int rc;
if (!has_force) {
force = false;
}
rc = do_open_tray(has_device ? device : NULL,
has_id ? id : NULL,
force, &local_err);
if (rc && rc != -ENOSYS && rc != -EINPROGRESS) {
error_propagate(errp, local_err);
return;
}
error_free(local_err);
}
void qmp_blockdev_close_tray(bool has_device, const char *device,
bool has_id, const char *id,
Error **errp)
{
BlockBackend *blk;
Error *local_err = NULL;
device = has_device ? device : NULL;
id = has_id ? id : NULL;
blk = qmp_get_blk(device, id, errp);
if (!blk) {
return;
}
if (!blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device '%s' is not removable", device ?: id);
return;
}
if (!blk_dev_has_tray(blk)) {
/* Ignore this command on tray-less devices */
return;
}
if (!blk_dev_is_tray_open(blk)) {
return;
}
blk_dev_change_media_cb(blk, true, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
}
static void blockdev_remove_medium(bool has_device, const char *device,
bool has_id, const char *id, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
AioContext *aio_context;
bool has_attached_device;
device = has_device ? device : NULL;
id = has_id ? id : NULL;
blk = qmp_get_blk(device, id, errp);
if (!blk) {
return;
}
/* For BBs without a device, we can exchange the BDS tree at will */
has_attached_device = blk_get_attached_dev(blk);
if (has_attached_device && !blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device '%s' is not removable", device ?: id);
return;
}
if (has_attached_device && blk_dev_has_tray(blk) &&
!blk_dev_is_tray_open(blk))
{
error_setg(errp, "Tray of device '%s' is not open", device ?: id);
return;
}
bs = blk_bs(blk);
if (!bs) {
return;
}
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
goto out;
}
blk_remove_bs(blk);
if (!blk_dev_has_tray(blk)) {
/* For tray-less devices, blockdev-open-tray is a no-op (or may not be
* called at all); therefore, the medium needs to be ejected here.
* Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
* value passed here (i.e. false). */
blk_dev_change_media_cb(blk, false, &error_abort);
}
out:
aio_context_release(aio_context);
}
void qmp_blockdev_remove_medium(const char *id, Error **errp)
{
blockdev_remove_medium(false, NULL, true, id, errp);
}
static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
BlockDriverState *bs, Error **errp)
{
Error *local_err = NULL;
bool has_device;
int ret;
/* For BBs without a device, we can exchange the BDS tree at will */
has_device = blk_get_attached_dev(blk);
if (has_device && !blk_dev_has_removable_media(blk)) {
error_setg(errp, "Device is not removable");
return;
}
if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
error_setg(errp, "Tray of the device is not open");
return;
}
if (blk_bs(blk)) {
error_setg(errp, "There already is a medium in the device");
return;
}
ret = blk_insert_bs(blk, bs, errp);
if (ret < 0) {
return;
}
if (!blk_dev_has_tray(blk)) {
/* For tray-less devices, blockdev-close-tray is a no-op (or may not be
* called at all); therefore, the medium needs to be pushed into the
* slot here.
* Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
* value passed here (i.e. true). */
blk_dev_change_media_cb(blk, true, &local_err);
if (local_err) {
error_propagate(errp, local_err);
blk_remove_bs(blk);
return;
}
}
}
static void blockdev_insert_medium(bool has_device, const char *device,
bool has_id, const char *id,
const char *node_name, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
blk = qmp_get_blk(has_device ? device : NULL,
has_id ? id : NULL,
errp);
if (!blk) {
return;
}
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Node '%s' not found", node_name);
return;
}
if (bdrv_has_blk(bs)) {
error_setg(errp, "Node '%s' is already in use", node_name);
return;
}
qmp_blockdev_insert_anon_medium(blk, bs, errp);
}
void qmp_blockdev_insert_medium(const char *id, const char *node_name,
Error **errp)
{
blockdev_insert_medium(false, NULL, true, id, node_name, errp);
}
void qmp_blockdev_change_medium(bool has_device, const char *device,
bool has_id, const char *id,
const char *filename,
bool has_format, const char *format,
bool has_read_only,
BlockdevChangeReadOnlyMode read_only,
Error **errp)
{
BlockBackend *blk;
BlockDriverState *medium_bs = NULL;
int bdrv_flags;
bool detect_zeroes;
int rc;
QDict *options = NULL;
Error *err = NULL;
blk = qmp_get_blk(has_device ? device : NULL,
has_id ? id : NULL,
errp);
if (!blk) {
goto fail;
}
if (blk_bs(blk)) {
blk_update_root_state(blk);
}
bdrv_flags = blk_get_open_flags_from_root_state(blk);
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
if (!has_read_only) {
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
}
switch (read_only) {
case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
break;
case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
bdrv_flags &= ~BDRV_O_RDWR;
break;
case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
bdrv_flags |= BDRV_O_RDWR;
break;
default:
abort();
}
options = qdict_new();
detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
if (has_format) {
qdict_put_str(options, "driver", format);
}
medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
if (!medium_bs) {
goto fail;
}
rc = do_open_tray(has_device ? device : NULL,
has_id ? id : NULL,
false, &err);
if (rc && rc != -ENOSYS) {
error_propagate(errp, err);
goto fail;
}
error_free(err);
err = NULL;
blockdev_remove_medium(has_device, device, has_id, id, &err);
if (err) {
error_propagate(errp, err);
goto fail;
}
qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
if (err) {
error_propagate(errp, err);
goto fail;
}
qmp_blockdev_close_tray(has_device, device, has_id, id, errp);
fail:
/* If the medium has been inserted, the device has its own reference, so
* ours must be relinquished; and if it has not been inserted successfully,
* the reference must be relinquished anyway */
bdrv_unref(medium_bs);
}
/* throttling disk I/O limits */
void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
{
ThrottleConfig cfg;
BlockDriverState *bs;
BlockBackend *blk;
AioContext *aio_context;
blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
arg->has_id ? arg->id : NULL,
errp);
if (!blk) {
return;
}
aio_context = blk_get_aio_context(blk);
aio_context_acquire(aio_context);
bs = blk_bs(blk);
if (!bs) {
error_setg(errp, "Device has no medium");
goto out;
}
throttle_config_init(&cfg);
cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd;
cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd;
cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
if (arg->has_bps_max) {
cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
}
if (arg->has_bps_rd_max) {
cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
}
if (arg->has_bps_wr_max) {
cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
}
if (arg->has_iops_max) {
cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
}
if (arg->has_iops_rd_max) {
cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
}
if (arg->has_iops_wr_max) {
cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
}
if (arg->has_bps_max_length) {
cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
}
if (arg->has_bps_rd_max_length) {
cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
}
if (arg->has_bps_wr_max_length) {
cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
}
if (arg->has_iops_max_length) {
cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
}
if (arg->has_iops_rd_max_length) {
cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
}
if (arg->has_iops_wr_max_length) {
cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
}
if (arg->has_iops_size) {
cfg.op_size = arg->iops_size;
}
if (!throttle_is_valid(&cfg, errp)) {
goto out;
}
if (throttle_enabled(&cfg)) {
/* Enable I/O limits if they're not enabled yet, otherwise
* just update the throttling group. */
if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
blk_io_limits_enable(blk,
arg->has_group ? arg->group :
arg->has_device ? arg->device :
arg->id);
} else if (arg->has_group) {
blk_io_limits_update_group(blk, arg->group);
}
/* Set the new throttling configuration */
blk_set_io_limits(blk, &cfg);
} else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
/* If all throttling settings are set to 0, disable I/O limits */
blk_io_limits_disable(blk);
}
out:
aio_context_release(aio_context);
}
void qmp_block_dirty_bitmap_add(const char *node, const char *name,
bool has_granularity, uint32_t granularity,
bool has_persistent, bool persistent,
@ -4595,62 +4092,6 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
aio_context_release(old_context);
}
void qmp_block_latency_histogram_set(
const char *id,
bool has_boundaries, uint64List *boundaries,
bool has_boundaries_read, uint64List *boundaries_read,
bool has_boundaries_write, uint64List *boundaries_write,
bool has_boundaries_flush, uint64List *boundaries_flush,
Error **errp)
{
BlockBackend *blk = qmp_get_blk(NULL, id, errp);
BlockAcctStats *stats;
int ret;
if (!blk) {
return;
}
stats = blk_get_stats(blk);
if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
!has_boundaries_flush)
{
block_latency_histograms_clear(stats);
return;
}
if (has_boundaries || has_boundaries_read) {
ret = block_latency_histogram_set(
stats, BLOCK_ACCT_READ,
has_boundaries_read ? boundaries_read : boundaries);
if (ret) {
error_setg(errp, "Device '%s' set read boundaries fail", id);
return;
}
}
if (has_boundaries || has_boundaries_write) {
ret = block_latency_histogram_set(
stats, BLOCK_ACCT_WRITE,
has_boundaries_write ? boundaries_write : boundaries);
if (ret) {
error_setg(errp, "Device '%s' set write boundaries fail", id);
return;
}
}
if (has_boundaries || has_boundaries_flush) {
ret = block_latency_histogram_set(
stats, BLOCK_ACCT_FLUSH,
has_boundaries_flush ? boundaries_flush : boundaries);
if (ret) {
error_setg(errp, "Device '%s' set flush boundaries fail", id);
return;
}
}
}
QemuOptsList qemu_common_drive_opts = {
.name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),

View File

@ -737,7 +737,13 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
if (qemu_opt_get_bool(opts, "mux", 0)) {
assert(permit_mux_mon);
monitor_init_hmp(chr, true);
monitor_init_hmp(chr, true, &err);
if (err) {
error_report_err(err);
object_unparent(OBJECT(chr));
chr = NULL;
goto out;
}
}
out:

2
configure vendored
View File

@ -6316,7 +6316,7 @@ tools=""
if test "$want_tools" = "yes" ; then
tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) qemu-edid\$(EXESUF) $tools"
if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
tools="qemu-nbd\$(EXESUF) $tools"
tools="qemu-nbd\$(EXESUF) qemu-storage-daemon\$(EXESUF) $tools"
fi
if [ "$ivshmem" = "yes" ]; then
tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools"

View File

@ -190,6 +190,11 @@ Use ``migrate-set-parameters`` instead.
Use ``migrate-set-parameters`` and ``query-migrate-parameters`` instead.
``object-add`` option ``props`` (since 5.0)
'''''''''''''''''''''''''''''''''''''''''''
Specify the properties for the object as top-level arguments instead.
``query-block`` result field ``dirty-bitmaps[i].status`` (since 4.0)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

View File

@ -3367,7 +3367,7 @@ int gdbserver_start(const char *device)
/* Initialize a monitor terminal for gdb */
mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
NULL, NULL, &error_abort);
monitor_init_hmp(mon_chr, false);
monitor_init_hmp(mon_chr, false, &error_abort);
} else {
qemu_chr_fe_deinit(&s->chr, true);
mon_chr = s->mon_chr;

View File

@ -18,6 +18,7 @@
#include "qapi/visitor.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qom/object_interfaces.h"
#include "hw/xen/xen_common.h"
#include "hw/block/xen_blkif.h"
#include "hw/qdev-properties.h"
@ -858,10 +859,18 @@ static XenBlockIOThread *xen_block_iothread_create(const char *id,
{
XenBlockIOThread *iothread = g_new(XenBlockIOThread, 1);
Error *local_err = NULL;
QDict *opts;
QObject *ret_data;
iothread->id = g_strdup(id);
qmp_object_add(TYPE_IOTHREAD, id, false, NULL, &local_err);
opts = qdict_new();
qdict_put_str(opts, "qom-type", TYPE_IOTHREAD);
qdict_put_str(opts, "id", id);
qmp_object_add(opts, &ret_data, &local_err);
qobject_unref(opts);
qobject_unref(ret_data);
if (local_err) {
error_propagate(errp, local_err);

View File

@ -122,6 +122,7 @@ struct BlockDriver {
int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp);
void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state);
void (*bdrv_reopen_commit_post)(BDRVReopenState *reopen_state);
void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state);
void (*bdrv_join_options)(QDict *options, QDict *old_options);

View File

@ -353,6 +353,7 @@ void nbd_client_put(NBDClient *client);
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
const char *tls_authz, Error **errp);
void nbd_server_start_options(NbdServerOptions *arg, Error **errp);
/* nbd_read
* Reads @size bytes from @ioc. Returns 0 on success.

View File

@ -7,6 +7,7 @@
extern __thread Monitor *cur_mon;
typedef struct MonitorHMP MonitorHMP;
typedef struct MonitorOptions MonitorOptions;
#define QMP_REQ_QUEUE_LEN_MAX 8
@ -16,8 +17,9 @@ bool monitor_cur_is_qmp(void);
void monitor_init_globals(void);
void monitor_init_globals_core(void);
void monitor_init_qmp(Chardev *chr, bool pretty);
void monitor_init_hmp(Chardev *chr, bool use_readline);
void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp);
void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp);
int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp);
int monitor_init_opts(QemuOpts *opts, Error **errp);
void monitor_cleanup(void);

View File

@ -162,4 +162,11 @@ void user_creatable_del(const char *id, Error **errp);
*/
void user_creatable_cleanup(void);
/**
* qmp_object_add:
*
* QMP command handler for object-add. See the QAPI schema for documentation.
*/
void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp);
#endif

View File

@ -24,6 +24,8 @@ enum {
QEMU_ARCH_NIOS2 = (1 << 17),
QEMU_ARCH_HPPA = (1 << 18),
QEMU_ARCH_RISCV = (1 << 19),
QEMU_ARCH_NONE = (1 << 31),
};
extern const uint32_t arch_type;

View File

@ -2,3 +2,5 @@ obj-y += misc.o
common-obj-y += monitor.o qmp.o hmp.o
common-obj-y += qmp-cmds.o qmp-cmds-control.o
common-obj-y += hmp-cmds.o
storage-daemon-obj-y += monitor.o qmp.o qmp-cmds-control.o

View File

@ -2341,6 +2341,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
Error *local_err = NULL;
BlockInfoList *block_list, *info;
SocketAddress *addr;
BlockExportNbd export;
if (writable && !all) {
error_setg(&local_err, "-w only valid together with -a");
@ -2373,8 +2374,13 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
continue;
}
qmp_nbd_server_add(info->value->device, false, NULL, false, NULL,
true, writable, false, NULL, &local_err);
export = (BlockExportNbd) {
.device = info->value->device,
.has_writable = true,
.writable = writable,
};
qmp_nbd_server_add(&export, &local_err);
if (local_err != NULL) {
qmp_nbd_server_stop(NULL);
@ -2395,8 +2401,15 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
bool writable = qdict_get_try_bool(qdict, "writable", false);
Error *local_err = NULL;
qmp_nbd_server_add(device, !!name, name, false, NULL, true, writable,
false, NULL, &local_err);
BlockExportNbd export = {
.device = (char *) device,
.has_name = !!name,
.name = (char *) name,
.has_writable = true,
.writable = writable,
};
qmp_nbd_server_add(&export, &local_err);
hmp_handle_error(mon, local_err);
}

View File

@ -1399,12 +1399,16 @@ static void monitor_readline_flush(void *opaque)
monitor_flush(&mon->common);
}
void monitor_init_hmp(Chardev *chr, bool use_readline)
void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp)
{
MonitorHMP *mon = g_new0(MonitorHMP, 1);
if (!qemu_chr_fe_init(&mon->common.chr, chr, errp)) {
g_free(mon);
return;
}
monitor_data_init(&mon->common, false, false, false);
qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
mon->use_readline = use_readline;
if (mon->use_readline) {

View File

@ -248,6 +248,8 @@ static void monitor_init_qmp_commands(void)
QCO_NO_OPTIONS);
qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
QCO_NO_OPTIONS);
qmp_register_command(&qmp_commands, "object-add", qmp_object_add,
QCO_NO_OPTIONS);
QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",

View File

@ -25,7 +25,9 @@
#include "qemu/osdep.h"
#include "monitor-internal.h"
#include "qapi/error.h"
#include "qapi/opts-visitor.h"
#include "qapi/qapi-emit-events.h"
#include "qapi/qapi-visit-control.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qemu/error-report.h"
@ -609,50 +611,68 @@ void monitor_init_globals_core(void)
NULL);
}
int monitor_init_opts(QemuOpts *opts, Error **errp)
int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp)
{
Chardev *chr;
bool qmp;
bool pretty = false;
const char *chardev;
const char *mode;
Error *local_err = NULL;
mode = qemu_opt_get(opts, "mode");
if (mode == NULL) {
mode = "readline";
}
if (strcmp(mode, "readline") == 0) {
qmp = false;
} else if (strcmp(mode, "control") == 0) {
qmp = true;
} else {
error_setg(errp, "unknown monitor mode \"%s\"", mode);
return -1;
}
if (!qmp && qemu_opt_get(opts, "pretty")) {
warn_report("'pretty' is deprecated for HMP monitors, it has no effect "
"and will be removed in future versions");
}
if (qemu_opt_get_bool(opts, "pretty", 0)) {
pretty = true;
}
chardev = qemu_opt_get(opts, "chardev");
if (!chardev) {
error_report("chardev is required");
exit(1);
}
chr = qemu_chr_find(chardev);
chr = qemu_chr_find(opts->chardev);
if (chr == NULL) {
error_setg(errp, "chardev \"%s\" not found", chardev);
error_setg(errp, "chardev \"%s\" not found", opts->chardev);
return -1;
}
if (qmp) {
monitor_init_qmp(chr, pretty);
} else {
monitor_init_hmp(chr, true);
if (!opts->has_mode) {
opts->mode = allow_hmp ? MONITOR_MODE_READLINE : MONITOR_MODE_CONTROL;
}
switch (opts->mode) {
case MONITOR_MODE_CONTROL:
monitor_init_qmp(chr, opts->pretty, &local_err);
break;
case MONITOR_MODE_READLINE:
if (!allow_hmp) {
error_setg(errp, "Only QMP is supported");
return -1;
}
if (opts->pretty) {
warn_report("'pretty' is deprecated for HMP monitors, it has no "
"effect and will be removed in future versions");
}
monitor_init_hmp(chr, true, &local_err);
break;
default:
g_assert_not_reached();
}
if (local_err) {
error_propagate(errp, local_err);
return -1;
}
return 0;
}
int monitor_init_opts(QemuOpts *opts, Error **errp)
{
Visitor *v;
MonitorOptions *options;
Error *local_err = NULL;
v = opts_visitor_new(opts);
visit_type_MonitorOptions(v, NULL, &options, &local_err);
visit_free(v);
if (local_err) {
goto out;
}
monitor_init(options, true, &local_err);
qapi_free_MonitorOptions(options);
out:
if (local_err) {
error_propagate(errp, local_err);
return -1;
}
return 0;
}

View File

@ -30,7 +30,7 @@
#include "sysemu/blockdev.h"
#include "sysemu/block-backend.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-block-core.h"
#include "qapi/qapi-commands-block.h"
#include "qapi/qapi-commands-control.h"
#include "qapi/qapi-commands-machine.h"
#include "qapi/qapi-commands-misc.h"

View File

@ -395,10 +395,16 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
monitor_list_append(&mon->common);
}
void monitor_init_qmp(Chardev *chr, bool pretty)
void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
{
MonitorQMP *mon = g_new0(MonitorQMP, 1);
if (!qemu_chr_fe_init(&mon->common.chr, chr, errp)) {
g_free(mon);
return;
}
qemu_chr_fe_set_echo(&mon->common.chr, true);
/* Note: we run QMP monitor in I/O thread when @chr supports that */
monitor_data_init(&mon->common, true, false,
qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
@ -408,9 +414,6 @@ void monitor_init_qmp(Chardev *chr, bool pretty)
qemu_mutex_init(&mon->qmp_queue_lock);
mon->qmp_requests = g_queue_new();
qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
qemu_chr_fe_set_echo(&mon->common.chr, true);
json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
if (mon->common.use_io_thread) {
/*

View File

@ -7,7 +7,7 @@ util-obj-y += qapi-util.o
QAPI_COMMON_MODULES = audio authz block-core block char common control crypto
QAPI_COMMON_MODULES += dump error introspect job machine migration misc
QAPI_COMMON_MODULES += net qdev qom rdma rocker run-state sockets tpm
QAPI_COMMON_MODULES += net pragma qdev qom rdma rocker run-state sockets tpm
QAPI_COMMON_MODULES += trace transaction ui
QAPI_TARGET_MODULES = machine-target misc-target
QAPI_MODULES = $(QAPI_COMMON_MODULES) $(QAPI_TARGET_MODULES)
@ -31,3 +31,8 @@ obj-y += qapi-events.o
obj-y += $(QAPI_TARGET_MODULES:%=qapi-commands-%.o)
obj-y += qapi-commands.o
obj-y += qapi-init-commands.o
QAPI_MODULES_STORAGE_DAEMON = block-core char common control crypto
QAPI_MODULES_STORAGE_DAEMON += introspect job qom sockets pragma transaction
storage-daemon-obj-y += $(QAPI_MODULES_STORAGE_DAEMON:%=qapi-commands-%.o)

View File

@ -563,78 +563,6 @@
{ 'struct': 'BlockLatencyHistogramInfo',
'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } }
##
# @block-latency-histogram-set:
#
# Manage read, write and flush latency histograms for the device.
#
# If only @id parameter is specified, remove all present latency histograms
# for the device. Otherwise, add/reset some of (or all) latency histograms.
#
# @id: The name or QOM path of the guest device.
#
# @boundaries: list of interval boundary values (see description in
# BlockLatencyHistogramInfo definition). If specified, all
# latency histograms are removed, and empty ones created for all
# io types with intervals corresponding to @boundaries (except for
# io types, for which specific boundaries are set through the
# following parameters).
#
# @boundaries-read: list of interval boundary values for read latency
# histogram. If specified, old read latency histogram is
# removed, and empty one created with intervals
# corresponding to @boundaries-read. The parameter has higher
# priority then @boundaries.
#
# @boundaries-write: list of interval boundary values for write latency
# histogram.
#
# @boundaries-flush: list of interval boundary values for flush latency
# histogram.
#
# Returns: error if device is not found or any boundary arrays are invalid.
#
# Since: 4.0
#
# Example: set new histograms for all io types with intervals
# [0, 10), [10, 50), [50, 100), [100, +inf):
#
# -> { "execute": "block-latency-histogram-set",
# "arguments": { "id": "drive0",
# "boundaries": [10, 50, 100] } }
# <- { "return": {} }
#
# Example: set new histogram only for write, other histograms will remain
# not changed (or not created):
#
# -> { "execute": "block-latency-histogram-set",
# "arguments": { "id": "drive0",
# "boundaries-write": [10, 50, 100] } }
# <- { "return": {} }
#
# Example: set new histograms with the following intervals:
# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf)
# write: [0, 1000), [1000, 5000), [5000, +inf)
#
# -> { "execute": "block-latency-histogram-set",
# "arguments": { "id": "drive0",
# "boundaries": [10, 50, 100],
# "boundaries-write": [1000, 5000] } }
# <- { "return": {} }
#
# Example: remove all latency histograms:
#
# -> { "execute": "block-latency-histogram-set",
# "arguments": { "id": "drive0" } }
# <- { "return": {} }
##
{ 'command': 'block-latency-histogram-set',
'data': {'id': 'str',
'*boundaries': ['uint64'],
'*boundaries-read': ['uint64'],
'*boundaries-write': ['uint64'],
'*boundaries-flush': ['uint64'] } }
##
# @BlockInfo:
#
@ -2356,78 +2284,6 @@
'*copy-mode': 'MirrorCopyMode',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
##
# @block_set_io_throttle:
#
# Change I/O throttle limits for a block drive.
#
# Since QEMU 2.4, each device with I/O limits is member of a throttle
# group.
#
# If two or more devices are members of the same group, the limits
# will apply to the combined I/O of the whole group in a round-robin
# fashion. Therefore, setting new I/O limits to a device will affect
# the whole group.
#
# The name of the group can be specified using the 'group' parameter.
# If the parameter is unset, it is assumed to be the current group of
# that device. If it's not in any group yet, the name of the device
# will be used as the name for its group.
#
# The 'group' parameter can also be used to move a device to a
# different group. In this case the limits specified in the parameters
# will be applied to the new group only.
#
# I/O limits can be disabled by setting all of them to 0. In this case
# the device will be removed from its group and the rest of its
# members will not be affected. The 'group' parameter is ignored.
#
# Returns: - Nothing on success
# - If @device is not a valid block device, DeviceNotFound
#
# Since: 1.1
#
# Example:
#
# -> { "execute": "block_set_io_throttle",
# "arguments": { "id": "virtio-blk-pci0/virtio-backend",
# "bps": 0,
# "bps_rd": 0,
# "bps_wr": 0,
# "iops": 512,
# "iops_rd": 0,
# "iops_wr": 0,
# "bps_max": 0,
# "bps_rd_max": 0,
# "bps_wr_max": 0,
# "iops_max": 0,
# "iops_rd_max": 0,
# "iops_wr_max": 0,
# "bps_max_length": 0,
# "iops_size": 0 } }
# <- { "return": {} }
#
# -> { "execute": "block_set_io_throttle",
# "arguments": { "id": "ide0-1-0",
# "bps": 1000000,
# "bps_rd": 0,
# "bps_wr": 0,
# "iops": 0,
# "iops_rd": 0,
# "iops_wr": 0,
# "bps_max": 8000000,
# "bps_rd_max": 0,
# "bps_wr_max": 0,
# "iops_max": 0,
# "iops_rd_max": 0,
# "iops_wr_max": 0,
# "bps_max_length": 60,
# "iops_size": 0 } }
# <- { "return": {} }
##
{ 'command': 'block_set_io_throttle', 'boxed': true,
'data': 'BlockIOThrottle' }
##
# @BlockIOThrottle:
#
@ -3688,6 +3544,8 @@
#
# @pool: Ceph pool name.
#
# @namespace: Rados namespace name in the Ceph pool. (Since 5.0)
#
# @image: Image name in the Ceph pool.
#
# @conf: path to Ceph configuration file. Values
@ -3714,6 +3572,7 @@
##
{ 'struct': 'BlockdevOptionsRbd',
'data': { 'pool': 'str',
'*namespace': 'str',
'image': 'str',
'*conf': 'str',
'*snapshot': 'str',
@ -4757,248 +4616,6 @@
'data': { 'job-id': 'str',
'options': 'BlockdevCreateOptions' } }
##
# @blockdev-open-tray:
#
# Opens a block device's tray. If there is a block driver state tree inserted as
# a medium, it will become inaccessible to the guest (but it will remain
# associated to the block device, so closing the tray will make it accessible
# again).
#
# If the tray was already open before, this will be a no-op.
#
# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in
# which no such event will be generated, these include:
#
# - if the guest has locked the tray, @force is false and the guest does not
# respond to the eject request
# - if the BlockBackend denoted by @device does not have a guest device attached
# to it
# - if the guest device does not have an actual tray
#
# @device: Block device name (deprecated, use @id instead)
#
# @id: The name or QOM path of the guest device (since: 2.8)
#
# @force: if false (the default), an eject request will be sent to
# the guest if it has locked the tray (and the tray will not be opened
# immediately); if true, the tray will be opened regardless of whether
# it is locked
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "blockdev-open-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751016,
# "microseconds": 716996 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": true } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-open-tray',
'data': { '*device': 'str',
'*id': 'str',
'*force': 'bool' } }
##
# @blockdev-close-tray:
#
# Closes a block device's tray. If there is a block driver state tree associated
# with the block device (which is currently ejected), that tree will be loaded
# as the medium.
#
# If the tray was already closed before, this will be a no-op.
#
# @device: Block device name (deprecated, use @id instead)
#
# @id: The name or QOM path of the guest device (since: 2.8)
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "blockdev-close-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751345,
# "microseconds": 272147 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": false } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-close-tray',
'data': { '*device': 'str',
'*id': 'str' } }
##
# @blockdev-remove-medium:
#
# Removes a medium (a block driver state tree) from a block device. That block
# device's tray must currently be open (unless there is no attached guest
# device).
#
# If the tray is open and there is no medium inserted, this will be a no-op.
#
# @id: The name or QOM path of the guest device
#
# Since: 2.12
#
# Example:
#
# -> { "execute": "blockdev-remove-medium",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "error": { "class": "GenericError",
# "desc": "Tray of device 'ide0-1-0' is not open" } }
#
# -> { "execute": "blockdev-open-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751627,
# "microseconds": 549958 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": true } }
#
# <- { "return": {} }
#
# -> { "execute": "blockdev-remove-medium",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-remove-medium',
'data': { 'id': 'str' } }
##
# @blockdev-insert-medium:
#
# Inserts a medium (a block driver state tree) into a block device. That block
# device's tray must currently be open (unless there is no attached guest
# device) and there must be no medium inserted already.
#
# @id: The name or QOM path of the guest device
#
# @node-name: name of a node in the block driver state graph
#
# Since: 2.12
#
# Example:
#
# -> { "execute": "blockdev-add",
# "arguments": {
# "node-name": "node0",
# "driver": "raw",
# "file": { "driver": "file",
# "filename": "fedora.iso" } } }
# <- { "return": {} }
#
# -> { "execute": "blockdev-insert-medium",
# "arguments": { "id": "ide0-1-0",
# "node-name": "node0" } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-insert-medium',
'data': { 'id': 'str',
'node-name': 'str'} }
##
# @BlockdevChangeReadOnlyMode:
#
# Specifies the new read-only mode of a block device subject to the
# @blockdev-change-medium command.
#
# @retain: Retains the current read-only mode
#
# @read-only: Makes the device read-only
#
# @read-write: Makes the device writable
#
# Since: 2.3
#
##
{ 'enum': 'BlockdevChangeReadOnlyMode',
'data': ['retain', 'read-only', 'read-write'] }
##
# @blockdev-change-medium:
#
# Changes the medium inserted into a block device by ejecting the current medium
# and loading a new image file which is inserted as the new medium (this command
# combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium
# and blockdev-close-tray).
#
# @device: Block device name (deprecated, use @id instead)
#
# @id: The name or QOM path of the guest device
# (since: 2.8)
#
# @filename: filename of the new image to be loaded
#
# @format: format to open the new image with (defaults to
# the probed format)
#
# @read-only-mode: change the read-only mode of the device; defaults
# to 'retain'
#
# Since: 2.5
#
# Examples:
#
# 1. Change a removable medium
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "ide0-1-0",
# "filename": "/srv/images/Fedora-12-x86_64-DVD.iso",
# "format": "raw" } }
# <- { "return": {} }
#
# 2. Load a read-only medium into a writable drive
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "floppyA",
# "filename": "/srv/images/ro.img",
# "format": "raw",
# "read-only-mode": "retain" } }
#
# <- { "error":
# { "class": "GenericError",
# "desc": "Could not open '/srv/images/ro.img': Permission denied" } }
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "floppyA",
# "filename": "/srv/images/ro.img",
# "format": "raw",
# "read-only-mode": "read-only" } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-change-medium',
'data': { '*device': 'str',
'*id': 'str',
'filename': 'str',
'*format': 'str',
'*read-only-mode': 'BlockdevChangeReadOnlyMode' } }
##
# @BlockErrorAction:
#
@ -5447,3 +5064,347 @@
'data' : { 'node-name': 'str',
'iothread': 'StrOrNull',
'*force': 'bool' } }
##
# @NbdServerOptions:
#
# @addr: Address on which to listen.
# @tls-creds: ID of the TLS credentials object (since 2.6).
# @tls-authz: ID of the QAuthZ authorization object used to validate
# the client's x509 distinguished name. This object is
# is only resolved at time of use, so can be deleted and
# recreated on the fly while the NBD server is active.
# If missing, it will default to denying access (since 4.0).
#
# Keep this type consistent with the nbd-server-start arguments. The only
# intended difference is using SocketAddress instead of SocketAddressLegacy.
#
# Since: 4.2
##
{ 'struct': 'NbdServerOptions',
'data': { 'addr': 'SocketAddress',
'*tls-creds': 'str',
'*tls-authz': 'str'} }
##
# @nbd-server-start:
#
# Start an NBD server listening on the given host and port. Block
# devices can then be exported using @nbd-server-add. The NBD
# server will present them as named exports; for example, another
# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
#
# @addr: Address on which to listen.
# @tls-creds: ID of the TLS credentials object (since 2.6).
# @tls-authz: ID of the QAuthZ authorization object used to validate
# the client's x509 distinguished name. This object is
# is only resolved at time of use, so can be deleted and
# recreated on the fly while the NBD server is active.
# If missing, it will default to denying access (since 4.0).
#
# Returns: error if the server is already running.
#
# Keep this type consistent with the NbdServerOptions type. The only intended
# difference is using SocketAddressLegacy instead of SocketAddress.
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-start',
'data': { 'addr': 'SocketAddressLegacy',
'*tls-creds': 'str',
'*tls-authz': 'str'} }
##
# @BlockExportNbd:
#
# An NBD block export.
#
# @device: The device name or node name of the node to be exported
#
# @name: Export name. If unspecified, the @device parameter is used as the
# export name. (Since 2.12)
#
# @description: Free-form description of the export, up to 4096 bytes.
# (Since 5.0)
#
# @writable: Whether clients should be able to write to the device via the
# NBD connection (default false).
#
# @bitmap: Also export the dirty bitmap reachable from @device, so the
# NBD client can use NBD_OPT_SET_META_CONTEXT with
# "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0)
#
# Since: 5.0
##
{ 'struct': 'BlockExportNbd',
'data': {'device': 'str', '*name': 'str', '*description': 'str',
'*writable': 'bool', '*bitmap': 'str' } }
##
# @nbd-server-add:
#
# Export a block node to QEMU's embedded NBD server.
#
# Returns: error if the server is not running, or export with the same name
# already exists.
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-add',
'data': 'BlockExportNbd', 'boxed': true }
##
# @NbdServerRemoveMode:
#
# Mode for removing an NBD export.
#
# @safe: Remove export if there are no existing connections, fail otherwise.
#
# @hard: Drop all connections immediately and remove export.
#
# Potential additional modes to be added in the future:
#
# hide: Just hide export from new clients, leave existing connections as is.
# Remove export after all clients are disconnected.
#
# soft: Hide export from new clients, answer with ESHUTDOWN for all further
# requests from existing clients.
#
# Since: 2.12
##
{'enum': 'NbdServerRemoveMode', 'data': ['safe', 'hard']}
##
# @nbd-server-remove:
#
# Remove NBD export by name.
#
# @name: Export name.
#
# @mode: Mode of command operation. See @NbdServerRemoveMode description.
# Default is 'safe'.
#
# Returns: error if
# - the server is not running
# - export is not found
# - mode is 'safe' and there are existing connections
#
# Since: 2.12
##
{ 'command': 'nbd-server-remove',
'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} }
##
# @nbd-server-stop:
#
# Stop QEMU's embedded NBD server, and unregister all devices previously
# added via @nbd-server-add.
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-stop' }
##
# @BlockExportType:
#
# An enumeration of block export types
#
# @nbd: NBD export
#
# Since: 4.2
##
{ 'enum': 'BlockExportType',
'data': [ 'nbd' ] }
##
# @BlockExport:
#
# Describes a block export, i.e. how single node should be exported on an
# external interface.
#
# Since: 4.2
##
{ 'union': 'BlockExport',
'base': { 'type': 'BlockExportType' },
'discriminator': 'type',
'data': {
'nbd': 'BlockExportNbd'
} }
##
# @QuorumOpType:
#
# An enumeration of the quorum operation types
#
# @read: read operation
#
# @write: write operation
#
# @flush: flush operation
#
# Since: 2.6
##
{ 'enum': 'QuorumOpType',
'data': [ 'read', 'write', 'flush' ] }
##
# @QUORUM_FAILURE:
#
# Emitted by the Quorum block driver if it fails to establish a quorum
#
# @reference: device name if defined else node name
#
# @sector-num: number of the first sector of the failed read operation
#
# @sectors-count: failed read operation sector count
#
# Note: This event is rate-limited.
#
# Since: 2.0
#
# Example:
#
# <- { "event": "QUORUM_FAILURE",
# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
#
##
{ 'event': 'QUORUM_FAILURE',
'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
##
# @QUORUM_REPORT_BAD:
#
# Emitted to report a corruption of a Quorum file
#
# @type: quorum operation type (Since 2.6)
#
# @error: error message. Only present on failure. This field
# contains a human-readable error message. There are no semantics other
# than that the block layer reported an error and clients should not
# try to interpret the error string.
#
# @node-name: the graph node name of the block driver state
#
# @sector-num: number of the first sector of the failed read operation
#
# @sectors-count: failed read operation sector count
#
# Note: This event is rate-limited.
#
# Since: 2.0
#
# Example:
#
# 1. Read operation
#
# { "event": "QUORUM_REPORT_BAD",
# "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
# "type": "read" },
# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
#
# 2. Flush operation
#
# { "event": "QUORUM_REPORT_BAD",
# "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
# "type": "flush", "error": "Broken pipe" },
# "timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
#
##
{ 'event': 'QUORUM_REPORT_BAD',
'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str',
'sector-num': 'int', 'sectors-count': 'int' } }
##
# @BlockdevSnapshotInternal:
#
# @device: the device name or node-name of a root node to generate the snapshot
# from
#
# @name: the name of the internal snapshot to be created
#
# Notes: In transaction, if @name is empty, or any snapshot matching @name
# exists, the operation will fail. Only some image formats support it,
# for example, qcow2, rbd, and sheepdog.
#
# Since: 1.7
##
{ 'struct': 'BlockdevSnapshotInternal',
'data': { 'device': 'str', 'name': 'str' } }
##
# @blockdev-snapshot-internal-sync:
#
# Synchronously take an internal snapshot of a block device, when the
# format of the image used supports it. If the name is an empty
# string, or a snapshot with name already exists, the operation will
# fail.
#
# For the arguments, see the documentation of BlockdevSnapshotInternal.
#
# Returns: - nothing on success
# - If @device is not a valid block device, GenericError
# - If any snapshot matching @name exists, or @name is empty,
# GenericError
# - If the format of the image used does not support it,
# BlockFormatFeatureNotSupported
#
# Since: 1.7
#
# Example:
#
# -> { "execute": "blockdev-snapshot-internal-sync",
# "arguments": { "device": "ide-hd0",
# "name": "snapshot0" }
# }
# <- { "return": {} }
#
##
{ 'command': 'blockdev-snapshot-internal-sync',
'data': 'BlockdevSnapshotInternal' }
##
# @blockdev-snapshot-delete-internal-sync:
#
# Synchronously delete an internal snapshot of a block device, when the format
# of the image used support it. The snapshot is identified by name or id or
# both. One of the name or id is required. Return SnapshotInfo for the
# successfully deleted snapshot.
#
# @device: the device name or node-name of a root node to delete the snapshot
# from
#
# @id: optional the snapshot's ID to be deleted
#
# @name: optional the snapshot's name to be deleted
#
# Returns: - SnapshotInfo on success
# - If @device is not a valid block device, GenericError
# - If snapshot not found, GenericError
# - If the format of the image used does not support it,
# BlockFormatFeatureNotSupported
# - If @id and @name are both not specified, GenericError
#
# Since: 1.7
#
# Example:
#
# -> { "execute": "blockdev-snapshot-delete-internal-sync",
# "arguments": { "device": "ide-hd0",
# "name": "snapshot0" }
# }
# <- { "return": {
# "id": "1",
# "name": "snapshot0",
# "vm-state-size": 0,
# "date-sec": 1000012,
# "date-nsec": 10,
# "vm-clock-sec": 100,
# "vm-clock-nsec": 20
# }
# }
#
##
{ 'command': 'blockdev-snapshot-delete-internal-sync',
'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
'returns': 'SnapshotInfo' }

View File

@ -60,23 +60,6 @@
{ 'enum': 'FloppyDriveType',
'data': ['144', '288', '120', 'none', 'auto']}
##
# @BlockdevSnapshotInternal:
#
# @device: the device name or node-name of a root node to generate the snapshot
# from
#
# @name: the name of the internal snapshot to be created
#
# Notes: In transaction, if @name is empty, or any snapshot matching @name
# exists, the operation will fail. Only some image formats support it,
# for example, qcow2, rbd, and sheepdog.
#
# Since: 1.7
##
{ 'struct': 'BlockdevSnapshotInternal',
'data': { 'device': 'str', 'name': 'str' } }
##
# @PRManagerInfo:
#
@ -104,84 +87,6 @@
{ 'command': 'query-pr-managers', 'returns': ['PRManagerInfo'],
'allow-preconfig': true }
##
# @blockdev-snapshot-internal-sync:
#
# Synchronously take an internal snapshot of a block device, when the
# format of the image used supports it. If the name is an empty
# string, or a snapshot with name already exists, the operation will
# fail.
#
# For the arguments, see the documentation of BlockdevSnapshotInternal.
#
# Returns: - nothing on success
# - If @device is not a valid block device, GenericError
# - If any snapshot matching @name exists, or @name is empty,
# GenericError
# - If the format of the image used does not support it,
# BlockFormatFeatureNotSupported
#
# Since: 1.7
#
# Example:
#
# -> { "execute": "blockdev-snapshot-internal-sync",
# "arguments": { "device": "ide-hd0",
# "name": "snapshot0" }
# }
# <- { "return": {} }
#
##
{ 'command': 'blockdev-snapshot-internal-sync',
'data': 'BlockdevSnapshotInternal' }
##
# @blockdev-snapshot-delete-internal-sync:
#
# Synchronously delete an internal snapshot of a block device, when the format
# of the image used support it. The snapshot is identified by name or id or
# both. One of the name or id is required. Return SnapshotInfo for the
# successfully deleted snapshot.
#
# @device: the device name or node-name of a root node to delete the snapshot
# from
#
# @id: optional the snapshot's ID to be deleted
#
# @name: optional the snapshot's name to be deleted
#
# Returns: - SnapshotInfo on success
# - If @device is not a valid block device, GenericError
# - If snapshot not found, GenericError
# - If the format of the image used does not support it,
# BlockFormatFeatureNotSupported
# - If @id and @name are both not specified, GenericError
#
# Since: 1.7
#
# Example:
#
# -> { "execute": "blockdev-snapshot-delete-internal-sync",
# "arguments": { "device": "ide-hd0",
# "name": "snapshot0" }
# }
# <- { "return": {
# "id": "1",
# "name": "snapshot0",
# "vm-state-size": 0,
# "date-sec": 1000012,
# "date-nsec": 10,
# "vm-clock-sec": 100,
# "vm-clock-nsec": 20
# }
# }
#
##
{ 'command': 'blockdev-snapshot-delete-internal-sync',
'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
'returns': 'SnapshotInfo' }
##
# @eject:
#
@ -211,109 +116,246 @@
'*force': 'bool' } }
##
# @nbd-server-start:
# @blockdev-open-tray:
#
# Start an NBD server listening on the given host and port. Block
# devices can then be exported using @nbd-server-add. The NBD
# server will present them as named exports; for example, another
# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
# Opens a block device's tray. If there is a block driver state tree inserted as
# a medium, it will become inaccessible to the guest (but it will remain
# associated to the block device, so closing the tray will make it accessible
# again).
#
# @addr: Address on which to listen.
# @tls-creds: ID of the TLS credentials object (since 2.6).
# @tls-authz: ID of the QAuthZ authorization object used to validate
# the client's x509 distinguished name. This object is
# is only resolved at time of use, so can be deleted and
# recreated on the fly while the NBD server is active.
# If missing, it will default to denying access (since 4.0).
# If the tray was already open before, this will be a no-op.
#
# Returns: error if the server is already running.
# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in
# which no such event will be generated, these include:
#
# - if the guest has locked the tray, @force is false and the guest does not
# respond to the eject request
# - if the BlockBackend denoted by @device does not have a guest device attached
# to it
# - if the guest device does not have an actual tray
#
# @device: Block device name (deprecated, use @id instead)
#
# @id: The name or QOM path of the guest device (since: 2.8)
#
# @force: if false (the default), an eject request will be sent to
# the guest if it has locked the tray (and the tray will not be opened
# immediately); if true, the tray will be opened regardless of whether
# it is locked
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "blockdev-open-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751016,
# "microseconds": 716996 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": true } }
#
# <- { "return": {} }
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-start',
'data': { 'addr': 'SocketAddressLegacy',
'*tls-creds': 'str',
'*tls-authz': 'str'} }
{ 'command': 'blockdev-open-tray',
'data': { '*device': 'str',
'*id': 'str',
'*force': 'bool' } }
##
# @nbd-server-add:
# @blockdev-close-tray:
#
# Export a block node to QEMU's embedded NBD server.
# Closes a block device's tray. If there is a block driver state tree associated
# with the block device (which is currently ejected), that tree will be loaded
# as the medium.
#
# @device: The device name or node name of the node to be exported
# If the tray was already closed before, this will be a no-op.
#
# @name: Export name. If unspecified, the @device parameter is used as the
# export name. (Since 2.12)
# @device: Block device name (deprecated, use @id instead)
#
# @description: Free-form description of the export, up to 4096 bytes.
# (Since 5.0)
# @id: The name or QOM path of the guest device (since: 2.8)
#
# @writable: Whether clients should be able to write to the device via the
# NBD connection (default false).
# Since: 2.5
#
# @bitmap: Also export the dirty bitmap reachable from @device, so the
# NBD client can use NBD_OPT_SET_META_CONTEXT with
# "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0)
# Example:
#
# Returns: error if the server is not running, or export with the same name
# already exists.
# -> { "execute": "blockdev-close-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751345,
# "microseconds": 272147 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": false } }
#
# <- { "return": {} }
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-add',
'data': {'device': 'str', '*name': 'str', '*description': 'str',
'*writable': 'bool', '*bitmap': 'str' } }
{ 'command': 'blockdev-close-tray',
'data': { '*device': 'str',
'*id': 'str' } }
##
# @NbdServerRemoveMode:
# @blockdev-remove-medium:
#
# Mode for removing an NBD export.
# Removes a medium (a block driver state tree) from a block device. That block
# device's tray must currently be open (unless there is no attached guest
# device).
#
# @safe: Remove export if there are no existing connections, fail otherwise.
# If the tray is open and there is no medium inserted, this will be a no-op.
#
# @hard: Drop all connections immediately and remove export.
#
# Potential additional modes to be added in the future:
#
# hide: Just hide export from new clients, leave existing connections as is.
# Remove export after all clients are disconnected.
#
# soft: Hide export from new clients, answer with ESHUTDOWN for all further
# requests from existing clients.
# @id: The name or QOM path of the guest device
#
# Since: 2.12
#
# Example:
#
# -> { "execute": "blockdev-remove-medium",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "error": { "class": "GenericError",
# "desc": "Tray of device 'ide0-1-0' is not open" } }
#
# -> { "execute": "blockdev-open-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751627,
# "microseconds": 549958 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": true } }
#
# <- { "return": {} }
#
# -> { "execute": "blockdev-remove-medium",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "return": {} }
#
##
{'enum': 'NbdServerRemoveMode', 'data': ['safe', 'hard']}
{ 'command': 'blockdev-remove-medium',
'data': { 'id': 'str' } }
##
# @nbd-server-remove:
# @blockdev-insert-medium:
#
# Remove NBD export by name.
# Inserts a medium (a block driver state tree) into a block device. That block
# device's tray must currently be open (unless there is no attached guest
# device) and there must be no medium inserted already.
#
# @name: Export name.
# @id: The name or QOM path of the guest device
#
# @mode: Mode of command operation. See @NbdServerRemoveMode description.
# Default is 'safe'.
#
# Returns: error if
# - the server is not running
# - export is not found
# - mode is 'safe' and there are existing connections
# @node-name: name of a node in the block driver state graph
#
# Since: 2.12
#
# Example:
#
# -> { "execute": "blockdev-add",
# "arguments": {
# "node-name": "node0",
# "driver": "raw",
# "file": { "driver": "file",
# "filename": "fedora.iso" } } }
# <- { "return": {} }
#
# -> { "execute": "blockdev-insert-medium",
# "arguments": { "id": "ide0-1-0",
# "node-name": "node0" } }
#
# <- { "return": {} }
#
##
{ 'command': 'nbd-server-remove',
'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} }
{ 'command': 'blockdev-insert-medium',
'data': { 'id': 'str',
'node-name': 'str'} }
##
# @nbd-server-stop:
# @BlockdevChangeReadOnlyMode:
#
# Stop QEMU's embedded NBD server, and unregister all devices previously
# added via @nbd-server-add.
# Specifies the new read-only mode of a block device subject to the
# @blockdev-change-medium command.
#
# @retain: Retains the current read-only mode
#
# @read-only: Makes the device read-only
#
# @read-write: Makes the device writable
#
# Since: 2.3
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-stop' }
{ 'enum': 'BlockdevChangeReadOnlyMode',
'data': ['retain', 'read-only', 'read-write'] }
##
# @blockdev-change-medium:
#
# Changes the medium inserted into a block device by ejecting the current medium
# and loading a new image file which is inserted as the new medium (this command
# combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium
# and blockdev-close-tray).
#
# @device: Block device name (deprecated, use @id instead)
#
# @id: The name or QOM path of the guest device
# (since: 2.8)
#
# @filename: filename of the new image to be loaded
#
# @format: format to open the new image with (defaults to
# the probed format)
#
# @read-only-mode: change the read-only mode of the device; defaults
# to 'retain'
#
# Since: 2.5
#
# Examples:
#
# 1. Change a removable medium
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "ide0-1-0",
# "filename": "/srv/images/Fedora-12-x86_64-DVD.iso",
# "format": "raw" } }
# <- { "return": {} }
#
# 2. Load a read-only medium into a writable drive
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "floppyA",
# "filename": "/srv/images/ro.img",
# "format": "raw",
# "read-only-mode": "retain" } }
#
# <- { "error":
# { "class": "GenericError",
# "desc": "Could not open '/srv/images/ro.img': Permission denied" } }
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "floppyA",
# "filename": "/srv/images/ro.img",
# "format": "raw",
# "read-only-mode": "read-only" } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-change-medium',
'data': { '*device': 'str',
'*id': 'str',
'filename': 'str',
'*format': 'str',
'*read-only-mode': 'BlockdevChangeReadOnlyMode' } }
##
# @DEVICE_TRAY_MOVED:
@ -369,85 +411,145 @@
'data': { 'id': 'str', 'connected': 'bool' } }
##
# @QuorumOpType:
# @block_set_io_throttle:
#
# An enumeration of the quorum operation types
# Change I/O throttle limits for a block drive.
#
# @read: read operation
# Since QEMU 2.4, each device with I/O limits is member of a throttle
# group.
#
# @write: write operation
# If two or more devices are members of the same group, the limits
# will apply to the combined I/O of the whole group in a round-robin
# fashion. Therefore, setting new I/O limits to a device will affect
# the whole group.
#
# @flush: flush operation
# The name of the group can be specified using the 'group' parameter.
# If the parameter is unset, it is assumed to be the current group of
# that device. If it's not in any group yet, the name of the device
# will be used as the name for its group.
#
# Since: 2.6
##
{ 'enum': 'QuorumOpType',
'data': [ 'read', 'write', 'flush' ] }
##
# @QUORUM_FAILURE:
# The 'group' parameter can also be used to move a device to a
# different group. In this case the limits specified in the parameters
# will be applied to the new group only.
#
# Emitted by the Quorum block driver if it fails to establish a quorum
# I/O limits can be disabled by setting all of them to 0. In this case
# the device will be removed from its group and the rest of its
# members will not be affected. The 'group' parameter is ignored.
#
# @reference: device name if defined else node name
# Returns: - Nothing on success
# - If @device is not a valid block device, DeviceNotFound
#
# @sector-num: number of the first sector of the failed read operation
#
# @sectors-count: failed read operation sector count
#
# Note: This event is rate-limited.
#
# Since: 2.0
# Since: 1.1
#
# Example:
#
# <- { "event": "QUORUM_FAILURE",
# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
# -> { "execute": "block_set_io_throttle",
# "arguments": { "id": "virtio-blk-pci0/virtio-backend",
# "bps": 0,
# "bps_rd": 0,
# "bps_wr": 0,
# "iops": 512,
# "iops_rd": 0,
# "iops_wr": 0,
# "bps_max": 0,
# "bps_rd_max": 0,
# "bps_wr_max": 0,
# "iops_max": 0,
# "iops_rd_max": 0,
# "iops_wr_max": 0,
# "bps_max_length": 0,
# "iops_size": 0 } }
# <- { "return": {} }
#
# -> { "execute": "block_set_io_throttle",
# "arguments": { "id": "ide0-1-0",
# "bps": 1000000,
# "bps_rd": 0,
# "bps_wr": 0,
# "iops": 0,
# "iops_rd": 0,
# "iops_wr": 0,
# "bps_max": 8000000,
# "bps_rd_max": 0,
# "bps_wr_max": 0,
# "iops_max": 0,
# "iops_rd_max": 0,
# "iops_wr_max": 0,
# "bps_max_length": 60,
# "iops_size": 0 } }
# <- { "return": {} }
##
{ 'event': 'QUORUM_FAILURE',
'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
{ 'command': 'block_set_io_throttle', 'boxed': true,
'data': 'BlockIOThrottle' }
##
# @QUORUM_REPORT_BAD:
# @block-latency-histogram-set:
#
# Emitted to report a corruption of a Quorum file
# Manage read, write and flush latency histograms for the device.
#
# @type: quorum operation type (Since 2.6)
# If only @id parameter is specified, remove all present latency histograms
# for the device. Otherwise, add/reset some of (or all) latency histograms.
#
# @error: error message. Only present on failure. This field
# contains a human-readable error message. There are no semantics other
# than that the block layer reported an error and clients should not
# try to interpret the error string.
# @id: The name or QOM path of the guest device.
#
# @node-name: the graph node name of the block driver state
# @boundaries: list of interval boundary values (see description in
# BlockLatencyHistogramInfo definition). If specified, all
# latency histograms are removed, and empty ones created for all
# io types with intervals corresponding to @boundaries (except for
# io types, for which specific boundaries are set through the
# following parameters).
#
# @sector-num: number of the first sector of the failed read operation
# @boundaries-read: list of interval boundary values for read latency
# histogram. If specified, old read latency histogram is
# removed, and empty one created with intervals
# corresponding to @boundaries-read. The parameter has higher
# priority then @boundaries.
#
# @sectors-count: failed read operation sector count
# @boundaries-write: list of interval boundary values for write latency
# histogram.
#
# Note: This event is rate-limited.
# @boundaries-flush: list of interval boundary values for flush latency
# histogram.
#
# Since: 2.0
# Returns: error if device is not found or any boundary arrays are invalid.
#
# Example:
# Since: 4.0
#
# 1. Read operation
# Example: set new histograms for all io types with intervals
# [0, 10), [10, 50), [50, 100), [100, +inf):
#
# { "event": "QUORUM_REPORT_BAD",
# "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
# "type": "read" },
# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
# -> { "execute": "block-latency-histogram-set",
# "arguments": { "id": "drive0",
# "boundaries": [10, 50, 100] } }
# <- { "return": {} }
#
# 2. Flush operation
# Example: set new histogram only for write, other histograms will remain
# not changed (or not created):
#
# { "event": "QUORUM_REPORT_BAD",
# "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
# "type": "flush", "error": "Broken pipe" },
# "timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
# -> { "execute": "block-latency-histogram-set",
# "arguments": { "id": "drive0",
# "boundaries-write": [10, 50, 100] } }
# <- { "return": {} }
#
# Example: set new histograms with the following intervals:
# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf)
# write: [0, 1000), [1000, 5000), [5000, +inf)
#
# -> { "execute": "block-latency-histogram-set",
# "arguments": { "id": "drive0",
# "boundaries": [10, 50, 100],
# "boundaries-write": [1000, 5000] } }
# <- { "return": {} }
#
# Example: remove all latency histograms:
#
# -> { "execute": "block-latency-histogram-set",
# "arguments": { "id": "drive0" } }
# <- { "return": {} }
##
{ 'event': 'QUORUM_REPORT_BAD',
'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str',
'sector-num': 'int', 'sectors-count': 'int' } }
{ 'command': 'block-latency-histogram-set',
'data': {'id': 'str',
'*boundaries': ['uint64'],
'*boundaries-read': ['uint64'],
'*boundaries-write': ['uint64'],
'*boundaries-flush': ['uint64'] } }

View File

@ -216,3 +216,40 @@
# <- { "return": {} }
##
{ 'command': 'quit' }
##
# @MonitorMode:
#
# An enumeration of monitor modes.
#
# @readline: HMP monitor (human-oriented command line interface)
#
# @control: QMP monitor (JSON-based machine interface)
#
# Since: 5.0
##
{ 'enum': 'MonitorMode', 'data': [ 'readline', 'control' ] }
##
# @MonitorOptions:
#
# Options to be used for adding a new monitor.
#
# @id: Name of the monitor
#
# @mode: Selects the monitor mode (default: readline in the system
# emulator, control in qemu-storage-daemon)
#
# @pretty: Enables pretty printing (QMP only)
#
# @chardev: Name of a character device to expose the monitor on
#
# Since: 5.0
##
{ 'struct': 'MonitorOptions',
'data': {
'*id': 'str',
'*mode': 'MonitorMode',
'*pretty': 'bool',
'chardev': 'str'
} }

24
qapi/pragma.json Normal file
View File

@ -0,0 +1,24 @@
{ 'pragma': { 'doc-required': true } }
# Whitelists to permit QAPI rule violations; think twice before you
# add to them!
{ 'pragma': {
# Commands allowed to return a non-dictionary:
'returns-whitelist': [
'human-monitor-command',
'qom-get',
'query-migrate-cache-size',
'query-tpm-models',
'query-tpm-types',
'ringbuf-read' ],
'name-case-whitelist': [
'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
'CpuInfoMIPS', # PC, visible through query-cpu
'CpuInfoTricore', # PC, visible through query-cpu
'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings
'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings
'QapiErrorClass', # all members, visible through errors
'UuidInfo', # UUID, visible through query-uuid
'X86CPURegister32', # all members, visible indirectly through qom-get
'CpuInfo' # CPU, visible through query-cpu
] } }

View File

@ -49,30 +49,7 @@
#
##
{ 'pragma': { 'doc-required': true } }
# Whitelists to permit QAPI rule violations; think twice before you
# add to them!
{ 'pragma': {
# Commands allowed to return a non-dictionary:
'returns-whitelist': [
'human-monitor-command',
'qom-get',
'query-migrate-cache-size',
'query-tpm-models',
'query-tpm-types',
'ringbuf-read' ],
'name-case-whitelist': [
'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
'CpuInfoMIPS', # PC, visible through query-cpu
'CpuInfoTricore', # PC, visible through query-cpu
'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings
'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings
'QapiErrorClass', # all members, visible through errors
'UuidInfo', # UUID, visible through query-uuid
'X86CPURegister32', # all members, visible indirectly through qom-get
'CpuInfo' # CPU, visible through query-cpu
] } }
{ 'include': 'pragma.json' }
# Documentation generated with qapi-gen.py is in source order, with
# included sub-schemas inserted at the first include directive

View File

@ -210,7 +210,12 @@
#
# @id: the name of the new object
#
# @props: a dictionary of properties to be passed to the backend
# @props: a dictionary of properties to be passed to the backend. Deprecated
# since 5.0, specify the properties on the top level instead. It is an
# error to specify the same option both on the top level and in @props.
#
# Additional arguments depend on qom-type and are passed to the backend
# unchanged.
#
# Returns: Nothing on success
# Error if @qom-type is not a valid class name
@ -221,12 +226,13 @@
#
# -> { "execute": "object-add",
# "arguments": { "qom-type": "rng-random", "id": "rng1",
# "props": { "filename": "/dev/hwrng" } } }
# "filename": "/dev/hwrng" } }
# <- { "return": {} }
#
##
{ 'command': 'object-add',
'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'} }
'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'},
'gen': false } # so we can get the additional arguments
##
# @object-del:

View File

@ -5,7 +5,7 @@
# = Transactions
##
{ 'include': 'block.json' }
{ 'include': 'block-core.json' }
##
# @Abort:

340
qemu-storage-daemon.c Normal file
View File

@ -0,0 +1,340 @@
/*
* QEMU storage daemon
*
* Copyright (c) 2003-2008 Fabrice Bellard
* Copyright (c) 2019 Kevin Wolf <kwolf@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include <getopt.h>
#include "block/block.h"
#include "block/nbd.h"
#include "chardev/char.h"
#include "crypto/init.h"
#include "monitor/monitor.h"
#include "monitor/monitor-internal.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-block.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qapi-visit-control.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qobject-input-visitor.h"
#include "qemu-common.h"
#include "qemu-version.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qemu/help_option.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qom/object_interfaces.h"
#include "storage-daemon/qapi/qapi-commands.h"
#include "storage-daemon/qapi/qapi-init-commands.h"
#include "sysemu/runstate.h"
#include "trace/control.h"
static volatile bool exit_requested = false;
void qemu_system_killed(int signal, pid_t pid)
{
exit_requested = true;
}
void qmp_quit(Error **errp)
{
exit_requested = true;
}
static void help(void)
{
printf(
"Usage: %s [options]\n"
"QEMU storage daemon\n"
"\n"
" -h, --help display this help and exit\n"
" -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
" specify tracing options\n"
" -V, --version output version information and exit\n"
"\n"
" --blockdev [driver=]<driver>[,node-name=<N>][,discard=ignore|unmap]\n"
" [,cache.direct=on|off][,cache.no-flush=on|off]\n"
" [,read-only=on|off][,auto-read-only=on|off]\n"
" [,force-share=on|off][,detect-zeroes=on|off|unmap]\n"
" [,driver specific parameters...]\n"
" configure a block backend\n"
"\n"
" --chardev <options> configure a character device backend\n"
" (see the qemu(1) man page for possible options)\n"
"\n"
" --export [type=]nbd,device=<node-name>[,name=<export-name>]\n"
" [,writable=on|off][,bitmap=<name>]\n"
" export the specified block node over NBD\n"
" (requires --nbd-server)\n"
"\n"
" --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n"
" configure a QMP monitor\n"
"\n"
" --nbd-server addr.type=inet,addr.host=<host>,addr.port=<port>\n"
" [,tls-creds=<id>][,tls-authz=<id>]\n"
" --nbd-server addr.type=unix,addr.path=<path>\n"
" [,tls-creds=<id>][,tls-authz=<id>]\n"
" start an NBD server for exporting block nodes\n"
"\n"
" --object help list object types that can be added\n"
" --object <type>,help list properties for the given object type\n"
" --object <type>[,<property>=<value>...]\n"
" create a new object of type <type>, setting\n"
" properties in the order they are specified. Note\n"
" that the 'id' property must be set.\n"
" See the qemu(1) man page for documentation of the\n"
" objects that can be added.\n"
"\n"
QEMU_HELP_BOTTOM "\n",
error_get_progname());
}
enum {
OPTION_BLOCKDEV = 256,
OPTION_CHARDEV,
OPTION_EXPORT,
OPTION_MONITOR,
OPTION_NBD_SERVER,
OPTION_OBJECT,
};
extern QemuOptsList qemu_chardev_opts;
static QemuOptsList qemu_object_opts = {
.name = "object",
.implied_opt_name = "qom-type",
.head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
.desc = {
{ }
},
};
static void init_qmp_commands(void)
{
qmp_init_marshal(&qmp_commands);
qmp_register_command(&qmp_commands, "query-qmp-schema",
qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG);
QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
}
static void init_export(BlockExport *export, Error **errp)
{
switch (export->type) {
case BLOCK_EXPORT_TYPE_NBD:
qmp_nbd_server_add(&export->u.nbd, errp);
break;
default:
g_assert_not_reached();
}
}
static void process_options(int argc, char *argv[])
{
int c;
static const struct option long_options[] = {
{"blockdev", required_argument, NULL, OPTION_BLOCKDEV},
{"chardev", required_argument, NULL, OPTION_CHARDEV},
{"export", required_argument, NULL, OPTION_EXPORT},
{"help", no_argument, NULL, 'h'},
{"monitor", required_argument, NULL, OPTION_MONITOR},
{"nbd-server", required_argument, NULL, OPTION_NBD_SERVER},
{"object", required_argument, NULL, OPTION_OBJECT},
{"trace", required_argument, NULL, 'T'},
{"version", no_argument, NULL, 'V'},
{0, 0, 0, 0}
};
/*
* In contrast to the system emulator, options are processed in the order
* they are given on the command lines. This means that things must be
* defined first before they can be referenced in another option.
*/
while ((c = getopt_long(argc, argv, "hT:V", long_options, NULL)) != -1) {
switch (c) {
case '?':
exit(EXIT_FAILURE);
case 'h':
help();
exit(EXIT_SUCCESS);
case 'T':
{
char *trace_file = trace_opt_parse(optarg);
trace_init_file(trace_file);
g_free(trace_file);
break;
}
case 'V':
printf("qemu-storage-daemon version "
QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n");
exit(EXIT_SUCCESS);
case OPTION_BLOCKDEV:
{
Visitor *v;
BlockdevOptions *options;
v = qobject_input_visitor_new_str(optarg, "driver",
&error_fatal);
visit_type_BlockdevOptions(v, NULL, &options, &error_fatal);
visit_free(v);
qmp_blockdev_add(options, &error_fatal);
qapi_free_BlockdevOptions(options);
break;
}
case OPTION_CHARDEV:
{
/* TODO This interface is not stable until we QAPIfy it */
QemuOpts *opts = qemu_opts_parse_noisily(&qemu_chardev_opts,
optarg, true);
if (opts == NULL) {
exit(EXIT_FAILURE);
}
if (!qemu_chr_new_from_opts(opts, NULL, &error_fatal)) {
/* No error, but NULL returned means help was printed */
exit(EXIT_SUCCESS);
}
qemu_opts_del(opts);
break;
}
case OPTION_EXPORT:
{
Visitor *v;
BlockExport *export;
v = qobject_input_visitor_new_str(optarg, "type", &error_fatal);
visit_type_BlockExport(v, NULL, &export, &error_fatal);
visit_free(v);
init_export(export, &error_fatal);
qapi_free_BlockExport(export);
break;
}
case OPTION_MONITOR:
{
Visitor *v;
MonitorOptions *monitor;
v = qobject_input_visitor_new_str(optarg, "chardev",
&error_fatal);
visit_type_MonitorOptions(v, NULL, &monitor, &error_fatal);
visit_free(v);
/* TODO Catch duplicate monitor IDs */
monitor_init(monitor, false, &error_fatal);
qapi_free_MonitorOptions(monitor);
break;
}
case OPTION_NBD_SERVER:
{
Visitor *v;
NbdServerOptions *options;
v = qobject_input_visitor_new_str(optarg, NULL, &error_fatal);
visit_type_NbdServerOptions(v, NULL, &options, &error_fatal);
visit_free(v);
nbd_server_start_options(options, &error_fatal);
qapi_free_NbdServerOptions(options);
break;
}
case OPTION_OBJECT:
{
QemuOpts *opts;
const char *type;
QDict *args;
QObject *ret_data = NULL;
/* FIXME The keyval parser rejects 'help' arguments, so we must
* unconditionall try QemuOpts first. */
opts = qemu_opts_parse(&qemu_object_opts,
optarg, true, &error_fatal);
type = qemu_opt_get(opts, "qom-type");
if (type && user_creatable_print_help(type, opts)) {
exit(EXIT_SUCCESS);
}
qemu_opts_del(opts);
args = keyval_parse(optarg, "qom-type", &error_fatal);
qmp_object_add(args, &ret_data, &error_fatal);
qobject_unref(args);
qobject_unref(ret_data);
break;
}
default:
g_assert_not_reached();
}
}
if (optind != argc) {
error_report("Unexpected argument: %s", argv[optind]);
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
#ifdef CONFIG_POSIX
signal(SIGPIPE, SIG_IGN);
#endif
error_init(argv[0]);
qemu_init_exec_dir(argv[0]);
os_setup_signal_handling();
module_call_init(MODULE_INIT_QOM);
module_call_init(MODULE_INIT_TRACE);
qemu_add_opts(&qemu_trace_opts);
qcrypto_init(&error_fatal);
bdrv_init();
monitor_init_globals_core();
init_qmp_commands();
if (!trace_init_backends()) {
return EXIT_FAILURE;
}
qemu_set_log(LOG_TRACE);
qemu_init_main_loop(&error_fatal);
process_options(argc, argv);
while (!exit_requested) {
main_loop_wait(false);
}
return EXIT_SUCCESS;
}

View File

@ -2,3 +2,4 @@ qom-obj-y = object.o container.o qom-qobject.o
qom-obj-y += object_interfaces.o
common-obj-$(CONFIG_SOFTMMU) += qom-hmp-cmds.o qom-qmp-cmds.o
storage-daemon-obj-y += qom-qmp-cmds.o

View File

@ -14,6 +14,7 @@
*/
#include "qemu/osdep.h"
#include "block/qdict.h"
#include "hw/qdev-core.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-qdev.h"
@ -240,13 +241,34 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
return prop_list;
}
void qmp_object_add(const char *type, const char *id,
bool has_props, QObject *props, Error **errp)
void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp)
{
QObject *props;
QDict *pdict;
Visitor *v;
Object *obj;
const char *type;
const char *id;
type = qdict_get_try_str(qdict, "qom-type");
if (!type) {
error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
return;
} else {
type = g_strdup(type);
qdict_del(qdict, "qom-type");
}
id = qdict_get_try_str(qdict, "id");
if (!id) {
error_setg(errp, QERR_MISSING_PARAMETER, "id");
return;
} else {
id = g_strdup(id);
qdict_del(qdict, "id");
}
props = qdict_get(qdict, "props");
if (props) {
pdict = qobject_to(QDict, props);
if (!pdict) {
@ -254,17 +276,23 @@ void qmp_object_add(const char *type, const char *id,
return;
}
qobject_ref(pdict);
} else {
pdict = qdict_new();
qdict_del(qdict, "props");
qdict_join(qdict, pdict, false);
if (qdict_size(pdict) != 0) {
error_setg(errp, "Option in 'props' conflicts with top level");
qobject_unref(pdict);
return;
}
qobject_unref(pdict);
}
v = qobject_input_visitor_new(QOBJECT(pdict));
obj = user_creatable_add_type(type, id, pdict, v, errp);
v = qobject_input_visitor_new(QOBJECT(qdict));
obj = user_creatable_add_type(type, id, qdict, v, errp);
visit_free(v);
if (obj) {
object_unref(obj);
}
qobject_unref(pdict);
*ret_data = QOBJECT(qdict_new());
}
void qmp_object_del(const char *id, Error **errp)

View File

@ -44,6 +44,11 @@ class QAPIGen:
return ''
def write(self, output_dir):
# Include paths starting with ../ are used to reuse modules of the main
# schema in specialised schemas. Don't overwrite the files that are
# already generated for the main schema.
if self.fname.startswith('../'):
return
pathname = os.path.join(output_dir, self.fname)
odir = os.path.dirname(pathname)
if odir:

View File

@ -0,0 +1 @@
storage-daemon-obj-y += qapi/

View File

@ -0,0 +1 @@
storage-daemon-obj-y += qapi-commands.o qapi-init-commands.o qapi-introspect.o

View File

@ -0,0 +1,26 @@
# -*- Mode: Python -*-
# Note that modules are shared with the QEMU main schema under the assumption
# that the storage daemon schema is a subset of the main schema. For the shared
# modules, no code is generated here, but we reuse the code files generated
# from the main schema.
#
# If you wish to extend the storage daemon schema to contain things that are
# not in the main schema, be aware that array types of types defined in shared
# modules are only generated if an array of the respective type is already used
# in the main schema. Therefore, if you use such arrays, you may need to define
# the array type in the main schema, even if it is unused outside of the
# storage daemon.
{ 'include': '../../qapi/pragma.json' }
{ 'include': '../../qapi/block-core.json' }
{ 'include': '../../qapi/char.json' }
{ 'include': '../../qapi/common.json' }
{ 'include': '../../qapi/control.json' }
{ 'include': '../../qapi/crypto.json' }
{ 'include': '../../qapi/introspect.json' }
{ 'include': '../../qapi/job.json' }
{ 'include': '../../qapi/qom.json' }
{ 'include': '../../qapi/sockets.json' }
{ 'include': '../../qapi/transaction.json' }

View File

@ -1,3 +1,4 @@
stub-obj-y += arch_type.o
stub-obj-y += bdrv-next-monitor-owned.o
stub-obj-y += blk-commit-all.o
stub-obj-y += blockdev-close-all-bdrv-states.o
@ -18,6 +19,7 @@ stub-obj-y += machine-init-done.o
stub-obj-y += migr-blocker.o
stub-obj-y += change-state-handler.o
stub-obj-y += monitor.o
stub-obj-y += monitor-core.o
stub-obj-y += notify-event.o
stub-obj-y += qtest.o
stub-obj-y += replay.o

4
stubs/arch_type.c Normal file
View File

@ -0,0 +1,4 @@
#include "qemu/osdep.h"
#include "sysemu/arch_init.h"
const uint32_t arch_type = QEMU_ARCH_NONE;

21
stubs/monitor-core.c Normal file
View File

@ -0,0 +1,21 @@
#include "qemu/osdep.h"
#include "monitor/monitor.h"
#include "qemu-common.h"
#include "qapi/qapi-emit-events.h"
__thread Monitor *cur_mon;
void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
{
}
void qapi_event_emit(QAPIEvent event, QDict *qdict)
{
}
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
{
abort();
}

View File

@ -1,14 +1,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qapi-emit-events.h"
#include "monitor/monitor.h"
__thread Monitor *cur_mon;
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
{
abort();
}
#include "../monitor/monitor-internal.h"
int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
{
@ -16,14 +9,10 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
return -1;
}
void monitor_init_qmp(Chardev *chr, bool pretty)
void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp)
{
}
void monitor_init_hmp(Chardev *chr, bool use_readline)
{
}
void qapi_event_emit(QAPIEvent event, QDict *qdict)
void monitor_fdsets_cleanup(void)
{
}

View File

@ -30,6 +30,7 @@ _cleanup()
{
_cleanup_test_img
rm "$TEST_DIR/blkdebug.conf"
rm -f "$TEST_IMG.data_file"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@ -218,6 +219,58 @@ _make_test_img 64M
$QEMU_IO -c "write 0 1M" -c "write 0 1M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo === Avoid freeing preallocated zero clusters on failure ===
echo
cat > "$TEST_DIR/blkdebug.conf" <<EOF
[inject-error]
event = "write_aio"
errno = "5"
once = "on"
EOF
_make_test_img $CLUSTER_SIZE
# Create a preallocated zero cluster
$QEMU_IO -c "write 0 $CLUSTER_SIZE" -c "write -z 0 $CLUSTER_SIZE" "$TEST_IMG" \
| _filter_qemu_io
# Try to overwrite it (prompting an I/O error from blkdebug), thus
# triggering the alloc abort code
$QEMU_IO -c "write 0 $CLUSTER_SIZE" "$BLKDBG_TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo === Avoid freeing external data clusters on failure ===
echo
# Similar test as the last one, except we test what happens when there
# is an error when writing to an external data file instead of when
# writing to a preallocated zero cluster
_make_test_img -o "data_file=$TEST_IMG.data_file" $CLUSTER_SIZE
# Put blkdebug above the data-file, and a raw node on top of that so
# that blkdebug will see a write_aio event and emit an error
$QEMU_IO -c "write 0 $CLUSTER_SIZE" \
"json:{
'driver': 'qcow2',
'file': { 'driver': 'file', 'filename': '$TEST_IMG' },
'data-file': {
'driver': 'raw',
'file': {
'driver': 'blkdebug',
'config': '$TEST_DIR/blkdebug.conf',
'image': {
'driver': 'file',
'filename': '$TEST_IMG.data_file'
}
}
}
}" \
| _filter_qemu_io
_check_test_img
# success, all done
echo "*** done"
rm -f $seq.full

View File

@ -643,4 +643,20 @@ write failed: Input/output error
wrote 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== Avoid freeing preallocated zero clusters on failure ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024
wrote 1024/1024 bytes at offset 0
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1024/1024 bytes at offset 0
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
write failed: Input/output error
No errors were found on the image.
=== Avoid freeing external data clusters on failure ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 data_file=TEST_DIR/t.IMGFMT.data_file
write failed: Input/output error
No errors were found on the image.
*** done

View File

@ -651,4 +651,20 @@ write failed: Input/output error
wrote 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== Avoid freeing preallocated zero clusters on failure ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024
wrote 1024/1024 bytes at offset 0
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1024/1024 bytes at offset 0
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
write failed: Input/output error
No errors were found on the image.
=== Avoid freeing external data clusters on failure ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 data_file=TEST_DIR/t.IMGFMT.data_file
write failed: Input/output error
No errors were found on the image.
*** done

View File

@ -970,8 +970,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
self.assertEqual(self.get_node('hd1'), None)
self.assert_qmp(self.get_node('hd2'), 'ro', True)
# We don't allow setting a backing file that uses a different AioContext
def test_iothreads(self):
def run_test_iothreads(self, iothread_a, iothread_b, errmsg = None):
opts = hd_opts(0)
result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
self.assert_qmp(result, 'return', {})
@ -986,20 +985,46 @@ class TestBlockdevReopen(iotests.QMPTestCase):
result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1')
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd0', iothread='iothread0')
result = self.vm.qmp('device_add', driver='virtio-scsi', id='scsi0',
iothread=iothread_a)
self.assert_qmp(result, 'return', {})
self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread1')
result = self.vm.qmp('device_add', driver='virtio-scsi', id='scsi1',
iothread=iothread_b)
self.assert_qmp(result, 'return', {})
self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
if iothread_a:
result = self.vm.qmp('device_add', driver='scsi-hd', drive='hd0',
share_rw=True, bus="scsi0.0")
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread0')
self.assert_qmp(result, 'return', {})
if iothread_b:
result = self.vm.qmp('device_add', driver='scsi-hd', drive='hd2',
share_rw=True, bus="scsi1.0")
self.assert_qmp(result, 'return', {})
self.reopen(opts, {'backing': 'hd2'})
# Attaching the backing file may or may not work
self.reopen(opts, {'backing': 'hd2'}, errmsg)
# But removing the backing file should always work
self.reopen(opts, {'backing': None})
self.vm.shutdown()
# We don't allow setting a backing file that uses a different AioContext if
# neither of them can switch to the other AioContext
def test_iothreads_error(self):
self.run_test_iothreads('iothread0', 'iothread1',
"Cannot change iothread of active block backend")
def test_iothreads_compatible_users(self):
self.run_test_iothreads('iothread0', 'iothread0')
def test_iothreads_switch_backing(self):
self.run_test_iothreads('iothread0', None)
def test_iothreads_switch_overlay(self):
self.run_test_iothreads(None, 'iothread0')
if __name__ == '__main__':
iotests.main(supported_fmts=["qcow2"],

View File

@ -1,6 +1,6 @@
..................
.....................
----------------------------------------------------------------------
Ran 18 tests
Ran 21 tests
OK
{"execute": "job-finalize", "arguments": {"id": "commit0"}}

View File

@ -71,8 +71,8 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
*/
__thread Monitor *cur_mon;
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); }
void monitor_init_qmp(Chardev *chr, bool pretty) {}
void monitor_init_hmp(Chardev *chr, bool use_readline) {}
void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) {}
void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp) {}
static void test_socket_fd_pass_name_good(void)