* [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup"
@ 2014-12-17 12:51 Fam Zheng
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow
v5: Address Max's and Markus' comments:
Split patch 1. (Markus)
Fix typos and pastos. (Markus, Max)
Actually acquire aio context. (Max)
Drop unnecessary initialization of fields in blockdev_backup_prepare. (Max)
Add "sync" in the document example.
Add Max's rev-by in patch 4.
The existing drive-backup command accepts a target file path, but that
interface provides little flexibility on the properties of target block device,
compared to what is possible with "blockdev-add", "drive_add" or "-drive".
This is also a building block to allow image fleecing (creating a point in time
snapshot and export with nbd-server-add).
(For symmetry, blockdev-mirror will be added in a separate series.)
Fam
Fam Zheng (4):
qapi: Comment version info in TransactionAction
qmp: Add command 'blockdev-backup'
block: Add blockdev-backup to transaction
qemu-iotests: Test blockdev-backup in 055
block/backup.c | 28 ++++++
blockdev.c | 133 ++++++++++++++++++++++++++++
qapi-schema.json | 8 ++
qapi/block-core.json | 54 ++++++++++++
qmp-commands.hx | 42 +++++++++
tests/qemu-iotests/055 | 211 +++++++++++++++++++++++++++++++++++++--------
tests/qemu-iotests/055.out | 4 +-
7 files changed, 441 insertions(+), 39 deletions(-)
--
1.9.3
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction
2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng
@ 2014-12-17 12:51 ` Fam Zheng
2014-12-17 21:18 ` Eric Blake
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' Fam Zheng
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow
Signed-off-by: Fam Zheng <famz@redhat.com>
---
qapi-schema.json | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/qapi-schema.json b/qapi-schema.json
index 563b4ad..47d99cf 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1254,6 +1254,12 @@
#
# A discriminated record of operations that can be performed with
# @transaction.
+#
+# Since 1.1
+#
+# drive-backup since 1.6
+# abort since 1.6
+# blockdev-snapshot-internal-sync since 1.7
##
{ 'union': 'TransactionAction',
'data': {
--
1.9.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup'
2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng
@ 2014-12-17 12:51 ` Fam Zheng
2014-12-17 20:53 ` John Snow
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction Fam Zheng
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 4/4] qemu-iotests: Test blockdev-backup in 055 Fam Zheng
3 siblings, 1 reply; 10+ messages in thread
From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow
Similar to drive-backup, but this command uses a device id as target
instead of creating/opening an image file.
Also add blocker on target bs, since the target is also a named device
now.
Add check and report error for bs == target which became possible but is
an illegal case with introduction of blockdev-backup.
Signed-off-by: Fam Zheng <famz@redhat.com>
---
block/backup.c | 28 +++++++++++++++++++++++++++
blockdev.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++
qapi/block-core.json | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++
qmp-commands.hx | 42 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 178 insertions(+)
diff --git a/block/backup.c b/block/backup.c
index 792e655..1c535b1 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -360,6 +360,7 @@ static void coroutine_fn backup_run(void *opaque)
hbitmap_free(job->bitmap);
bdrv_iostatus_disable(target);
+ bdrv_op_unblock_all(target, job->common.blocker);
data = g_malloc(sizeof(*data));
data->ret = ret;
@@ -379,6 +380,11 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
assert(target);
assert(cb);
+ if (bs == target) {
+ error_setg(errp, "Source and target cannot be the same");
+ return;
+ }
+
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
!bdrv_iostatus_is_enabled(bs)) {
@@ -386,6 +392,26 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
return;
}
+ if (!bdrv_is_inserted(bs)) {
+ error_setg(errp, "Device is not inserted: %s",
+ bdrv_get_device_name(bs));
+ return;
+ }
+
+ if (!bdrv_is_inserted(target)) {
+ error_setg(errp, "Device is not inserted: %s",
+ bdrv_get_device_name(target));
+ return;
+ }
+
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
+ return;
+ }
+
+ if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
+ return;
+ }
+
len = bdrv_getlength(bs);
if (len < 0) {
error_setg_errno(errp, -len, "unable to get length for '%s'",
@@ -399,6 +425,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
return;
}
+ bdrv_op_block_all(target, job->common.blocker);
+
job->on_source_error = on_source_error;
job->on_target_error = on_target_error;
job->target = target;
diff --git a/blockdev.c b/blockdev.c
index 5651a8e..dbbab1d 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2240,6 +2240,8 @@ void qmp_drive_backup(const char *device, const char *target,
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
+ /* Although backup_run has this check too, we need to use bs->drv below, so
+ * do an early check redundantly. */
if (!bdrv_is_inserted(bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
goto out;
@@ -2256,6 +2258,7 @@ void qmp_drive_backup(const char *device, const char *target,
}
}
+ /* Early check to avoid creating target */
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
goto out;
}
@@ -2323,6 +2326,57 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
return bdrv_named_nodes_list();
}
+void qmp_blockdev_backup(const char *device, const char *target,
+ enum MirrorSyncMode sync,
+ bool has_speed, int64_t speed,
+ bool has_on_source_error,
+ BlockdevOnError on_source_error,
+ bool has_on_target_error,
+ BlockdevOnError on_target_error,
+ Error **errp)
+{
+ BlockDriverState *bs;
+ BlockDriverState *target_bs;
+ Error *local_err = NULL;
+ AioContext *aio_context;
+
+ if (!has_speed) {
+ speed = 0;
+ }
+ if (!has_on_source_error) {
+ on_source_error = BLOCKDEV_ON_ERROR_REPORT;
+ }
+ if (!has_on_target_error) {
+ on_target_error = BLOCKDEV_ON_ERROR_REPORT;
+ }
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
+ }
+
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
+ target_bs = bdrv_find(target);
+ if (!target_bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, target);
+ goto out;
+ }
+
+ bdrv_ref(target_bs);
+ bdrv_set_aio_context(target_bs, bdrv_get_aio_context(bs));
+ backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
+ block_job_cb, bs, &local_err);
+ if (local_err != NULL) {
+ bdrv_unref(target_bs);
+ error_propagate(errp, local_err);
+ }
+out:
+ aio_context_release(aio_context);
+}
+
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
void qmp_drive_mirror(const char *device, const char *target,
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 6e8db15..d9f0598 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -703,6 +703,41 @@
'*on-target-error': 'BlockdevOnError' } }
##
+# @BlockdevBackup
+#
+# @device: the name of the device which should be copied.
+#
+# @target: the name of the backup target device.
+#
+# @sync: what parts of the disk image should be copied to the destination
+# (all the disk, only the sectors allocated in the topmost image, or
+# only new I/O).
+#
+# @speed: #optional the maximum speed, in bytes per second. The default is 0,
+# for unlimited.
+#
+# @on-source-error: #optional the action to take on an error on the source,
+# default 'report'. 'stop' and 'enospc' can only be used
+# if the block device supports io-status (see BlockInfo).
+#
+# @on-target-error: #optional the action to take on an error on the target,
+# default 'report' (no limitations, since this applies to
+# a different block device than @device).
+#
+# Note that @on-source-error and @on-target-error only affect background I/O.
+# If an error occurs during a guest write request, the device's rerror/werror
+# actions will be used.
+#
+# Since: 2.3
+##
+{ 'type': 'BlockdevBackup',
+ 'data': { 'device': 'str', 'target': 'str',
+ 'sync': 'MirrorSyncMode',
+ '*speed': 'int',
+ '*on-source-error': 'BlockdevOnError',
+ '*on-target-error': 'BlockdevOnError' } }
+
+##
# @blockdev-snapshot-sync
#
# Generates a synchronous snapshot of a block device.
@@ -822,6 +857,25 @@
{ 'command': 'drive-backup', 'data': 'DriveBackup' }
##
+# @blockdev-backup
+#
+# Start a point-in-time copy of a block device to a new destination. The
+# status of ongoing blockdev-backup operations can be checked with
+# query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
+# The operation can be stopped before it has completed using the
+# block-job-cancel command.
+#
+# For the arguments, see the documentation of BlockdevBackup.
+#
+# Returns: Nothing on success.
+# If @device or @target is not a valid block device, DeviceNotFound.
+#
+# Since 2.3
+##
+{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' }
+
+
+##
# @query-named-block-nodes
#
# Get the named block driver list
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 3348782..a7bb90b 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1094,6 +1094,48 @@ Example:
"sync": "full",
"target": "backup.img" } }
<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "blockdev-backup",
+ .args_type = "sync:s,device:B,target:B,speed:i?,"
+ "on-source-error:s?,on-target-error:s?",
+ .mhandler.cmd_new = qmp_marshal_input_blockdev_backup,
+ },
+
+SQMP
+blockdev-backup
+------------
+
+The device version of drive-backup: this command takes an existing named device
+as backup target.
+
+Arguments:
+
+- "device": the name of the device which should be copied.
+ (json-string)
+- "target": the name of the backup target device. (json-string)
+- "sync": what parts of the disk image should be copied to the destination;
+ possibilities include "full" for all the disk, "top" for only the
+ sectors allocated in the topmost image, or "none" to only replicate
+ new I/O (MirrorSyncMode).
+- "speed": the maximum speed, in bytes per second (json-int, optional)
+- "on-source-error": the action to take on an error on the source, default
+ 'report'. 'stop' and 'enospc' can only be used
+ if the block device supports io-status.
+ (BlockdevOnError, optional)
+- "on-target-error": the action to take on an error on the target, default
+ 'report' (no limitations, since this applies to
+ a different block device than device).
+ (BlockdevOnError, optional)
+
+Example:
+-> { "execute": "blockdev-backup", "arguments": { "device": "src-id",
+ "sync": "full",
+ "target": "tgt-id" } }
+<- { "return": {} }
+
EQMP
{
--
1.9.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction
2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' Fam Zheng
@ 2014-12-17 12:51 ` Fam Zheng
2014-12-17 21:20 ` John Snow
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 4/4] qemu-iotests: Test blockdev-backup in 055 Fam Zheng
3 siblings, 1 reply; 10+ messages in thread
From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow
Signed-off-by: Fam Zheng <famz@redhat.com>
---
blockdev.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
qapi-schema.json | 2 ++
2 files changed, 81 insertions(+)
diff --git a/blockdev.c b/blockdev.c
index dbbab1d..9f9ae88 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1559,6 +1559,79 @@ static void drive_backup_clean(BlkTransactionState *common)
}
}
+typedef struct BlockdevBackupState {
+ BlkTransactionState common;
+ BlockDriverState *bs;
+ BlockJob *job;
+ AioContext *aio_context;
+} BlockdevBackupState;
+
+static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp)
+{
+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ BlockdevBackup *backup;
+ BlockDriverState *bs, *target;
+ Error *local_err = NULL;
+
+ assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
+ backup = common->action->blockdev_backup;
+
+ bs = bdrv_find(backup->device);
+ if (!bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device);
+ return;
+ }
+
+ target = bdrv_find(backup->target);
+ if (!target) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target);
+ return;
+ }
+
+ /* AioContext is released in .clean() */
+ state->aio_context = bdrv_get_aio_context(bs);
+ if (state->aio_context != bdrv_get_aio_context(target)) {
+ state->aio_context = NULL;
+ error_setg(errp, "Backup between two IO threads is not implemented");
+ return;
+ }
+ aio_context_acquire(state->aio_context);
+
+ qmp_blockdev_backup(backup->device, backup->target,
+ backup->sync,
+ backup->has_speed, backup->speed,
+ backup->has_on_source_error, backup->on_source_error,
+ backup->has_on_target_error, backup->on_target_error,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ state->bs = bdrv_find(backup->device);
+ state->job = state->bs->job;
+}
+
+static void blockdev_backup_abort(BlkTransactionState *common)
+{
+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ BlockDriverState *bs = state->bs;
+
+ /* Only cancel if it's the job we started */
+ if (bs && bs->job && bs->job == state->job) {
+ block_job_cancel_sync(bs->job);
+ }
+}
+
+static void blockdev_backup_clean(BlkTransactionState *common)
+{
+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+
+ if (state->aio_context) {
+ aio_context_release(state->aio_context);
+ }
+}
+
static void abort_prepare(BlkTransactionState *common, Error **errp)
{
error_setg(errp, "Transaction aborted using Abort action");
@@ -1582,6 +1655,12 @@ static const BdrvActionOps actions[] = {
.abort = drive_backup_abort,
.clean = drive_backup_clean,
},
+ [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
+ .instance_size = sizeof(BlockdevBackupState),
+ .prepare = blockdev_backup_prepare,
+ .abort = blockdev_backup_abort,
+ .clean = blockdev_backup_clean,
+ },
[TRANSACTION_ACTION_KIND_ABORT] = {
.instance_size = sizeof(BlkTransactionState),
.prepare = abort_prepare,
diff --git a/qapi-schema.json b/qapi-schema.json
index 47d99cf..fbfc52f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1260,11 +1260,13 @@
# drive-backup since 1.6
# abort since 1.6
# blockdev-snapshot-internal-sync since 1.7
+# blockdev-backup since 2.3
##
{ 'union': 'TransactionAction',
'data': {
'blockdev-snapshot-sync': 'BlockdevSnapshot',
'drive-backup': 'DriveBackup',
+ 'blockdev-backup': 'BlockdevBackup',
'abort': 'Abort',
'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal'
} }
--
1.9.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v5 4/4] qemu-iotests: Test blockdev-backup in 055
2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng
` (2 preceding siblings ...)
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction Fam Zheng
@ 2014-12-17 12:51 ` Fam Zheng
3 siblings, 0 replies; 10+ messages in thread
From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow
This applies cases on drive-backup on blockdev-backup, except cases with
target format and mode.
Also add a case to check source == target.
Signed-off-by: Fam Zheng <famz@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
tests/qemu-iotests/055 | 211 +++++++++++++++++++++++++++++++++++++--------
tests/qemu-iotests/055.out | 4 +-
2 files changed, 176 insertions(+), 39 deletions(-)
diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055
index 0872444..e81d4d0 100755
--- a/tests/qemu-iotests/055
+++ b/tests/qemu-iotests/055
@@ -1,8 +1,8 @@
#!/usr/bin/env python
#
-# Tests for drive-backup
+# Tests for drive-backup and blockdev-backup
#
-# Copyright (C) 2013 Red Hat, Inc.
+# Copyright (C) 2013, 2014 Red Hat, Inc.
#
# Based on 041.
#
@@ -27,6 +27,7 @@ from iotests import qemu_img, qemu_io
test_img = os.path.join(iotests.test_dir, 'test.img')
target_img = os.path.join(iotests.test_dir, 'target.img')
+blockdev_target_img = os.path.join(iotests.test_dir, 'blockdev-target.img')
class TestSingleDrive(iotests.QMPTestCase):
image_len = 64 * 1024 * 1024 # MB
@@ -38,34 +39,41 @@ class TestSingleDrive(iotests.QMPTestCase):
qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img)
+ qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len))
- self.vm = iotests.VM().add_drive(test_img)
+ self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(test_img)
+ os.remove(blockdev_target_img)
try:
os.remove(target_img)
except OSError:
pass
- def test_cancel(self):
+ def do_test_cancel(self, cmd, target):
self.assert_no_active_block_jobs()
- result = self.vm.qmp('drive-backup', device='drive0',
- target=target_img, sync='full')
+ result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
self.assert_qmp(result, 'return', {})
event = self.cancel_and_wait()
self.assert_qmp(event, 'data/type', 'backup')
- def test_pause(self):
+ def test_cancel_drive_backup(self):
+ self.do_test_cancel('drive-backup', target_img)
+
+ def test_cancel_blockdev_backup(self):
+ self.do_test_cancel('blockdev-backup', 'drive1')
+
+ def do_test_pause(self, cmd, target, image):
self.assert_no_active_block_jobs()
self.vm.pause_drive('drive0')
- result = self.vm.qmp('drive-backup', device='drive0',
- target=target_img, sync='full')
+ result = self.vm.qmp(cmd, device='drive0',
+ target=target, sync='full')
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('block-job-pause', device='drive0')
@@ -86,14 +94,25 @@ class TestSingleDrive(iotests.QMPTestCase):
self.wait_until_completed()
self.vm.shutdown()
- self.assertTrue(iotests.compare_images(test_img, target_img),
+ self.assertTrue(iotests.compare_images(test_img, image),
'target image does not match source after backup')
+ def test_pause_drive_backup(self):
+ self.do_test_pause('drive-backup', target_img, target_img)
+
+ def test_pause_blockdev_backup(self):
+ self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
+
def test_medium_not_found(self):
result = self.vm.qmp('drive-backup', device='ide1-cd0',
target=target_img, sync='full')
self.assert_qmp(result, 'error/class', 'GenericError')
+ def test_medium_not_found_blockdev_backup(self):
+ result = self.vm.qmp('blockdev-backup', device='ide1-cd0',
+ target='drive1', sync='full')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
def test_image_not_found(self):
result = self.vm.qmp('drive-backup', device='drive0',
target=target_img, sync='full', mode='existing')
@@ -105,31 +124,53 @@ class TestSingleDrive(iotests.QMPTestCase):
format='spaghetti-noodles')
self.assert_qmp(result, 'error/class', 'GenericError')
- def test_device_not_found(self):
- result = self.vm.qmp('drive-backup', device='nonexistent',
- target=target_img, sync='full')
+ def do_test_device_not_found(self, cmd, **args):
+ result = self.vm.qmp(cmd, **args)
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+ def test_device_not_found(self):
+ self.do_test_device_not_found('drive-backup', device='nonexistent',
+ target=target_img, sync='full')
+
+ self.do_test_device_not_found('blockdev-backup', device='nonexistent',
+ target='drive0', sync='full')
+
+ self.do_test_device_not_found('blockdev-backup', device='drive0',
+ target='nonexistent', sync='full')
+
+ self.do_test_device_not_found('blockdev-backup', device='nonexistent',
+ target='nonexistent', sync='full')
+
+ def test_target_is_source(self):
+ result = self.vm.qmp('blockdev-backup', device='drive0',
+ target='drive0', sync='full')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
class TestSetSpeed(iotests.QMPTestCase):
image_len = 80 * 1024 * 1024 # MB
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSetSpeed.image_len))
qemu_io('-f', iotests.imgfmt, '-c', 'write -P1 0 512', test_img)
- self.vm = iotests.VM().add_drive(test_img)
+ qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len))
+
+ self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(test_img)
- os.remove(target_img)
+ os.remove(blockdev_target_img)
+ try:
+ os.remove(target_img)
+ except OSError:
+ pass
- def test_set_speed(self):
+ def do_test_set_speed(self, cmd, target):
self.assert_no_active_block_jobs()
self.vm.pause_drive('drive0')
- result = self.vm.qmp('drive-backup', device='drive0',
- target=target_img, sync='full')
+ result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
self.assert_qmp(result, 'return', {})
# Default speed is 0
@@ -148,10 +189,10 @@ class TestSetSpeed(iotests.QMPTestCase):
event = self.cancel_and_wait(resume=True)
self.assert_qmp(event, 'data/type', 'backup')
- # Check setting speed in drive-backup works
+ # Check setting speed option works
self.vm.pause_drive('drive0')
- result = self.vm.qmp('drive-backup', device='drive0',
- target=target_img, sync='full', speed=4*1024*1024)
+ result = self.vm.qmp(cmd, device='drive0',
+ target=target, sync='full', speed=4*1024*1024)
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block-jobs')
@@ -161,18 +202,24 @@ class TestSetSpeed(iotests.QMPTestCase):
event = self.cancel_and_wait(resume=True)
self.assert_qmp(event, 'data/type', 'backup')
- def test_set_speed_invalid(self):
+ def test_set_speed_drive_backup(self):
+ self.do_test_set_speed('drive-backup', target_img)
+
+ def test_set_speed_blockdev_backup(self):
+ self.do_test_set_speed('blockdev-backup', 'drive1')
+
+ def do_test_set_speed_invalid(self, cmd, target):
self.assert_no_active_block_jobs()
- result = self.vm.qmp('drive-backup', device='drive0',
- target=target_img, sync='full', speed=-1)
+ result = self.vm.qmp(cmd, device='drive0',
+ target=target, sync='full', speed=-1)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_no_active_block_jobs()
self.vm.pause_drive('drive0')
- result = self.vm.qmp('drive-backup', device='drive0',
- target=target_img, sync='full')
+ result = self.vm.qmp(cmd, device='drive0',
+ target=target, sync='full')
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
@@ -181,6 +228,12 @@ class TestSetSpeed(iotests.QMPTestCase):
event = self.cancel_and_wait(resume=True)
self.assert_qmp(event, 'data/type', 'backup')
+ def test_set_speed_invalid_drive_backup(self):
+ self.do_test_set_speed_invalid('drive-backup', target_img)
+
+ def test_set_speed_invalid_blockdev_backup(self):
+ self.do_test_set_speed_invalid('blockdev-backup', 'drive1')
+
class TestSingleTransaction(iotests.QMPTestCase):
image_len = 64 * 1024 * 1024 # MB
@@ -190,41 +243,50 @@ class TestSingleTransaction(iotests.QMPTestCase):
qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img)
+ qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len))
- self.vm = iotests.VM().add_drive(test_img)
+ self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(test_img)
+ os.remove(blockdev_target_img)
try:
os.remove(target_img)
except OSError:
pass
- def test_cancel(self):
+ def do_test_cancel(self, cmd, target):
self.assert_no_active_block_jobs()
result = self.vm.qmp('transaction', actions=[{
- 'type': 'drive-backup',
+ 'type': cmd,
'data': { 'device': 'drive0',
- 'target': target_img,
+ 'target': target,
'sync': 'full' },
}
])
+
self.assert_qmp(result, 'return', {})
event = self.cancel_and_wait()
self.assert_qmp(event, 'data/type', 'backup')
- def test_pause(self):
+ def test_cancel_drive_backup(self):
+ self.do_test_cancel('drive-backup', target_img)
+
+ def test_cancel_blockdev_backup(self):
+ self.do_test_cancel('blockdev-backup', 'drive1')
+
+ def do_test_pause(self, cmd, target, image):
self.assert_no_active_block_jobs()
self.vm.pause_drive('drive0')
result = self.vm.qmp('transaction', actions=[{
- 'type': 'drive-backup',
+ 'type': cmd,
'data': { 'device': 'drive0',
- 'target': target_img,
+ 'target': target,
'sync': 'full' },
}
])
@@ -248,19 +310,31 @@ class TestSingleTransaction(iotests.QMPTestCase):
self.wait_until_completed()
self.vm.shutdown()
- self.assertTrue(iotests.compare_images(test_img, target_img),
+ self.assertTrue(iotests.compare_images(test_img, image),
'target image does not match source after backup')
- def test_medium_not_found(self):
+ def test_pause_drive_backup(self):
+ self.do_test_pause('drive-backup', target_img, target_img)
+
+ def test_pause_blockdev_backup(self):
+ self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
+
+ def do_test_medium_not_found(self, cmd, target):
result = self.vm.qmp('transaction', actions=[{
- 'type': 'drive-backup',
+ 'type': cmd,
'data': { 'device': 'ide1-cd0',
- 'target': target_img,
+ 'target': target,
'sync': 'full' },
}
])
self.assert_qmp(result, 'error/class', 'GenericError')
+ def test_medium_not_found_drive_backup(self):
+ self.do_test_medium_not_found('drive-backup', target_img)
+
+ def test_medium_not_found_blockdev_backup(self):
+ self.do_test_medium_not_found('blockdev-backup', 'drive1')
+
def test_image_not_found(self):
result = self.vm.qmp('transaction', actions=[{
'type': 'drive-backup',
@@ -283,6 +357,43 @@ class TestSingleTransaction(iotests.QMPTestCase):
])
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+ result = self.vm.qmp('transaction', actions=[{
+ 'type': 'blockdev-backup',
+ 'data': { 'device': 'nonexistent',
+ 'target': 'drive1',
+ 'sync': 'full' },
+ }
+ ])
+ self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+
+ result = self.vm.qmp('transaction', actions=[{
+ 'type': 'blockdev-backup',
+ 'data': { 'device': 'drive0',
+ 'target': 'nonexistent',
+ 'sync': 'full' },
+ }
+ ])
+ self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+
+ result = self.vm.qmp('transaction', actions=[{
+ 'type': 'blockdev-backup',
+ 'data': { 'device': 'nonexistent',
+ 'target': 'nonexistent',
+ 'sync': 'full' },
+ }
+ ])
+ self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+
+ def test_target_is_source(self):
+ result = self.vm.qmp('transaction', actions=[{
+ 'type': 'blockdev-backup',
+ 'data': { 'device': 'drive0',
+ 'target': 'drive0',
+ 'sync': 'full' },
+ }
+ ])
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
def test_abort(self):
result = self.vm.qmp('transaction', actions=[{
'type': 'drive-backup',
@@ -298,5 +409,31 @@ class TestSingleTransaction(iotests.QMPTestCase):
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_no_active_block_jobs()
+ result = self.vm.qmp('transaction', actions=[{
+ 'type': 'blockdev-backup',
+ 'data': { 'device': 'nonexistent',
+ 'target': 'drive1',
+ 'sync': 'full' },
+ }, {
+ 'type': 'Abort',
+ 'data': {},
+ }
+ ])
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_no_active_block_jobs()
+
+ result = self.vm.qmp('transaction', actions=[{
+ 'type': 'blockdev-backup',
+ 'data': { 'device': 'drive0',
+ 'target': 'nonexistent',
+ 'sync': 'full' },
+ }, {
+ 'type': 'Abort',
+ 'data': {},
+ }
+ ])
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_no_active_block_jobs()
+
if __name__ == '__main__':
iotests.main(supported_fmts=['raw', 'qcow2'])
diff --git a/tests/qemu-iotests/055.out b/tests/qemu-iotests/055.out
index 6323079..42314e9 100644
--- a/tests/qemu-iotests/055.out
+++ b/tests/qemu-iotests/055.out
@@ -1,5 +1,5 @@
-..............
+........................
----------------------------------------------------------------------
-Ran 14 tests
+Ran 24 tests
OK
--
1.9.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup'
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' Fam Zheng
@ 2014-12-17 20:53 ` John Snow
2014-12-18 10:22 ` Fam Zheng
0 siblings, 1 reply; 10+ messages in thread
From: John Snow @ 2014-12-17 20:53 UTC (permalink / raw)
To: Fam Zheng, qemu-devel
Cc: Kevin Wolf, Markus Armbruster, Stefan Hajnoczi, mreitz
On 12/17/2014 07:51 AM, Fam Zheng wrote:
> Similar to drive-backup, but this command uses a device id as target
> instead of creating/opening an image file.
>
> Also add blocker on target bs, since the target is also a named device
> now.
>
> Add check and report error for bs == target which became possible but is
> an illegal case with introduction of blockdev-backup.
>
> Signed-off-by: Fam Zheng <famz@redhat.com>
> ---
> block/backup.c | 28 +++++++++++++++++++++++++++
> blockdev.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> qapi/block-core.json | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> qmp-commands.hx | 42 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 178 insertions(+)
>
> diff --git a/block/backup.c b/block/backup.c
> index 792e655..1c535b1 100644
> --- a/block/backup.c
> +++ b/block/backup.c
> @@ -360,6 +360,7 @@ static void coroutine_fn backup_run(void *opaque)
> hbitmap_free(job->bitmap);
>
> bdrv_iostatus_disable(target);
> + bdrv_op_unblock_all(target, job->common.blocker);
>
> data = g_malloc(sizeof(*data));
> data->ret = ret;
> @@ -379,6 +380,11 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
> assert(target);
> assert(cb);
>
> + if (bs == target) {
> + error_setg(errp, "Source and target cannot be the same");
> + return;
> + }
> +
> if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
> on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
> !bdrv_iostatus_is_enabled(bs)) {
> @@ -386,6 +392,26 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
> return;
> }
>
> + if (!bdrv_is_inserted(bs)) {
> + error_setg(errp, "Device is not inserted: %s",
> + bdrv_get_device_name(bs));
> + return;
> + }
> +
> + if (!bdrv_is_inserted(target)) {
> + error_setg(errp, "Device is not inserted: %s",
> + bdrv_get_device_name(target));
> + return;
> + }
> +
> + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
> + return;
> + }
> +
> + if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
> + return;
> + }
> +
> len = bdrv_getlength(bs);
> if (len < 0) {
> error_setg_errno(errp, -len, "unable to get length for '%s'",
> @@ -399,6 +425,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
> return;
> }
>
> + bdrv_op_block_all(target, job->common.blocker);
> +
> job->on_source_error = on_source_error;
> job->on_target_error = on_target_error;
> job->target = target;
> diff --git a/blockdev.c b/blockdev.c
> index 5651a8e..dbbab1d 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -2240,6 +2240,8 @@ void qmp_drive_backup(const char *device, const char *target,
> aio_context = bdrv_get_aio_context(bs);
> aio_context_acquire(aio_context);
>
> + /* Although backup_run has this check too, we need to use bs->drv below, so
> + * do an early check redundantly. */
> if (!bdrv_is_inserted(bs)) {
> error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
> goto out;
> @@ -2256,6 +2258,7 @@ void qmp_drive_backup(const char *device, const char *target,
> }
> }
>
> + /* Early check to avoid creating target */
> if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
> goto out;
> }
> @@ -2323,6 +2326,57 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
> return bdrv_named_nodes_list();
> }
>
> +void qmp_blockdev_backup(const char *device, const char *target,
> + enum MirrorSyncMode sync,
> + bool has_speed, int64_t speed,
> + bool has_on_source_error,
> + BlockdevOnError on_source_error,
> + bool has_on_target_error,
> + BlockdevOnError on_target_error,
> + Error **errp)
> +{
> + BlockDriverState *bs;
> + BlockDriverState *target_bs;
> + Error *local_err = NULL;
> + AioContext *aio_context;
> +
> + if (!has_speed) {
> + speed = 0;
> + }
> + if (!has_on_source_error) {
> + on_source_error = BLOCKDEV_ON_ERROR_REPORT;
> + }
> + if (!has_on_target_error) {
> + on_target_error = BLOCKDEV_ON_ERROR_REPORT;
> + }
> +
> + bs = bdrv_find(device);
> + if (!bs) {
> + error_set(errp, QERR_DEVICE_NOT_FOUND, device);
> + return;
> + }
> +
> + aio_context = bdrv_get_aio_context(bs);
> + aio_context_acquire(aio_context);
> +
> + target_bs = bdrv_find(target);
> + if (!target_bs) {
> + error_set(errp, QERR_DEVICE_NOT_FOUND, target);
> + goto out;
> + }
> +
> + bdrv_ref(target_bs);
> + bdrv_set_aio_context(target_bs, bdrv_get_aio_context(bs));
why call bdrv_get_aio_context(bs) again instead of just using
aio_context? Will this cause issues?
> + backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
> + block_job_cb, bs, &local_err);
> + if (local_err != NULL) {
> + bdrv_unref(target_bs);
> + error_propagate(errp, local_err);
> + }
> +out:
> + aio_context_release(aio_context);
> +}
> +
> #define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
>
> void qmp_drive_mirror(const char *device, const char *target,
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 6e8db15..d9f0598 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -703,6 +703,41 @@
> '*on-target-error': 'BlockdevOnError' } }
>
> ##
> +# @BlockdevBackup
> +#
> +# @device: the name of the device which should be copied.
> +#
> +# @target: the name of the backup target device.
> +#
> +# @sync: what parts of the disk image should be copied to the destination
> +# (all the disk, only the sectors allocated in the topmost image, or
> +# only new I/O).
> +#
> +# @speed: #optional the maximum speed, in bytes per second. The default is 0,
> +# for unlimited.
> +#
> +# @on-source-error: #optional the action to take on an error on the source,
> +# default 'report'. 'stop' and 'enospc' can only be used
> +# if the block device supports io-status (see BlockInfo).
> +#
> +# @on-target-error: #optional the action to take on an error on the target,
> +# default 'report' (no limitations, since this applies to
> +# a different block device than @device).
> +#
> +# Note that @on-source-error and @on-target-error only affect background I/O.
> +# If an error occurs during a guest write request, the device's rerror/werror
> +# actions will be used.
> +#
> +# Since: 2.3
> +##
> +{ 'type': 'BlockdevBackup',
> + 'data': { 'device': 'str', 'target': 'str',
> + 'sync': 'MirrorSyncMode',
> + '*speed': 'int',
> + '*on-source-error': 'BlockdevOnError',
> + '*on-target-error': 'BlockdevOnError' } }
> +
> +##
> # @blockdev-snapshot-sync
> #
> # Generates a synchronous snapshot of a block device.
> @@ -822,6 +857,25 @@
> { 'command': 'drive-backup', 'data': 'DriveBackup' }
>
> ##
> +# @blockdev-backup
> +#
> +# Start a point-in-time copy of a block device to a new destination. The
> +# status of ongoing blockdev-backup operations can be checked with
> +# query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
> +# The operation can be stopped before it has completed using the
> +# block-job-cancel command.
> +#
> +# For the arguments, see the documentation of BlockdevBackup.
> +#
> +# Returns: Nothing on success.
> +# If @device or @target is not a valid block device, DeviceNotFound.
> +#
> +# Since 2.3
> +##
> +{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' }
> +
> +
> +##
> # @query-named-block-nodes
> #
> # Get the named block driver list
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 3348782..a7bb90b 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -1094,6 +1094,48 @@ Example:
> "sync": "full",
> "target": "backup.img" } }
> <- { "return": {} }
> +
> +EQMP
> +
> + {
> + .name = "blockdev-backup",
> + .args_type = "sync:s,device:B,target:B,speed:i?,"
> + "on-source-error:s?,on-target-error:s?",
> + .mhandler.cmd_new = qmp_marshal_input_blockdev_backup,
> + },
> +
> +SQMP
> +blockdev-backup
> +------------
^ Pad out the dashes to match the line length of the command. Mentioning
only because Eric got me on that one last time :)
> +
> +The device version of drive-backup: this command takes an existing named device
> +as backup target.
> +
> +Arguments:
> +
> +- "device": the name of the device which should be copied.
> + (json-string)
> +- "target": the name of the backup target device. (json-string)
> +- "sync": what parts of the disk image should be copied to the destination;
> + possibilities include "full" for all the disk, "top" for only the
> + sectors allocated in the topmost image, or "none" to only replicate
> + new I/O (MirrorSyncMode).
> +- "speed": the maximum speed, in bytes per second (json-int, optional)
> +- "on-source-error": the action to take on an error on the source, default
> + 'report'. 'stop' and 'enospc' can only be used
> + if the block device supports io-status.
> + (BlockdevOnError, optional)
> +- "on-target-error": the action to take on an error on the target, default
> + 'report' (no limitations, since this applies to
> + a different block device than device).
> + (BlockdevOnError, optional)
> +
> +Example:
> +-> { "execute": "blockdev-backup", "arguments": { "device": "src-id",
> + "sync": "full",
> + "target": "tgt-id" } }
> +<- { "return": {} }
> +
> EQMP
>
> {
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng
@ 2014-12-17 21:18 ` Eric Blake
0 siblings, 0 replies; 10+ messages in thread
From: Eric Blake @ 2014-12-17 21:18 UTC (permalink / raw)
To: Fam Zheng, qemu-devel
Cc: Kevin Wolf, jsnow, Markus Armbruster, Stefan Hajnoczi, mreitz
[-- Attachment #1: Type: text/plain, Size: 776 bytes --]
On 12/17/2014 05:51 AM, Fam Zheng wrote:
> Signed-off-by: Fam Zheng <famz@redhat.com>
> ---
> qapi-schema.json | 6 ++++++
> 1 file changed, 6 insertions(+)
Reviewed-by: Eric Blake <eblake@redhat.com>
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 563b4ad..47d99cf 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1254,6 +1254,12 @@
> #
> # A discriminated record of operations that can be performed with
> # @transaction.
> +#
> +# Since 1.1
> +#
> +# drive-backup since 1.6
> +# abort since 1.6
> +# blockdev-snapshot-internal-sync since 1.7
> ##
> { 'union': 'TransactionAction',
> 'data': {
>
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 539 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction Fam Zheng
@ 2014-12-17 21:20 ` John Snow
2014-12-18 10:31 ` Fam Zheng
0 siblings, 1 reply; 10+ messages in thread
From: John Snow @ 2014-12-17 21:20 UTC (permalink / raw)
To: Fam Zheng
Cc: kwolf >> Kevin Wolf, mrei >> Max Reitz, qemu-devel,
stefan Hajnoczi, arm >> Markus Armbruster
On 12/17/2014 07:51 AM, Fam Zheng wrote:
> Signed-off-by: Fam Zheng <famz@redhat.com>
> ---
> blockdev.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> qapi-schema.json | 2 ++
> 2 files changed, 81 insertions(+)
>
> diff --git a/blockdev.c b/blockdev.c
> index dbbab1d..9f9ae88 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -1559,6 +1559,79 @@ static void drive_backup_clean(BlkTransactionState *common)
> }
> }
>
> +typedef struct BlockdevBackupState {
> + BlkTransactionState common;
> + BlockDriverState *bs;
> + BlockJob *job;
> + AioContext *aio_context;
> +} BlockdevBackupState;
> +
> +static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp)
> +{
> + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
> + BlockdevBackup *backup;
> + BlockDriverState *bs, *target;
> + Error *local_err = NULL;
> +
> + assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
> + backup = common->action->blockdev_backup;
> +
> + bs = bdrv_find(backup->device);
> + if (!bs) {
> + error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device);
> + return;
> + }
> +
> + target = bdrv_find(backup->target);
> + if (!target) {
> + error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target);
> + return;
> + }
> +
> + /* AioContext is released in .clean() */
> + state->aio_context = bdrv_get_aio_context(bs);
> + if (state->aio_context != bdrv_get_aio_context(target)) {
> + state->aio_context = NULL;
> + error_setg(errp, "Backup between two IO threads is not implemented");
> + return;
> + }
> + aio_context_acquire(state->aio_context);
> +
> + qmp_blockdev_backup(backup->device, backup->target,
> + backup->sync,
> + backup->has_speed, backup->speed,
> + backup->has_on_source_error, backup->on_source_error,
> + backup->has_on_target_error, backup->on_target_error,
> + &local_err);
> + if (local_err) {
> + error_propagate(errp, local_err);
> + return;
> + }
> +
> + state->bs = bdrv_find(backup->device);
Just some questions:
why not use 'bs' instead of calling the function again? Granted, it
should work a second time if it worked once.
> + state->job = state->bs->job;
Why stash both bs and bs->job? Can the BDS change what its job member
points to between .prepare() and .abort() ?
> +}
> +
> +static void blockdev_backup_abort(BlkTransactionState *common)
> +{
> + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
> + BlockDriverState *bs = state->bs;
> +
> + /* Only cancel if it's the job we started */
> + if (bs && bs->job && bs->job == state->job) {
> + block_job_cancel_sync(bs->job);
> + }
> +}
> +
> +static void blockdev_backup_clean(BlkTransactionState *common)
> +{
> + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
> +
> + if (state->aio_context) {
> + aio_context_release(state->aio_context);
> + }
> +}
> +
> static void abort_prepare(BlkTransactionState *common, Error **errp)
> {
> error_setg(errp, "Transaction aborted using Abort action");
> @@ -1582,6 +1655,12 @@ static const BdrvActionOps actions[] = {
> .abort = drive_backup_abort,
> .clean = drive_backup_clean,
> },
> + [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
> + .instance_size = sizeof(BlockdevBackupState),
> + .prepare = blockdev_backup_prepare,
> + .abort = blockdev_backup_abort,
> + .clean = blockdev_backup_clean,
> + },
> [TRANSACTION_ACTION_KIND_ABORT] = {
> .instance_size = sizeof(BlkTransactionState),
> .prepare = abort_prepare,
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 47d99cf..fbfc52f 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1260,11 +1260,13 @@
> # drive-backup since 1.6
> # abort since 1.6
> # blockdev-snapshot-internal-sync since 1.7
> +# blockdev-backup since 2.3
> ##
> { 'union': 'TransactionAction',
> 'data': {
> 'blockdev-snapshot-sync': 'BlockdevSnapshot',
> 'drive-backup': 'DriveBackup',
> + 'blockdev-backup': 'BlockdevBackup',
> 'abort': 'Abort',
> 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal'
> } }
>
At any rate, regardless of the answers to my questions:
Reviewed-by: John Snow <jsnow@redhat.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup'
2014-12-17 20:53 ` John Snow
@ 2014-12-18 10:22 ` Fam Zheng
0 siblings, 0 replies; 10+ messages in thread
From: Fam Zheng @ 2014-12-18 10:22 UTC (permalink / raw)
To: John Snow
Cc: Kevin Wolf, mreitz, qemu-devel, Stefan Hajnoczi,
Markus Armbruster
On Wed, 12/17 15:53, John Snow wrote:
> >+ aio_context = bdrv_get_aio_context(bs);
> >+ aio_context_acquire(aio_context);
> >+
> >+ target_bs = bdrv_find(target);
> >+ if (!target_bs) {
> >+ error_set(errp, QERR_DEVICE_NOT_FOUND, target);
> >+ goto out;
> >+ }
> >+
> >+ bdrv_ref(target_bs);
> >+ bdrv_set_aio_context(target_bs, bdrv_get_aio_context(bs));
>
> why call bdrv_get_aio_context(bs) again instead of just using aio_context?
> Will this cause issues?
Could be simply aio_context.
[...]
> >diff --git a/qmp-commands.hx b/qmp-commands.hx
> >index 3348782..a7bb90b 100644
> >--- a/qmp-commands.hx
> >+++ b/qmp-commands.hx
> >@@ -1094,6 +1094,48 @@ Example:
> > "sync": "full",
> > "target": "backup.img" } }
> > <- { "return": {} }
> >+
> >+EQMP
> >+
> >+ {
> >+ .name = "blockdev-backup",
> >+ .args_type = "sync:s,device:B,target:B,speed:i?,"
> >+ "on-source-error:s?,on-target-error:s?",
> >+ .mhandler.cmd_new = qmp_marshal_input_blockdev_backup,
> >+ },
> >+
> >+SQMP
> >+blockdev-backup
> >+------------
>
> ^ Pad out the dashes to match the line length of the command. Mentioning
> only because Eric got me on that one last time :)
OK, thanks.
Fam
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction
2014-12-17 21:20 ` John Snow
@ 2014-12-18 10:31 ` Fam Zheng
0 siblings, 0 replies; 10+ messages in thread
From: Fam Zheng @ 2014-12-18 10:31 UTC (permalink / raw)
To: John Snow
Cc: Kevin Wolf, Markus Armbruster, qemu-devel, stefan Hajnoczi,
Max Reitz
On Wed, 12/17 16:20, John Snow wrote:
> On 12/17/2014 07:51 AM, Fam Zheng wrote:
> >Signed-off-by: Fam Zheng <famz@redhat.com>
> >---
> > blockdev.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > qapi-schema.json | 2 ++
> > 2 files changed, 81 insertions(+)
> >
> >diff --git a/blockdev.c b/blockdev.c
> >index dbbab1d..9f9ae88 100644
> >--- a/blockdev.c
> >+++ b/blockdev.c
> >@@ -1559,6 +1559,79 @@ static void drive_backup_clean(BlkTransactionState *common)
> > }
> > }
> >
> >+typedef struct BlockdevBackupState {
> >+ BlkTransactionState common;
> >+ BlockDriverState *bs;
> >+ BlockJob *job;
> >+ AioContext *aio_context;
> >+} BlockdevBackupState;
> >+
> >+static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp)
> >+{
> >+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
> >+ BlockdevBackup *backup;
> >+ BlockDriverState *bs, *target;
> >+ Error *local_err = NULL;
> >+
> >+ assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
> >+ backup = common->action->blockdev_backup;
> >+
> >+ bs = bdrv_find(backup->device);
> >+ if (!bs) {
> >+ error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device);
> >+ return;
> >+ }
> >+
> >+ target = bdrv_find(backup->target);
> >+ if (!target) {
> >+ error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target);
> >+ return;
> >+ }
> >+
> >+ /* AioContext is released in .clean() */
> >+ state->aio_context = bdrv_get_aio_context(bs);
> >+ if (state->aio_context != bdrv_get_aio_context(target)) {
> >+ state->aio_context = NULL;
> >+ error_setg(errp, "Backup between two IO threads is not implemented");
> >+ return;
> >+ }
> >+ aio_context_acquire(state->aio_context);
> >+
> >+ qmp_blockdev_backup(backup->device, backup->target,
> >+ backup->sync,
> >+ backup->has_speed, backup->speed,
> >+ backup->has_on_source_error, backup->on_source_error,
> >+ backup->has_on_target_error, backup->on_target_error,
> >+ &local_err);
> >+ if (local_err) {
> >+ error_propagate(errp, local_err);
> >+ return;
> >+ }
> >+
> >+ state->bs = bdrv_find(backup->device);
>
> Just some questions:
>
> why not use 'bs' instead of calling the function again? Granted, it should
> work a second time if it worked once.
Yeah, it should be "bs", a mistake but not wrong.
>
> >+ state->job = state->bs->job;
>
> Why stash both bs and bs->job? Can the BDS change what its job member points
> to between .prepare() and .abort() ?
Copied from drive_backup_prepare. Actually there is a comment about this in
blockdev_backup_abort below:
>
> >+}
> >+
> >+static void blockdev_backup_abort(BlkTransactionState *common)
> >+{
> >+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
> >+ BlockDriverState *bs = state->bs;
> >+
> >+ /* Only cancel if it's the job we started */
> >+ if (bs && bs->job && bs->job == state->job) {
I think there is one case this is useful: a transaction containing two block
jobs, which *will* fail and abort(). With this check, only the right (started)
block_job_cancel_sync is called.
> >+ block_job_cancel_sync(bs->job);
> >+ }
> >+}
> >+
> >+static void blockdev_backup_clean(BlkTransactionState *common)
> >+{
> >+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
> >+
> >+ if (state->aio_context) {
> >+ aio_context_release(state->aio_context);
> >+ }
> >+}
> >+
> > static void abort_prepare(BlkTransactionState *common, Error **errp)
> > {
> > error_setg(errp, "Transaction aborted using Abort action");
> >@@ -1582,6 +1655,12 @@ static const BdrvActionOps actions[] = {
> > .abort = drive_backup_abort,
> > .clean = drive_backup_clean,
> > },
> >+ [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
> >+ .instance_size = sizeof(BlockdevBackupState),
> >+ .prepare = blockdev_backup_prepare,
> >+ .abort = blockdev_backup_abort,
> >+ .clean = blockdev_backup_clean,
> >+ },
> > [TRANSACTION_ACTION_KIND_ABORT] = {
> > .instance_size = sizeof(BlkTransactionState),
> > .prepare = abort_prepare,
> >diff --git a/qapi-schema.json b/qapi-schema.json
> >index 47d99cf..fbfc52f 100644
> >--- a/qapi-schema.json
> >+++ b/qapi-schema.json
> >@@ -1260,11 +1260,13 @@
> > # drive-backup since 1.6
> > # abort since 1.6
> > # blockdev-snapshot-internal-sync since 1.7
> >+# blockdev-backup since 2.3
> > ##
> > { 'union': 'TransactionAction',
> > 'data': {
> > 'blockdev-snapshot-sync': 'BlockdevSnapshot',
> > 'drive-backup': 'DriveBackup',
> >+ 'blockdev-backup': 'BlockdevBackup',
> > 'abort': 'Abort',
> > 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal'
> > } }
> >
>
> At any rate, regardless of the answers to my questions:
> Reviewed-by: John Snow <jsnow@redhat.com>
>
Thanks! I'm going to fix the above bdrv_find() while keeping your rev-by. I
hope that is OK for you. :)
Fam
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2014-12-18 10:30 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng
2014-12-17 21:18 ` Eric Blake
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' Fam Zheng
2014-12-17 20:53 ` John Snow
2014-12-18 10:22 ` Fam Zheng
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction Fam Zheng
2014-12-17 21:20 ` John Snow
2014-12-18 10:31 ` Fam Zheng
2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 4/4] qemu-iotests: Test blockdev-backup in 055 Fam Zheng
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.