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:
commit
67f17e23ba
37
Makefile
37
Makefile
@ -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
|
||||
|
||||
|
@ -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
44
block.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
590
block/qapi-sysemu.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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,
|
||||
|
46
block/rbd.c
46
block/rbd.c
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
559
blockdev.c
559
blockdev.c
@ -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),
|
||||
|
@ -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
2
configure
vendored
@ -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"
|
||||
|
@ -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)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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) {
|
||||
/*
|
||||
|
@ -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)
|
||||
|
@ -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' }
|
||||
|
528
qapi/block.json
528
qapi/block.json
@ -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'] } }
|
||||
|
@ -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
24
qapi/pragma.json
Normal 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
|
||||
] } }
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -5,7 +5,7 @@
|
||||
# = Transactions
|
||||
##
|
||||
|
||||
{ 'include': 'block.json' }
|
||||
{ 'include': 'block-core.json' }
|
||||
|
||||
##
|
||||
# @Abort:
|
||||
|
340
qemu-storage-daemon.c
Normal file
340
qemu-storage-daemon.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
1
storage-daemon/Makefile.objs
Normal file
1
storage-daemon/Makefile.objs
Normal file
@ -0,0 +1 @@
|
||||
storage-daemon-obj-y += qapi/
|
1
storage-daemon/qapi/Makefile.objs
Normal file
1
storage-daemon/qapi/Makefile.objs
Normal file
@ -0,0 +1 @@
|
||||
storage-daemon-obj-y += qapi-commands.o qapi-init-commands.o qapi-introspect.o
|
26
storage-daemon/qapi/qapi-schema.json
Normal file
26
storage-daemon/qapi/qapi-schema.json
Normal 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' }
|
@ -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
4
stubs/arch_type.c
Normal 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
21
stubs/monitor-core.c
Normal 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();
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"],
|
||||
|
@ -1,6 +1,6 @@
|
||||
..................
|
||||
.....................
|
||||
----------------------------------------------------------------------
|
||||
Ran 18 tests
|
||||
Ran 21 tests
|
||||
|
||||
OK
|
||||
{"execute": "job-finalize", "arguments": {"id": "commit0"}}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user