From nobody Mon Apr 14 21:13: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 (208.118.235.17 [208.118.235.17]) by mx.zohomail.com with SMTPS id 1528111411438175.3122813940065; Mon, 4 Jun 2018 04:23:31 -0700 (PDT) Received: from localhost ([::1]:38929 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fPnaE-0000ug-Gm for importer@patchew.org; Mon, 04 Jun 2018 07:23:30 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:36624) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fPnXi-0007sX-VS for qemu-devel@nongnu.org; Mon, 04 Jun 2018 07:20:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fPnXh-0001RO-LB for qemu-devel@nongnu.org; Mon, 04 Jun 2018 07:20:54 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:33444 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 1fPnXe-0001Q6-NT; Mon, 04 Jun 2018 07:20:50 -0400 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 4DF0D401EF0F; Mon, 4 Jun 2018 11:20:50 +0000 (UTC) Received: from localhost (ovpn-116-202.ams2.redhat.com [10.36.116.202]) by smtp.corp.redhat.com (Postfix) with ESMTP id E762920357CA; Mon, 4 Jun 2018 11:20:49 +0000 (UTC) From: Stefan Hajnoczi To: Date: Mon, 4 Jun 2018 12:20:29 +0100 Message-Id: <20180604112036.2715-5-stefanha@redhat.com> In-Reply-To: <20180604112036.2715-1-stefanha@redhat.com> References: <20180604112036.2715-1-stefanha@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.5]); Mon, 04 Jun 2018 11:20:50 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Mon, 04 Jun 2018 11:20:50 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'stefanha@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] [PULL 04/11] qcow2: Implement copy offloading 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 , Peter Maydell , Fam Zheng , Stefan Hajnoczi , qemu-block@nongnu.org, Peter Lieven , Max Reitz , Ronnie Sahlberg , Paolo Bonzini 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" From: Fam Zheng The two callbacks are implemented quite similarly to the read/write functions: bdrv_co_copy_range_from maps for read and calls into bs->file or bs->backing depending on the allocation status; bdrv_co_copy_range_to maps for write and calls into bs->file. Reviewed-by: Stefan Hajnoczi Signed-off-by: Fam Zheng Message-id: 20180601092648.24614-5-famz@redhat.com Signed-off-by: Stefan Hajnoczi --- block/qcow2.c | 229 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 199 insertions(+), 30 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index a007dc4246..79e5ed7fbb 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1761,6 +1761,39 @@ static int coroutine_fn qcow2_co_block_status(BlockD= riverState *bs, return status; } =20 +static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, + QCowL2Meta **pl2meta, + bool link_l2) +{ + int ret =3D 0; + QCowL2Meta *l2meta =3D *pl2meta; + + while (l2meta !=3D NULL) { + QCowL2Meta *next; + + if (!ret && link_l2) { + ret =3D qcow2_alloc_cluster_link_l2(bs, l2meta); + if (ret) { + goto out; + } + } + + /* Take the request off the list of running requests */ + if (l2meta->nb_clusters !=3D 0) { + QLIST_REMOVE(l2meta, next_in_flight); + } + + qemu_co_queue_restart_all(&l2meta->dependent_requests); + + next =3D l2meta->next; + g_free(l2meta); + l2meta =3D next; + } +out: + *pl2meta =3D l2meta; + return ret; +} + static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t off= set, uint64_t bytes, QEMUIOVector *qiov, int flags) @@ -2047,24 +2080,9 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriver= State *bs, uint64_t offset, } } =20 - while (l2meta !=3D NULL) { - QCowL2Meta *next; - - ret =3D qcow2_alloc_cluster_link_l2(bs, l2meta); - if (ret < 0) { - goto fail; - } - - /* Take the request off the list of running requests */ - if (l2meta->nb_clusters !=3D 0) { - QLIST_REMOVE(l2meta, next_in_flight); - } - - qemu_co_queue_restart_all(&l2meta->dependent_requests); - - next =3D l2meta->next; - g_free(l2meta); - l2meta =3D next; + ret =3D qcow2_handle_l2meta(bs, &l2meta, true); + if (ret) { + goto fail; } =20 bytes -=3D cur_bytes; @@ -2075,18 +2093,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriver= State *bs, uint64_t offset, ret =3D 0; =20 fail: - while (l2meta !=3D NULL) { - QCowL2Meta *next; - - if (l2meta->nb_clusters !=3D 0) { - QLIST_REMOVE(l2meta, next_in_flight); - } - qemu_co_queue_restart_all(&l2meta->dependent_requests); - - next =3D l2meta->next; - g_free(l2meta); - l2meta =3D next; - } + qcow2_handle_l2meta(bs, &l2meta, false); =20 qemu_co_mutex_unlock(&s->lock); =20 @@ -3273,6 +3280,166 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriv= erState *bs, return ret; } =20 +static int coroutine_fn +qcow2_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + BDRVQcow2State *s =3D bs->opaque; + int ret; + unsigned int cur_bytes; /* number of bytes in current iteration */ + BdrvChild *child =3D NULL; + BdrvRequestFlags cur_flags; + + assert(!bs->encrypted); + qemu_co_mutex_lock(&s->lock); + + while (bytes !=3D 0) { + uint64_t copy_offset =3D 0; + /* prepare next request */ + cur_bytes =3D MIN(bytes, INT_MAX); + cur_flags =3D flags; + + ret =3D qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©= _offset); + if (ret < 0) { + goto out; + } + + switch (ret) { + case QCOW2_CLUSTER_UNALLOCATED: + if (bs->backing && bs->backing->bs) { + int64_t backing_length =3D bdrv_getlength(bs->backing->bs); + if (src_offset >=3D backing_length) { + cur_flags |=3D BDRV_REQ_ZERO_WRITE; + } else { + child =3D bs->backing; + cur_bytes =3D MIN(cur_bytes, backing_length - src_offs= et); + copy_offset =3D src_offset; + } + } else { + cur_flags |=3D BDRV_REQ_ZERO_WRITE; + } + break; + + case QCOW2_CLUSTER_ZERO_PLAIN: + case QCOW2_CLUSTER_ZERO_ALLOC: + cur_flags |=3D BDRV_REQ_ZERO_WRITE; + break; + + case QCOW2_CLUSTER_COMPRESSED: + ret =3D -ENOTSUP; + goto out; + break; + + case QCOW2_CLUSTER_NORMAL: + child =3D bs->file; + copy_offset +=3D offset_into_cluster(s, src_offset); + if ((copy_offset & 511) !=3D 0) { + ret =3D -EIO; + goto out; + } + break; + + default: + abort(); + } + qemu_co_mutex_unlock(&s->lock); + ret =3D bdrv_co_copy_range_from(child, + copy_offset, + dst, dst_offset, + cur_bytes, cur_flags); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto out; + } + + bytes -=3D cur_bytes; + src_offset +=3D cur_bytes; + dst_offset +=3D cur_bytes; + } + ret =3D 0; + +out: + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int coroutine_fn +qcow2_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags flags) +{ + BDRVQcow2State *s =3D bs->opaque; + int offset_in_cluster; + int ret; + unsigned int cur_bytes; /* number of sectors in current iteration */ + uint64_t cluster_offset; + uint8_t *cluster_data =3D NULL; + QCowL2Meta *l2meta =3D NULL; + + assert(!bs->encrypted); + s->cluster_cache_offset =3D -1; /* disable compressed cache */ + + qemu_co_mutex_lock(&s->lock); + + while (bytes !=3D 0) { + + l2meta =3D NULL; + + offset_in_cluster =3D offset_into_cluster(s, dst_offset); + cur_bytes =3D MIN(bytes, INT_MAX); + + /* TODO: + * If src->bs =3D=3D dst->bs, we could simply copy by incrementing + * the refcnt, without copying user data. + * Or if src->bs =3D=3D dst->bs->backing->bs, we could copy by dis= carding. */ + ret =3D qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes, + &cluster_offset, &l2meta); + if (ret < 0) { + goto fail; + } + + assert((cluster_offset & 511) =3D=3D 0); + + ret =3D qcow2_pre_write_overlap_check(bs, 0, + cluster_offset + offset_in_cluster, cur_bytes); + if (ret < 0) { + goto fail; + } + + qemu_co_mutex_unlock(&s->lock); + ret =3D bdrv_co_copy_range_to(src, src_offset, + bs->file, + cluster_offset + offset_in_cluster, + cur_bytes, flags); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto fail; + } + + ret =3D qcow2_handle_l2meta(bs, &l2meta, true); + if (ret) { + goto fail; + } + + bytes -=3D cur_bytes; + dst_offset +=3D cur_bytes; + } + ret =3D 0; + +fail: + qcow2_handle_l2meta(bs, &l2meta, false); + + qemu_co_mutex_unlock(&s->lock); + + qemu_vfree(cluster_data); + trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); + + return ret; +} + static int qcow2_truncate(BlockDriverState *bs, int64_t offset, PreallocMode prealloc, Error **errp) { @@ -4521,6 +4688,8 @@ BlockDriver bdrv_qcow2 =3D { =20 .bdrv_co_pwrite_zeroes =3D qcow2_co_pwrite_zeroes, .bdrv_co_pdiscard =3D qcow2_co_pdiscard, + .bdrv_co_copy_range_from =3D qcow2_co_copy_range_from, + .bdrv_co_copy_range_to =3D qcow2_co_copy_range_to, .bdrv_truncate =3D qcow2_truncate, .bdrv_co_pwritev_compressed =3D qcow2_co_pwritev_compressed, .bdrv_make_empty =3D qcow2_make_empty, --=20 2.17.1