@@ -4140,13 +4140,15 @@
##
# @x-blockdev-reopen:
#
-# Reopens a block device using the given set of options. Any option
-# not specified will be reset to its default value regardless of its
-# previous status. If an option cannot be changed or a particular
+# Reopens one or more block devices using the given set of options.
+# Any option not specified will be reset to its default value regardless
+# of its previous status. If an option cannot be changed or a particular
# driver does not support reopening then the command will return an
-# error.
+# error. All devices in the list are reopened in one transaction, so
+# if one of them fails then the whole transaction is cancelled.
#
-# The top-level @node-name option (from BlockdevOptions) must be
+# The command receives a list of block devices to reopen. For each one
+# of them, the top-level @node-name option (from BlockdevOptions) must be
# specified and is used to select the block device to be reopened.
# Other @node-name options must be either omitted or set to the
# current name of the appropriate node. This command won't change any
@@ -4166,8 +4168,8 @@
#
# 4) NULL: the current child (if any) is detached.
#
-# Options (1) and (2) are supported in all cases, but at the moment
-# only @backing allows replacing or detaching an existing child.
+# Options (1) and (2) are supported in all cases. Option (3) is
+# supported for @file and @backing, and option (4) for @backing only.
#
# Unlike with blockdev-add, the @backing option must always be present
# unless the node being reopened does not have a backing file and its
@@ -4177,7 +4179,7 @@
# Since: 4.0
##
{ 'command': 'x-blockdev-reopen',
- 'data': 'BlockdevOptions', 'boxed': true }
+ 'data': { 'options': ['BlockdevOptions'] } }
##
# @blockdev-del:
@@ -3528,38 +3528,16 @@ fail:
visit_free(v);
}
-void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
+void qmp_x_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
{
- BlockDriverState *bs;
- QObject *obj;
- Visitor *v = qobject_output_visitor_new(&obj);
- BlockReopenQueue *queue;
- QDict *qdict;
-
- /* Check for the selected node name */
- if (!options->has_node_name) {
- error_setg(errp, "Node name not specified");
- goto fail;
- }
-
- bs = bdrv_find_node(options->node_name);
- if (!bs) {
- error_setg(errp, "Cannot find node named '%s'", options->node_name);
- goto fail;
- }
-
- /* Put all options in a QDict and flatten it */
- visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
- visit_complete(v, &obj);
- qdict = qobject_to(QDict, obj);
-
- qdict_flatten(qdict);
-
- /* Perform the reopen operation */
+ BlockReopenQueue *queue = NULL;
+ GSList *aio_ctxs = NULL;
+ GSList *visitors = NULL;
+ GSList *drained = NULL;
BdrvNextIterator it;
- GSList *aio_ctxs = NULL, *ctx;
BlockDriverState *it_bs;
+ /* Acquire all AIO contexts */
for (it_bs = bdrv_first(&it); it_bs; it_bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(it_bs);
@@ -3569,19 +3547,50 @@ void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
}
}
- bdrv_subtree_drained_begin(bs);
- queue = bdrv_reopen_queue(NULL, bs, qdict, false);
+ /* Add each one of the BDS that we want to reopen to the queue */
+ for (; reopen_list != NULL; reopen_list = reopen_list->next) {
+ BlockdevOptions *options = reopen_list->value;
+ QDict *qdict;
+ Visitor *v;
+ BlockDriverState *bs;
+ QObject *obj;
+
+ /* Check for the selected node name */
+ if (!options->has_node_name) {
+ error_setg(errp, "Node name not specified");
+ goto fail;
+ }
+
+ bs = bdrv_find_node(options->node_name);
+ if (!bs) {
+ error_setg(errp, "Cannot find node named '%s'", options->node_name);
+ goto fail;
+ }
+
+ v = qobject_output_visitor_new(&obj);
+ visitors = g_slist_prepend(visitors, v);
+
+ /* Put all options in a QDict and flatten it */
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
+ visit_complete(v, &obj);
+ qdict = qobject_to(QDict, obj);
+
+ qdict_flatten(qdict);
+
+ bdrv_subtree_drained_begin(bs);
+ queue = bdrv_reopen_queue(queue, bs, qdict, false);
+ drained = g_slist_prepend(drained, bs);
+ }
+
+ /* Perform the reopen operation */
bdrv_reopen_multiple(queue, errp);
- bdrv_subtree_drained_end(bs);
-
- for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
- AioContext *aio_context = ctx->data;
- aio_context_release(aio_context);
- }
- g_slist_free(aio_ctxs);
+ queue = NULL;
fail:
- visit_free(v);
+ bdrv_reopen_queue_free(queue);
+ g_slist_free_full(drained, (GDestroyNotify) bdrv_subtree_drained_end);
+ g_slist_free_full(aio_ctxs, (GDestroyNotify) aio_context_release);
+ g_slist_free_full(visitors, (GDestroyNotify) visit_free);
}
void qmp_blockdev_del(const char *node_name, Error **errp)
@@ -260,9 +260,12 @@ class TestBlockdevMirrorReopen(MirrorBaseClass):
result = self.vm.qmp('blockdev-add', node_name="backing",
driver="null-co")
self.assert_qmp(result, 'return', {})
- result = self.vm.qmp('x-blockdev-reopen', node_name="target",
- driver=iotests.imgfmt, file="target-file",
- backing="backing")
+ result = self.vm.qmp('x-blockdev-reopen', options = [{
+ 'node-name': "target",
+ 'driver': iotests.imgfmt,
+ 'file': "target-file",
+ 'backing': "backing"
+ }])
self.assert_qmp(result, 'return', {})
class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen):
@@ -136,7 +136,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
assert sha256_1 == self.getSha256()
# Reopen to RW
- result = self.vm.qmp('x-blockdev-reopen', **{
+ result = self.vm.qmp('x-blockdev-reopen', options = [{
'node-name': 'node0',
'driver': iotests.imgfmt,
'file': {
@@ -144,7 +144,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
'filename': disk
},
'read-only': False
- })
+ }])
self.assert_qmp(result, 'return', {})
# Check that bitmap is reopened to RW and we can write to it.
@@ -84,8 +84,18 @@ class TestBlockdevReopen(iotests.QMPTestCase):
"Expected output of %d qemu-io commands, found %d" %
(found, self.total_io_cmds))
- # Run x-blockdev-reopen with 'opts' but applying 'newopts'
- # on top of it. The original 'opts' dict is unmodified
+ # Run x-blockdev-reopen on a list of block devices
+ def reopenMultiple(self, opts, errmsg = None):
+ result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, options = opts)
+ if errmsg:
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', errmsg)
+ else:
+ self.assert_qmp(result, 'return', {})
+
+ # Run x-blockdev-reopen on a single block device (specified by
+ # 'opts') but applying 'newopts' on top of it. The original 'opts'
+ # dict is unmodified
def reopen(self, opts, newopts = {}, errmsg = None):
opts = copy.deepcopy(opts)
@@ -100,12 +110,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
subdict = opts[prefix]
subdict[key] = value
- result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts)
- if errmsg:
- self.assert_qmp(result, 'error/class', 'GenericError')
- self.assert_qmp(result, 'error/desc', errmsg)
- else:
- self.assert_qmp(result, 'return', {})
+ self.reopenMultiple([ opts ], errmsg)
# Run query-named-block-nodes and return the specified entry
@@ -141,10 +146,10 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# We cannot change any of these
self.reopen(opts, {'node-name': 'not-found'}, "Cannot find node named 'not-found'")
self.reopen(opts, {'node-name': ''}, "Cannot find node named ''")
- self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string")
+ self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'options[0].node-name', expected: string")
self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'")
self.reopen(opts, {'driver': ''}, "Invalid parameter ''")
- self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string")
+ self.reopen(opts, {'driver': None}, "Invalid parameter type for 'options[0].driver', expected: string")
self.reopen(opts, {'file': 'not-found'}, "Cannot find device= nor node_name=not-found")
self.reopen(opts, {'file': ''}, "Cannot find device= nor node_name=")
self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef")
@@ -153,7 +158,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'")
self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'")
self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
- self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string")
+ self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'options[0].file.filename', expected: string")
# node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it
del opts['node-name']
@@ -62,7 +62,7 @@ vm.get_qmp_events()
del blockdev_opts['file']['size']
vm.qmp_log('x-blockdev-reopen', filters=[filter_qmp_testfiles],
- **blockdev_opts)
+ options = [ blockdev_opts ])
vm.qmp_log('block-job-resume', device='drive0')
vm.event_wait('JOB_STATUS_CHANGE', timeout=1.0,
@@ -2,7 +2,7 @@
{"return": {}}
{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "on-target-error": "enospc", "sync": "full", "target": "target"}}
{"return": {}}
-{"execute": "x-blockdev-reopen", "arguments": {"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}}
+{"execute": "x-blockdev-reopen", "arguments": {"options": [{"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}]}}
{"return": {}}
{"execute": "block-job-resume", "arguments": {"device": "drive0"}}
{"return": {}}
@@ -119,8 +119,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase):
command = 'x-blockdev-reopen' if reOpen else 'blockdev-add'
- result = vm.qmp(command, **
- {
+ opts = {
'driver': iotests.imgfmt,
'node-name': id,
'read-only': readOnly,
@@ -130,7 +129,11 @@ class EncryptionSetupTestCase(iotests.QMPTestCase):
'filename': test_img,
}
}
- )
+
+ if reOpen:
+ result = vm.qmp(command, options = [ opts ])
+ else:
+ result = vm.qmp(command, **opts)
self.assert_qmp(result, 'return', {})
@@ -98,7 +98,7 @@ class TestPreallocateFilter(TestPreallocateBase):
self.check_big()
def test_reopen_opts(self):
- result = self.vm.qmp('x-blockdev-reopen', **{
+ result = self.vm.qmp('x-blockdev-reopen', options = [{
'node-name': 'disk',
'driver': iotests.imgfmt,
'file': {
@@ -112,7 +112,7 @@ class TestPreallocateFilter(TestPreallocateBase):
'filename': disk
}
}
- })
+ }])
self.assert_qmp(result, 'return', {})
self.vm.hmp_qemu_io('drive0', 'write 0 1M')
Signed-off-by: Alberto Garcia <berto@igalia.com> --- qapi/block-core.json | 18 ++++---- blockdev.c | 85 +++++++++++++++++++++----------------- tests/qemu-iotests/155 | 9 ++-- tests/qemu-iotests/165 | 4 +- tests/qemu-iotests/245 | 27 +++++++----- tests/qemu-iotests/248 | 2 +- tests/qemu-iotests/248.out | 2 +- tests/qemu-iotests/296 | 9 ++-- tests/qemu-iotests/298 | 4 +- 9 files changed, 91 insertions(+), 69 deletions(-)