From nobody Fri Oct 24 09:56:51 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 1519841414876228.9234414718128; Wed, 28 Feb 2018 10:10:14 -0800 (PST) Received: from localhost ([::1]:45961 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6BB-0007y3-Ok for importer@patchew.org; Wed, 28 Feb 2018 13:10:13 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41489) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er66W-00044E-TX for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:25 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66V-00042e-Oe for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:24 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:42064 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 1er66P-0003yq-BE; Wed, 28 Feb 2018 13:05:17 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 73839402290A; Wed, 28 Feb 2018 18:05:15 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 9112B10B0F53; Wed, 28 Feb 2018 18:05:10 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:04:52 +0100 Message-Id: <20180228180507.3964-2-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.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:05:15 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:05:15 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.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 01/16] block: BDS deletion during bdrv_drain_recurse 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" Draining a BDS child may lead to other children of the same parent being detached and/or deleted. We should prepare for the former case (by copying the children list before iterating through it) and prevent the latter (by bdrv_ref()'ing all nodes if we are in the main loop). Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: Jeff Cody --- block/io.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/block/io.c b/block/io.c index 4d3d1f640a..ead12c4136 100644 --- a/block/io.c +++ b/block/io.c @@ -189,31 +189,51 @@ static void bdrv_drain_invoke(BlockDriverState *bs, b= ool begin, bool recursive) =20 static bool bdrv_drain_recurse(BlockDriverState *bs) { - BdrvChild *child, *tmp; + BdrvChild *child; bool waited; + struct BDSToDrain { + BlockDriverState *bs; + QLIST_ENTRY(BDSToDrain) next; + }; + QLIST_HEAD(, BDSToDrain) bs_list =3D QLIST_HEAD_INITIALIZER(bs_list); + bool in_main_loop =3D + qemu_get_current_aio_context() =3D=3D qemu_get_aio_context(); =20 /* Wait for drained requests to finish */ waited =3D BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); =20 - QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { - BlockDriverState *bs =3D child->bs; - bool in_main_loop =3D - qemu_get_current_aio_context() =3D=3D qemu_get_aio_context(); - assert(bs->refcnt > 0); + /* Draining children may result in other children being removed from t= his + * parent and maybe even deleted, so copy the children list first */ + QLIST_FOREACH(child, &bs->children, next) { + struct BDSToDrain *bs2d =3D g_new0(struct BDSToDrain, 1); + + bs2d->bs =3D child->bs; if (in_main_loop) { /* In case the recursive bdrv_drain_recurse processes a * block_job_defer_to_main_loop BH and modifies the graph, - * let's hold a reference to bs until we are done. + * let's hold a reference to the BDS until we are done. * * IOThread doesn't have such a BH, and it is not safe to call * bdrv_unref without BQL, so skip doing it there. */ - bdrv_ref(bs); + bdrv_ref(bs2d->bs); } - waited |=3D bdrv_drain_recurse(bs); + + QLIST_INSERT_HEAD(&bs_list, bs2d, next); + } + + while (!QLIST_EMPTY(&bs_list)) { + struct BDSToDrain *bs2d =3D QLIST_FIRST(&bs_list); + QLIST_REMOVE(bs2d, next); + + assert(bs2d->bs->refcnt > 0); + + waited |=3D bdrv_drain_recurse(bs2d->bs); if (in_main_loop) { - bdrv_unref(bs); + bdrv_unref(bs2d->bs); } + + g_free(bs2d); } =20 return waited; --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519841260599387.9354535055196; Wed, 28 Feb 2018 10:07:40 -0800 (PST) Received: from localhost ([::1]:45942 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er68h-0005Na-8K for importer@patchew.org; Wed, 28 Feb 2018 13:07:39 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41486) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er66W-00044C-Sm for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:25 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66V-00042p-P7 for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:24 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:47014 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 1er66P-0003zE-Ur; Wed, 28 Feb 2018 13:05:18 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6F771818535A; Wed, 28 Feb 2018 18:05:17 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0EF9C2144B21; Wed, 28 Feb 2018 18:05:16 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:04:53 +0100 Message-Id: <20180228180507.3964-3-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.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:05:17 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:05:17 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.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 02/16] block: BDS deletion in bdrv_do_drained_begin() 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" Draining a BDS (in the main loop) may cause it to go be deleted. That is rather suboptimal if we still plan to access it afterwards, so let us enclose the main body of the function with a bdrv_ref()/bdrv_unref() pair. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: Jeff Cody --- block/io.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/block/io.c b/block/io.c index ead12c4136..3b61e26114 100644 --- a/block/io.c +++ b/block/io.c @@ -294,12 +294,27 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool= recursive, BdrvChild *parent) { BdrvChild *child, *next; + bool in_main_loop =3D + qemu_get_current_aio_context() =3D=3D qemu_get_aio_context(); + /* bdrv_close() invokes bdrv_drain() with bs->refcnt =3D=3D 0; then, + * we may not invoke bdrv_ref()/bdrv_unref() because the latter + * would result in the refcount going back to 0, creating an + * infinite loop. + * Also, we have to be in the main loop because we may not call + * bdrv_unref() elsewhere. But because of that, the BDS is not in + * danger of going away without the bdrv_ref()/bdrv_unref() pair + * elsewhere, so we are fine then. */ + bool add_ref =3D in_main_loop && bs->refcnt > 0; =20 if (qemu_in_coroutine()) { bdrv_co_yield_to_drain(bs, true, recursive, parent); return; } =20 + if (add_ref) { + bdrv_ref(bs); + } + /* Stop things in parent-to-child order */ if (atomic_fetch_inc(&bs->quiesce_counter) =3D=3D 0) { aio_disable_external(bdrv_get_aio_context(bs)); @@ -315,6 +330,10 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool = recursive, bdrv_do_drained_begin(child->bs, true, child); } } + + if (add_ref) { + bdrv_unref(bs); + } } =20 void bdrv_drained_begin(BlockDriverState *bs) --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519841571156639.9745234428568; Wed, 28 Feb 2018 10:12:51 -0800 (PST) Received: from localhost ([::1]:45987 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6Dh-0001jh-OS for importer@patchew.org; Wed, 28 Feb 2018 13:12:49 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41571) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er66f-0004Bf-LI for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:35 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66e-00047v-8x for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:33 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:47032 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 1er66Y-00044Y-8M; Wed, 28 Feb 2018 13:05:26 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CFA6E818B103; Wed, 28 Feb 2018 18:05:25 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id F16F79C042; Wed, 28 Feb 2018 18:05:18 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:04:54 +0100 Message-Id: <20180228180507.3964-4-mreitz@redhat.com> In-Reply-To: <20180228180507.3964-1-mreitz@redhat.com> References: <20180228180507.3964-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:05:25 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:05:25 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.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 03/16] tests: Add bdrv-drain test for node deletion 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 adds two bdrv-drain tests for what happens if some BDS goes away during the drainage. The basic idea is that you have a parent BDS with some child nodes. Then, you drain one of the children. Because of that, the party who actually owns the parent decides to (A) delete it, or (B) detach all its children from it -- both while the child is still being drained. A real-world case where this can happen is the mirror block job, which may exit if you drain one of its children. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- tests/test-bdrv-drain.c | 165 ++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 165 insertions(+) diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index d760e2b243..c8c9178f25 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -610,6 +610,169 @@ static void test_blockjob_drain_subtree(void) test_blockjob_common(BDRV_SUBTREE_DRAIN); } =20 + +typedef struct BDRVTestTopState { + BdrvChild *wait_child; +} BDRVTestTopState; + +static void bdrv_test_top_close(BlockDriverState *bs) +{ + BdrvChild *c, *next_c; + QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { + bdrv_unref_child(bs, c); + } +} + +static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t = bytes, + QEMUIOVector *qiov, int fl= ags) +{ + BDRVTestTopState *tts =3D bs->opaque; + return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags); +} + +static BlockDriver bdrv_test_top_driver =3D { + .format_name =3D "test_top_driver", + .instance_size =3D sizeof(BDRVTestTopState), + + .bdrv_close =3D bdrv_test_top_close, + .bdrv_co_preadv =3D bdrv_test_top_co_preadv, + + .bdrv_child_perm =3D bdrv_format_default_perms, +}; + +typedef struct TestCoDeleteByDrainData { + BlockBackend *blk; + bool detach_instead_of_delete; + bool done; +} TestCoDeleteByDrainData; + +static void coroutine_fn test_co_delete_by_drain(void *opaque) +{ + TestCoDeleteByDrainData *dbdd =3D opaque; + BlockBackend *blk =3D dbdd->blk; + BlockDriverState *bs =3D blk_bs(blk); + BDRVTestTopState *tts =3D bs->opaque; + void *buffer =3D g_malloc(65536); + QEMUIOVector qiov; + struct iovec iov =3D { + .iov_base =3D buffer, + .iov_len =3D 65536, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + + /* Pretend some internal write operation from parent to child. + * Important: We have to read from the child, not from the parent! + * Draining works by first propagating it all up the tree to the + * root and then waiting for drainage from root to the leaves + * (protocol nodes). If we have a request waiting on the root, + * everything will be drained before we go back down the tree, but + * we do not want that. We want to be in the middle of draining + * when this following requests returns. */ + bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0); + /* The drain is running concurrently, so it must have its own + * reference to @bs */ + g_assert_cmpint(bs->refcnt, =3D=3D, 2); + + if (!dbdd->detach_instead_of_delete) { + blk_unref(blk); + } else { + BdrvChild *c, *next_c; + QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { + bdrv_unref_child(bs, c); + } + } + + dbdd->done =3D true; +} + +/** + * Test what happens when some BDS has some children, you drain one of + * them and this results in the BDS being deleted. + * + * If @detach_instead_of_delete is set, the BDS is not going to be + * deleted but will only detach all of its children. + */ +static void do_test_delete_by_drain(bool detach_instead_of_delete) +{ + BlockBackend *blk; + BlockDriverState *bs, *child_bs, *null_bs; + BDRVTestTopState *tts; + TestCoDeleteByDrainData dbdd; + Coroutine *co; + + bs =3D bdrv_new_open_driver(&bdrv_test_top_driver, "top", BDRV_O_RDWR, + &error_abort); + bs->total_sectors =3D 65536 >> BDRV_SECTOR_BITS; + tts =3D bs->opaque; + + null_bs =3D bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_P= ROTOCOL, + &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort= ); + + /* This child will be the one to pass to requests through to, and + * it will stall until a drain occurs */ + child_bs =3D bdrv_new_open_driver(&bdrv_test, "child", BDRV_O_RDWR, + &error_abort); + child_bs->total_sectors =3D 65536 >> BDRV_SECTOR_BITS; + /* Takes our reference to child_bs */ + tts->wait_child =3D bdrv_attach_child(bs, child_bs, "wait-child", &chi= ld_file, + &error_abort); + + /* This child is just there to be deleted + * (for detach_instead_of_delete =3D=3D true) */ + null_bs =3D bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_P= ROTOCOL, + &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort= ); + + blk =3D blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk, bs, &error_abort); + + /* Referenced by blk now */ + bdrv_unref(bs); + + g_assert_cmpint(bs->refcnt, =3D=3D, 1); + g_assert_cmpint(child_bs->refcnt, =3D=3D, 1); + g_assert_cmpint(null_bs->refcnt, =3D=3D, 1); + + + dbdd =3D (TestCoDeleteByDrainData){ + .blk =3D blk, + .detach_instead_of_delete =3D detach_instead_of_delete, + .done =3D false, + }; + co =3D qemu_coroutine_create(test_co_delete_by_drain, &dbdd); + qemu_coroutine_enter(co); + + /* Drain the child while the read operation is still pending. + * This should result in the operation finishing and + * test_co_delete_by_drain() resuming. Thus, @bs will be deleted + * and the coroutine will exit while this drain operation is still + * in progress. */ + bdrv_ref(child_bs); + bdrv_drain(child_bs); + bdrv_unref(child_bs); + + while (!dbdd.done) { + aio_poll(qemu_get_aio_context(), true); + } + + if (detach_instead_of_delete) { + /* Here, the reference has not passed over to the coroutine, + * so we have to delete the BB ourselves */ + blk_unref(blk); + } +} + + +static void test_delete_by_drain(void) +{ + do_test_delete_by_drain(false); + do_test_delete_by_drain(true); +} + + int main(int argc, char **argv) { bdrv_init(); @@ -647,5 +810,7 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/blockjob/drain_subtree", test_blockjob_drain_subtree); =20 + g_test_add_func("/bdrv-drain/deletion", test_delete_by_drain); + return g_test_run(); } --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519841733065200.8964455534616; Wed, 28 Feb 2018 10:15:33 -0800 (PST) Received: from localhost ([::1]:46008 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6GJ-0004Cx-Rr for importer@patchew.org; Wed, 28 Feb 2018 13:15:31 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41608) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er66j-0004Fp-VH for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:43 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66j-0004AW-2D for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:37 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:42114 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 1er66a-00045Y-6Y; Wed, 28 Feb 2018 13:05:28 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C5AFD402314E; Wed, 28 Feb 2018 18:05:27 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 64C1410B0F53; Wed, 28 Feb 2018 18:05:27 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:04:55 +0100 Message-Id: <20180228180507.3964-5-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.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:05:27 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:05:27 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.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 04/16] block/mirror: Pull out mirror_perform() 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" When converting mirror's I/O to coroutines, we are going to need a point where these coroutines are created. mirror_perform() is going to be that point. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Jeff Cody --- block/mirror.c | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index f5bf620942..d197c8936e 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -82,6 +82,12 @@ typedef struct MirrorOp { uint64_t bytes; } MirrorOp; =20 +typedef enum MirrorMethod { + MIRROR_METHOD_COPY, + MIRROR_METHOD_ZERO, + MIRROR_METHOD_DISCARD, +} MirrorMethod; + static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { @@ -321,6 +327,22 @@ static void mirror_do_zero_or_discard(MirrorBlockJob *= s, } } =20 +static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, + unsigned bytes, MirrorMethod mirror_method) +{ + switch (mirror_method) { + case MIRROR_METHOD_COPY: + return mirror_do_read(s, offset, bytes); + case MIRROR_METHOD_ZERO: + case MIRROR_METHOD_DISCARD: + mirror_do_zero_or_discard(s, offset, bytes, + mirror_method =3D=3D MIRROR_METHOD_DISCA= RD); + return bytes; + default: + abort(); + } +} + static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) { BlockDriverState *source =3D s->source; @@ -387,11 +409,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBl= ockJob *s) int ret; int64_t io_bytes; int64_t io_bytes_acct; - enum MirrorMethod { - MIRROR_METHOD_COPY, - MIRROR_METHOD_ZERO, - MIRROR_METHOD_DISCARD - } mirror_method =3D MIRROR_METHOD_COPY; + MirrorMethod mirror_method =3D MIRROR_METHOD_COPY; =20 assert(!(offset % s->granularity)); ret =3D bdrv_block_status_above(source, NULL, offset, @@ -429,22 +447,11 @@ static uint64_t coroutine_fn mirror_iteration(MirrorB= lockJob *s) } =20 io_bytes =3D mirror_clip_bytes(s, offset, io_bytes); - switch (mirror_method) { - case MIRROR_METHOD_COPY: - io_bytes =3D io_bytes_acct =3D mirror_do_read(s, offset, io_by= tes); - break; - case MIRROR_METHOD_ZERO: - case MIRROR_METHOD_DISCARD: - mirror_do_zero_or_discard(s, offset, io_bytes, - mirror_method =3D=3D MIRROR_METHOD_D= ISCARD); - if (write_zeroes_ok) { - io_bytes_acct =3D 0; - } else { - io_bytes_acct =3D io_bytes; - } - break; - default: - abort(); + io_bytes =3D mirror_perform(s, offset, io_bytes, mirror_method); + if (mirror_method !=3D MIRROR_METHOD_COPY && write_zeroes_ok) { + io_bytes_acct =3D 0; + } else { + io_bytes_acct =3D io_bytes; } assert(io_bytes); offset +=3D io_bytes; @@ -638,7 +645,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJo= b *s) continue; } =20 - mirror_do_zero_or_discard(s, offset, bytes, false); + mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO); offset +=3D bytes; } =20 --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519841888790578.1339944078053; Wed, 28 Feb 2018 10:18:08 -0800 (PST) Received: from localhost ([::1]:46029 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6Ip-0006Eb-ER for importer@patchew.org; Wed, 28 Feb 2018 13:18:07 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41633) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er66o-0004IX-1j for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:44 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66l-0004Bz-DQ for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:42 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:47050 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 1er66c-00046a-9h; Wed, 28 Feb 2018 13:05:30 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id DD468818535A; Wed, 28 Feb 2018 18:05:29 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 578B02144B21; Wed, 28 Feb 2018 18:05:29 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:04:56 +0100 Message-Id: <20180228180507.3964-6-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.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:05:29 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:05:29 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.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 05/16] block/mirror: Convert to coroutines 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" In order to talk to the source BDS (and maybe in the future to the target BDS as well) directly, we need to convert our existing AIO requests into coroutine I/O requests. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- block/mirror.c | 152 ++++++++++++++++++++++++++++++++++-------------------= ---- 1 file changed, 90 insertions(+), 62 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index d197c8936e..ad481f650d 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -80,6 +80,10 @@ typedef struct MirrorOp { QEMUIOVector qiov; int64_t offset; uint64_t bytes; + + /* The pointee is set by mirror_co_read(), mirror_co_zero(), and + * mirror_co_discard() before yielding for the first time */ + int64_t *bytes_handled; } MirrorOp; =20 typedef enum MirrorMethod { @@ -101,7 +105,7 @@ static BlockErrorAction mirror_error_action(MirrorBlock= Job *s, bool read, } } =20 -static void mirror_iteration_done(MirrorOp *op, int ret) +static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) { MirrorBlockJob *s =3D op->s; struct iovec *iov; @@ -138,9 +142,8 @@ static void mirror_iteration_done(MirrorOp *op, int ret) } } =20 -static void mirror_write_complete(void *opaque, int ret) +static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) { - MirrorOp *op =3D opaque; MirrorBlockJob *s =3D op->s; =20 aio_context_acquire(blk_get_aio_context(s->common.blk)); @@ -157,9 +160,8 @@ static void mirror_write_complete(void *opaque, int ret) aio_context_release(blk_get_aio_context(s->common.blk)); } =20 -static void mirror_read_complete(void *opaque, int ret) +static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret) { - MirrorOp *op =3D opaque; MirrorBlockJob *s =3D op->s; =20 aio_context_acquire(blk_get_aio_context(s->common.blk)); @@ -174,8 +176,9 @@ static void mirror_read_complete(void *opaque, int ret) =20 mirror_iteration_done(op, ret); } else { - blk_aio_pwritev(s->target, op->offset, &op->qiov, - 0, mirror_write_complete, op); + ret =3D blk_co_pwritev(s->target, op->offset, + op->qiov.size, &op->qiov, 0); + mirror_write_complete(op, ret); } aio_context_release(blk_get_aio_context(s->common.blk)); } @@ -232,60 +235,57 @@ static inline void mirror_wait_for_io(MirrorBlockJob = *s) s->waiting_for_io =3D false; } =20 -/* Submit async read while handling COW. - * Returns: The number of bytes copied after and including offset, - * excluding any bytes copied prior to offset due to alignment. - * This will be @bytes if no alignment is necessary, or - * (new_end - offset) if tail is rounded up or down due to - * alignment or buffer limit. +/* Perform a mirror copy operation. + * + * *op->bytes_handled is set to the number of bytes copied after and + * including offset, excluding any bytes copied prior to offset due + * to alignment. This will be op->bytes if no alignment is necessary, + * or (new_end - op->offset) if the tail is rounded up or down due to + * alignment or buffer limit. */ -static uint64_t mirror_do_read(MirrorBlockJob *s, int64_t offset, - uint64_t bytes) +static void coroutine_fn mirror_co_read(void *opaque) { + MirrorOp *op =3D opaque; + MirrorBlockJob *s =3D op->s; BlockBackend *source =3D s->common.blk; int nb_chunks; uint64_t ret; - MirrorOp *op; uint64_t max_bytes; =20 max_bytes =3D s->granularity * s->max_iov; =20 /* We can only handle as much as buf_size at a time. */ - bytes =3D MIN(s->buf_size, MIN(max_bytes, bytes)); - assert(bytes); - assert(bytes < BDRV_REQUEST_MAX_BYTES); - ret =3D bytes; + op->bytes =3D MIN(s->buf_size, MIN(max_bytes, op->bytes)); + assert(op->bytes); + assert(op->bytes < BDRV_REQUEST_MAX_BYTES); + *op->bytes_handled =3D op->bytes; =20 if (s->cow_bitmap) { - ret +=3D mirror_cow_align(s, &offset, &bytes); + *op->bytes_handled +=3D mirror_cow_align(s, &op->offset, &op->byte= s); } - assert(bytes <=3D s->buf_size); + /* Cannot exceed BDRV_REQUEST_MAX_BYTES + INT_MAX */ + assert(*op->bytes_handled <=3D UINT_MAX); + assert(op->bytes <=3D s->buf_size); /* The offset is granularity-aligned because: * 1) Caller passes in aligned values; * 2) mirror_cow_align is used only when target cluster is larger. */ - assert(QEMU_IS_ALIGNED(offset, s->granularity)); + assert(QEMU_IS_ALIGNED(op->offset, s->granularity)); /* The range is sector-aligned, since bdrv_getlength() rounds up. */ - assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); - nb_chunks =3D DIV_ROUND_UP(bytes, s->granularity); + assert(QEMU_IS_ALIGNED(op->bytes, BDRV_SECTOR_SIZE)); + nb_chunks =3D DIV_ROUND_UP(op->bytes, s->granularity); =20 while (s->buf_free_count < nb_chunks) { - trace_mirror_yield_in_flight(s, offset, s->in_flight); + trace_mirror_yield_in_flight(s, op->offset, s->in_flight); mirror_wait_for_io(s); } =20 - /* Allocate a MirrorOp that is used as an AIO callback. */ - op =3D g_new(MirrorOp, 1); - op->s =3D s; - op->offset =3D offset; - op->bytes =3D bytes; - /* Now make a QEMUIOVector taking enough granularity-sized chunks * from s->buf_free. */ qemu_iovec_init(&op->qiov, nb_chunks); while (nb_chunks-- > 0) { MirrorBuffer *buf =3D QSIMPLEQ_FIRST(&s->buf_free); - size_t remaining =3D bytes - op->qiov.size; + size_t remaining =3D op->bytes - op->qiov.size; =20 QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); s->buf_free_count--; @@ -294,53 +294,81 @@ static uint64_t mirror_do_read(MirrorBlockJob *s, int= 64_t offset, =20 /* Copy the dirty cluster. */ s->in_flight++; - s->bytes_in_flight +=3D bytes; - trace_mirror_one_iteration(s, offset, bytes); + s->bytes_in_flight +=3D op->bytes; + trace_mirror_one_iteration(s, op->offset, op->bytes); =20 - blk_aio_preadv(source, offset, &op->qiov, 0, mirror_read_complete, op); - return ret; + ret =3D blk_co_preadv(source, op->offset, op->bytes, &op->qiov, 0); + mirror_read_complete(op, ret); } =20 -static void mirror_do_zero_or_discard(MirrorBlockJob *s, - int64_t offset, - uint64_t bytes, - bool is_discard) +static void coroutine_fn mirror_co_zero(void *opaque) { - MirrorOp *op; + MirrorOp *op =3D opaque; + int ret; =20 - /* Allocate a MirrorOp that is used as an AIO callback. The qiov is ze= roed - * so the freeing in mirror_iteration_done is nop. */ - op =3D g_new0(MirrorOp, 1); - op->s =3D s; - op->offset =3D offset; - op->bytes =3D bytes; + op->s->in_flight++; + op->s->bytes_in_flight +=3D op->bytes; + *op->bytes_handled =3D op->bytes; =20 - s->in_flight++; - s->bytes_in_flight +=3D bytes; - if (is_discard) { - blk_aio_pdiscard(s->target, offset, - op->bytes, mirror_write_complete, op); - } else { - blk_aio_pwrite_zeroes(s->target, offset, - op->bytes, s->unmap ? BDRV_REQ_MAY_UNMAP : 0, - mirror_write_complete, op); - } + ret =3D blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, + op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); + mirror_write_complete(op, ret); +} + +static void coroutine_fn mirror_co_discard(void *opaque) +{ + MirrorOp *op =3D opaque; + int ret; + + op->s->in_flight++; + op->s->bytes_in_flight +=3D op->bytes; + *op->bytes_handled =3D op->bytes; + + ret =3D blk_co_pdiscard(op->s->target, op->offset, op->bytes); + mirror_write_complete(op, ret); } =20 static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, unsigned bytes, MirrorMethod mirror_method) { + MirrorOp *op; + Coroutine *co; + int64_t bytes_handled =3D -1; + + op =3D g_new(MirrorOp, 1); + *op =3D (MirrorOp){ + .s =3D s, + .offset =3D offset, + .bytes =3D bytes, + .bytes_handled =3D &bytes_handled, + }; + switch (mirror_method) { case MIRROR_METHOD_COPY: - return mirror_do_read(s, offset, bytes); + co =3D qemu_coroutine_create(mirror_co_read, op); + break; case MIRROR_METHOD_ZERO: + co =3D qemu_coroutine_create(mirror_co_zero, op); + break; case MIRROR_METHOD_DISCARD: - mirror_do_zero_or_discard(s, offset, bytes, - mirror_method =3D=3D MIRROR_METHOD_DISCA= RD); - return bytes; + co =3D qemu_coroutine_create(mirror_co_discard, op); + break; default: abort(); } + + qemu_coroutine_enter(co); + /* At this point, ownership of op has been moved to the coroutine + * and the object may already be freed */ + + /* Assert that this value has been set */ + assert(bytes_handled >=3D 0); + + /* Same assertion as in mirror_co_read() (and for mirror_co_read() + * and mirror_co_discard(), bytes_handled =3D=3D op->bytes, which + * is the @bytes parameter given to this function) */ + assert(bytes_handled <=3D UINT_MAX); + return bytes_handled; } =20 static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519841553978648.4363646809048; Wed, 28 Feb 2018 10:12:33 -0800 (PST) Received: from localhost ([::1]:45984 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6DQ-0001Qr-PL for importer@patchew.org; Wed, 28 Feb 2018 13:12:32 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41649) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er66p-0004Jw-Hp for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:44 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66o-0004Da-Df for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:43 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:53776 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 1er66i-0004A1-Lq; Wed, 28 Feb 2018 13:05:36 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 447B44040073; Wed, 28 Feb 2018 18:05:36 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 6A7E510B0F53; Wed, 28 Feb 2018 18:05:31 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:04:57 +0100 Message-Id: <20180228180507.3964-7-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.3 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:36 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Wed, 28 Feb 2018 18:05:36 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.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 06/16] block/mirror: Use CoQueue to wait on in-flight ops 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" Attach a CoQueue to each in-flight operation so if we need to wait for any we can use it to wait instead of just blindly yielding and hoping for some operation to wake us. A later patch will use this infrastructure to allow requests accessing the same area of the virtual disk to specifically wait for each other. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- block/mirror.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index ad481f650d..906fe8e535 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -13,6 +13,7 @@ =20 #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/coroutine.h" #include "trace.h" #include "block/blockjob_int.h" #include "block/block_int.h" @@ -34,6 +35,8 @@ typedef struct MirrorBuffer { QSIMPLEQ_ENTRY(MirrorBuffer) next; } MirrorBuffer; =20 +typedef struct MirrorOp MirrorOp; + typedef struct MirrorBlockJob { BlockJob common; RateLimit limit; @@ -67,15 +70,15 @@ typedef struct MirrorBlockJob { unsigned long *in_flight_bitmap; int in_flight; int64_t bytes_in_flight; + QTAILQ_HEAD(MirrorOpList, MirrorOp) ops_in_flight; int ret; bool unmap; - bool waiting_for_io; int target_cluster_size; int max_iov; bool initial_zeroing_ongoing; } MirrorBlockJob; =20 -typedef struct MirrorOp { +struct MirrorOp { MirrorBlockJob *s; QEMUIOVector qiov; int64_t offset; @@ -84,7 +87,11 @@ typedef struct MirrorOp { /* The pointee is set by mirror_co_read(), mirror_co_zero(), and * mirror_co_discard() before yielding for the first time */ int64_t *bytes_handled; -} MirrorOp; + + CoQueue waiting_requests; + + QTAILQ_ENTRY(MirrorOp) next; +}; =20 typedef enum MirrorMethod { MIRROR_METHOD_COPY, @@ -125,7 +132,9 @@ static void coroutine_fn mirror_iteration_done(MirrorOp= *op, int ret) =20 chunk_num =3D op->offset / s->granularity; nb_chunks =3D DIV_ROUND_UP(op->bytes, s->granularity); + bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); + QTAILQ_REMOVE(&s->ops_in_flight, op, next); if (ret >=3D 0) { if (s->cow_bitmap) { bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); @@ -135,11 +144,9 @@ static void coroutine_fn mirror_iteration_done(MirrorO= p *op, int ret) } } qemu_iovec_destroy(&op->qiov); - g_free(op); =20 - if (s->waiting_for_io) { - qemu_coroutine_enter(s->common.co); - } + qemu_co_queue_restart_all(&op->waiting_requests); + g_free(op); } =20 static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) @@ -229,10 +236,11 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_= t *offset, =20 static inline void mirror_wait_for_io(MirrorBlockJob *s) { - assert(!s->waiting_for_io); - s->waiting_for_io =3D true; - qemu_coroutine_yield(); - s->waiting_for_io =3D false; + MirrorOp *op; + + op =3D QTAILQ_FIRST(&s->ops_in_flight); + assert(op); + qemu_co_queue_wait(&op->waiting_requests, NULL); } =20 /* Perform a mirror copy operation. @@ -342,6 +350,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64= _t offset, .bytes =3D bytes, .bytes_handled =3D &bytes_handled, }; + qemu_co_queue_init(&op->waiting_requests); =20 switch (mirror_method) { case MIRROR_METHOD_COPY: @@ -357,6 +366,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64= _t offset, abort(); } =20 + QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); qemu_coroutine_enter(co); /* At this point, ownership of op has been moved to the coroutine * and the object may already be freed */ @@ -1285,6 +1295,8 @@ static void mirror_start_job(const char *job_id, Bloc= kDriverState *bs, } } =20 + QTAILQ_INIT(&s->ops_in_flight); + trace_mirror_start(bs, s, opaque); block_job_start(&s->common); return; --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 Reviewed-by: Fam Zheng --- 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 From nobody Fri Oct 24 09:56:51 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 1519841713041501.2608131929228; Wed, 28 Feb 2018 10:15:13 -0800 (PST) Received: from localhost ([::1]:46005 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6Fv-0003qm-2Q for importer@patchew.org; Wed, 28 Feb 2018 13:15:07 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41737) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er66z-0004Sb-MA for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:54 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66y-0004JF-GM for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:53 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:43292 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 1er66t-0004GE-0O; Wed, 28 Feb 2018 13:05:47 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 99F0387ABA; Wed, 28 Feb 2018 18:05:46 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E2F86946C1; Wed, 28 Feb 2018 18:05:39 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:04:59 +0100 Message-Id: <20180228180507.3964-9-mreitz@redhat.com> In-Reply-To: <20180228180507.3964-1-mreitz@redhat.com> References: <20180228180507.3964-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Wed, 28 Feb 2018 18:05:46 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Wed, 28 Feb 2018 18:05:46 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.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 08/16] block/mirror: Use source as a BdrvChild 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" With this, the mirror_top_bs is no longer just a technically required node in the BDS graph but actually represents the block job operation. Also, drop MirrorBlockJob.source, as we can reach it through mirror_top_bs->backing. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- block/mirror.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index d65fab3b45..addfdff4dd 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -43,7 +43,6 @@ typedef struct MirrorBlockJob { RateLimit limit; BlockBackend *target; BlockDriverState *mirror_top_bs; - BlockDriverState *source; BlockDriverState *base; =20 /* The name of the graph node to replace */ @@ -301,7 +300,6 @@ static void coroutine_fn mirror_co_read(void *opaque) { MirrorOp *op =3D opaque; MirrorBlockJob *s =3D op->s; - BlockBackend *source =3D s->common.blk; int nb_chunks; uint64_t ret; uint64_t max_bytes; @@ -351,7 +349,8 @@ static void coroutine_fn mirror_co_read(void *opaque) s->bytes_in_flight +=3D op->bytes; trace_mirror_one_iteration(s, op->offset, op->bytes); =20 - ret =3D blk_co_preadv(source, op->offset, op->bytes, &op->qiov, 0); + ret =3D bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->byte= s, + &op->qiov, 0); mirror_read_complete(op, ret); } =20 @@ -429,7 +428,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64= _t offset, =20 static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) { - BlockDriverState *source =3D s->source; + BlockDriverState *source =3D s->mirror_top_bs->backing->bs; MirrorOp *pseudo_op; int64_t offset; uint64_t delay_ns =3D 0, ret =3D 0; @@ -604,7 +603,7 @@ static void mirror_exit(BlockJob *job, void *opaque) MirrorBlockJob *s =3D container_of(job, MirrorBlockJob, common); MirrorExitData *data =3D opaque; AioContext *replace_aio_context =3D NULL; - BlockDriverState *src =3D s->source; + BlockDriverState *src =3D s->mirror_top_bs->backing->bs; BlockDriverState *target_bs =3D blk_bs(s->target); BlockDriverState *mirror_top_bs =3D s->mirror_top_bs; Error *local_err =3D NULL; @@ -719,7 +718,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJo= b *s) { int64_t offset; BlockDriverState *base =3D s->base; - BlockDriverState *bs =3D s->source; + BlockDriverState *bs =3D s->mirror_top_bs->backing->bs; BlockDriverState *target_bs =3D blk_bs(s->target); int ret; int64_t count; @@ -801,7 +800,7 @@ static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s =3D opaque; MirrorExitData *data; - BlockDriverState *bs =3D s->source; + BlockDriverState *bs =3D s->mirror_top_bs->backing->bs; BlockDriverState *target_bs =3D blk_bs(s->target); bool need_drain =3D true; int64_t length; @@ -1288,7 +1287,6 @@ static void mirror_start_job(const char *job_id, Bloc= kDriverState *bs, /* The block job now has a reference to this node */ bdrv_unref(mirror_top_bs); =20 - s->source =3D bs; s->mirror_top_bs =3D mirror_top_bs; =20 /* No resize for the target either; while the mirror is still running,= a --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519841290183341.55719872845305; Wed, 28 Feb 2018 10:08:10 -0800 (PST) Received: from localhost ([::1]:45949 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er699-0005nV-RN for importer@patchew.org; Wed, 28 Feb 2018 13:08:07 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41750) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er670-0004Ts-T2 for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:56 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er66z-0004K2-P3 for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:54 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:38032 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 1er66u-0004HU-VI; Wed, 28 Feb 2018 13:05:49 -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 88A7E8D6D6; Wed, 28 Feb 2018 18:05:48 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 29E302026612; Wed, 28 Feb 2018 18:05:48 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:05:00 +0100 Message-Id: <20180228180507.3964-10-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.2]); Wed, 28 Feb 2018 18:05:48 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 28 Feb 2018 18:05:48 +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 09/16] block: Generalize should_update_child() rule 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" Currently, bdrv_replace_node() refuses to create loops from one BDS to itself if the BDS to be replaced is the backing node of the BDS to replace it: Say there is a node A and a node B. Replacing B by A means making all references to B point to A. If B is a child of A (i.e. A has a reference to B), that would mean we would have to make this reference point to A itself -- so we'd create a loop. bdrv_replace_node() (through should_update_child()) refuses to do so if B is the backing node of A. There is no reason why we should create loops if B is not the backing node of A, though. The BDS graph should never contain loops, so we should always refuse to create them. If B is a child of A and B is to be replaced by A, we should simply leave B in place there because it is the most sensible choice. A more specific argument would be: Putting filter drivers into the BDS graph is basically the same as appending an overlay to a backing chain. But the main child BDS of a filter driver is not "backing" but "file", so restricting the no-loop rule to backing nodes would fail here. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- include/block/block_int.h | 2 ++ block.c | 44 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/include/block/block_int.h b/include/block/block_int.h index 5ae7738cf8..7767fc28d3 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -601,6 +601,8 @@ struct BdrvChild { QLIST_ENTRY(BdrvChild) next_parent; }; =20 +typedef QLIST_HEAD(BdrvChildList, BdrvChild) BdrvChildList; + /* * Note: the function bdrv_append() copies and swaps contents of * BlockDriverStates, so if you add new fields to this struct, please diff --git a/block.c b/block.c index 814e5a02da..d41193025f 100644 --- a/block.c +++ b/block.c @@ -3323,16 +3323,39 @@ static bool should_update_child(BdrvChild *c, Block= DriverState *to) return false; } =20 - if (c->role =3D=3D &child_backing) { - /* If @from is a backing file of @to, ignore the child to avoid - * creating a loop. We only want to change the pointer of other - * parents. */ - QLIST_FOREACH(to_c, &to->children, next) { - if (to_c =3D=3D c) { - break; - } - } - if (to_c) { + /* If the child @c belongs to the BDS @to, replacing the current + * c->bs by @to would mean to create a loop. + * + * Such a case occurs when appending a BDS to a backing chain. + * For instance, imagine the following chain: + * + * guest device -> node A -> further backing chain... + * + * Now we create a new BDS B which we want to put on top of this + * chain, so we first attach A as its backing node: + * + * node B + * | + * v + * guest device -> node A -> further backing chain... + * + * Finally we want to replace A by B. When doing that, we want to + * replace all pointers to A by pointers to B -- except for the + * pointer from B because (1) that would create a loop, and (2) + * that pointer should simply stay intact: + * + * guest device -> node B + * | + * v + * node A -> further backing chain... + * + * In general, when replacing a node A (c->bs) by a node B (@to), + * if A is a child of B, that means we cannot replace A by B there + * because that would create a loop. Silently detaching A from B + * is also not really an option. So overall just leaving A in + * place there is the most sensible choice. */ + QLIST_FOREACH(to_c, &to->children, next) { + if (to_c =3D=3D c) { return false; } } @@ -3358,6 +3381,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockD= riverState *to, =20 /* Put all parents into @list and calculate their cumulative permissio= ns */ QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { + assert(c->bs =3D=3D from); if (!should_update_child(c, to)) { continue; } --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519842063293392.2040518221278; Wed, 28 Feb 2018 10:21:03 -0800 (PST) Received: from localhost ([::1]:46047 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6La-0008To-7Y for importer@patchew.org; Wed, 28 Feb 2018 13:20:58 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41755) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er671-0004Ud-HO for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:56 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er670-0004KI-3a for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:05:55 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:38034 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 1er66w-0004IM-S5; Wed, 28 Feb 2018 13:05:50 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 72F1F8D6D6; Wed, 28 Feb 2018 18:05:50 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1725C9C042; Wed, 28 Feb 2018 18:05:49 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:05:01 +0100 Message-Id: <20180228180507.3964-11-mreitz@redhat.com> In-Reply-To: <20180228180507.3964-1-mreitz@redhat.com> References: <20180228180507.3964-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 28 Feb 2018 18:05:50 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 28 Feb 2018 18:05:50 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.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 10/16] hbitmap: Add @advance param to hbitmap_iter_next() 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 new parameter allows the caller to just query the next dirty position without moving the iterator. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- include/qemu/hbitmap.h | 5 ++++- block/backup.c | 2 +- block/dirty-bitmap.c | 2 +- tests/test-hbitmap.c | 26 +++++++++++++------------- util/hbitmap.c | 10 +++++++--- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 6b6490ecad..ddca52c48e 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -324,11 +324,14 @@ void hbitmap_free_meta(HBitmap *hb); /** * hbitmap_iter_next: * @hbi: HBitmapIter to operate on. + * @advance: If true, advance the iterator. Otherwise, the next call + * of this function will return the same result (if that + * position is still dirty). * * Return the next bit that is set in @hbi's associated HBitmap, * or -1 if all remaining bits are zero. */ -int64_t hbitmap_iter_next(HBitmapIter *hbi); +int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance); =20 /** * hbitmap_iter_next_word: diff --git a/block/backup.c b/block/backup.c index 4a16a37229..c083ce0deb 100644 --- a/block/backup.c +++ b/block/backup.c @@ -368,7 +368,7 @@ static int coroutine_fn backup_run_incremental(BackupBl= ockJob *job) HBitmapIter hbi; =20 hbitmap_iter_init(&hbi, job->copy_bitmap, 0); - while ((cluster =3D hbitmap_iter_next(&hbi)) !=3D -1) { + while ((cluster =3D hbitmap_iter_next(&hbi, true)) !=3D -1) { do { if (yield_and_check(job)) { return 0; diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 909f0517f8..d8e999226e 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -493,7 +493,7 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) =20 int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) { - return hbitmap_iter_next(&iter->hbi); + return hbitmap_iter_next(&iter->hbi, true); } =20 /* Called within bdrv_dirty_bitmap_lock..unlock */ diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index f29631f939..f2158f767d 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -46,7 +46,7 @@ static void hbitmap_test_check(TestHBitmapData *data, =20 i =3D first; for (;;) { - next =3D hbitmap_iter_next(&hbi); + next =3D hbitmap_iter_next(&hbi, true); if (next < 0) { next =3D data->size; } @@ -435,25 +435,25 @@ static void test_hbitmap_iter_granularity(TestHBitmap= Data *data, /* Note that hbitmap_test_check has to be invoked manually in this tes= t. */ hbitmap_test_init(data, 131072 << 7, 7); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); =20 hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), =3D=3D, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), =3D=3D, (L2 + L1 + 1) <= < 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); =20 hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); =20 hbitmap_test_set(data, (131072 << 7) - 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), =3D=3D, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), =3D=3D, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), =3D=3D, (L2 + L1 + 1) <= < 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), =3D=3D, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); =20 hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), =3D=3D, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), =3D=3D, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); } =20 static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t = diff) @@ -893,7 +893,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapDa= ta *data, for (i =3D 0; i < num_positions; i++) { hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); hbitmap_iter_init(&iter, data->hb, 0); - next =3D hbitmap_iter_next(&iter); + next =3D hbitmap_iter_next(&iter, true); if (i =3D=3D num_positions - 1) { g_assert_cmpint(next, =3D=3D, -1); } else { @@ -919,10 +919,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapDa= ta *data, =20 hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); =20 - hbitmap_iter_next(&hbi); + hbitmap_iter_next(&hbi, true); =20 hbitmap_reset_all(data->hb); - hbitmap_iter_next(&hbi); + hbitmap_iter_next(&hbi, true); } =20 static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t st= art) diff --git a/util/hbitmap.c b/util/hbitmap.c index 58a2c93842..bcd304041a 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -141,7 +141,7 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) return cur; } =20 -int64_t hbitmap_iter_next(HBitmapIter *hbi) +int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance) { unsigned long cur =3D hbi->cur[HBITMAP_LEVELS - 1] & hbi->hb->levels[HBITMAP_LEVELS - 1][hbi->pos]; @@ -154,8 +154,12 @@ int64_t hbitmap_iter_next(HBitmapIter *hbi) } } =20 - /* The next call will resume work from the next bit. */ - hbi->cur[HBITMAP_LEVELS - 1] =3D cur & (cur - 1); + if (advance) { + /* The next call will resume work from the next bit. */ + hbi->cur[HBITMAP_LEVELS - 1] =3D cur & (cur - 1); + } else { + hbi->cur[HBITMAP_LEVELS - 1] =3D cur; + } item =3D ((uint64_t)hbi->pos << BITS_PER_LEVEL) + ctzl(cur); =20 return item << hbi->granularity; --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519842240207686.7368182332928; Wed, 28 Feb 2018 10:24:00 -0800 (PST) Received: from localhost ([::1]:46069 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6OU-0003Mw-RX for importer@patchew.org; Wed, 28 Feb 2018 13:23:58 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41797) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er67A-0004et-20 for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:05 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er678-0004VK-Sa for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:04 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:42196 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 1er673-0004NT-Et; Wed, 28 Feb 2018 13:05:57 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0F433402290A; Wed, 28 Feb 2018 18:05:57 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0800910B0F53; Wed, 28 Feb 2018 18:05:51 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:05:02 +0100 Message-Id: <20180228180507.3964-12-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.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:05:57 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:05:57 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.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 11/16] test-hbitmap: Add non-advancing iter_next tests 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" Add a function that wraps hbitmap_iter_next() and always calls it in non-advancing mode first, and in advancing mode next. The result should always be the same. By using this function everywhere we called hbitmap_iter_next() before, we should get good test coverage for non-advancing hbitmap_iter_next(). Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- tests/test-hbitmap.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index f2158f767d..5e67ac1d3a 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -30,6 +30,18 @@ typedef struct TestHBitmapData { } TestHBitmapData; =20 =20 +static int64_t check_hbitmap_iter_next(HBitmapIter *hbi) +{ + int next0, next1; + + next0 =3D hbitmap_iter_next(hbi, false); + next1 =3D hbitmap_iter_next(hbi, true); + + g_assert_cmpint(next0, =3D=3D, next1); + + return next0; +} + /* Check that the HBitmap and the shadow bitmap contain the same data, * ignoring the same "first" bits. */ @@ -46,7 +58,7 @@ static void hbitmap_test_check(TestHBitmapData *data, =20 i =3D first; for (;;) { - next =3D hbitmap_iter_next(&hbi, true); + next =3D check_hbitmap_iter_next(&hbi); if (next < 0) { next =3D data->size; } @@ -435,25 +447,25 @@ static void test_hbitmap_iter_granularity(TestHBitmap= Data *data, /* Note that hbitmap_test_check has to be invoked manually in this tes= t. */ hbitmap_test_init(data, 131072 << 7, 7); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); =20 hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), =3D=3D, (L2 + L1 + 1) <= < 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), =3D=3D, (L2 + L1 + 1) <= < 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); =20 hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); =20 hbitmap_test_set(data, (131072 << 7) - 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), =3D=3D, (L2 + L1 + 1) <= < 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), =3D=3D, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), =3D=3D, (L2 + L1 + 1) <= < 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), =3D=3D, 131071 << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); =20 hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), =3D=3D, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), =3D=3D, 131071 << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); } =20 static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t = diff) @@ -893,7 +905,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapDa= ta *data, for (i =3D 0; i < num_positions; i++) { hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); hbitmap_iter_init(&iter, data->hb, 0); - next =3D hbitmap_iter_next(&iter, true); + next =3D check_hbitmap_iter_next(&iter); if (i =3D=3D num_positions - 1) { g_assert_cmpint(next, =3D=3D, -1); } else { @@ -919,10 +931,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapDa= ta *data, =20 hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); =20 - hbitmap_iter_next(&hbi, true); + check_hbitmap_iter_next(&hbi); =20 hbitmap_reset_all(data->hb); - hbitmap_iter_next(&hbi, true); + check_hbitmap_iter_next(&hbi); } =20 static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t st= art) --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519841806711941.6558639958254; Wed, 28 Feb 2018 10:16:46 -0800 (PST) Received: from localhost ([::1]:46019 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6HV-0005Jl-Gr for importer@patchew.org; Wed, 28 Feb 2018 13:16:45 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41877) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er67I-0004lx-4d for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:13 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er67D-0004cQ-Vs for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:12 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:42282 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 1er67B-0004Xq-FA; Wed, 28 Feb 2018 13:06:05 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 13F474075166; Wed, 28 Feb 2018 18:06:05 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 93B0B9C051; Wed, 28 Feb 2018 18:05:58 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:05:03 +0100 Message-Id: <20180228180507.3964-13-mreitz@redhat.com> In-Reply-To: <20180228180507.3964-1-mreitz@redhat.com> References: <20180228180507.3964-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Wed, 28 Feb 2018 18:06:05 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Wed, 28 Feb 2018 18:06:05 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.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 12/16] block/dirty-bitmap: Add bdrv_dirty_iter_next_area 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 new function allows to look for a consecutively dirty area in a dirty bitmap. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng Reviewed-by: John Snow --- include/block/dirty-bitmap.h | 2 ++ block/dirty-bitmap.c | 55 ++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 57 insertions(+) diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index e3f4bbf51d..8c8f63e722 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -79,6 +79,8 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes); int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); +bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_off= set, + uint64_t *offset, int *bytes); void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index d8e999226e..5d6b8dba89 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -496,6 +496,61 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) return hbitmap_iter_next(&iter->hbi, true); } =20 +/** + * Return the next consecutively dirty area in the dirty bitmap + * belonging to the given iterator @iter. + * + * @max_offset: Maximum value that may be returned for + * *offset + *bytes + * @offset: Will contain the start offset of the next dirty area + * @bytes: Will contain the length of the next dirty area + * + * Returns: True if a dirty area could be found before max_offset + * (which means that *offset and *bytes then contain valid + * values), false otherwise. + * + * Note that @iter is never advanced if false is returned. If an area + * is found (which means that true is returned), it will be advanced + * past that area. + */ +bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_off= set, + uint64_t *offset, int *bytes) +{ + uint32_t granularity =3D bdrv_dirty_bitmap_granularity(iter->bitmap); + uint64_t gran_max_offset; + int64_t ret; + int size; + + if (max_offset =3D=3D iter->bitmap->size) { + /* If max_offset points to the image end, round it up by the + * bitmap granularity */ + gran_max_offset =3D ROUND_UP(max_offset, granularity); + } else { + gran_max_offset =3D max_offset; + } + + ret =3D hbitmap_iter_next(&iter->hbi, false); + if (ret < 0 || ret + granularity > gran_max_offset) { + return false; + } + + *offset =3D ret; + size =3D 0; + + assert(granularity <=3D INT_MAX); + + do { + /* Advance iterator */ + ret =3D hbitmap_iter_next(&iter->hbi, true); + size +=3D granularity; + } while (ret + granularity <=3D gran_max_offset && + hbitmap_iter_next(&iter->hbi, false) =3D=3D ret + granularity= && + size <=3D INT_MAX - granularity); + + *bytes =3D MIN(size, max_offset - *offset); + return true; +} + /* Called within bdrv_dirty_bitmap_lock..unlock */ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes) --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519841966684505.7609562576737; Wed, 28 Feb 2018 10:19:26 -0800 (PST) Received: from localhost ([::1]:46035 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6K5-0007F1-Dw for importer@patchew.org; Wed, 28 Feb 2018 13:19:25 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41898) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er67J-0004nJ-DP for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:14 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er67I-0004g9-9c for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:13 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:47148 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 1er67D-0004ar-CA; Wed, 28 Feb 2018 13:06:07 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id F08F88182D27; Wed, 28 Feb 2018 18:06:06 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 96FDD9C042; Wed, 28 Feb 2018 18:06:06 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:05:04 +0100 Message-Id: <20180228180507.3964-14-mreitz@redhat.com> In-Reply-To: <20180228180507.3964-1-mreitz@redhat.com> References: <20180228180507.3964-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:06:07 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:06:07 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.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 13/16] block/mirror: Add MirrorBDSOpaque 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 will allow us to access the block job data when the mirror block driver becomes more complex. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- block/mirror.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/block/mirror.c b/block/mirror.c index addfdff4dd..d7bd1d3195 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -78,6 +78,10 @@ typedef struct MirrorBlockJob { bool initial_zeroing_ongoing; } MirrorBlockJob; =20 +typedef struct MirrorBDSOpaque { + MirrorBlockJob *job; +} MirrorBDSOpaque; + struct MirrorOp { MirrorBlockJob *s; QEMUIOVector qiov; @@ -602,6 +606,7 @@ static void mirror_exit(BlockJob *job, void *opaque) { MirrorBlockJob *s =3D container_of(job, MirrorBlockJob, common); MirrorExitData *data =3D opaque; + MirrorBDSOpaque *bs_opaque =3D s->mirror_top_bs->opaque; AioContext *replace_aio_context =3D NULL; BlockDriverState *src =3D s->mirror_top_bs->backing->bs; BlockDriverState *target_bs =3D blk_bs(s->target); @@ -694,6 +699,7 @@ static void mirror_exit(BlockJob *job, void *opaque) blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); blk_insert_bs(job->blk, mirror_top_bs, &error_abort); =20 + bs_opaque->job =3D NULL; block_job_completed(&s->common, data->ret); =20 g_free(data); @@ -1227,6 +1233,7 @@ static void mirror_start_job(const char *job_id, Bloc= kDriverState *bs, Error **errp) { MirrorBlockJob *s; + MirrorBDSOpaque *bs_opaque; BlockDriverState *mirror_top_bs; bool target_graph_mod; bool target_is_backing; @@ -1260,6 +1267,8 @@ static void mirror_start_job(const char *job_id, Bloc= kDriverState *bs, mirror_top_bs->implicit =3D true; } mirror_top_bs->total_sectors =3D bs->total_sectors; + bs_opaque =3D g_new0(MirrorBDSOpaque, 1); + mirror_top_bs->opaque =3D bs_opaque; bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs)); =20 /* bdrv_append takes ownership of the mirror_top_bs reference, need to= keep @@ -1284,6 +1293,8 @@ static void mirror_start_job(const char *job_id, Bloc= kDriverState *bs, if (!s) { goto fail; } + bs_opaque->job =3D s; + /* The block job now has a reference to this node */ bdrv_unref(mirror_top_bs); =20 @@ -1373,6 +1384,7 @@ fail: =20 g_free(s->replaces); blk_unref(s->target); + bs_opaque->job =3D NULL; block_job_early_fail(&s->common); } =20 --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 151984260419764.71341356011669; Wed, 28 Feb 2018 10:30:04 -0800 (PST) Received: from localhost ([::1]:46113 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6UF-0001A8-Oq for importer@patchew.org; Wed, 28 Feb 2018 13:29:55 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41928) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er67L-0004op-T7 for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:18 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er67K-0004hQ-1t for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:15 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:42230 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 1er67F-0004dS-EY; Wed, 28 Feb 2018 13:06:09 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0E340402290A; Wed, 28 Feb 2018 18:06:09 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8244D9C051; Wed, 28 Feb 2018 18:06:08 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:05:05 +0100 Message-Id: <20180228180507.3964-15-mreitz@redhat.com> In-Reply-To: <20180228180507.3964-1-mreitz@redhat.com> References: <20180228180507.3964-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:06:09 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:06:09 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.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 14/16] block/mirror: Add active mirroring 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 implements active synchronous mirroring. In active mode, the passive mechanism will still be in place and is used to copy all initially dirty clusters off the source disk; but every write request will write data both to the source and the target disk, so the source cannot be dirtied faster than data is mirrored to the target. Also, once the block job has converged (BLOCK_JOB_READY sent), source and target are guaranteed to stay in sync (unless an error occurs). Active mode is completely optional and currently disabled at runtime. A later patch will add a way for users to enable it. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- qapi/block-core.json | 18 ++++ block/mirror.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++= +++- 2 files changed, 265 insertions(+), 5 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 5c5921bfb7..c73b769c27 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -937,6 +937,24 @@ { 'enum': 'MirrorSyncMode', 'data': ['top', 'full', 'none', 'incremental'] } =20 +## +# @MirrorCopyMode: +# +# An enumeration whose values tell the mirror block job when to +# trigger writes to the target. +# +# @background: copy data in background only. +# +# @write-blocking: when data is written to the source, write it +# (synchronously) to the target as well. In +# addition, data is copied in background just like in +# @background mode. +# +# Since: 2.12 +## +{ 'enum': 'MirrorCopyMode', + 'data': ['background', 'write-blocking'] } + ## # @BlockJobType: # diff --git a/block/mirror.c b/block/mirror.c index d7bd1d3195..77badbeb29 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -53,8 +53,12 @@ typedef struct MirrorBlockJob { Error *replace_blocker; bool is_none_mode; BlockMirrorBackingMode backing_mode; + MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; bool synced; + /* Set when the target is synced (dirty bitmap is clean, nothing + * in flight) and the job is running in active mode */ + bool actively_synced; bool should_complete; int64_t granularity; size_t buf_size; @@ -76,6 +80,7 @@ typedef struct MirrorBlockJob { int target_cluster_size; int max_iov; bool initial_zeroing_ongoing; + int in_active_write_counter; } MirrorBlockJob; =20 typedef struct MirrorBDSOpaque { @@ -93,6 +98,7 @@ struct MirrorOp { int64_t *bytes_handled; =20 bool is_pseudo_op; + bool is_active_write; CoQueue waiting_requests; =20 QTAILQ_ENTRY(MirrorOp) next; @@ -108,6 +114,7 @@ static BlockErrorAction mirror_error_action(MirrorBlock= Job *s, bool read, int error) { s->synced =3D false; + s->actively_synced =3D false; if (read) { return block_job_error_action(&s->common, s->on_source_error, true, error); @@ -274,7 +281,7 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t = *offset, return ret; } =20 -static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) +static inline void mirror_wait_for_any_operation(MirrorBlockJob *s, bool a= ctive) { MirrorOp *op; =20 @@ -284,7 +291,7 @@ static inline void mirror_wait_for_free_in_flight_slot(= MirrorBlockJob *s) * 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) { + if (!op->is_pseudo_op && op->is_active_write =3D=3D active) { qemu_co_queue_wait(&op->waiting_requests, NULL); return; } @@ -292,6 +299,12 @@ static inline void mirror_wait_for_free_in_flight_slot= (MirrorBlockJob *s) abort(); } =20 +static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) +{ + /* Only non-active operations use up in-flight slots */ + mirror_wait_for_any_operation(s, false); +} + /* Perform a mirror copy operation. * * *op->bytes_handled is set to the number of bytes copied after and @@ -849,6 +862,7 @@ static void coroutine_fn mirror_run(void *opaque) /* Report BLOCK_JOB_READY and wait for complete. */ block_job_event_ready(&s->common); s->synced =3D true; + s->actively_synced =3D true; while (!block_job_is_cancelled(&s->common) && !s->should_complete)= { block_job_yield(&s->common); } @@ -900,6 +914,12 @@ static void coroutine_fn mirror_run(void *opaque) int64_t cnt, delta; bool should_complete; =20 + /* Do not start passive operations while there are active + * writes in progress */ + while (s->in_active_write_counter) { + mirror_wait_for_any_operation(s, true); + } + if (s->ret < 0) { ret =3D s->ret; goto immediate_exit; @@ -947,6 +967,9 @@ static void coroutine_fn mirror_run(void *opaque) */ block_job_event_ready(&s->common); s->synced =3D true; + if (s->copy_mode !=3D MIRROR_COPY_MODE_BACKGROUND) { + s->actively_synced =3D true; + } } =20 should_complete =3D s->should_complete || @@ -1137,16 +1160,232 @@ static const BlockJobDriver commit_active_job_driv= er =3D { .drain =3D mirror_drain, }; =20 +static void do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + BdrvDirtyBitmapIter *iter; + QEMUIOVector target_qiov; + uint64_t dirty_offset; + int dirty_bytes; + + if (qiov) { + qemu_iovec_init(&target_qiov, qiov->niov); + } + + iter =3D bdrv_dirty_iter_new(job->dirty_bitmap); + bdrv_set_dirty_iter(iter, offset); + + while (true) { + bool valid_area; + int ret; + + bdrv_dirty_bitmap_lock(job->dirty_bitmap); + valid_area =3D bdrv_dirty_iter_next_area(iter, offset + bytes, + &dirty_offset, &dirty_bytes= ); + if (!valid_area) { + bdrv_dirty_bitmap_unlock(job->dirty_bitmap); + break; + } + + bdrv_reset_dirty_bitmap_locked(job->dirty_bitmap, + dirty_offset, dirty_bytes); + bdrv_dirty_bitmap_unlock(job->dirty_bitmap); + + job->common.len +=3D dirty_bytes; + + assert(dirty_offset - offset <=3D SIZE_MAX); + if (qiov) { + qemu_iovec_reset(&target_qiov); + qemu_iovec_concat(&target_qiov, qiov, + dirty_offset - offset, dirty_bytes); + } + + switch (method) { + case MIRROR_METHOD_COPY: + ret =3D blk_co_pwritev(job->target, dirty_offset, dirty_bytes, + qiov ? &target_qiov : NULL, flags); + break; + + case MIRROR_METHOD_ZERO: + assert(!qiov); + ret =3D blk_co_pwrite_zeroes(job->target, dirty_offset, dirty_= bytes, + flags); + break; + + case MIRROR_METHOD_DISCARD: + assert(!qiov); + ret =3D blk_co_pdiscard(job->target, dirty_offset, dirty_bytes= ); + break; + + default: + abort(); + } + + if (ret >=3D 0) { + job->common.offset +=3D dirty_bytes; + } else { + BlockErrorAction action; + + bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_offset, dirty_b= ytes); + job->actively_synced =3D false; + + action =3D mirror_error_action(job, false, -ret); + if (action =3D=3D BLOCK_ERROR_ACTION_REPORT) { + if (!job->ret) { + job->ret =3D ret; + } + break; + } + } + } + + bdrv_dirty_iter_free(iter); + if (qiov) { + qemu_iovec_destroy(&target_qiov); + } +} + +static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s, + uint64_t offset, + uint64_t bytes) +{ + MirrorOp *op; + uint64_t start_chunk =3D offset / s->granularity; + uint64_t end_chunk =3D DIV_ROUND_UP(offset + bytes, s->granularity); + + op =3D g_new(MirrorOp, 1); + *op =3D (MirrorOp){ + .s =3D s, + .offset =3D offset, + .bytes =3D bytes, + .is_active_write =3D true, + }; + qemu_co_queue_init(&op->waiting_requests); + QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); + + s->in_active_write_counter++; + + mirror_wait_on_conflicts(op, s, offset, bytes); + + bitmap_set(s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); + + return op; +} + +static void coroutine_fn active_write_settle(MirrorOp *op) +{ + uint64_t start_chunk =3D op->offset / op->s->granularity; + uint64_t end_chunk =3D DIV_ROUND_UP(op->offset + op->bytes, + op->s->granularity); + + if (!--op->s->in_active_write_counter && op->s->actively_synced) { + BdrvChild *source =3D op->s->mirror_top_bs->backing; + + if (QLIST_FIRST(&source->bs->parents) =3D=3D source && + QLIST_NEXT(source, next_parent) =3D=3D NULL) + { + /* Assert that we are back in sync once all active write + * operations are settled. + * Note that we can only assert this if the mirror node + * is the source node's only parent. */ + assert(!bdrv_get_dirty_count(op->s->dirty_bitmap)); + } + } + bitmap_clear(op->s->in_flight_bitmap, start_chunk, end_chunk - start_c= hunk); + QTAILQ_REMOVE(&op->s->ops_in_flight, op, next); + qemu_co_queue_restart_all(&op->waiting_requests); + g_free(op); +} + static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } =20 +static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs, + MirrorMethod method, uint64_t offset, uint64_t bytes, QEMUIOVector *qi= ov, + int flags) +{ + MirrorOp *op =3D NULL; + MirrorBDSOpaque *s =3D bs->opaque; + int ret =3D 0; + bool copy_to_target; + + copy_to_target =3D s->job->ret >=3D 0 && + s->job->copy_mode =3D=3D MIRROR_COPY_MODE_WRITE_BLOCK= ING; + + if (copy_to_target) { + op =3D active_write_prepare(s->job, offset, bytes); + } + + switch (method) { + case MIRROR_METHOD_COPY: + ret =3D bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); + break; + + case MIRROR_METHOD_ZERO: + ret =3D bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); + break; + + case MIRROR_METHOD_DISCARD: + ret =3D bdrv_co_pdiscard(bs->backing->bs, offset, bytes); + break; + + default: + abort(); + } + + if (ret < 0) { + goto out; + } + + if (copy_to_target) { + do_sync_target_write(s->job, method, offset, bytes, qiov, flags); + } + +out: + if (copy_to_target) { + active_write_settle(op); + } + return ret; +} + static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); + MirrorBDSOpaque *s =3D bs->opaque; + QEMUIOVector bounce_qiov; + void *bounce_buf; + int ret =3D 0; + bool copy_to_target; + + copy_to_target =3D s->job->ret >=3D 0 && + s->job->copy_mode =3D=3D MIRROR_COPY_MODE_WRITE_BLOCK= ING; + + if (copy_to_target) { + /* The guest might concurrently modify the data to write; but + * the data on source and destination must match, so we have + * to use a bounce buffer if we are going to write to the + * target now. */ + bounce_buf =3D qemu_blockalign(bs, bytes); + iov_to_buf_full(qiov->iov, qiov->niov, 0, bounce_buf, bytes); + + qemu_iovec_init(&bounce_qiov, 1); + qemu_iovec_add(&bounce_qiov, bounce_buf, bytes); + qiov =3D &bounce_qiov; + } + + ret =3D bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, offset, bytes= , qiov, + flags); + + if (copy_to_target) { + qemu_iovec_destroy(&bounce_qiov); + qemu_vfree(bounce_buf); + } + + return ret; } =20 static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) @@ -1161,13 +1400,15 @@ static int coroutine_fn bdrv_mirror_top_flush(Block= DriverState *bs) static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, offset, bytes,= NULL, + flags); } =20 static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - return bdrv_co_pdiscard(bs->backing->bs, offset, bytes); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, offset, byt= es, + NULL, 0); } =20 static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *= opts) @@ -1335,6 +1576,7 @@ static void mirror_start_job(const char *job_id, Bloc= kDriverState *bs, s->on_target_error =3D on_target_error; s->is_none_mode =3D is_none_mode; s->backing_mode =3D backing_mode; + s->copy_mode =3D MIRROR_COPY_MODE_BACKGROUND; s->base =3D base; s->granularity =3D granularity; s->buf_size =3D ROUND_UP(buf_size, granularity); --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519842440960922.8212644476761; Wed, 28 Feb 2018 10:27:20 -0800 (PST) Received: from localhost ([::1]:46095 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6Rb-00075N-VL for importer@patchew.org; Wed, 28 Feb 2018 13:27:12 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41927) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er67L-0004oo-Sr for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:17 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er67K-0004i8-Ix for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:15 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:47154 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 1er67H-0004fB-IV; Wed, 28 Feb 2018 13:06:11 -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 25C5F818AAF5; Wed, 28 Feb 2018 18:06:11 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 92EC52024CA6; Wed, 28 Feb 2018 18:06:10 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:05:06 +0100 Message-Id: <20180228180507.3964-16-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.8]); Wed, 28 Feb 2018 18:06:11 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 28 Feb 2018 18:06:11 +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 15/16] block/mirror: Add copy mode QAPI interface 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 allows the user to specify whether to use active or only background mode for mirror block jobs. Currently, this setting will remain constant for the duration of the entire block job. Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- qapi/block-core.json | 11 +++++++++-- include/block/block_int.h | 4 +++- block/mirror.c | 12 +++++++----- blockdev.c | 9 ++++++++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index c73b769c27..1186c007ae 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1573,6 +1573,9 @@ # written. Both will result in identical contents. # Default is true. (Since 2.4) # +# @copy-mode: when to copy data to the destination; defaults to 'backgroun= d' +# (Since: 2.12) +# # Since: 1.3 ## { 'struct': 'DriveMirror', @@ -1582,7 +1585,7 @@ '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', - '*unmap': 'bool' } } + '*unmap': 'bool', '*copy-mode': 'MirrorCopyMode' } } =20 ## # @BlockDirtyBitmap: @@ -1761,6 +1764,9 @@ # above @device. If this option is not given, a node na= me is # autogenerated. (Since: 2.9) # +# @copy-mode: when to copy data to the destination; defaults to 'backgroun= d' +# (Since: 2.12) +# # Returns: nothing on success. # # Since: 2.6 @@ -1781,7 +1787,8 @@ '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', - '*filter-node-name': 'str' } } + '*filter-node-name': 'str', + '*copy-mode': 'MirrorCopyMode' } } =20 ## # @block_set_io_throttle: diff --git a/include/block/block_int.h b/include/block/block_int.h index 7767fc28d3..f6e0937c47 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -962,6 +962,7 @@ void commit_active_start(const char *job_id, BlockDrive= rState *bs, * @filter_node_name: The node name that should be assigned to the filter * driver that the mirror job inserts into the graph above @bs. NULL means= that * a node name should be autogenerated. + * @copy_mode: When to trigger writes to the target. * @errp: Error object. * * Start a mirroring operation on @bs. Clusters that are allocated @@ -975,7 +976,8 @@ void mirror_start(const char *job_id, BlockDriverState = *bs, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, - bool unmap, const char *filter_node_name, Error **errp); + bool unmap, const char *filter_node_name, + MirrorCopyMode copy_mode, Error **errp); =20 /* * backup_job_create: diff --git a/block/mirror.c b/block/mirror.c index 77badbeb29..e12c46df9d 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1470,7 +1470,7 @@ static void mirror_start_job(const char *job_id, Bloc= kDriverState *bs, const BlockJobDriver *driver, bool is_none_mode, BlockDriverState *base, bool auto_complete, const char *filter_node_n= ame, - bool is_mirror, + bool is_mirror, MirrorCopyMode copy_mode, Error **errp) { MirrorBlockJob *s; @@ -1576,7 +1576,7 @@ static void mirror_start_job(const char *job_id, Bloc= kDriverState *bs, s->on_target_error =3D on_target_error; s->is_none_mode =3D is_none_mode; s->backing_mode =3D backing_mode; - s->copy_mode =3D MIRROR_COPY_MODE_BACKGROUND; + s->copy_mode =3D copy_mode; s->base =3D base; s->granularity =3D granularity; s->buf_size =3D ROUND_UP(buf_size, granularity); @@ -1643,7 +1643,8 @@ void mirror_start(const char *job_id, BlockDriverStat= e *bs, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, - bool unmap, const char *filter_node_name, Error **errp) + bool unmap, const char *filter_node_name, + MirrorCopyMode copy_mode, Error **errp) { bool is_none_mode; BlockDriverState *base; @@ -1658,7 +1659,7 @@ void mirror_start(const char *job_id, BlockDriverStat= e *bs, speed, granularity, buf_size, backing_mode, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, is_none_mode, base, false, - filter_node_name, true, errp); + filter_node_name, true, copy_mode, errp); } =20 void commit_active_start(const char *job_id, BlockDriverState *bs, @@ -1681,7 +1682,8 @@ void commit_active_start(const char *job_id, BlockDri= verState *bs, MIRROR_LEAVE_BACKING_CHAIN, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, - filter_node_name, false, &local_err); + filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, + &local_err); if (local_err) { error_propagate(errp, local_err); goto error_restore_flags; diff --git a/blockdev.c b/blockdev.c index 3fb1ca803c..cf97aeddf7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3489,6 +3489,7 @@ static void blockdev_mirror_common(const char *job_id= , BlockDriverState *bs, bool has_unmap, bool unmap, bool has_filter_node_name, const char *filter_node_name, + bool has_copy_mode, MirrorCopyMode copy= _mode, Error **errp) { =20 @@ -3513,6 +3514,9 @@ static void blockdev_mirror_common(const char *job_id= , BlockDriverState *bs, if (!has_filter_node_name) { filter_node_name =3D NULL; } + if (!has_copy_mode) { + copy_mode =3D MIRROR_COPY_MODE_BACKGROUND; + } =20 if (granularity !=3D 0 && (granularity < 512 || granularity > 1048576 = * 64)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", @@ -3543,7 +3547,7 @@ static void blockdev_mirror_common(const char *job_id= , BlockDriverState *bs, has_replaces ? replaces : NULL, speed, granularity, buf_size, sync, backing_mode, on_source_error, on_target_error, unmap, filter_node_name, - errp); + copy_mode, errp); } =20 void qmp_drive_mirror(DriveMirror *arg, Error **errp) @@ -3689,6 +3693,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) arg->has_on_target_error, arg->on_target_error, arg->has_unmap, arg->unmap, false, NULL, + arg->has_copy_mode, arg->copy_mode, &local_err); bdrv_unref(target_bs); error_propagate(errp, local_err); @@ -3709,6 +3714,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char = *job_id, BlockdevOnError on_target_error, bool has_filter_node_name, const char *filter_node_name, + bool has_copy_mode, MirrorCopyMode copy_mode, Error **errp) { BlockDriverState *bs; @@ -3741,6 +3747,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char = *job_id, has_on_target_error, on_target_error, true, true, has_filter_node_name, filter_node_name, + has_copy_mode, copy_mode, &local_err); error_propagate(errp, local_err); =20 --=20 2.14.3 From nobody Fri Oct 24 09:56:51 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 1519842140988719.7003267410244; Wed, 28 Feb 2018 10:22:20 -0800 (PST) Received: from localhost ([::1]:46054 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er6Mt-0001Gs-OI for importer@patchew.org; Wed, 28 Feb 2018 13:22:19 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42002) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1er67V-0004wK-TM for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:28 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1er67U-0004v5-F7 for qemu-devel@nongnu.org; Wed, 28 Feb 2018 13:06:25 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:42260 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 1er67O-0004mZ-Gm; Wed, 28 Feb 2018 13:06:18 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 23222402291E; Wed, 28 Feb 2018 18:06:18 +0000 (UTC) Received: from localhost (unknown [10.40.205.159]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B5E0210B0F53; Wed, 28 Feb 2018 18:06:12 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 28 Feb 2018 19:05:07 +0100 Message-Id: <20180228180507.3964-17-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.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:06:18 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Wed, 28 Feb 2018 18:06:18 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.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 16/16] iotests: Add test for active mirroring 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" Signed-off-by: Max Reitz Reviewed-by: Fam Zheng --- tests/qemu-iotests/151 | 120 +++++++++++++++++++++++++++++++++++++++++= ++++ tests/qemu-iotests/151.out | 5 ++ tests/qemu-iotests/group | 1 + 3 files changed, 126 insertions(+) create mode 100755 tests/qemu-iotests/151 create mode 100644 tests/qemu-iotests/151.out diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151 new file mode 100755 index 0000000000..8d8e050f98 --- /dev/null +++ b/tests/qemu-iotests/151 @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# +# Tests for active mirroring +# +# Copyright (C) 2017 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import iotests +from iotests import qemu_img + +source_img =3D os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) +target_img =3D os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) + +class TestActiveMirror(iotests.QMPTestCase): + image_len =3D 128 * 1024 * 1024 # MB + potential_writes_in_flight =3D True + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, source_img, '128M') + qemu_img('create', '-f', iotests.imgfmt, target_img, '128M') + + blk_source =3D {'id': 'source', + 'if': 'none', + 'node-name': 'source-node', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': source_img}} + + blk_target =3D {'node-name': 'target-node', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': target_img}} + + self.vm =3D iotests.VM() + self.vm.add_drive_raw(self.qmp_to_opts(blk_source)) + self.vm.add_blockdev(self.qmp_to_opts(blk_target)) + self.vm.add_device('virtio-blk,drive=3Dsource') + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + + if not self.potential_writes_in_flight: + self.assertTrue(iotests.compare_images(source_img, target_img), + 'mirror target does not match source') + + os.remove(source_img) + os.remove(target_img) + + def doActiveIO(self, sync_source_and_target): + # Fill the source image + self.vm.hmp_qemu_io('source', + 'write -P 1 0 %i' % self.image_len); + + # Start some background requests + for offset in range(1 * self.image_len / 8, 3 * self.image_len / 8= , 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 2 %i 1M' % offset) + for offset in range(2 * self.image_len / 8, 3 * self.image_len / 8= , 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + # Start the block job + result =3D self.vm.qmp('blockdev-mirror', + job_id=3D'mirror', + filter_node_name=3D'mirror-node', + device=3D'source-node', + target=3D'target-node', + sync=3D'full', + copy_mode=3D'write-blocking') + self.assert_qmp(result, 'return', {}) + + # Start some more requests + for offset in range(3 * self.image_len / 8, 5 * self.image_len / 8= , 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) + for offset in range(4 * self.image_len / 8, 5 * self.image_len / 8= , 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + # Wait for the READY event + self.wait_ready(drive=3D'mirror') + + # Now start some final requests; all of these (which land on + # the source) should be settled using the active mechanism. + # The mirror code itself asserts that the source BDS's dirty + # bitmap will stay clean between READY and COMPLETED. + for offset in range(5 * self.image_len / 8, 7 * self.image_len / 8= , 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) + for offset in range(6 * self.image_len / 8, 7 * self.image_len / 8= , 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + if sync_source_and_target: + # If source and target should be in sync after the mirror, + # we have to flush before completion + self.vm.hmp_qemu_io('source', 'aio_flush') + self.potential_writes_in_flight =3D False + + self.complete_and_wait(drive=3D'mirror', wait_ready=3DFalse) + + def testActiveIO(self): + self.doActiveIO(False) + + def testActiveIOFlushed(self): + self.doActiveIO(True) + + + +if __name__ =3D=3D '__main__': + iotests.main(supported_fmts=3D['qcow2', 'raw']) diff --git a/tests/qemu-iotests/151.out b/tests/qemu-iotests/151.out new file mode 100644 index 0000000000..fbc63e62f8 --- /dev/null +++ b/tests/qemu-iotests/151.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index a2dfe79d86..92b4f00bd8 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -157,6 +157,7 @@ 148 rw auto quick 149 rw auto sudo 150 rw auto quick +151 rw auto 152 rw auto quick 153 rw auto quick 154 rw auto backing quick --=20 2.14.3