9ae3a8
From 2b05ad88980acb91357598f5ed041ec33aec0de6 Mon Sep 17 00:00:00 2001
9ae3a8
From: Kevin Wolf <kwolf@redhat.com>
9ae3a8
Date: Mon, 9 Sep 2013 14:27:56 +0200
9ae3a8
Subject: [PATCH 05/38] block: make all steps in qmp_transaction() as callback
9ae3a8
9ae3a8
RH-Author: Kevin Wolf <kwolf@redhat.com>
9ae3a8
Message-id: <1378736903-18489-6-git-send-email-kwolf@redhat.com>
9ae3a8
Patchwork-id: 54192
9ae3a8
O-Subject: [RHEL-7.0 qemu-kvm PATCH 05/32] block: make all steps in qmp_transaction() as callback
9ae3a8
Bugzilla: 1005818
9ae3a8
RH-Acked-by: Fam Zheng <famz@redhat.com>
9ae3a8
RH-Acked-by: Max Reitz <mreitz@redhat.com>
9ae3a8
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
9ae3a8
From: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
9ae3a8
9ae3a8
Bugzilla: 1005818
9ae3a8
9ae3a8
Make it easier to add other operations to qmp_transaction() by using
9ae3a8
callbacks, with external snapshots serving as an example implementation
9ae3a8
of the callbacks.
9ae3a8
9ae3a8
Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
9ae3a8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
Reviewed-by: Eric Blake <eblake@redhat.com>
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
(cherry picked from commit ba0c86a34e29b31ef360feda74c94200a5403fdd)
9ae3a8
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
---
9ae3a8
 blockdev.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++----------------
9ae3a8
 1 file changed, 71 insertions(+), 24 deletions(-)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 blockdev.c |   95 ++++++++++++++++++++++++++++++++++++++++++++---------------
9ae3a8
 1 files changed, 71 insertions(+), 24 deletions(-)
9ae3a8
9ae3a8
diff --git a/blockdev.c b/blockdev.c
9ae3a8
index b040f0f..b8521c7 100644
9ae3a8
--- a/blockdev.c
9ae3a8
+++ b/blockdev.c
9ae3a8
@@ -779,14 +779,43 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
9ae3a8
 
9ae3a8
 
9ae3a8
 /* New and old BlockDriverState structs for group snapshots */
9ae3a8
-typedef struct BlkTransactionStates {
9ae3a8
+
9ae3a8
+typedef struct BlkTransactionStates BlkTransactionStates;
9ae3a8
+
9ae3a8
+/* Only prepare() may fail. In a single transaction, only one of commit() or
9ae3a8
+   abort() will be called, clean() will always be called if it present. */
9ae3a8
+typedef struct BdrvActionOps {
9ae3a8
+    /* Size of state struct, in bytes. */
9ae3a8
+    size_t instance_size;
9ae3a8
+    /* Prepare the work, must NOT be NULL. */
9ae3a8
+    void (*prepare)(BlkTransactionStates *common, Error **errp);
9ae3a8
+    /* Commit the changes, must NOT be NULL. */
9ae3a8
+    void (*commit)(BlkTransactionStates *common);
9ae3a8
+    /* Abort the changes on fail, can be NULL. */
9ae3a8
+    void (*abort)(BlkTransactionStates *common);
9ae3a8
+    /* Clean up resource in the end, can be NULL. */
9ae3a8
+    void (*clean)(BlkTransactionStates *common);
9ae3a8
+} BdrvActionOps;
9ae3a8
+
9ae3a8
+/*
9ae3a8
+ * This structure must be arranged as first member in child type, assuming
9ae3a8
+ * that compiler will also arrange it to the same address with parent instance.
9ae3a8
+ * Later it will be used in free().
9ae3a8
+ */
9ae3a8
+struct BlkTransactionStates {
9ae3a8
+    BlockdevAction *action;
9ae3a8
+    const BdrvActionOps *ops;
9ae3a8
+    QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
9ae3a8
+};
9ae3a8
+
9ae3a8
+/* external snapshot private data */
9ae3a8
+typedef struct ExternalSnapshotStates {
9ae3a8
+    BlkTransactionStates common;
9ae3a8
     BlockDriverState *old_bs;
9ae3a8
     BlockDriverState *new_bs;
9ae3a8
-    QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
9ae3a8
-} BlkTransactionStates;
9ae3a8
+} ExternalSnapshotStates;
9ae3a8
 
9ae3a8
-static void external_snapshot_prepare(BlockdevAction *action,
9ae3a8
-                                      BlkTransactionStates *states,
9ae3a8
+static void external_snapshot_prepare(BlkTransactionStates *common,
9ae3a8
                                       Error **errp)
9ae3a8
 {
9ae3a8
     BlockDriver *proto_drv;
9ae3a8
@@ -797,6 +826,9 @@ static void external_snapshot_prepare(BlockdevAction *action,
9ae3a8
     const char *new_image_file;
9ae3a8
     const char *format = "qcow2";
9ae3a8
     enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
9ae3a8
+    ExternalSnapshotStates *states =
9ae3a8
+                             DO_UPCAST(ExternalSnapshotStates, common, common);
9ae3a8
+    BlockdevAction *action = common->action;
9ae3a8
 
9ae3a8
     /* get parameters */
9ae3a8
     g_assert(action->kind == BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
9ae3a8
@@ -871,8 +903,11 @@ static void external_snapshot_prepare(BlockdevAction *action,
9ae3a8
     }
9ae3a8
 }
9ae3a8
 
9ae3a8
-static void external_snapshot_commit(BlkTransactionStates *states)
9ae3a8
+static void external_snapshot_commit(BlkTransactionStates *common)
9ae3a8
 {
9ae3a8
+    ExternalSnapshotStates *states =
9ae3a8
+                             DO_UPCAST(ExternalSnapshotStates, common, common);
9ae3a8
+
9ae3a8
     /* This removes our old bs from the bdrv_states, and adds the new bs */
9ae3a8
     bdrv_append(states->new_bs, states->old_bs);
9ae3a8
     /* We don't need (or want) to use the transactional
9ae3a8
@@ -882,13 +917,24 @@ static void external_snapshot_commit(BlkTransactionStates *states)
9ae3a8
                 NULL);
9ae3a8
 }
9ae3a8
 
9ae3a8
-static void external_snapshot_abort(BlkTransactionStates *states)
9ae3a8
+static void external_snapshot_abort(BlkTransactionStates *common)
9ae3a8
 {
9ae3a8
+    ExternalSnapshotStates *states =
9ae3a8
+                             DO_UPCAST(ExternalSnapshotStates, common, common);
9ae3a8
     if (states->new_bs) {
9ae3a8
         bdrv_delete(states->new_bs);
9ae3a8
     }
9ae3a8
 }
9ae3a8
 
9ae3a8
+static const BdrvActionOps actions[] = {
9ae3a8
+    [BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
9ae3a8
+        .instance_size = sizeof(ExternalSnapshotStates),
9ae3a8
+        .prepare  = external_snapshot_prepare,
9ae3a8
+        .commit   = external_snapshot_commit,
9ae3a8
+        .abort = external_snapshot_abort,
9ae3a8
+    },
9ae3a8
+};
9ae3a8
+
9ae3a8
 /*
9ae3a8
  * 'Atomic' group snapshots.  The snapshots are taken as a set, and if any fail
9ae3a8
  *  then we do not pivot any of the devices in the group, and abandon the
9ae3a8
@@ -909,32 +955,28 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
9ae3a8
     /* We don't do anything in this loop that commits us to the snapshot */
9ae3a8
     while (NULL != dev_entry) {
9ae3a8
         BlockdevAction *dev_info = NULL;
9ae3a8
+        const BdrvActionOps *ops;
9ae3a8
 
9ae3a8
         dev_info = dev_entry->value;
9ae3a8
         dev_entry = dev_entry->next;
9ae3a8
 
9ae3a8
-        states = g_malloc0(sizeof(BlkTransactionStates));
9ae3a8
+        assert(dev_info->kind < ARRAY_SIZE(actions));
9ae3a8
+
9ae3a8
+        ops = &actions[dev_info->kind];
9ae3a8
+        states = g_malloc0(ops->instance_size);
9ae3a8
+        states->ops = ops;
9ae3a8
+        states->action = dev_info;
9ae3a8
         QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
9ae3a8
 
9ae3a8
-        switch (dev_info->kind) {
9ae3a8
-        case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
9ae3a8
-            external_snapshot_prepare(dev_info, states, errp);
9ae3a8
-            if (error_is_set(&local_err)) {
9ae3a8
-                error_propagate(errp, local_err);
9ae3a8
-                goto delete_and_fail;
9ae3a8
-            }
9ae3a8
-            break;
9ae3a8
-        default:
9ae3a8
-            abort();
9ae3a8
+        states->ops->prepare(states, &local_err);
9ae3a8
+        if (error_is_set(&local_err)) {
9ae3a8
+            error_propagate(errp, local_err);
9ae3a8
+            goto delete_and_fail;
9ae3a8
         }
9ae3a8
-
9ae3a8
     }
9ae3a8
 
9ae3a8
-
9ae3a8
-    /* Now we are going to do the actual pivot.  Everything up to this point
9ae3a8
-     * is reversible, but we are committed at this point */
9ae3a8
     QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
9ae3a8
-        external_snapshot_commit(states);
9ae3a8
+        states->ops->commit(states);
9ae3a8
     }
9ae3a8
 
9ae3a8
     /* success */
9ae3a8
@@ -946,10 +988,15 @@ delete_and_fail:
9ae3a8
     * the original bs for all images
9ae3a8
     */
9ae3a8
     QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
9ae3a8
-        external_snapshot_abort(states);
9ae3a8
+        if (states->ops->abort) {
9ae3a8
+            states->ops->abort(states);
9ae3a8
+        }
9ae3a8
     }
9ae3a8
 exit:
9ae3a8
     QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
9ae3a8
+        if (states->ops->clean) {
9ae3a8
+            states->ops->clean(states);
9ae3a8
+        }
9ae3a8
         g_free(states);
9ae3a8
     }
9ae3a8
 }
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8