qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH] qemu-img: implement copy offload (-C) for dd


From: Sergio Lopez
Subject: [Qemu-devel] [PATCH] qemu-img: implement copy offload (-C) for dd
Date: Thu, 21 Feb 2019 18:37:58 +0100

This parameter is analogous to convert's "-C", making use of
bdrv_co_copy_range().

Signed-off-by: Sergio Lopez <address@hidden>
---
 qemu-img-cmds.hx |   4 +-
 qemu-img.c       | 146 ++++++++++++++++++++++++++++++++++++-----------
 qemu-img.texi    |   2 +-
 3 files changed, 117 insertions(+), 35 deletions(-)

diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 1526f327a5..7b85d183e5 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -56,9 +56,9 @@ STEXI
 ETEXI
 
 DEF("dd", img_dd,
-    "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] 
[count=blocks] [skip=blocks] if=input of=output")
+    "dd [--image-opts] [-U] [-C] [-f fmt] [-O output_fmt] [bs=block_size] 
[count=blocks] [skip=blocks] if=input of=output")
 STEXI
address@hidden dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] 
address@hidden address@hidden address@hidden address@hidden address@hidden
address@hidden dd [--image-opts] [-U] [-C] [-f @var{fmt}] [-O @var{output_fmt}] 
address@hidden address@hidden address@hidden address@hidden address@hidden
 ETEXI
 
 DEF("info", img_info,
diff --git a/qemu-img.c b/qemu-img.c
index 25288c4d18..75ac0318a1 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -4310,7 +4310,6 @@ struct DdInfo {
 struct DdIo {
     int bsz;    /* Block size */
     char *filename;
-    uint8_t *buf;
     int64_t offset;
 };
 
@@ -4320,6 +4319,18 @@ struct DdOpts {
     unsigned int flag;
 };
 
+typedef struct ImgDdState {
+    BlockBackend *src;
+    int64_t src_offset;
+    BlockBackend *dst;
+    int64_t dst_offset;
+    int bsz;
+    int64_t size;
+    bool copy_range;
+    int running_coroutines;
+    int ret;
+} ImgDdState;
+
 static int img_dd_bs(const char *arg,
                      struct DdIo *in, struct DdIo *out,
                      struct DdInfo *dd)
@@ -4383,6 +4394,86 @@ static int img_dd_skip(const char *arg,
     return 0;
 }
 
+static void coroutine_fn dd_co_do_copy(void *opaque)
+{
+    ImgDdState *s = opaque;
+    int64_t block_count, in_pos, out_pos;
+    uint8_t *buf;
+    int ret = 0;
+
+    s->running_coroutines++;
+
+    in_pos = s->src_offset;
+    out_pos = s->dst_offset;
+
+    if (s->copy_range) {
+        /*
+         * We don't impose any kind of alignment requirements nor
+         * adjustments here. It's up to the user to set the best
+         * parameters for a particular storage/driver.
+         */
+        for (; in_pos < s->size && ret == 0; block_count++) {
+            int bytes = s->bsz;
+
+            if (in_pos + bytes > s->size) {
+                bytes = s->size - in_pos;
+            }
+
+            ret = blk_co_copy_range(s->src, in_pos,
+                                    s->dst, out_pos,
+                                    bytes, 0, 0);
+            in_pos += bytes;
+            out_pos += bytes;
+        }
+
+        /*
+         * If copy_range succeded, jump to the end. Otherwise,
+         * fall through to try with the usual bouncing buffers.
+         */
+        if (ret == 0) {
+            goto out;
+        } else {
+            warn_report("copy_range failed, continuing with the conventional "
+                        "read/write approach.");
+        }
+    }
+
+    buf = g_new(uint8_t, s->bsz);
+
+    for (; in_pos < s->size; block_count++) {
+        int in_ret, out_ret;
+
+        if (in_pos + s->bsz > s->size) {
+            in_ret = blk_pread(s->src, in_pos, buf, s->size - in_pos);
+        } else {
+            in_ret = blk_pread(s->src, in_pos, buf, s->bsz);
+        }
+        if (in_ret < 0) {
+            error_report("error while reading from input image file: %s",
+                         strerror(-in_ret));
+            ret = -1;
+            goto free_out;
+        }
+        in_pos += in_ret;
+
+        out_ret = blk_pwrite(s->dst, out_pos, buf, in_ret, 0);
+
+        if (out_ret < 0) {
+            error_report("error while writing to output image file: %s",
+                         strerror(-out_ret));
+            ret = -1;
+            goto free_out;
+        }
+        out_pos += out_ret;
+    }
+
+ free_out:
+    g_free(buf);
+ out:
+    s->running_coroutines--;
+    s->ret = ret;
+}
+
 static int img_dd(int argc, char **argv)
 {
     int ret = 0;
@@ -4390,6 +4481,7 @@ static int img_dd(int argc, char **argv)
     char *tmp;
     BlockDriver *drv = NULL, *proto_drv = NULL;
     BlockBackend *blk1 = NULL, *blk2 = NULL;
+    Coroutine *co = NULL;
     QemuOpts *opts = NULL;
     QemuOptsList *create_opts = NULL;
     Error *local_err = NULL;
@@ -4398,8 +4490,9 @@ static int img_dd(int argc, char **argv)
     const char *out_fmt = "raw";
     const char *fmt = NULL;
     int64_t size = 0;
-    int64_t block_count = 0, out_pos, in_pos;
+    int64_t in_pos;
     bool force_share = false;
+    bool copy_range = false;
     struct DdInfo dd = {
         .flags = 0,
         .count = 0,
@@ -4407,13 +4500,11 @@ static int img_dd(int argc, char **argv)
     struct DdIo in = {
         .bsz = 512, /* Block size is by default 512 bytes */
         .filename = NULL,
-        .buf = NULL,
         .offset = 0
     };
     struct DdIo out = {
         .bsz = 512,
         .filename = NULL,
-        .buf = NULL,
         .offset = 0
     };
 
@@ -4433,7 +4524,7 @@ static int img_dd(int argc, char **argv)
         { 0, 0, 0, 0 }
     };
 
-    while ((c = getopt_long(argc, argv, ":hf:O:U", long_options, NULL))) {
+    while ((c = getopt_long(argc, argv, ":hf:O:UC", long_options, NULL))) {
         if (c == EOF) {
             break;
         }
@@ -4456,6 +4547,9 @@ static int img_dd(int argc, char **argv)
         case 'U':
             force_share = true;
             break;
+        case 'C':
+            copy_range = true;
+            break;
         case OPTION_OBJECT:
             if (!qemu_opts_parse_noisily(&qemu_object_opts, optarg, true)) {
                 ret = -1;
@@ -4606,35 +4700,25 @@ static int img_dd(int argc, char **argv)
         in_pos = in.offset * in.bsz;
     }
 
-    in.buf = g_new(uint8_t, in.bsz);
-
-    for (out_pos = 0; in_pos < size; block_count++) {
-        int in_ret, out_ret;
-
-        if (in_pos + in.bsz > size) {
-            in_ret = blk_pread(blk1, in_pos, in.buf, size - in_pos);
-        } else {
-            in_ret = blk_pread(blk1, in_pos, in.buf, in.bsz);
-        }
-        if (in_ret < 0) {
-            error_report("error while reading from input image file: %s",
-                         strerror(-in_ret));
-            ret = -1;
-            goto out;
-        }
-        in_pos += in_ret;
+    ImgDdState s = (ImgDdState) {
+        .src = blk1,
+        .src_offset = in_pos,
+        .dst = blk2,
+        .dst_offset = 0,
+        .bsz = in.bsz,
+        .size = size,
+        .copy_range = copy_range,
+    };
 
-        out_ret = blk_pwrite(blk2, out_pos, in.buf, in_ret, 0);
+    co = qemu_coroutine_create(dd_co_do_copy, &s);
+    qemu_coroutine_enter(co);
 
-        if (out_ret < 0) {
-            error_report("error while writing to output image file: %s",
-                         strerror(-out_ret));
-            ret = -1;
-            goto out;
-        }
-        out_pos += out_ret;
+    while (s.running_coroutines) {
+        main_loop_wait(false);
     }
 
+    ret = s.ret;
+
 out:
     g_free(arg);
     qemu_opts_del(opts);
@@ -4643,8 +4727,6 @@ out:
     blk_unref(blk2);
     g_free(in.filename);
     g_free(out.filename);
-    g_free(in.buf);
-    g_free(out.buf);
 
     if (ret) {
         return 1;
diff --git a/qemu-img.texi b/qemu-img.texi
index 3b6710a580..5c5079e3bc 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -390,7 +390,7 @@ way.
 The size can also be specified using the @var{size} option with @code{-o},
 it doesn't need to be specified separately in this case.
 
address@hidden dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] 
address@hidden address@hidden address@hidden address@hidden address@hidden
address@hidden dd [--image-opts] [-U] [-C] [-f @var{fmt}] [-O @var{output_fmt}] 
address@hidden address@hidden address@hidden address@hidden address@hidden
 
 Dd copies from @var{input} file to @var{output} file converting it from
 @var{fmt} format to @var{output_fmt} format.
-- 
2.20.1




reply via email to

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