From nobody Fri Oct 24 22:17:01 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1519841634630147.55134636344314; Wed, 28 Feb 2018 10:13:54 -0800 (PST) Received: from localhost ([::1]:45991 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6Ej-0002es-AL for importer@patchew.org; Wed, 28 Feb 2018 13:13:53 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41672) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er66r-0004Ld-22 for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:46 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66p-0004EJ-HL for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:45 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:53778 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1er66k-0004BJ-OT; Wed, 28 Feb 2018 13:05:38 -0500 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 59F064040073; Wed, 28 Feb 2018 18:05:38 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id C4E142026612; Wed, 28 Feb 2018 18:05:37 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:04:58 +0100 Message-Id: <20180228180507.3964-8-mreitz@redhat.com> In-Reply-To: <20180228180507.3964-1-mreitz@redhat.com> References: <20180228180507.3964-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Wed, 28 Feb 2018 18:05:38 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Wed, 28 Feb 2018 18:05:38 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'mreitz@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v3 07/16] block/mirror: Wait for in-flight op conflicts X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Fam Zheng , qemu-devel@nongnu.org, Max Reitz , Stefan Hajnoczi , John Snow Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This patch makes the mirror code differentiate between simply waiting for any operation to complete (mirror_wait_for_free_in_flight_slot()) and specifically waiting for all operations touching a certain range of the virtual disk to complete (mirror_wait_on_conflicts()). Signed-off-by: Max Reitz --- block/mirror.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++------= ---- 1 file changed, 84 insertions(+), 18 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 906fe8e535..d65fab3b45 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/coroutine.h" +#include "qemu/range.h" #include "trace.h" #include "block/blockjob_int.h" #include "block/block_int.h" @@ -88,6 +89,7 @@ struct MirrorOp { * mirror_co_discard() before yielding for the first time */ int64_t *bytes_handled; =20 + bool is_pseudo_op; CoQueue waiting_requests; =20 QTAILQ_ENTRY(MirrorOp) next; @@ -112,6 +114,41 @@ static BlockErrorAction mirror_error_action(MirrorBloc= kJob *s, bool read, } } =20 +static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self, + MirrorBlockJob *s, + uint64_t offset, + uint64_t bytes) +{ + uint64_t self_start_chunk =3D offset / s->granularity; + uint64_t self_end_chunk =3D DIV_ROUND_UP(offset + bytes, s->granularit= y); + uint64_t self_nb_chunks =3D self_end_chunk - self_start_chunk; + + while (find_next_bit(s->in_flight_bitmap, self_end_chunk, + self_start_chunk) < self_end_chunk && + s->ret >=3D 0) + { + MirrorOp *op; + + QTAILQ_FOREACH(op, &s->ops_in_flight, next) { + uint64_t op_start_chunk =3D op->offset / s->granularity; + uint64_t op_nb_chunks =3D DIV_ROUND_UP(op->offset + op->bytes, + s->granularity) - + op_start_chunk; + + if (op =3D=3D self) { + continue; + } + + if (ranges_overlap(self_start_chunk, self_nb_chunks, + op_start_chunk, op_nb_chunks)) + { + qemu_co_queue_wait(&op->waiting_requests, NULL); + break; + } + } + } +} + static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) { MirrorBlockJob *s =3D op->s; @@ -234,13 +271,22 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_= t *offset, return ret; } =20 -static inline void mirror_wait_for_io(MirrorBlockJob *s) +static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) { MirrorOp *op; =20 - op =3D QTAILQ_FIRST(&s->ops_in_flight); - assert(op); - qemu_co_queue_wait(&op->waiting_requests, NULL); + QTAILQ_FOREACH(op, &s->ops_in_flight, next) { + /* Do not wait on pseudo ops, because it may in turn wait on + * some other operation to start, which may in fact be the + * caller of this function. Since there is only one pseudo op + * at any given time, we will always find some real operation + * to wait on. */ + if (!op->is_pseudo_op) { + qemu_co_queue_wait(&op->waiting_requests, NULL); + return; + } + } + abort(); } =20 /* Perform a mirror copy operation. @@ -284,7 +330,7 @@ static void coroutine_fn mirror_co_read(void *opaque) =20 while (s->buf_free_count < nb_chunks) { trace_mirror_yield_in_flight(s, op->offset, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); } =20 /* Now make a QEMUIOVector taking enough granularity-sized chunks @@ -384,8 +430,9 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64= _t offset, static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) { BlockDriverState *source =3D s->source; - int64_t offset, first_chunk; - uint64_t delay_ns =3D 0; + MirrorOp *pseudo_op; + int64_t offset; + uint64_t delay_ns =3D 0, ret =3D 0; /* At least the first dirty chunk is mirrored in one iteration. */ int nb_chunks =3D 1; bool write_zeroes_ok =3D bdrv_can_write_zeroes_with_unmap(blk_bs(s->ta= rget)); @@ -401,11 +448,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBl= ockJob *s) } bdrv_dirty_bitmap_unlock(s->dirty_bitmap); =20 - first_chunk =3D offset / s->granularity; - while (test_bit(first_chunk, s->in_flight_bitmap)) { - trace_mirror_yield_in_flight(s, offset, s->in_flight); - mirror_wait_for_io(s); - } + mirror_wait_on_conflicts(NULL, s, offset, 1); =20 block_job_pause_point(&s->common); =20 @@ -442,6 +485,21 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBl= ockJob *s) nb_chunks * s->granularity); bdrv_dirty_bitmap_unlock(s->dirty_bitmap); =20 + /* Before claiming an area in the in-flight bitmap, we have to + * create a MirrorOp for it so that conflicting requests can wait + * for it. mirror_perform() will create the real MirrorOps later, + * for now we just create a pseudo operation that will wake up all + * conflicting requests once all real operations have been + * launched. */ + pseudo_op =3D g_new(MirrorOp, 1); + *pseudo_op =3D (MirrorOp){ + .offset =3D offset, + .bytes =3D nb_chunks * s->granularity, + .is_pseudo_op =3D true, + }; + qemu_co_queue_init(&pseudo_op->waiting_requests); + QTAILQ_INSERT_TAIL(&s->ops_in_flight, pseudo_op, next); + bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks); while (nb_chunks > 0 && offset < s->bdev_length) { int ret; @@ -477,11 +535,12 @@ static uint64_t coroutine_fn mirror_iteration(MirrorB= lockJob *s) =20 while (s->in_flight >=3D MAX_IN_FLIGHT) { trace_mirror_yield_in_flight(s, offset, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); } =20 if (s->ret < 0) { - return 0; + ret =3D 0; + goto fail; } =20 io_bytes =3D mirror_clip_bytes(s, offset, io_bytes); @@ -498,7 +557,14 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBl= ockJob *s) delay_ns =3D ratelimit_calculate_delay(&s->limit, io_bytes_acc= t); } } - return delay_ns; + + ret =3D delay_ns; +fail: + QTAILQ_REMOVE(&s->ops_in_flight, pseudo_op, next); + qemu_co_queue_restart_all(&pseudo_op->waiting_requests); + g_free(pseudo_op); + + return ret; } =20 static void mirror_free_init(MirrorBlockJob *s) @@ -525,7 +591,7 @@ static void mirror_free_init(MirrorBlockJob *s) static void mirror_wait_for_all_io(MirrorBlockJob *s) { while (s->in_flight > 0) { - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); } } =20 @@ -679,7 +745,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJo= b *s) if (s->in_flight >=3D MAX_IN_FLIGHT) { trace_mirror_yield(s, UINT64_MAX, s->buf_free_count, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); continue; } =20 @@ -854,7 +920,7 @@ static void coroutine_fn mirror_run(void *opaque) if (s->in_flight >=3D MAX_IN_FLIGHT || s->buf_free_count =3D= =3D 0 || (cnt =3D=3D 0 && s->in_flight > 0)) { trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight= ); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); continue; } else if (cnt !=3D 0) { delay_ns =3D mirror_iteration(s); --=20 2.14.3