qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Paolo Bonzini <pbonzini@redhat.com>
To: qemu-devel@nongnu.org
Cc: kwolf@redhat.com, jcody@redhat.com, eblake@redhat.com,
	stefanha@linux.vnet.ibm.com
Subject: [Qemu-devel] [PATCH 40/47] mirror: perform COW if the cluster size is bigger than the granularity
Date: Tue, 24 Jul 2012 13:04:18 +0200	[thread overview]
Message-ID: <1343127865-16608-41-git-send-email-pbonzini@redhat.com> (raw)
In-Reply-To: <1343127865-16608-1-git-send-email-pbonzini@redhat.com>

When mirroring runs, the backing files for the target may not yet be
ready.  However, this means that a copy-on-write operation on the target
would fill the missing sectors with zeros.  Copy-on-write only happens
if the granularity of the dirty bitmap is smaller than the cluster size
(and only for clusters that are allocated in the source after the job
has started copying).  So far, the granularity was fixed to 1MB; to avoid
the problem we detected the situation and required the backing files to
be available in that case only.

However, we want to lower the granularity for efficiency, so we need
a better solution.  The solution is to always copy a whole cluster the
first time it is touched.  The code keeps a bitmap of clusters that
have already been allocated by the mirroring job, and only does "manual"
copy-on-write if the chunk being copied is zero in the bitmap.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block/mirror.c         |   60 ++++++++++++++++++++++++++++++++++++++++--------
 blockdev.c             |   13 +----------
 tests/qemu-iotests/039 |   25 ++++++++++++++++++--
 trace-events           |    1 +
 4 files changed, 76 insertions(+), 23 deletions(-)

diff --git a/block/mirror.c b/block/mirror.c
index c3340d1..6f8ae62 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -15,6 +15,7 @@
 #include "blockjob.h"
 #include "block_int.h"
 #include "qemu/ratelimit.h"
+#include "bitmap.h"
 
 enum {
     /*
@@ -36,6 +37,8 @@ typedef struct MirrorBlockJob {
     bool synced;
     bool complete;
     int64_t sector_num;
+    size_t buf_size;
+    unsigned long *cow_bitmap;
     HBitmapIter hbi;
     uint8_t *buf;
 } MirrorBlockJob;
@@ -47,7 +50,7 @@ static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
     BlockDriverState *target = s->target;
     QEMUIOVector qiov;
     int ret, nb_sectors;
-    int64_t end;
+    int64_t end, sector_num, cluster_num;
     struct iovec iov;
 
     s->sector_num = hbitmap_iter_next(&s->hbi);
@@ -58,24 +61,43 @@ static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
         assert(s->sector_num >= 0);
     }
 
+    /* If we have no backing file yet in the destination, and the cluster size
+     * is very large, we need to do COW ourselves.  The first time a cluster is
+     * copied, copy it entirely.
+     *
+     * Because both BDRV_SECTORS_PER_DIRTY_CHUNK and the cluster size are
+     * powers of two, the number of sectors to copy cannot exceed one cluster.
+     */
+    sector_num = s->sector_num;
+    nb_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
+    cluster_num = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
+    if (s->cow_bitmap && !test_bit(cluster_num, s->cow_bitmap)) {
+        trace_mirror_cow(s, sector_num);
+        bdrv_round_to_clusters(s->target,
+                               sector_num, BDRV_SECTORS_PER_DIRTY_CHUNK,
+                               &sector_num, &nb_sectors);
+        bitmap_set(s->cow_bitmap, sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK,
+                   nb_sectors / BDRV_SECTORS_PER_DIRTY_CHUNK);
+    }
+
     end = s->common.len >> BDRV_SECTOR_BITS;
-    nb_sectors = MIN(BDRV_SECTORS_PER_DIRTY_CHUNK, end - s->sector_num);
-    bdrv_reset_dirty(source, s->sector_num, nb_sectors);
+    nb_sectors = MIN(nb_sectors, end - sector_num);
+    bdrv_reset_dirty(source, sector_num, nb_sectors);
 
     /* Copy the dirty cluster.  */
     iov.iov_base = s->buf;
     iov.iov_len  = nb_sectors * 512;
     qemu_iovec_init_external(&qiov, &iov, 1);
 
-    trace_mirror_one_iteration(s, s->sector_num, nb_sectors);
-    ret = bdrv_co_readv(source, s->sector_num, nb_sectors, &qiov);
+    trace_mirror_one_iteration(s, sector_num, nb_sectors);
+    ret = bdrv_co_readv(source, sector_num, nb_sectors, &qiov);
     if (ret < 0) {
         *p_action = block_job_error_action(&s->common, source,
                                            s->on_source_error, true, -ret);
         s->synced = false;
         goto fail;
     }
-    ret = bdrv_co_writev(target, s->sector_num, nb_sectors, &qiov);
+    ret = bdrv_co_writev(target, sector_num, nb_sectors, &qiov);
     if (ret < 0) {
         *p_action = block_job_error_action(&s->common, target,
                                            s->on_target_error, false, -ret);
@@ -86,7 +108,7 @@ static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
 
 fail:
     /* Try again later.  */
-    bdrv_set_dirty(source, s->sector_num, nb_sectors);
+    bdrv_set_dirty(source, sector_num, nb_sectors);
     return ret;
 }
 
@@ -94,7 +116,9 @@ static void coroutine_fn mirror_run(void *opaque)
 {
     MirrorBlockJob *s = opaque;
     BlockDriverState *bs = s->common.bs;
-    int64_t sector_num, end;
+    int64_t sector_num, end, length;
+    BlockDriverInfo bdi;
+    char backing_filename[1024];
     int ret = 0;
     int n;
 
@@ -108,8 +132,23 @@ static void coroutine_fn mirror_run(void *opaque)
         return;
     }
 
+    /* If we have no backing file yet in the destination, we cannot let
+     * the destination do COW.  Instead, we copy sectors around the
+     * dirty data if needed.  We need a bitmap to do that.
+     */
+    bdrv_get_backing_filename(s->target, backing_filename,
+                              sizeof(backing_filename));
+    if (backing_filename[0] && !s->target->backing_hd) {
+        bdrv_get_info(s->target, &bdi);
+        if (s->buf_size < bdi.cluster_size) {
+            s->buf_size = bdi.cluster_size;
+            length = (bdrv_getlength(bs) + BLOCK_SIZE - 1) / BLOCK_SIZE;
+            s->cow_bitmap = bitmap_new(length);
+        }
+    }
+
     end = s->common.len >> BDRV_SECTOR_BITS;
-    s->buf = qemu_blockalign(bs, BLOCK_SIZE);
+    s->buf = qemu_blockalign(bs, s->buf_size);
 
     if (s->mode == MIRROR_SYNC_MODE_FULL || s->mode == MIRROR_SYNC_MODE_TOP) {
         /* First part, loop on the sectors and initialize the dirty bitmap.  */
@@ -218,6 +257,7 @@ static void coroutine_fn mirror_run(void *opaque)
 
 immediate_exit:
     g_free(s->buf);
+    g_free(s->cow_bitmap);
     bdrv_set_dirty_tracking(bs, false);
     bdrv_iostatus_disable(s->target);
     if (s->complete && ret == 0) {
@@ -313,6 +353,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
     s->on_target_error = on_target_error;
     s->target = target;
     s->mode = mode;
+    s->buf_size = BLOCK_SIZE;
+
     bdrv_set_dirty_tracking(bs, true);
     bdrv_set_on_error(s->target, on_target_error, on_target_error);
     bdrv_iostatus_enable(s->target);
diff --git a/blockdev.c b/blockdev.c
index eb528cd..e160610 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -836,7 +836,6 @@ void qmp_drive_mirror(const char *device, const char *target,
                       bool has_on_target_error, BlockdevOnError on_target_error,
                       Error **errp)
 {
-    BlockDriverInfo bdi;
     BlockDriverState *bs;
     BlockDriverState *source, *target_bs;
     BlockDriver *proto_drv;
@@ -927,6 +926,7 @@ void qmp_drive_mirror(const char *device, const char *target,
         return;
     }
 
+    /* Mirroring takes care of copy-on-write using data from the source.  */
     target_bs = bdrv_new("");
     ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv);
 
@@ -936,17 +936,6 @@ void qmp_drive_mirror(const char *device, const char *target,
         return;
     }
 
-    /* We need a backing file if we will copy parts of a cluster.  */
-    if (bdrv_get_info(target_bs, &bdi) >= 0 && bdi.cluster_size != 0 &&
-        bdi.cluster_size >= BDRV_SECTORS_PER_DIRTY_CHUNK * 512) {
-        ret = bdrv_ensure_backing_file(target_bs);
-        if (ret < 0) {
-            bdrv_delete(target_bs);
-            error_set(errp, QERR_OPEN_FILE_FAILED, target);
-            return;
-        }
-    }
-
     mirror_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
                  block_job_cb, bs, &local_err);
     if (local_err != NULL) {
diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039
index 3e17881..17fa05f 100755
--- a/tests/qemu-iotests/039
+++ b/tests/qemu-iotests/039
@@ -195,8 +195,8 @@ class TestSingleDrive(ImageMirroringTestCase):
     def test_large_cluster(self):
         self.assert_no_active_mirrors()
 
-        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
-                        % (TestSingleDrive.image_len, mid_img), target_img)
+        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
+                        % (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img)
         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
                              mode='existing', target=target_img)
         self.assert_qmp(result, 'return', {})
@@ -280,6 +280,27 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
         self.assertTrue(self.compare_images(test_img, target_img),
                         'target image does not match source after mirroring')
 
+    def test_large_cluster(self):
+        self.assert_no_active_mirrors()
+
+        # qemu-img create fails if the image is not there
+        qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
+                        %(TestMirrorNoBacking.image_len), target_backing_img)
+        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
+                        % (TestMirrorNoBacking.image_len, target_backing_img), target_img)
+        os.remove(target_backing_img)
+
+        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
+                             mode='existing', target=target_img)
+        self.assert_qmp(result, 'return', {})
+
+        self.complete_and_wait()
+        result = self.vm.qmp('query-block')
+        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
+        self.vm.shutdown()
+        self.assertTrue(self.compare_images(test_img, target_img),
+                        'target image does not match source after mirroring')
+
 class TestReadErrors(ImageMirroringTestCase):
     image_len = 2 * 1024 * 1024 # MB
 
diff --git a/trace-events b/trace-events
index 496824c..6b504d8 100644
--- a/trace-events
+++ b/trace-events
@@ -82,6 +82,7 @@ mirror_before_flush(void *s) "s %p"
 mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64
 mirror_before_sleep(void *s, int64_t cnt, int synced) "s %p dirty count %"PRId64" synced %d"
 mirror_one_iteration(void *s, int64_t sector_num, int nb_sectors) "s %p sector_num %"PRId64" nb_sectors %d"
+mirror_cow(void *s, int64_t sector_num) "s %p sector_num %"PRId64
 
 # blockdev.c
 qmp_block_job_cancel(void *job) "job %p"
-- 
1.7.10.4

  parent reply	other threads:[~2012-07-24 11:06 UTC|newest]

Thread overview: 136+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-07-24 11:03 [Qemu-devel] [PATCH 00/47] Block job improvements for 1.2 Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 01/47] qapi: generalize documentation of streaming commands Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 02/47] qerror/block: introduce QERR_BLOCK_JOB_NOT_ACTIVE Paolo Bonzini
2012-07-26 15:26   ` Kevin Wolf
2012-07-26 15:41     ` Paolo Bonzini
2012-07-26 16:49       ` Luiz Capitulino
2012-07-26 16:59         ` Paolo Bonzini
2012-07-26 17:02           ` Luiz Capitulino
2012-07-24 11:03 ` [Qemu-devel] [PATCH 03/47] block: move job APIs to separate files Paolo Bonzini
2012-07-26 15:50   ` Kevin Wolf
2012-07-24 11:03 ` [Qemu-devel] [PATCH 04/47] block: add block_job_query Paolo Bonzini
2012-07-30 14:47   ` Kevin Wolf
2012-07-30 15:05     ` Paolo Bonzini
2012-07-31  8:47       ` Kevin Wolf
2012-07-31  8:50         ` Paolo Bonzini
2012-08-02 19:28           ` Jeff Cody
2012-07-24 11:03 ` [Qemu-devel] [PATCH 05/47] block: add support for job pause/resume Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 06/47] qmp: add block-job-pause and block-job-resume Paolo Bonzini
2012-08-01  7:42   ` Kevin Wolf
2012-07-24 11:03 ` [Qemu-devel] [PATCH 07/47] qemu-iotests: add test for pausing a streaming operation Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 08/47] block: rename block_job_complete to block_job_completed Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 09/47] block: rename BlockErrorAction, BlockQMPEventAction Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 10/47] block: move BlockdevOnError declaration to QAPI Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 11/47] block: reorganize io error code Paolo Bonzini
2012-08-01  9:30   ` Kevin Wolf
2012-08-01  9:46     ` Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 12/47] block: sort BlockDeviceIoStatus errors by severity Paolo Bonzini
2012-08-01  9:44   ` Paolo Bonzini
2012-08-01  9:44   ` Kevin Wolf
2012-07-24 11:03 ` [Qemu-devel] [PATCH 13/47] block: introduce block job error Paolo Bonzini
2012-07-25 17:40   ` Eric Blake
2012-08-01 10:14   ` Kevin Wolf
2012-08-01 11:17     ` Paolo Bonzini
2012-08-01 11:49       ` Kevin Wolf
2012-08-01 12:09         ` Paolo Bonzini
2012-08-01 12:23           ` Kevin Wolf
2012-08-01 12:30             ` Paolo Bonzini
2012-08-01 13:09               ` Kevin Wolf
2012-08-01 13:21                 ` Paolo Bonzini
2012-08-01 14:01                   ` Kevin Wolf
2012-08-01 14:34                     ` Paolo Bonzini
2012-08-01 14:59                       ` Kevin Wolf
2012-08-01 15:15                         ` Paolo Bonzini
2012-08-06  9:29                           ` Kevin Wolf
2012-08-06  9:44                             ` Paolo Bonzini
2012-08-06 10:45                               ` Kevin Wolf
2012-08-06 10:58                                 ` Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 14/47] stream: add on-error argument Paolo Bonzini
2012-07-31 18:40   ` Eric Blake
2012-08-01 10:29   ` Kevin Wolf
2012-08-01 11:11     ` Paolo Bonzini
2012-08-01 11:45       ` Kevin Wolf
2012-07-24 11:03 ` [Qemu-devel] [PATCH 15/47] blkdebug: process all set_state rules in the old state Paolo Bonzini
2012-07-24 20:06   ` Blue Swirl
2012-07-24 11:03 ` [Qemu-devel] [PATCH 16/47] qemu-iotests: map underscore to dash in QMP argument names Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 17/47] qemu-iotests: add tests for streaming error handling Paolo Bonzini
2012-08-01 10:43   ` Kevin Wolf
2012-08-01 11:09     ` Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 18/47] block: live snapshot documentation tweaks Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 19/47] block: add bdrv_query_info Paolo Bonzini
2012-09-11 13:07   ` Kevin Wolf
2012-09-11 13:12     ` Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 20/47] block: add bdrv_query_stats Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 21/47] block: add bdrv_ensure_backing_file Paolo Bonzini
2012-09-11 13:32   ` Kevin Wolf
2012-09-11 13:46     ` Paolo Bonzini
2012-09-11 13:58       ` Kevin Wolf
2012-09-11 14:10         ` Paolo Bonzini
2012-09-11 15:38           ` Kevin Wolf
2012-07-24 11:04 ` [Qemu-devel] [PATCH 22/47] block: make device optional in BlockInfo Paolo Bonzini
2012-09-11 13:38   ` Kevin Wolf
2012-09-11 13:49     ` Paolo Bonzini
2012-09-11 14:02       ` Kevin Wolf
2012-09-11 14:14         ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 23/47] block: add target info to QMP query-blockjobs command Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 24/47] block: introduce new dirty bitmap functionality Paolo Bonzini
2012-09-11 14:57   ` Kevin Wolf
2012-09-11 16:17     ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 25/47] block: add block-job-complete Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 26/47] block: introduce BLOCK_JOB_READY event Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 27/47] block: introduce mirror job Paolo Bonzini
2012-07-25 23:02   ` Eric Blake
2012-09-13 12:54   ` Kevin Wolf
2012-09-13 14:07     ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 28/47] qmp: add drive-mirror command Paolo Bonzini
2012-07-26 23:42   ` Eric Blake
2012-07-27  7:04     ` Paolo Bonzini
2012-07-31  9:26   ` Kevin Wolf
2012-07-31  9:33     ` Paolo Bonzini
2012-07-31  9:46       ` Kevin Wolf
2012-07-31 10:02         ` Paolo Bonzini
2012-07-31 10:25           ` Kevin Wolf
2012-07-31 10:51             ` Paolo Bonzini
2012-07-31 11:13               ` Kevin Wolf
2012-07-31 11:25                 ` Paolo Bonzini
2012-07-31 12:17                   ` Kevin Wolf
2012-07-31 12:52                     ` Paolo Bonzini
2012-09-13 13:15   ` Kevin Wolf
2012-09-13 13:24     ` Paolo Bonzini
2012-09-13 13:26       ` Kevin Wolf
2012-09-13 13:38         ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 29/47] mirror: support querying target file Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 30/47] mirror: implement completion Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 31/47] qemu-iotests: add mirroring test case Paolo Bonzini
2012-07-26 23:46   ` Eric Blake
2012-07-27  7:04     ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 32/47] block: forward bdrv_iostatus_reset to block job Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 33/47] mirror: add support for on-source-error/on-target-error Paolo Bonzini
2012-07-27 15:26   ` Eric Blake
2012-07-30 13:29     ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 34/47] qmp: add pull_event function Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 35/47] qemu-iotests: add testcases for mirroring on-source-error/on-target-error Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 36/47] host-utils: add ffsl and flsl Paolo Bonzini
2012-07-27 16:05   ` Eric Blake
2012-07-30 13:30     ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 37/47] add hierarchical bitmap data type and test cases Paolo Bonzini
2012-07-28 13:26   ` Eric Blake
2012-07-30 13:39     ` Paolo Bonzini
2012-07-30 14:18       ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 38/47] block: implement dirty bitmap using HBitmap Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 39/47] block: make round_to_clusters public Paolo Bonzini
2012-07-24 11:04 ` Paolo Bonzini [this message]
2012-07-24 11:04 ` [Qemu-devel] [PATCH 41/47] block: return count of dirty sectors, not chunks Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 42/47] block: allow customizing the granularity of the dirty bitmap Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 43/47] mirror: allow customizing the granularity Paolo Bonzini
2012-07-28 13:43   ` Eric Blake
2012-07-30 13:40     ` Paolo Bonzini
2012-07-30 13:53       ` Eric Blake
2012-07-30 14:03         ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 44/47] mirror: switch mirror_iteration to AIO Paolo Bonzini
2012-07-28 13:46   ` Eric Blake
2012-07-30 13:41     ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 45/47] mirror: add buf-size argument to drive-mirror Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 46/47] mirror: support more than one in-flight AIO operation Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 47/47] mirror: support arbitrarily-sized iterations Paolo Bonzini
2012-07-28 13:51 ` [Qemu-devel] [PATCH 00/47] Block job improvements for 1.2 Eric Blake

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=1343127865-16608-41-git-send-email-pbonzini@redhat.com \
    --to=pbonzini@redhat.com \
    --cc=eblake@redhat.com \
    --cc=jcody@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@linux.vnet.ibm.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).