All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sergio Lopez <slp@redhat.com>
To: qemu-block@nongnu.org
Cc: qemu-devel@nongnu.org, kwolf@redhat.com, mreitz@redhat.com,
	Sergio Lopez <slp@redhat.com>
Subject: [Qemu-devel] [PATCH] qemu-img: implement copy offload (-C) for dd
Date: Thu, 21 Feb 2019 18:37:58 +0100	[thread overview]
Message-ID: <20190221173758.24672-1-slp@redhat.com> (raw)

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

Signed-off-by: Sergio Lopez <slp@redhat.com>
---
 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
-@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
+@item dd [--image-opts] [-U] [-C] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
 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.
 
-@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
+@item dd [--image-opts] [-U] [-C] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
 
 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	other threads:[~2019-02-21 17:45 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-21 17:37 Sergio Lopez [this message]
2019-02-21 18:08 ` [Qemu-devel] [PATCH] qemu-img: implement copy offload (-C) for dd Eric Blake
2019-02-21 19:32   ` Sergio Lopez
2019-02-22 10:04     ` Kevin Wolf
2019-02-25 13:40       ` Sergio Lopez
2019-02-25 14:01         ` Kevin Wolf
2019-02-25 15:29           ` Sergio Lopez

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190221173758.24672-1-slp@redhat.com \
    --to=slp@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.