From nobody Fri Oct 24 22:17:00 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 --- 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