qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 2/3] block: adjust qmp_transaction to be extenda


From: Wenchao Xia
Subject: Re: [Qemu-devel] [PATCH 2/3] block: adjust qmp_transaction to be extendable
Date: Wed, 03 Apr 2013 13:51:43 +0800
User-agent: Mozilla/5.0 (Windows NT 5.1; rv:17.0) Gecko/20130307 Thunderbird/17.0.4

于 2013-4-2 21:55, Kevin Wolf 写道:
Am 01.04.2013 um 12:01 hat Wenchao Xia geschrieben:
   Now code for external snapshot are packaged as one case
in qmp_transaction, so later other operation could be added.
   The logic in qmp_transaction is changed a bit: Original code
tries to create all images first and then update all *bdrv
once together, new code create and update one *bdrv one time,
and use bdrv_deappend() to rollback on fail. This allows mixing
different kind of requests in qmp_transaction() later.

Signed-off-by: Wenchao Xia <address@hidden>
---
  blockdev.c |  250 +++++++++++++++++++++++++++++++++++++-----------------------
  1 files changed, 153 insertions(+), 97 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 8cdc9ce..75416fb 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -779,9 +779,155 @@ void qmp_blockdev_snapshot_sync(const char *device, const 
char *snapshot_file,


  /* New and old BlockDriverState structs for group snapshots */
-typedef struct BlkTransactionStates {
+typedef struct BdrvActionOps {
+    int (*commit)(BlockdevAction *action, void **p_opaque, Error **errp);
+    void (*rollback)(BlockdevAction *action, void *opaque);
+    void (*clean)(BlockdevAction *action, void *opaque);
+} BdrvActionOps;

You don't really implement the transactional pattern that was used by
the original code (and is used elsewhere). It should work like this:

1. Prepare: This stage performs all operations that can fail. They are
    not made active yet. For example with snapshotting, open a new
    BlockDriver state, but don't change the backing file chain yet.

2. Commit: Activate the change. This operation can never fail. For this
    reason, you never have to undo anything done here.

3. Rollback: Basically just free everything that prepare did up to the
    error.

If you do it your way, you get into serious trouble if rollback involves
operations that can fail.

In the original code, here start the prepare:

  That is a clear comment, thanks. I changed the model since expecting
commit operation may need rollback, if not I am confident to keep
original model.
  Since bdrv_snapshot_delete() can fail, I guess assertion of its
success should be used in the rollback later.

@@ -806,125 +950,37 @@ void qmp_transaction(BlockdevActionList *dev_list, Error 
**errp)
      /* 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);

+        states->action = dev_info;
          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;
+            states->ops = &external_snapshot_ops;
              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, false);
-            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("");
-        /* TODO Inherit bs->options or only take explicit options with an
-         * extended QMP command? */
-        ret = bdrv_open(states->new_bs, new_image_file, NULL,
-                        flags | BDRV_O_NO_BACKING, drv);
-        if (ret != 0) {
-            error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
+        if (states->ops->commit(states->action, &states->opaque, errp)) {
              goto delete_and_fail;
          }
      }

The following block is the commit:

-
-    /* 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;

And this is rollback:

  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);
-        }
+        states->ops->rollback(states->action, states->opaque);
      }

Kevin



--
Best Regards

Wenchao Xia




reply via email to

[Prev in Thread] Current Thread [Next in Thread]