From: Fam Zheng <famz@redhat.com>
To: qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>, Jeff Cody <jcody@redhat.com>,
Max Reitz <mreitz@redhat.com>,
vsementsov@parallels.com, stefanha@redhat.com,
John Snow <jsnow@redhat.com>
Subject: [Qemu-devel] [PATCH v6 11/14] block/backup: support block job transactions
Date: Tue, 15 Sep 2015 14:11:50 +0800 [thread overview]
Message-ID: <1442297513-7001-12-git-send-email-famz@redhat.com> (raw)
In-Reply-To: <1442297513-7001-1-git-send-email-famz@redhat.com>
From: Stefan Hajnoczi <stefanha@redhat.com>
Join the transaction when the 'transactional-cancel' QMP argument is
true.
This ensures that the sync bitmap is not thrown away if another block
job in the transaction is cancelled or fails. This is critical so
incremental backup with multiple disks can be retried in case of
cancellation/failure.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Fam Zheng <famz@redhat.com>
---
block/backup.c | 25 +++++++--
blockdev.c | 132 +++++++++++++++++++++++++++++++++++-----------
include/block/block_int.h | 3 +-
qapi-schema.json | 4 +-
qapi/block-core.json | 29 ++++++++++
5 files changed, 155 insertions(+), 38 deletions(-)
diff --git a/block/backup.c b/block/backup.c
index 609b199..5880116 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -227,11 +227,29 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
}
}
+static void backup_commit(BlockJob *job)
+{
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+ if (s->sync_bitmap) {
+ backup_cleanup_sync_bitmap(s, 0);
+ }
+}
+
+static void backup_abort(BlockJob *job)
+{
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+ if (s->sync_bitmap) {
+ backup_cleanup_sync_bitmap(s, -1);
+ }
+}
+
static const BlockJobDriver backup_job_driver = {
.instance_size = sizeof(BackupBlockJob),
.job_type = BLOCK_JOB_TYPE_BACKUP,
.set_speed = backup_set_speed,
.iostatus_reset = backup_iostatus_reset,
+ .commit = backup_commit,
+ .abort = backup_abort,
};
static BlockErrorAction backup_error_action(BackupBlockJob *job,
@@ -444,10 +462,6 @@ static void coroutine_fn backup_run(void *opaque)
/* wait until pending backup_do_cow() calls have completed */
qemu_co_rwlock_wrlock(&job->flush_rwlock);
qemu_co_rwlock_unlock(&job->flush_rwlock);
-
- if (job->sync_bitmap) {
- backup_cleanup_sync_bitmap(job, ret);
- }
hbitmap_free(job->bitmap);
bdrv_iostatus_disable(target);
@@ -464,7 +478,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque,
- Error **errp)
+ BlockJobTxn *txn, Error **errp)
{
int64_t len;
@@ -546,6 +560,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
sync_bitmap : NULL;
job->common.len = len;
job->common.co = qemu_coroutine_create(backup_run);
+ block_job_txn_add_job(txn, &job->common);
qemu_coroutine_enter(job->common.co, job);
return;
diff --git a/blockdev.c b/blockdev.c
index 93a9eef..79207c8 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1592,16 +1592,31 @@ typedef struct DriveBackupState {
BlockJob *job;
} DriveBackupState;
+static void do_drive_backup(const char *device, const char *target,
+ bool has_format, const char *format,
+ enum MirrorSyncMode sync,
+ bool has_mode, enum NewImageMode mode,
+ bool has_speed, int64_t speed,
+ bool has_bitmap, const char *bitmap,
+ bool has_on_source_error,
+ BlockdevOnError on_source_error,
+ bool has_on_target_error,
+ BlockdevOnError on_target_error,
+ BlockJobTxn *txn, Error **errp);
+
static void drive_backup_prepare(BlkActionState *common, Error **errp)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
BlockDriverState *bs;
BlockBackend *blk;
+ DriveBackupTxn *backup_txn;
DriveBackup *backup;
+ BlockJobTxn *txn = NULL;
Error *local_err = NULL;
assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
- backup = common->action->drive_backup;
+ backup_txn = common->action->drive_backup;
+ backup = backup_txn->base;
blk = blk_by_name(backup->device);
if (!blk) {
@@ -1615,15 +1630,20 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
state->aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(state->aio_context);
- qmp_drive_backup(backup->device, backup->target,
- backup->has_format, backup->format,
- backup->sync,
- backup->has_mode, backup->mode,
- backup->has_speed, backup->speed,
- backup->has_bitmap, backup->bitmap,
- backup->has_on_source_error, backup->on_source_error,
- backup->has_on_target_error, backup->on_target_error,
- &local_err);
+ if (backup_txn->has_transactional_cancel &&
+ backup_txn->transactional_cancel) {
+ txn = common->block_job_txn;
+ }
+
+ do_drive_backup(backup->device, backup->target,
+ backup->has_format, backup->format,
+ backup->sync,
+ backup->has_mode, backup->mode,
+ backup->has_speed, backup->speed,
+ backup->has_bitmap, backup->bitmap,
+ backup->has_on_source_error, backup->on_source_error,
+ backup->has_on_target_error, backup->on_target_error,
+ txn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -1660,16 +1680,28 @@ typedef struct BlockdevBackupState {
AioContext *aio_context;
} BlockdevBackupState;
+static void do_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,
+ BlockJobTxn *txn, Error **errp);
+
static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
{
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ BlockdevBackupTxn *backup_txn;
BlockdevBackup *backup;
BlockDriverState *bs, *target;
BlockBackend *blk;
+ BlockJobTxn *txn = NULL;
Error *local_err = NULL;
assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
- backup = common->action->blockdev_backup;
+ backup_txn = common->action->blockdev_backup;
+ backup = backup_txn->base;
blk = blk_by_name(backup->device);
if (!blk) {
@@ -1694,12 +1726,17 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
}
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 (backup_txn->has_transactional_cancel &&
+ backup_txn->transactional_cancel) {
+ txn = common->block_job_txn;
+ }
+
+ do_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,
+ txn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -2582,15 +2619,17 @@ out:
aio_context_release(aio_context);
}
-void qmp_drive_backup(const char *device, const char *target,
- bool has_format, const char *format,
- enum MirrorSyncMode sync,
- bool has_mode, enum NewImageMode mode,
- bool has_speed, int64_t speed,
- bool has_bitmap, const char *bitmap,
- bool has_on_source_error, BlockdevOnError on_source_error,
- bool has_on_target_error, BlockdevOnError on_target_error,
- Error **errp)
+static void do_drive_backup(const char *device, const char *target,
+ bool has_format, const char *format,
+ enum MirrorSyncMode sync,
+ bool has_mode, enum NewImageMode mode,
+ bool has_speed, int64_t speed,
+ bool has_bitmap, const char *bitmap,
+ bool has_on_source_error,
+ BlockdevOnError on_source_error,
+ bool has_on_target_error,
+ BlockdevOnError on_target_error,
+ BlockJobTxn *txn, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
@@ -2707,7 +2746,7 @@ void qmp_drive_backup(const char *device, const char *target,
backup_start(bs, target_bs, speed, sync, bmap,
on_source_error, on_target_error,
- block_job_cb, bs, &local_err);
+ block_job_cb, bs, txn, &local_err);
if (local_err != NULL) {
bdrv_unref(target_bs);
error_propagate(errp, local_err);
@@ -2718,19 +2757,37 @@ out:
aio_context_release(aio_context);
}
+void qmp_drive_backup(const char *device, const char *target,
+ bool has_format, const char *format,
+ enum MirrorSyncMode sync,
+ bool has_mode, enum NewImageMode mode,
+ bool has_speed, int64_t speed,
+ bool has_bitmap, const char *bitmap,
+ bool has_on_source_error, BlockdevOnError on_source_error,
+ bool has_on_target_error, BlockdevOnError on_target_error,
+ Error **errp)
+{
+ return do_drive_backup(device, target, has_format, format, sync,
+ has_mode, mode, has_speed, speed,
+ has_bitmap, bitmap,
+ has_on_source_error, on_source_error,
+ has_on_target_error, on_target_error,
+ NULL, errp);
+}
+
BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
{
return bdrv_named_nodes_list(errp);
}
-void qmp_blockdev_backup(const char *device, const char *target,
+void do_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)
+ BlockJobTxn *txn, Error **errp)
{
BlockBackend *blk;
BlockDriverState *bs;
@@ -2768,7 +2825,7 @@ void qmp_blockdev_backup(const char *device, const char *target,
bdrv_ref(target_bs);
bdrv_set_aio_context(target_bs, aio_context);
backup_start(bs, target_bs, speed, sync, NULL, on_source_error,
- on_target_error, block_job_cb, bs, &local_err);
+ on_target_error, block_job_cb, bs, txn, &local_err);
if (local_err != NULL) {
bdrv_unref(target_bs);
error_propagate(errp, local_err);
@@ -2777,6 +2834,21 @@ out:
aio_context_release(aio_context);
}
+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)
+{
+ do_blockdev_backup(device, target, sync, has_speed, speed,
+ has_on_source_error, on_source_error,
+ has_on_target_error, on_target_error,
+ NULL, errp);
+}
+
void qmp_drive_mirror(const char *device, const char *target,
bool has_format, const char *format,
bool has_node_name, const char *node_name,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 281d790..6fd07a2 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -643,6 +643,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
* @on_target_error: The action to take upon error writing to the target.
* @cb: Completion function for the job.
* @opaque: Opaque pointer value passed to @cb.
+ * @txn: Transaction that this job is part of (may be NULL).
*
* Start a backup operation on @bs. Clusters in @bs are written to @target
* until the job is cancelled or manually completed.
@@ -653,7 +654,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque,
- Error **errp);
+ BlockJobTxn *txn, Error **errp);
void blk_dev_change_media_cb(BlockBackend *blk, bool load);
bool blk_dev_has_removable_media(BlockBackend *blk);
diff --git a/qapi-schema.json b/qapi-schema.json
index 46d464f..b872458 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1499,8 +1499,8 @@
{ 'union': 'TransactionAction',
'data': {
'blockdev-snapshot-sync': 'BlockdevSnapshot',
- 'drive-backup': 'DriveBackup',
- 'blockdev-backup': 'BlockdevBackup',
+ 'drive-backup': 'DriveBackupTxn',
+ 'blockdev-backup': 'BlockdevBackupTxn',
'abort': 'Abort',
'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal',
'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index bb2189e..c57f9a0 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -736,6 +736,11 @@
# default 'report' (no limitations, since this applies to
# a different block device than @device).
#
+# @transactional-cancel: #optional whether failure or cancellation of other
+# block jobs with @transactional-cancel true in the same
+# transaction causes the whole group to cancel.
+# (Since 2.5)
+#
# 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.
@@ -750,6 +755,18 @@
'*on-target-error': 'BlockdevOnError' } }
##
+# @DriveBackupTxn
+#
+# @transactional-cancel: #optional whether failure or cancellation of other
+# block jobs with @transactional-cancel true in the same
+# transaction causes the whole group to cancel.
+#
+# Since: 2.5
+##
+{ 'struct': 'DriveBackupTxn', 'base': 'DriveBackup',
+ 'data': {'*transactional-cancel': 'bool' } }
+
+##
# @BlockdevBackup
#
# @device: the name of the device which should be copied.
@@ -785,6 +802,18 @@
'*on-target-error': 'BlockdevOnError' } }
##
+# @BlockdevBackupTxn
+#
+# @transactional-cancel: #optional whether failure or cancellation of other
+# block jobs with @transactional-cancel true in the same
+# transaction causes the whole group to cancel.
+#
+# Since: 2.5
+##
+{ 'struct': 'BlockdevBackupTxn', 'base': 'BlockdevBackup',
+ 'data': {'*transactional-cancel': 'bool' } }
+
+##
# @blockdev-snapshot-sync
#
# Generates a synchronous snapshot of a block device.
--
2.4.3
next prev parent reply other threads:[~2015-09-15 6:12 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-15 6:11 [Qemu-devel] [PATCH v6 00/14] block: incremental backup transactions using BlockJobTxn Fam Zheng
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 01/14] qapi: Add transaction support to block-dirty-bitmap operations Fam Zheng
2015-09-15 14:36 ` Eric Blake
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 02/14] iotests: add transactional incremental backup test Fam Zheng
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 03/14] block: rename BlkTransactionState and BdrvActionOps Fam Zheng
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 04/14] backup: Extract dirty bitmap handling as a separate function Fam Zheng
2015-09-18 11:02 ` Max Reitz
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 05/14] blockjob: Introduce reference count Fam Zheng
2015-09-21 22:19 ` John Snow
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 06/14] blockjob: Add .commit and .abort block job actions Fam Zheng
2015-09-21 22:29 ` John Snow
2015-09-22 2:15 ` Fam Zheng
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 07/14] blockjob: Add "completed" and "ret" in BlockJob Fam Zheng
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 08/14] blockjob: Simplify block_job_finish_sync Fam Zheng
2015-09-21 22:50 ` John Snow
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 09/14] block: Add block job transactions Fam Zheng
2015-09-18 11:16 ` Max Reitz
2015-09-21 23:23 ` John Snow
2015-09-22 2:32 ` Fam Zheng
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 10/14] blockdev: make BlockJobTxn available to qmp 'transaction' Fam Zheng
2015-09-18 11:20 ` Max Reitz
2015-09-15 6:11 ` Fam Zheng [this message]
2015-09-15 14:43 ` [Qemu-devel] [PATCH v6 11/14] block/backup: support block job transactions Eric Blake
2015-09-18 11:40 ` Max Reitz
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 12/14] iotests: 124 - transactional failure test Fam Zheng
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 13/14] qmp-commands.hx: Update the supported 'transaction' operations Fam Zheng
2015-09-15 6:11 ` [Qemu-devel] [PATCH v6 14/14] tests: add BlockJobTxn unit test Fam Zheng
2015-09-18 11:45 ` Max Reitz
2015-09-22 2:11 ` Fam Zheng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1442297513-7001-12-git-send-email-famz@redhat.com \
--to=famz@redhat.com \
--cc=jcody@redhat.com \
--cc=jsnow@redhat.com \
--cc=kwolf@redhat.com \
--cc=mreitz@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@redhat.com \
--cc=vsementsov@parallels.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).