From f86002aeec6fc0b2d9b3c18361b9c275a7e370ed Mon Sep 17 00:00:00 2001
From: Federico Simoncelli <fsimonce@redhat.com>
Date: Fri, 23 Mar 2012 12:12:56 -0300
Subject: [RHEL6 qemu-kvm PATCH 3/5] add mirroring to transaction

RH-Author: Federico Simoncelli <fsimonce@redhat.com>
Message-id: <1332504778-17403-12-git-send-email-fsimonce@redhat.com>
Patchwork-id: 38954
O-Subject: [RHEL6.3 qemu-kvm PATCH v6 11/13] add mirroring to transaction
Bugzilla: 802284
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>

From: Paolo Bonzini <pbonzini@redhat.com>

With it comes a new image creation mode, "no-backing-file", that can
be used to stream an image so that the destination does not need the
original image's backing file(s).

Both bdrv_append and blkmirror will set the backing_hd on the target,
even if the image is created without one, so that both streaming and
copy-on-write work properly (at least with qcow2 or qed, not raw).

Streaming mode works with the following gotchas:

- streaming will rewrite every bit of the source image;

- zero writes are not supported by the blkmirror driver, hence both
  the source and the destination image will grow to full size.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

BZ: 802284

Based on an patch posted upstream:
http://lists.gnu.org/archive/html/qemu-devel/2012-03/msg00901.html

Deviations from upstream:
 - drive-mirror transaction renamed with a downstream prefix
---
 blockdev.c       |   71 ++++++++++++++++++++++++++++++++++++++++++-----------
 qapi-schema.json |   22 ++++++++++++++++-
 qemu-monitor.hx  |   16 ++++++++++--
 3 files changed, 90 insertions(+), 19 deletions(-)

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
---
 blockdev.c       |   71 ++++++++++++++++++++++++++++++++++++++++++-----------
 qapi-schema.json |   22 ++++++++++++++++-
 qemu-monitor.hx  |   16 ++++++++++--
 3 files changed, 90 insertions(+), 19 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index ffc9d36..bdbb5a1 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -679,6 +679,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
     int ret = 0;
     BlockdevActionList *dev_entry = dev_list;
     BlkTransactionStates *states, *next;
+    char *new_source = NULL;
 
     QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states;
     QSIMPLEQ_INIT(&snap_bdrv_states);
@@ -690,12 +691,14 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
     while (NULL != dev_entry) {
         BlockdevAction *dev_info = NULL;
         BlockDriver *proto_drv;
-        BlockDriver *drv;
+        BlockDriver *target_drv;
+        BlockDriver *drv = NULL;
         int flags;
         enum NewImageMode mode;
         const char *new_image_file;
         const char *device;
         const char *format = "qcow2";
+        uint64_t size;
 
         dev_info = dev_entry->value;
         dev_entry = dev_entry->next;
@@ -714,16 +717,36 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
                 format = dev_info->blockdev_snapshot_sync->format;
             }
             mode = dev_info->blockdev_snapshot_sync->mode;
+            new_source = g_strdup(new_image_file);
             break;
+
+        case BLOCKDEV_ACTION_KIND___COM_REDHAT_DRIVE_MIRROR:
+            device = dev_info->__com_redhat_drive_mirror->device;
+            drv = bdrv_find_format("blkmirror");
+            if (!dev_info->__com_redhat_drive_mirror->has_mode) {
+                dev_info->__com_redhat_drive_mirror->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+            }
+            new_image_file = dev_info->__com_redhat_drive_mirror->target;
+            if (dev_info->__com_redhat_drive_mirror->has_format) {
+                format = dev_info->__com_redhat_drive_mirror->format;
+            }
+            mode = dev_info->__com_redhat_drive_mirror->mode;
+            new_source = g_strdup_printf("blkmirror:%s:%s", format,
+                                         dev_info->__com_redhat_drive_mirror->target);
+            break;
+
         default:
             abort();
         }
 
-        drv = bdrv_find_format(format);
-        if (!drv) {
+        target_drv = bdrv_find_format(format);
+        if (!target_drv) {
             error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
             goto delete_and_fail;
         }
+        if (!drv) {
+            drv = target_drv;
+        }
 
         states->old_bs = bdrv_find(device);
         if (!states->old_bs) {
@@ -747,7 +770,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
 
         flags = states->old_bs->open_flags;
 
-        proto_drv = bdrv_find_protocol(new_image_file);
+        proto_drv = bdrv_find_protocol(new_source);
         if (!proto_drv) {
             error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
             goto delete_and_fail;
@@ -759,25 +782,42 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
         }
 
         /* create new image w/backing file */
-        if (mode != NEW_IMAGE_MODE_EXISTING) {
-            ret = bdrv_img_create(new_image_file, format,
-                                  states->old_bs->filename,
-                                  states->old_bs->drv->format_name,
-                                  NULL, -1, flags);
-            if (ret) {
-                error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
-                goto delete_and_fail;
-            }
+        switch (mode) {
+            case NEW_IMAGE_MODE_EXISTING:
+                ret = 0;
+                break;
+            case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
+                ret = bdrv_img_create(new_image_file, format,
+                                      states->old_bs->filename,
+                                      states->old_bs->drv->format_name,
+                                      NULL, -1, flags);
+                break;
+            case NEW_IMAGE_MODE_NO_BACKING_FILE:
+                bdrv_get_geometry(states->old_bs, &size);
+                size *= 512;
+                ret = bdrv_img_create(new_image_file, format,
+                                      NULL, NULL, NULL, size, flags);
+                break;
+            default:
+                ret = -1;
+                break;
+        }
+
+        if (ret) {
+            error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
+            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,
+        ret = bdrv_open(states->new_bs, new_source,
                         flags | BDRV_O_NO_BACKING, drv);
         if (ret != 0) {
-            error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
+            error_set(errp, QERR_OPEN_FILE_FAILED, new_source);
             goto delete_and_fail;
         }
+        g_free(new_source);
+        new_source = NULL;
     }
 
 
@@ -805,6 +845,7 @@ exit:
     QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
         g_free(states);
     }
+    g_free(new_source);
     return;
 }
 #endif
diff --git a/qapi-schema.json b/qapi-schema.json
index f436cea..24f6b99 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -14,10 +14,12 @@
 # @absolute-paths: QEMU should create a new image with absolute paths
 # for the backing file.
 #
+# @no-backing-file: QEMU should create a new image with no backing file.
+#
 # Since: 1.1
 ##
 { 'enum': 'NewImageMode'
-  'data': [ 'existing', 'absolute-paths' ] }
+  'data': [ 'existing', 'absolute-paths', 'no-backing-file' ] }
 
 ##
 # @BlockdevSnapshot
@@ -36,6 +38,23 @@
             '*mode': 'NewImageMode' } }
 
 ##
+# @BlockdevMirror
+#
+# @device:  the name of the device to start mirroring.
+#
+# @target: the image that will start receiving writes for @device. A new
+#          file will be created unless @mode is "existing".
+#
+# @format: #optional the format of the target image, default is 'qcow2'.
+#
+# @mode: #optional whether and how QEMU should create a new image, default is
+# 'absolute-paths'.
+##
+{ 'type': 'BlockdevMirror',
+  'data': { 'device': 'str', 'target': 'str', '*format': 'str',
+            '*mode': 'NewImageMode' } }
+
+##
 # @BlockdevAction
 #
 # A discriminated record of operations that can be performed with
@@ -44,6 +63,7 @@
 { 'union': 'BlockdevAction',
   'data': {
        'blockdev-snapshot-sync': 'BlockdevSnapshot',
+       '__com.redhat_drive-mirror': 'BlockdevMirror',
    } }
 
 ##
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index a96b733..b492bce 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -1497,12 +1497,16 @@ SQMP
 transaction
 -----------
 
-Atomically operate on one or more block devices.  The only supported
-operation for now is snapshotting.  If there is any failure performing
+Atomically operate on one or more block devices, snapshotting them
+or enabling mirrored writes.  If there is any failure performing
 any of the operations, all snapshots for the group are abandoned, and
 the original disks pre-snapshot attempt are used.
 
+Mirrored writes keep the previous image file open, and start writing
+data also to the new file specified in the command.
+
 A list of dictionaries is accepted, that contains the actions to be performed.
+
 For snapshots this is the device, the file to use for the new snapshot,
 and the format.  The default format, if not specified, is qcow2.
 
@@ -1517,7 +1521,7 @@ Arguments:
 
 actions array:
     - "type": the operation to perform.  The only supported
-      value is "blockdev-snapshot-sync". (json-string)
+      values are "blockdev-snapshot-sync" and "mirror". (json-string)
     - "data": a dictionary.  The contents depend on the value
       of "type".  When "type" is "blockdev-snapshot-sync":
       - "device": device name to snapshot (json-string)
@@ -1525,6 +1529,12 @@ actions array:
       - "format": format of new image (json-string, optional)
       - "mode": whether and how QEMU should create the snapshot file
         (NewImageMode, optional, default "absolute-paths")
+      When "type" is "__com.redhat_drive-mirror":
+      - "device": device name to snapshot (json-string)
+      - "target": name of destination image file (json-string)
+      - "format": format of new image (json-string, optional)
+      - "mode": how QEMU should look for an existing image file
+        (NewImageMode, optional, default "absolute-paths")
 
 Example:
 
-- 
1.7.3.2

