qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH v5 07/12] qemu-img: Empty images after commit


From: Max Reitz
Subject: [Qemu-devel] [PATCH v5 07/12] qemu-img: Empty images after commit
Date: Thu, 17 Apr 2014 23:59:47 +0200

After the top image has been committed into an image in its backing
chain, all images above that base image should be emptied to restore the
old qemu-img commit behavior.

Signed-off-by: Max Reitz <address@hidden>
---
 qemu-img.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 84 insertions(+), 3 deletions(-)

diff --git a/qemu-img.c b/qemu-img.c
index 7ee791f..42616da 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -49,6 +49,11 @@ typedef enum OutputFormat {
     OFORMAT_HUMAN,
 } OutputFormat;
 
+typedef struct BackingList {
+    BlockDriverState *bs;
+    QSIMPLEQ_ENTRY(BackingList) list;
+} BackingList;
+
 /* Default to cache=writeback as data integrity is not important for qemu-tcg. 
*/
 #define BDRV_O_FLAGS BDRV_O_CACHE_WB
 #define BDRV_DEFAULT_CACHE "writeback"
@@ -717,10 +722,13 @@ static int img_commit(int argc, char **argv)
 {
     int c, ret, flags;
     const char *filename, *fmt, *cache;
-    BlockDriverState *bs, *base_bs;
+    BlockDriverState *bs, *base_bs, *backing_bs;
     bool quiet = false;
     Error *local_err = NULL;
     CommonBlockJobCBInfo cbi;
+    BackingList *bl_element, *bl_next;
+    QSIMPLEQ_HEAD(, BackingList) backing_list =
+        QSIMPLEQ_HEAD_INITIALIZER(backing_list);
 
     fmt = NULL;
     cache = BDRV_DEFAULT_CACHE;
@@ -750,7 +758,7 @@ static int img_commit(int argc, char **argv)
     }
     filename = argv[optind++];
 
-    flags = BDRV_O_RDWR;
+    flags = BDRV_O_RDWR | BDRV_O_UNMAP;
     ret = bdrv_parse_cache_flags(cache, &flags);
     if (ret < 0) {
         error_report("Invalid cache option: %s", cache);
@@ -771,6 +779,32 @@ static int img_commit(int argc, char **argv)
         goto done;
     }
 
+    /* Build a list of the intermediate backing images in order to be able to
+     * empty them later; note that base_bs is included in this list although we
+     * actually want to empty bs instead. As bdrv_swap() will be called on both
+     * by the commit block job, it is however correct to use the pointer to
+     * base_bs in order to clear bs. */
+    backing_bs = bs;
+
+    do {
+        backing_bs = backing_bs->backing_hd;
+        assert(backing_bs);
+
+        bl_element = g_new(BackingList, 1);
+        bl_element->bs = backing_bs;
+
+        if (backing_bs != base_bs) {
+            /* Generally, the images should be emptied from top to bottom in
+             * order to keep them consistent even if one make_empty operation
+             * fails because it could empty an image only partially */
+            QSIMPLEQ_INSERT_TAIL(&backing_list, bl_element, list);
+        } else {
+            /* The final pointer in the backing chain will however later point
+             * to the image at the very top, so put it at the front instead */
+            QSIMPLEQ_INSERT_HEAD(&backing_list, bl_element, list);
+        }
+    } while (backing_bs != base_bs);
+
     cbi = (CommonBlockJobCBInfo){
         .errp = &local_err,
         .bs   = bs,
@@ -779,10 +813,57 @@ static int img_commit(int argc, char **argv)
     commit_active_start(bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT,
                         common_block_job_cb, &cbi, &local_err);
     if (local_err) {
-        goto done;
+        goto free_backing_list;
+    }
+
+    /* The block job will swap base_bs and bs (which is not what we really want
+     * here, but okay) and unref bs (and subsequently all intermediate block
+     * devices). In order to be able to empty these images afterwards, 
increment
+     * the reference counter here preemptively. */
+    QSIMPLEQ_FOREACH(bl_element, &backing_list, list) {
+        bdrv_ref(bl_element->bs);
     }
 
     run_block_job(bs->job, &local_err);
+    if (local_err) {
+        goto unref_backing;
+    }
+
+    QSIMPLEQ_FOREACH(bl_element, &backing_list, list) {
+        if (bl_element->bs->drv->bdrv_make_empty) {
+            if (bl_element->bs->read_only) {
+                /* If this is an intermediate file in the backing chain between
+                 * the top and the bottom image, it has been opened implicitly
+                 * read-only; reopen it R/W for emptying */
+                ret = bdrv_reopen(bl_element->bs, flags, &local_err);
+                if (ret < 0 || local_err) {
+                    if (!local_err) {
+                        error_setg_errno(&local_err, -ret,
+                                         "Could not write to %s",
+                                         bl_element->bs->filename);
+                    }
+                    goto unref_backing;
+                }
+            }
+
+            ret = bl_element->bs->drv->bdrv_make_empty(bl_element->bs);
+            if (ret) {
+                error_setg_errno(&local_err, -ret, "Could not empty %s",
+                                 bl_element->bs->filename);
+                goto unref_backing;
+            }
+        }
+    }
+
+unref_backing:
+    QSIMPLEQ_FOREACH(bl_element, &backing_list, list) {
+        bdrv_unref(bl_element->bs);
+    }
+
+free_backing_list:
+    QSIMPLEQ_FOREACH_SAFE(bl_element, &backing_list, list, bl_next) {
+        g_free(bl_element);
+    }
 
 done:
     bdrv_unref(bs);
-- 
1.9.2




reply via email to

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