All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kevin Wolf <kwolf@redhat.com>
To: qemu-block@nongnu.org
Cc: kwolf@redhat.com, peter.maydell@linaro.org, qemu-devel@nongnu.org
Subject: [PULL 45/64] mirror: Deal with filters
Date: Mon,  7 Sep 2020 13:09:17 +0200	[thread overview]
Message-ID: <20200907110936.261684-46-kwolf@redhat.com> (raw)
In-Reply-To: <20200907110936.261684-1-kwolf@redhat.com>

From: Max Reitz <mreitz@redhat.com>

This includes some permission limiting (for example, we only need to
take the RESIZE permission for active commits where the base is smaller
than the top).

base_overlay is introduced so we can query bdrv_is_allocated_above() on
it - we cannot do that with base itself, because a filter's block_status
is the same as its child node, so if there are filters on base,
bdrv_is_allocated_above() on base would return information including
base.

Use this opportunity to rename qmp_drive_mirror()'s "source" BDS to
"target_backing_bs", because that is what it really refers to.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 qapi/block-core.json |   6 ++-
 block/mirror.c       | 118 +++++++++++++++++++++++++++++++++----------
 blockdev.c           |  32 ++++++++----
 3 files changed, 118 insertions(+), 38 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index b04df13bea..e34796d98f 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1948,7 +1948,8 @@
 #
 # @replaces: with sync=full graph node name to be replaced by the new
 #            image when a whole image copy is done. This can be used to repair
-#            broken Quorum files. (Since 2.1)
+#            broken Quorum files.  By default, @device is replaced, although
+#            implicitly created filters on it are kept. (Since 2.1)
 #
 # @mode: whether and how QEMU should create a new image, default is
 #        'absolute-paths'.
@@ -2259,7 +2260,8 @@
 #
 # @replaces: with sync=full graph node name to be replaced by the new
 #            image when a whole image copy is done. This can be used to repair
-#            broken Quorum files.
+#            broken Quorum files.  By default, @device is replaced, although
+#            implicitly created filters on it are kept.
 #
 # @speed:  the maximum speed, in bytes per second
 #
diff --git a/block/mirror.c b/block/mirror.c
index e8e8844afc..f16b0d62bc 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -42,6 +42,7 @@ typedef struct MirrorBlockJob {
     BlockBackend *target;
     BlockDriverState *mirror_top_bs;
     BlockDriverState *base;
+    BlockDriverState *base_overlay;
 
     /* The name of the graph node to replace */
     char *replaces;
@@ -677,8 +678,10 @@ static int mirror_exit_common(Job *job)
                              &error_abort);
     if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
         BlockDriverState *backing = s->is_none_mode ? src : s->base;
-        if (backing_bs(target_bs) != backing) {
-            bdrv_set_backing_hd(target_bs, backing, &local_err);
+        BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs);
+
+        if (bdrv_cow_bs(unfiltered_target) != backing) {
+            bdrv_set_backing_hd(unfiltered_target, backing, &local_err);
             if (local_err) {
                 error_report_err(local_err);
                 local_err = NULL;
@@ -740,7 +743,7 @@ static int mirror_exit_common(Job *job)
      * valid.
      */
     block_job_remove_all_bdrv(bjob);
-    bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort);
+    bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort);
 
     /* We just changed the BDS the job BB refers to (with either or both of the
      * bdrv_replace_node() calls), so switch the BB back so the cleanup does
@@ -786,7 +789,6 @@ static void coroutine_fn mirror_throttle(MirrorBlockJob *s)
 static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
 {
     int64_t offset;
-    BlockDriverState *base = s->base;
     BlockDriverState *bs = s->mirror_top_bs->backing->bs;
     BlockDriverState *target_bs = blk_bs(s->target);
     int ret;
@@ -837,7 +839,8 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
             return 0;
         }
 
-        ret = bdrv_is_allocated_above(bs, base, false, offset, bytes, &count);
+        ret = bdrv_is_allocated_above(bs, s->base_overlay, true, offset, bytes,
+                                      &count);
         if (ret < 0) {
             return ret;
         }
@@ -936,7 +939,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
     } else {
         s->target_cluster_size = BDRV_SECTOR_SIZE;
     }
-    if (backing_filename[0] && !target_bs->backing &&
+    if (backing_filename[0] && !bdrv_backing_chain_next(target_bs) &&
         s->granularity < s->target_cluster_size) {
         s->buf_size = MAX(s->buf_size, s->target_cluster_size);
         s->cow_bitmap = bitmap_new(length);
@@ -1116,8 +1119,9 @@ static void mirror_complete(Job *job, Error **errp)
     if (s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) {
         int ret;
 
-        assert(!target->backing);
-        ret = bdrv_open_backing_file(target, NULL, "backing", errp);
+        assert(!bdrv_backing_chain_next(target));
+        ret = bdrv_open_backing_file(bdrv_skip_filters(target), NULL,
+                                     "backing", errp);
         if (ret < 0) {
             return;
         }
@@ -1555,8 +1559,8 @@ static BlockJob *mirror_start_job(
     MirrorBlockJob *s;
     MirrorBDSOpaque *bs_opaque;
     BlockDriverState *mirror_top_bs;
-    bool target_graph_mod;
     bool target_is_backing;
+    uint64_t target_perms, target_shared_perms;
     Error *local_err = NULL;
     int ret;
 
@@ -1575,7 +1579,7 @@ static BlockJob *mirror_start_job(
         buf_size = DEFAULT_MIRROR_BUF_SIZE;
     }
 
-    if (bs == target) {
+    if (bdrv_skip_filters(bs) == bdrv_skip_filters(target)) {
         error_setg(errp, "Can't mirror node into itself");
         return NULL;
     }
@@ -1639,15 +1643,50 @@ static BlockJob *mirror_start_job(
      * In the case of active commit, things look a bit different, though,
      * because the target is an already populated backing file in active use.
      * We can allow anything except resize there.*/
+
+    target_perms = BLK_PERM_WRITE;
+    target_shared_perms = BLK_PERM_WRITE_UNCHANGED;
+
     target_is_backing = bdrv_chain_contains(bs, target);
-    target_graph_mod = (backing_mode != MIRROR_LEAVE_BACKING_CHAIN);
+    if (target_is_backing) {
+        int64_t bs_size, target_size;
+        bs_size = bdrv_getlength(bs);
+        if (bs_size < 0) {
+            error_setg_errno(errp, -bs_size,
+                             "Could not inquire top image size");
+            goto fail;
+        }
+
+        target_size = bdrv_getlength(target);
+        if (target_size < 0) {
+            error_setg_errno(errp, -target_size,
+                             "Could not inquire base image size");
+            goto fail;
+        }
+
+        if (target_size < bs_size) {
+            target_perms |= BLK_PERM_RESIZE;
+        }
+
+        target_shared_perms |= BLK_PERM_CONSISTENT_READ
+                            |  BLK_PERM_WRITE
+                            |  BLK_PERM_GRAPH_MOD;
+    } else if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) {
+        /*
+         * We may want to allow this in the future, but it would
+         * require taking some extra care.
+         */
+        error_setg(errp, "Cannot mirror to a filter on top of a node in the "
+                   "source's backing chain");
+        goto fail;
+    }
+
+    if (backing_mode != MIRROR_LEAVE_BACKING_CHAIN) {
+        target_perms |= BLK_PERM_GRAPH_MOD;
+    }
+
     s->target = blk_new(s->common.job.aio_context,
-                        BLK_PERM_WRITE | BLK_PERM_RESIZE |
-                        (target_graph_mod ? BLK_PERM_GRAPH_MOD : 0),
-                        BLK_PERM_WRITE_UNCHANGED |
-                        (target_is_backing ? BLK_PERM_CONSISTENT_READ |
-                                             BLK_PERM_WRITE |
-                                             BLK_PERM_GRAPH_MOD : 0));
+                        target_perms, target_shared_perms);
     ret = blk_insert_bs(s->target, target, errp);
     if (ret < 0) {
         goto fail;
@@ -1672,6 +1711,7 @@ static BlockJob *mirror_start_job(
     s->zero_target = zero_target;
     s->copy_mode = copy_mode;
     s->base = base;
+    s->base_overlay = bdrv_find_overlay(bs, base);
     s->granularity = granularity;
     s->buf_size = ROUND_UP(buf_size, granularity);
     s->unmap = unmap;
@@ -1702,15 +1742,39 @@ static BlockJob *mirror_start_job(
     /* In commit_active_start() all intermediate nodes disappear, so
      * any jobs in them must be blocked */
     if (target_is_backing) {
-        BlockDriverState *iter;
-        for (iter = backing_bs(bs); iter != target; iter = backing_bs(iter)) {
-            /* XXX BLK_PERM_WRITE needs to be allowed so we don't block
-             * ourselves at s->base (if writes are blocked for a node, they are
-             * also blocked for its backing file). The other options would be a
-             * second filter driver above s->base (== target). */
+        BlockDriverState *iter, *filtered_target;
+        uint64_t iter_shared_perms;
+
+        /*
+         * The topmost node with
+         * bdrv_skip_filters(filtered_target) == bdrv_skip_filters(target)
+         */
+        filtered_target = bdrv_cow_bs(bdrv_find_overlay(bs, target));
+
+        assert(bdrv_skip_filters(filtered_target) ==
+               bdrv_skip_filters(target));
+
+        /*
+         * XXX BLK_PERM_WRITE needs to be allowed so we don't block
+         * ourselves at s->base (if writes are blocked for a node, they are
+         * also blocked for its backing file). The other options would be a
+         * second filter driver above s->base (== target).
+         */
+        iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE;
+
+        for (iter = bdrv_filter_or_cow_bs(bs); iter != target;
+             iter = bdrv_filter_or_cow_bs(iter))
+        {
+            if (iter == filtered_target) {
+                /*
+                 * From here on, all nodes are filters on the base.
+                 * This allows us to share BLK_PERM_CONSISTENT_READ.
+                 */
+                iter_shared_perms |= BLK_PERM_CONSISTENT_READ;
+            }
+
             ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
-                                     BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE,
-                                     errp);
+                                     iter_shared_perms, errp);
             if (ret < 0) {
                 goto fail;
             }
@@ -1746,7 +1810,7 @@ fail:
     bs_opaque->stop = true;
     bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing,
                              &error_abort);
-    bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort);
+    bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort);
 
     bdrv_unref(mirror_top_bs);
 
@@ -1774,7 +1838,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
         return;
     }
     is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
-    base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
+    base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL;
     mirror_start_job(job_id, bs, creation_flags, target, replaces,
                      speed, granularity, buf_size, backing_mode, zero_target,
                      on_source_error, on_target_error, unmap, NULL, NULL,
diff --git a/blockdev.c b/blockdev.c
index 57ee41b73e..73d96ce21c 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2899,6 +2899,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
                                    bool has_auto_dismiss, bool auto_dismiss,
                                    Error **errp)
 {
+    BlockDriverState *unfiltered_bs;
     int job_flags = JOB_DEFAULT;
 
     if (!has_speed) {
@@ -2950,10 +2951,19 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
         return;
     }
 
-    if (!bs->backing && sync == MIRROR_SYNC_MODE_TOP) {
+    if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) {
         sync = MIRROR_SYNC_MODE_FULL;
     }
 
+    if (!has_replaces) {
+        /* We want to mirror from @bs, but keep implicit filters on top */
+        unfiltered_bs = bdrv_skip_implicit_filters(bs);
+        if (unfiltered_bs != bs) {
+            replaces = unfiltered_bs->node_name;
+            has_replaces = true;
+        }
+    }
+
     if (has_replaces) {
         BlockDriverState *to_replace_bs;
         AioContext *replace_aio_context;
@@ -3000,7 +3010,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
 void qmp_drive_mirror(DriveMirror *arg, Error **errp)
 {
     BlockDriverState *bs;
-    BlockDriverState *source, *target_bs;
+    BlockDriverState *target_backing_bs, *target_bs;
     AioContext *aio_context;
     AioContext *old_context;
     BlockMirrorBackingMode backing_mode;
@@ -3035,12 +3045,12 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
     }
 
     flags = bs->open_flags | BDRV_O_RDWR;
-    source = backing_bs(bs);
-    if (!source && arg->sync == MIRROR_SYNC_MODE_TOP) {
+    target_backing_bs = bdrv_cow_bs(bdrv_skip_filters(bs));
+    if (!target_backing_bs && arg->sync == MIRROR_SYNC_MODE_TOP) {
         arg->sync = MIRROR_SYNC_MODE_FULL;
     }
     if (arg->sync == MIRROR_SYNC_MODE_NONE) {
-        source = bs;
+        target_backing_bs = bs;
     }
 
     size = bdrv_getlength(bs);
@@ -3066,7 +3076,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
     /* Don't open backing image in create() */
     flags |= BDRV_O_NO_BACKING;
 
-    if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source)
+    if ((arg->sync == MIRROR_SYNC_MODE_FULL || !target_backing_bs)
         && arg->mode != NEW_IMAGE_MODE_EXISTING)
     {
         /* create new image w/o backing file */
@@ -3074,15 +3084,19 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
         bdrv_img_create(arg->target, format,
                         NULL, NULL, NULL, size, flags, false, &local_err);
     } else {
+        /* Implicit filters should not appear in the filename */
+        BlockDriverState *explicit_backing =
+            bdrv_skip_implicit_filters(target_backing_bs);
+
         switch (arg->mode) {
         case NEW_IMAGE_MODE_EXISTING:
             break;
         case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
             /* create new image with backing file */
-            bdrv_refresh_filename(source);
+            bdrv_refresh_filename(explicit_backing);
             bdrv_img_create(arg->target, format,
-                            source->filename,
-                            source->drv->format_name,
+                            explicit_backing->filename,
+                            explicit_backing->drv->format_name,
                             NULL, size, flags, false, &local_err);
             break;
         default:
-- 
2.25.4



  parent reply	other threads:[~2020-09-07 11:31 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-07 11:08 [PULL 00/64] Block layer patches Kevin Wolf
2020-09-07 11:08 ` [PULL 01/64] block: Raise an error when backing file parameter is an empty string Kevin Wolf
2020-09-07 11:08 ` [PULL 02/64] block/nvme: Replace magic value by SCALE_MS definition Kevin Wolf
2020-09-07 11:08 ` [PULL 03/64] block/nvme: Avoid further processing if trace event not enabled Kevin Wolf
2020-09-07 11:08 ` [PULL 04/64] block/nvme: Let nvme_create_queue_pair() fail gracefully Kevin Wolf
2020-09-07 11:08 ` [PULL 05/64] block/nvme: Define INDEX macros to ease code review Kevin Wolf
2020-09-07 11:08 ` [PULL 06/64] block/nvme: Improve error message when IO queue creation failed Kevin Wolf
2020-09-07 11:08 ` [PULL 07/64] block/nvme: Use common error path in nvme_add_io_queue() Kevin Wolf
2020-09-07 11:08 ` [PULL 08/64] block/nvme: Rename local variable Kevin Wolf
2020-09-07 11:08 ` [PULL 09/64] block/nvme: Use union of NvmeIdCtrl / NvmeIdNs structures Kevin Wolf
2020-09-07 11:08 ` [PULL 10/64] block/nvme: Replace qemu_try_blockalign0 by qemu_try_blockalign/memset Kevin Wolf
2020-09-07 11:08 ` [PULL 11/64] block/nvme: Replace qemu_try_blockalign(bs) by qemu_try_memalign(pg_sz) Kevin Wolf
2020-09-07 11:08 ` [PULL 12/64] block/nvme: Simplify nvme_init_queue() arguments Kevin Wolf
2020-09-07 11:08 ` [PULL 13/64] block/nvme: Replace BDRV_POLL_WHILE by AIO_WAIT_WHILE Kevin Wolf
2020-09-07 11:08 ` [PULL 14/64] block/nvme: Simplify nvme_create_queue_pair() arguments Kevin Wolf
2020-09-07 11:08 ` [PULL 15/64] block/nvme: Extract nvme_poll_queue() Kevin Wolf
2020-09-07 11:08 ` [PULL 16/64] block/nvme: Use an array of EventNotifier Kevin Wolf
2020-09-07 11:08 ` [PULL 17/64] block: Add child access functions Kevin Wolf
2020-09-07 11:08 ` [PULL 18/64] block: Add chain helper functions Kevin Wolf
2020-09-07 11:08 ` [PULL 19/64] block: bdrv_cow_child() for bdrv_has_zero_init() Kevin Wolf
2020-09-07 11:08 ` [PULL 20/64] block: bdrv_set_backing_hd() is about bs->backing Kevin Wolf
2020-09-07 11:08 ` [PULL 21/64] block: Include filters when freezing backing chain Kevin Wolf
2020-09-07 11:08 ` [PULL 22/64] block: Drop bdrv_is_encrypted() Kevin Wolf
2020-09-07 11:08 ` [PULL 23/64] block: Add bdrv_supports_compressed_writes() Kevin Wolf
2020-09-07 11:08 ` [PULL 24/64] throttle: Support compressed writes Kevin Wolf
2020-09-07 11:08 ` [PULL 25/64] copy-on-read: " Kevin Wolf
2020-09-07 11:08 ` [PULL 26/64] block: Use bdrv_filter_(bs|child) where obvious Kevin Wolf
2020-09-07 11:08 ` [PULL 27/64] block: Use CAFs in block status functions Kevin Wolf
2020-09-07 11:09 ` [PULL 28/64] stream: Deal with filters Kevin Wolf
2020-09-07 11:09 ` [PULL 29/64] block: Use CAFs when working with backing chains Kevin Wolf
2020-09-07 11:09 ` [PULL 30/64] block: Use bdrv_cow_child() in bdrv_co_truncate() Kevin Wolf
2020-09-07 11:09 ` [PULL 31/64] block: Re-evaluate backing file handling in reopen Kevin Wolf
2020-09-07 11:09 ` [PULL 32/64] block: Flush all children in generic code Kevin Wolf
2020-09-07 11:09 ` [PULL 33/64] vmdk: Drop vmdk_co_flush() Kevin Wolf
2020-09-07 11:09 ` [PULL 34/64] block: Iterate over children in refresh_limits Kevin Wolf
2020-09-07 11:09 ` [PULL 35/64] block: Use CAFs in bdrv_refresh_filename() Kevin Wolf
2020-09-07 11:09 ` [PULL 36/64] block: Use CAF in bdrv_co_rw_vmstate() Kevin Wolf
2020-09-07 11:09 ` [PULL 37/64] block/snapshot: Fix fallback Kevin Wolf
2021-04-30 22:30   ` Peter Maydell
2021-05-03  9:40     ` Kevin Wolf
2021-05-03  9:45       ` Max Reitz
2021-05-03 10:17         ` Kevin Wolf
2020-09-07 11:09 ` [PULL 38/64] block: Use CAFs for debug breakpoints Kevin Wolf
2020-09-07 11:09 ` [PULL 39/64] block: Improve get_allocated_file_size's default Kevin Wolf
2020-09-07 11:09 ` [PULL 40/64] block/null: Implement bdrv_get_allocated_file_size Kevin Wolf
2020-09-07 11:09 ` [PULL 41/64] blockdev: Use CAF in external_snapshot_prepare() Kevin Wolf
2020-09-07 11:09 ` [PULL 42/64] block: Report data child for query-blockstats Kevin Wolf
2020-09-07 11:09 ` [PULL 43/64] block: Use child access functions for QAPI queries Kevin Wolf
2020-09-07 11:09 ` [PULL 44/64] block-copy: Use CAF to find sync=top base Kevin Wolf
2020-09-07 11:09 ` Kevin Wolf [this message]
2020-09-07 11:09 ` [PULL 46/64] backup: Deal with filters Kevin Wolf
2020-09-07 11:09 ` [PULL 47/64] commit: " Kevin Wolf
2020-09-07 11:09 ` [PULL 48/64] nbd: Use CAF when looking for dirty bitmap Kevin Wolf
2020-09-07 11:09 ` [PULL 49/64] qemu-img: Use child access functions Kevin Wolf
2020-09-07 11:09 ` [PULL 50/64] block: Drop backing_bs() Kevin Wolf
2020-09-07 11:09 ` [PULL 51/64] blockdev: Fix active commit choice Kevin Wolf
2020-09-07 11:09 ` [PULL 52/64] block: Inline bdrv_co_block_status_from_*() Kevin Wolf
2020-09-07 11:09 ` [PULL 53/64] block: Leave BDS.backing_{file,format} constant Kevin Wolf
2020-09-07 11:09 ` [PULL 54/64] iotests: Test that qcow2's data-file is flushed Kevin Wolf
2020-09-07 11:09 ` [PULL 55/64] iotests: Let complete_and_wait() work with commit Kevin Wolf
2020-09-07 11:09 ` [PULL 56/64] iotests: Add filter commit test cases Kevin Wolf
2020-09-07 11:09 ` [PULL 57/64] iotests: Add filter mirror " Kevin Wolf
2020-09-07 11:09 ` [PULL 58/64] iotests: Add test for commit in sub directory Kevin Wolf
2020-09-07 11:09 ` [PULL 59/64] iotests: Test committing to overridden backing Kevin Wolf
2020-09-07 11:09 ` [PULL 60/64] iotests: Allow running from different directory Kevin Wolf
2020-09-07 11:09 ` [PULL 61/64] file-win32: Fix "locking" option Kevin Wolf
2020-09-07 11:09 ` [PULL 62/64] block/nvme: Group controller registers in NVMeRegs structure Kevin Wolf
2020-09-07 11:09 ` [PULL 63/64] block/nvme: Use generic NvmeBar structure Kevin Wolf
2020-09-07 11:09 ` [PULL 64/64] block/nvme: Pair doorbell registers Kevin Wolf
2020-09-07 20:22 ` [PULL 00/64] Block layer patches Peter Maydell
2020-09-08  7:01   ` Kevin Wolf
2020-09-08  9:01     ` Max Reitz

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=20200907110936.261684-46-kwolf@redhat.com \
    --to=kwolf@redhat.com \
    --cc=peter.maydell@linaro.org \
    --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.