From patchwork Mon Dec 17 06:25:08 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wayne Xia X-Patchwork-Id: 206780 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id D5C142C008D for ; Mon, 17 Dec 2012 17:33:04 +1100 (EST) Received: from localhost ([::1]:44680 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TkUG6-0003aK-W8 for incoming@patchwork.ozlabs.org; Mon, 17 Dec 2012 01:33:03 -0500 Received: from eggs.gnu.org ([208.118.235.92]:52686) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TkUEo-0002pC-Sr for qemu-devel@nongnu.org; Mon, 17 Dec 2012 01:32:48 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TkUB0-0006tM-0F for qemu-devel@nongnu.org; Mon, 17 Dec 2012 01:31:42 -0500 Received: from e23smtp04.au.ibm.com ([202.81.31.146]:57245) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TkUAy-0006sd-Tc for qemu-devel@nongnu.org; Mon, 17 Dec 2012 01:27:45 -0500 Received: from /spool/local by e23smtp04.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 17 Dec 2012 16:21:18 +1000 Received: from d23dlp01.au.ibm.com (202.81.31.203) by e23smtp04.au.ibm.com (202.81.31.210) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 17 Dec 2012 16:21:15 +1000 Received: from d23relay05.au.ibm.com (d23relay05.au.ibm.com [9.190.235.152]) by d23dlp01.au.ibm.com (Postfix) with ESMTP id 7CBB92CE804A for ; Mon, 17 Dec 2012 17:27:39 +1100 (EST) Received: from d23av04.au.ibm.com (d23av04.au.ibm.com [9.190.235.139]) by d23relay05.au.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id qBH6GS1m60424332 for ; Mon, 17 Dec 2012 17:16:28 +1100 Received: from d23av04.au.ibm.com (loopback [127.0.0.1]) by d23av04.au.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id qBH6RdHL031909 for ; Mon, 17 Dec 2012 17:27:39 +1100 Received: from RedHat62GAWSWenchao (wenchaox.cn.ibm.com [9.115.122.150]) by d23av04.au.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id qBH6PGrj027846; Mon, 17 Dec 2012 17:27:37 +1100 From: Wenchao Xia To: qemu-devel@nongnu.org Date: Mon, 17 Dec 2012 14:25:08 +0800 Message-Id: <1355725509-5429-6-git-send-email-xiawenc@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1355725509-5429-1-git-send-email-xiawenc@linux.vnet.ibm.com> References: <1355725509-5429-1-git-send-email-xiawenc@linux.vnet.ibm.com> X-Content-Scanned: Fidelis XPS MAILER x-cbid: 12121706-9264-0000-0000-000002DBE65F X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 202.81.31.146 Cc: kwolf@redhat.com, aliguori@us.ibm.com, stefanha@gmail.com, blauwirbel@gmail.com, pbonzini@redhat.com, Wenchao Xia Subject: [Qemu-devel] [PATCH 5/6] snapshot: qmp interface X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch changes the implemtion of external block snapshot to use internal unified interface, now qmp handler just do a translation of request and submit. Also internal block snapshot qmp interface was added. Now add external snapshot, add/delete internal snapshot can be started in their own qmp interface or a group of BlockAction in qmp transaction interface. Signed-off-by: Wenchao Xia --- blockdev.c | 352 +++++++++++++++++++++++++++++++----------------------- qapi-schema.json | 102 ++++++++++++++-- 2 files changed, 292 insertions(+), 162 deletions(-) diff --git a/blockdev.c b/blockdev.c index 1c38c67..d1bbe23 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1175,6 +1175,195 @@ delete_and_fail: return -1; } +/* translation from qmp commands */ +static int fill_blk_trsact_create_sync(BlockdevSnapshot *create_sync, + BlkTransactionStatesSync *st_sync, + SNTime *time, + const char *time_str, + Error **errp) +{ + const char *format = "qcow2"; + enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; + enum SnapshotType type = SNAPSHOT_TYPE_EXTERNAL; + BlockDriverState *bs; + + const char *device = create_sync->device; + const char *name = create_sync->snapshot_file; + if (create_sync->has_mode) { + mode = create_sync->mode; + } + if (create_sync->has_format) { + format = create_sync->format; + } + if (create_sync->has_type) { + type = create_sync->type; + } + + /* find the target bs */ + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return -1; + } + + switch (type) { + case SNAPSHOT_TYPE_INTERNAL: + st_sync->type = BLK_SNAPSHOT_INTERNAL; + break; + case SNAPSHOT_TYPE_EXTERNAL: + st_sync->type = BLK_SNAPSHOT_EXTERNAL; + break; + default: + st_sync->type = BLK_SNAPSHOT_NOSUPPORT; + error_setg(errp, "Device %s requested invalid snapshot" + " type %d.", device, type); + return -1; + } + + switch (mode) { + case NEW_IMAGE_MODE_EXISTING: + st_sync->use_existing = TRUE; + break; + case NEW_IMAGE_MODE_ABSOLUTE_PATHS: + st_sync->use_existing = FALSE; + break; + default: + error_setg(errp, "Device %s requested invalid snapshot" + " mode %d.", device, mode); + return -1; + } + + if (st_sync->type == BLK_SNAPSHOT_INTERNAL) { + /* internal case, if caller need create new one with default string */ + if (((name == NULL) || (name[0] == '\0')) && + (!st_sync->use_existing)) { + st_sync->internal.sn_name = time_str; + } else { + st_sync->internal.sn_name = name; + } + st_sync->internal.bs = bs; + st_sync->internal.time = *time; + } else if (st_sync->type == BLK_SNAPSHOT_EXTERNAL) { + st_sync->external.new_image_file = name; + st_sync->external.format = format; + st_sync->external.old_bs = bs; + } + + return 0; +} +static int fill_blk_trsact_delete_sync(BlockdevSnapshotDelete *delete_sync, + BlkTransactionStatesSync *st_sync, + Error **errp) +{ + enum SnapshotType type = SNAPSHOT_TYPE_EXTERNAL; + BlockDriverState *bs; + + const char *device = delete_sync->device; + const char *name = delete_sync->snapshot_file; + if (delete_sync->has_type) { + type = delete_sync->type; + } + + /* find the target bs */ + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return -1; + } + + switch (type) { + case SNAPSHOT_TYPE_INTERNAL: + st_sync->type = BLK_SNAPSHOT_INTERNAL; + break; + case SNAPSHOT_TYPE_EXTERNAL: + st_sync->type = BLK_SNAPSHOT_EXTERNAL; + break; + default: + st_sync->type = BLK_SNAPSHOT_NOSUPPORT; + error_setg(errp, "Device %s requested invalid snapshot" + " type %d.", device, type); + return -1; + } + + if (st_sync->type == BLK_SNAPSHOT_INTERNAL) { + st_sync->internal.sn_name = name; + st_sync->internal.bs = bs; + } else if (st_sync->type == BLK_SNAPSHOT_EXTERNAL) { + st_sync->external.new_image_file = name; + st_sync->external.old_bs = bs; + } + + return 0; +} + +static int fill_blk_trsact(BlockdevAction *dev_info, + BlkTransactionStates *states, + SNTime *time, + const char *time_str, + Error **errp) +{ + switch (dev_info->kind) { + case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: + states->st_sync.op = BLK_SN_SYNC_CREATE; + states->async = FALSE; + return fill_blk_trsact_create_sync(dev_info->blockdev_snapshot_sync, + &states->st_sync, time, time_str, errp); + break; + case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_DELETE_SYNC: + states->st_sync.op = BLK_SN_SYNC_DELETE; + states->async = FALSE; + return fill_blk_trsact_delete_sync( + dev_info->blockdev_snapshot_delete_sync, + &states->st_sync, errp); + break; + default: + abort(); + } + return 0; +} + +/* Here this funtion prepare the request list, submit for atomic snapshot. */ +void qmp_transaction(BlockdevActionList *dev_list, Error **errp) +{ + BlockdevActionList *dev_entry = dev_list; + BlkTransactionStates *states; + int ret; + + BlkTransactionStatesList *snap_bdrv_states = blk_trans_st_list_new(); + + /* translate qmp request */ + /* for group snapshot create we use same time stamp here */ + SNTime time = get_sn_time(); + char time_str[256]; + generate_sn_name_from_time(&time, time_str, sizeof(time_str)); + while (NULL != dev_entry) { + BlockdevAction *dev_info = NULL; + + dev_info = dev_entry->value; + dev_entry = dev_entry->next; + + states = blk_trans_st_new(); + ret = fill_blk_trsact(dev_info, states, &time, time_str, errp); + if (ret < 0) { + blk_trans_st_delete(&states); + goto exit; + } + + ret = add_transaction(snap_bdrv_states, states, errp); + if (ret < 0) { + blk_trans_st_delete(&states); + goto exit; + } + } + + /* submit to internal API, no need to check return for no following + action now. */ + submit_transaction(snap_bdrv_states, errp); + +exit: + blk_trans_st_list_delete(&snap_bdrv_states); +} + static void blockdev_do_action(int kind, void *data, Error **errp) { BlockdevAction action; @@ -1190,6 +1379,7 @@ static void blockdev_do_action(int kind, void *data, Error **errp) void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file, bool has_format, const char *format, bool has_mode, enum NewImageMode mode, + bool has_type, enum SnapshotType type, Error **errp) { BlockdevSnapshot snapshot = { @@ -1199,162 +1389,28 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file, .format = (char *) format, .has_mode = has_mode, .mode = mode, + .has_type = has_type, + .type = type, }; blockdev_do_action(BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, &snapshot, errp); } - -/* New and old BlockDriverState structs for group snapshots */ -typedef struct BlkTransactionStates { - BlockDriverState *old_bs; - BlockDriverState *new_bs; - QSIMPLEQ_ENTRY(BlkTransactionStates) entry; -} BlkTransactionStates; - -/* - * 'Atomic' group snapshots. The snapshots are taken as a set, and if any fail - * then we do not pivot any of the devices in the group, and abandon the - * snapshots - */ -void qmp_transaction(BlockdevActionList *dev_list, Error **errp) +void qmp_blockdev_snapshot_delete_sync(const char *device, + const char *snapshot_file, + bool has_type, enum SnapshotType type, + Error **errp) { - int ret = 0; - BlockdevActionList *dev_entry = dev_list; - BlkTransactionStates *states, *next; - Error *local_err = NULL; - - QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states; - QSIMPLEQ_INIT(&snap_bdrv_states); - - /* drain all i/o before any snapshots */ - bdrv_drain_all(); - - /* We don't do anything in this loop that commits us to the snapshot */ - while (NULL != dev_entry) { - BlockdevAction *dev_info = NULL; - BlockDriver *proto_drv; - BlockDriver *drv; - int flags; - enum NewImageMode mode; - const char *new_image_file; - const char *device; - const char *format = "qcow2"; - - dev_info = dev_entry->value; - dev_entry = dev_entry->next; - - states = g_malloc0(sizeof(BlkTransactionStates)); - QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry); - - switch (dev_info->kind) { - case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: - device = dev_info->blockdev_snapshot_sync->device; - if (!dev_info->blockdev_snapshot_sync->has_mode) { - dev_info->blockdev_snapshot_sync->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; - } - new_image_file = dev_info->blockdev_snapshot_sync->snapshot_file; - if (dev_info->blockdev_snapshot_sync->has_format) { - format = dev_info->blockdev_snapshot_sync->format; - } - mode = dev_info->blockdev_snapshot_sync->mode; - break; - default: - abort(); - } - - drv = bdrv_find_format(format); - if (!drv) { - error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto delete_and_fail; - } - - states->old_bs = bdrv_find(device); - if (!states->old_bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); - goto delete_and_fail; - } - - if (!bdrv_is_inserted(states->old_bs)) { - error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); - goto delete_and_fail; - } - - if (bdrv_in_use(states->old_bs)) { - error_set(errp, QERR_DEVICE_IN_USE, device); - goto delete_and_fail; - } - - if (!bdrv_is_read_only(states->old_bs)) { - if (bdrv_flush(states->old_bs)) { - error_set(errp, QERR_IO_ERROR); - goto delete_and_fail; - } - } - - flags = states->old_bs->open_flags; - - proto_drv = bdrv_find_protocol(new_image_file); - if (!proto_drv) { - error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto delete_and_fail; - } - - /* create new image w/backing file */ - if (mode != NEW_IMAGE_MODE_EXISTING) { - bdrv_img_create(new_image_file, format, - states->old_bs->filename, - states->old_bs->drv->format_name, - NULL, -1, flags, &local_err); - if (error_is_set(&local_err)) { - error_propagate(errp, local_err); - goto delete_and_fail; - } - } - - /* We will manually add the backing_hd field to the bs later */ - states->new_bs = bdrv_new(""); - ret = bdrv_open(states->new_bs, new_image_file, - flags | BDRV_O_NO_BACKING, drv); - if (ret != 0) { - error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file); - goto delete_and_fail; - } - } - - - /* Now we are going to do the actual pivot. Everything up to this point - * is reversible, but we are committed at this point */ - QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) { - /* This removes our old bs from the bdrv_states, and adds the new bs */ - bdrv_append(states->new_bs, states->old_bs); - /* We don't need (or want) to use the transactional - * bdrv_reopen_multiple() across all the entries at once, because we - * don't want to abort all of them if one of them fails the reopen */ - bdrv_reopen(states->new_bs, states->new_bs->open_flags & ~BDRV_O_RDWR, - NULL); - } - - /* success */ - goto exit; - -delete_and_fail: - /* - * failure, and it is all-or-none; abandon each new bs, and keep using - * the original bs for all images - */ - QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) { - if (states->new_bs) { - bdrv_delete(states->new_bs); - } - } -exit: - QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) { - g_free(states); - } + BlockdevSnapshotDelete snapshot_delete = { + .device = (char *) device, + .snapshot_file = (char *) snapshot_file, + .has_type = has_type, + .type = type, + }; + blockdev_do_action(BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_DELETE_SYNC, + &snapshot_delete, errp); } - static void eject_device(BlockDriverState *bs, int force, Error **errp) { if (bdrv_in_use(bs)) { diff --git a/qapi-schema.json b/qapi-schema.json index 5dfa052..46f4c6b 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1458,17 +1458,36 @@ { 'command': 'block_resize', 'data': { 'device': 'str', 'size': 'int' }} ## +# @SnapshotType +# +# An enumeration that tells QEMU what type of snapshot to access. +# +# @internal: QEMU should use internal snapshot in format such as qcow2. +# +# @external: QEMU should use backing file chain. +# +# Since: 1.4. +## +{ 'enum': 'SnapshotType' + 'data': [ 'internal', 'external' ] } + +## # @NewImageMode # # An enumeration that tells QEMU how to set the backing file path in -# a new image file. +# a new image file, or how to use internal snapshot record. # -# @existing: QEMU should look for an existing image file. +# @existing: QEMU should look for an existing image file or internal snapshot +# record. In external snapshot case, qemu will skip create new image +# file, In internal snapshot case qemu will try use the existing +# one. if not found operation would fail. # -# @absolute-paths: QEMU should create a new image with absolute paths -# for the backing file. +# @absolute-paths: QEMU should create a new image with absolute paths for +# the backing file in external snapshot case, or create a new +# snapshot record in internal snapshot case which will +# overwrite internal snapshot record if it already exist. # -# Since: 1.1 +# Since: 1.1, internal support since 1.4. ## { 'enum': 'NewImageMode' 'data': [ 'existing', 'absolute-paths' ] } @@ -1478,16 +1497,39 @@ # # @device: the name of the device to generate the snapshot from. # -# @snapshot-file: the target of the new image. A new file will be created. +# @snapshot-file: the target name of the snapshot. In external case, it is +# the new file's name, A new file will be created. In internal +# case, it is the internal snapshot record's name and if it is +# 'blank' name will be generated according to time. # # @format: #optional the format of the snapshot image, default is 'qcow2'. # -# @mode: #optional whether and how QEMU should create a new image, default is -# 'absolute-paths'. +# @mode: #optional whether QEMU should create a new snapshot or use existing +# one, default is 'absolute-paths'. +# +# @type: #optional internal snapshot or external, default is 'external'. +# ## { 'type': 'BlockdevSnapshot', 'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str', - '*mode': 'NewImageMode' } } + '*mode': 'NewImageMode', '*type': 'SnapshotType'} } + +## +# @BlockdevSnapshotDelete +# +# @device: the name of the device to delete the snapshot from. +# +# @snapshot-file: the target name of the snapshot. In external case, it is +# the file's name to be merged, In internal case, it is the +# internal snapshot record's name. +# +# @type: #optional internal snapshot or external, default is +# 'external', note that delete 'external' snapshot is not supported +# now for that it is the same to commit it. +## +{ 'type': 'BlockdevSnapshotDelete', + 'data': { 'device': 'str', 'snapshot-file': 'str', + '*type': 'SnapshotType'} } ## # @BlockdevAction @@ -1498,6 +1540,7 @@ { 'union': 'BlockdevAction', 'data': { 'blockdev-snapshot-sync': 'BlockdevSnapshot', + 'blockdev-snapshot-delete-sync': 'BlockdevSnapshotDelete', } } ## @@ -1530,23 +1573,54 @@ # # @device: the name of the device to generate the snapshot from. # -# @snapshot-file: the target of the new image. If the file exists, or if it -# is a device, the snapshot will be created in the existing -# file/device. If does not exist, a new file will be created. +# @snapshot-file: the target name of the snapshot. In external case, it is +# the new file's name, A new file will be created. If the file +# exists, or if it is a device, the snapshot will be created in +# the existing file/device. If does not exist, a new file will +# be created. In internal case, it is the internal snapshot +# record's name, if it is 'blank' name will be generated +# according to time. # # @format: #optional the format of the snapshot image, default is 'qcow2'. # # @mode: #optional whether and how QEMU should create a new image, default is # 'absolute-paths'. # +# @type: #optional internal snapshot or external, default is +# 'external'. +# # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound # -# Since 0.14.0 +# Since 0.14.0, internal snapshot supprt since 1.4. ## { 'command': 'blockdev-snapshot-sync', 'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str', - '*mode': 'NewImageMode'} } + '*mode': 'NewImageMode', '*type': 'SnapshotType'} } + +## +# @blockdev-snapshot-delete-sync +# +# Delete a synchronous snapshot of a block device. +# +# @device: the name of the device to delete the snapshot from. +# +# @snapshot-file: the target name of the snapshot. In external case, it is +# the file's name to be merged, In internal case, it is the +# internal snapshot record's name. +# +# @type: #optional internal snapshot or external, default is +# 'external', note that delete 'external' snapshot is not supported +# now for that it is the same to commit it. +# +# Returns: nothing on success +# If @device is not a valid block device, DeviceNotFound +# +# Since 1.4 +## +{ 'command': 'blockdev-snapshot-delete-sync', + 'data': { 'device': 'str', 'snapshot-file': 'str', + '*type': 'SnapshotType'} } ## # @human-monitor-command: