|
|
26ba25 |
From a0e14e757707733db8e3b927b6bcae336ae219d8 Mon Sep 17 00:00:00 2001
|
|
|
26ba25 |
From: Fam Zheng <famz@redhat.com>
|
|
|
26ba25 |
Date: Fri, 29 Jun 2018 06:11:44 +0200
|
|
|
26ba25 |
Subject: [PATCH 170/268] qcow2: Implement copy offloading
|
|
|
26ba25 |
|
|
|
26ba25 |
RH-Author: Fam Zheng <famz@redhat.com>
|
|
|
26ba25 |
Message-id: <20180629061153.12687-5-famz@redhat.com>
|
|
|
26ba25 |
Patchwork-id: 81154
|
|
|
26ba25 |
O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH v2 04/13] qcow2: Implement copy offloading
|
|
|
26ba25 |
Bugzilla: 1482537
|
|
|
26ba25 |
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
26ba25 |
RH-Acked-by: Max Reitz <mreitz@redhat.com>
|
|
|
26ba25 |
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
|
|
26ba25 |
|
|
|
26ba25 |
The two callbacks are implemented quite similarly to the read/write
|
|
|
26ba25 |
functions: bdrv_co_copy_range_from maps for read and calls into bs->file
|
|
|
26ba25 |
or bs->backing depending on the allocation status; bdrv_co_copy_range_to
|
|
|
26ba25 |
maps for write and calls into bs->file.
|
|
|
26ba25 |
|
|
|
26ba25 |
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
26ba25 |
Signed-off-by: Fam Zheng <famz@redhat.com>
|
|
|
26ba25 |
Message-id: 20180601092648.24614-5-famz@redhat.com
|
|
|
26ba25 |
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
26ba25 |
(cherry picked from commit fd9fcd37a8645efe322956d94f76e90135522a16)
|
|
|
26ba25 |
Signed-off-by: Fam Zheng <famz@redhat.com>
|
|
|
26ba25 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
26ba25 |
---
|
|
|
26ba25 |
block/qcow2.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
|
|
|
26ba25 |
1 file changed, 199 insertions(+), 30 deletions(-)
|
|
|
26ba25 |
|
|
|
26ba25 |
diff --git a/block/qcow2.c b/block/qcow2.c
|
|
|
26ba25 |
index 092db81..c85ebcb 100644
|
|
|
26ba25 |
--- a/block/qcow2.c
|
|
|
26ba25 |
+++ b/block/qcow2.c
|
|
|
26ba25 |
@@ -1756,6 +1756,39 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
|
|
|
26ba25 |
return status;
|
|
|
26ba25 |
}
|
|
|
26ba25 |
|
|
|
26ba25 |
+static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs,
|
|
|
26ba25 |
+ QCowL2Meta **pl2meta,
|
|
|
26ba25 |
+ bool link_l2)
|
|
|
26ba25 |
+{
|
|
|
26ba25 |
+ int ret = 0;
|
|
|
26ba25 |
+ QCowL2Meta *l2meta = *pl2meta;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ while (l2meta != NULL) {
|
|
|
26ba25 |
+ QCowL2Meta *next;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ if (!ret && link_l2) {
|
|
|
26ba25 |
+ ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
|
|
|
26ba25 |
+ if (ret) {
|
|
|
26ba25 |
+ goto out;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ /* Take the request off the list of running requests */
|
|
|
26ba25 |
+ if (l2meta->nb_clusters != 0) {
|
|
|
26ba25 |
+ QLIST_REMOVE(l2meta, next_in_flight);
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ next = l2meta->next;
|
|
|
26ba25 |
+ g_free(l2meta);
|
|
|
26ba25 |
+ l2meta = next;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+out:
|
|
|
26ba25 |
+ *pl2meta = l2meta;
|
|
|
26ba25 |
+ return ret;
|
|
|
26ba25 |
+}
|
|
|
26ba25 |
+
|
|
|
26ba25 |
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
|
|
26ba25 |
uint64_t bytes, QEMUIOVector *qiov,
|
|
|
26ba25 |
int flags)
|
|
|
26ba25 |
@@ -2042,24 +2075,9 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|
|
26ba25 |
}
|
|
|
26ba25 |
}
|
|
|
26ba25 |
|
|
|
26ba25 |
- while (l2meta != NULL) {
|
|
|
26ba25 |
- QCowL2Meta *next;
|
|
|
26ba25 |
-
|
|
|
26ba25 |
- ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
|
|
|
26ba25 |
- if (ret < 0) {
|
|
|
26ba25 |
- goto fail;
|
|
|
26ba25 |
- }
|
|
|
26ba25 |
-
|
|
|
26ba25 |
- /* Take the request off the list of running requests */
|
|
|
26ba25 |
- if (l2meta->nb_clusters != 0) {
|
|
|
26ba25 |
- QLIST_REMOVE(l2meta, next_in_flight);
|
|
|
26ba25 |
- }
|
|
|
26ba25 |
-
|
|
|
26ba25 |
- qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
|
|
26ba25 |
-
|
|
|
26ba25 |
- next = l2meta->next;
|
|
|
26ba25 |
- g_free(l2meta);
|
|
|
26ba25 |
- l2meta = next;
|
|
|
26ba25 |
+ ret = qcow2_handle_l2meta(bs, &l2meta, true);
|
|
|
26ba25 |
+ if (ret) {
|
|
|
26ba25 |
+ goto fail;
|
|
|
26ba25 |
}
|
|
|
26ba25 |
|
|
|
26ba25 |
bytes -= cur_bytes;
|
|
|
26ba25 |
@@ -2070,18 +2088,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|
|
26ba25 |
ret = 0;
|
|
|
26ba25 |
|
|
|
26ba25 |
fail:
|
|
|
26ba25 |
- while (l2meta != NULL) {
|
|
|
26ba25 |
- QCowL2Meta *next;
|
|
|
26ba25 |
-
|
|
|
26ba25 |
- if (l2meta->nb_clusters != 0) {
|
|
|
26ba25 |
- QLIST_REMOVE(l2meta, next_in_flight);
|
|
|
26ba25 |
- }
|
|
|
26ba25 |
- qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
|
|
26ba25 |
-
|
|
|
26ba25 |
- next = l2meta->next;
|
|
|
26ba25 |
- g_free(l2meta);
|
|
|
26ba25 |
- l2meta = next;
|
|
|
26ba25 |
- }
|
|
|
26ba25 |
+ qcow2_handle_l2meta(bs, &l2meta, false);
|
|
|
26ba25 |
|
|
|
26ba25 |
qemu_co_mutex_unlock(&s->lock);
|
|
|
26ba25 |
|
|
|
26ba25 |
@@ -3264,6 +3271,166 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs,
|
|
|
26ba25 |
return ret;
|
|
|
26ba25 |
}
|
|
|
26ba25 |
|
|
|
26ba25 |
+static int coroutine_fn
|
|
|
26ba25 |
+qcow2_co_copy_range_from(BlockDriverState *bs,
|
|
|
26ba25 |
+ BdrvChild *src, uint64_t src_offset,
|
|
|
26ba25 |
+ BdrvChild *dst, uint64_t dst_offset,
|
|
|
26ba25 |
+ uint64_t bytes, BdrvRequestFlags flags)
|
|
|
26ba25 |
+{
|
|
|
26ba25 |
+ BDRVQcow2State *s = bs->opaque;
|
|
|
26ba25 |
+ int ret;
|
|
|
26ba25 |
+ unsigned int cur_bytes; /* number of bytes in current iteration */
|
|
|
26ba25 |
+ BdrvChild *child = NULL;
|
|
|
26ba25 |
+ BdrvRequestFlags cur_flags;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ assert(!bs->encrypted);
|
|
|
26ba25 |
+ qemu_co_mutex_lock(&s->lock);
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ while (bytes != 0) {
|
|
|
26ba25 |
+ uint64_t copy_offset = 0;
|
|
|
26ba25 |
+ /* prepare next request */
|
|
|
26ba25 |
+ cur_bytes = MIN(bytes, INT_MAX);
|
|
|
26ba25 |
+ cur_flags = flags;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset);
|
|
|
26ba25 |
+ if (ret < 0) {
|
|
|
26ba25 |
+ goto out;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ switch (ret) {
|
|
|
26ba25 |
+ case QCOW2_CLUSTER_UNALLOCATED:
|
|
|
26ba25 |
+ if (bs->backing && bs->backing->bs) {
|
|
|
26ba25 |
+ int64_t backing_length = bdrv_getlength(bs->backing->bs);
|
|
|
26ba25 |
+ if (src_offset >= backing_length) {
|
|
|
26ba25 |
+ cur_flags |= BDRV_REQ_ZERO_WRITE;
|
|
|
26ba25 |
+ } else {
|
|
|
26ba25 |
+ child = bs->backing;
|
|
|
26ba25 |
+ cur_bytes = MIN(cur_bytes, backing_length - src_offset);
|
|
|
26ba25 |
+ copy_offset = src_offset;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+ } else {
|
|
|
26ba25 |
+ cur_flags |= BDRV_REQ_ZERO_WRITE;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+ break;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ case QCOW2_CLUSTER_ZERO_PLAIN:
|
|
|
26ba25 |
+ case QCOW2_CLUSTER_ZERO_ALLOC:
|
|
|
26ba25 |
+ cur_flags |= BDRV_REQ_ZERO_WRITE;
|
|
|
26ba25 |
+ break;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ case QCOW2_CLUSTER_COMPRESSED:
|
|
|
26ba25 |
+ ret = -ENOTSUP;
|
|
|
26ba25 |
+ goto out;
|
|
|
26ba25 |
+ break;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ case QCOW2_CLUSTER_NORMAL:
|
|
|
26ba25 |
+ child = bs->file;
|
|
|
26ba25 |
+ copy_offset += offset_into_cluster(s, src_offset);
|
|
|
26ba25 |
+ if ((copy_offset & 511) != 0) {
|
|
|
26ba25 |
+ ret = -EIO;
|
|
|
26ba25 |
+ goto out;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+ break;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ default:
|
|
|
26ba25 |
+ abort();
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+ qemu_co_mutex_unlock(&s->lock);
|
|
|
26ba25 |
+ ret = bdrv_co_copy_range_from(child,
|
|
|
26ba25 |
+ copy_offset,
|
|
|
26ba25 |
+ dst, dst_offset,
|
|
|
26ba25 |
+ cur_bytes, cur_flags);
|
|
|
26ba25 |
+ qemu_co_mutex_lock(&s->lock);
|
|
|
26ba25 |
+ if (ret < 0) {
|
|
|
26ba25 |
+ goto out;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ bytes -= cur_bytes;
|
|
|
26ba25 |
+ src_offset += cur_bytes;
|
|
|
26ba25 |
+ dst_offset += cur_bytes;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+ ret = 0;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+out:
|
|
|
26ba25 |
+ qemu_co_mutex_unlock(&s->lock);
|
|
|
26ba25 |
+ return ret;
|
|
|
26ba25 |
+}
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+static int coroutine_fn
|
|
|
26ba25 |
+qcow2_co_copy_range_to(BlockDriverState *bs,
|
|
|
26ba25 |
+ BdrvChild *src, uint64_t src_offset,
|
|
|
26ba25 |
+ BdrvChild *dst, uint64_t dst_offset,
|
|
|
26ba25 |
+ uint64_t bytes, BdrvRequestFlags flags)
|
|
|
26ba25 |
+{
|
|
|
26ba25 |
+ BDRVQcow2State *s = bs->opaque;
|
|
|
26ba25 |
+ int offset_in_cluster;
|
|
|
26ba25 |
+ int ret;
|
|
|
26ba25 |
+ unsigned int cur_bytes; /* number of sectors in current iteration */
|
|
|
26ba25 |
+ uint64_t cluster_offset;
|
|
|
26ba25 |
+ uint8_t *cluster_data = NULL;
|
|
|
26ba25 |
+ QCowL2Meta *l2meta = NULL;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ assert(!bs->encrypted);
|
|
|
26ba25 |
+ s->cluster_cache_offset = -1; /* disable compressed cache */
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ qemu_co_mutex_lock(&s->lock);
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ while (bytes != 0) {
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ l2meta = NULL;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ offset_in_cluster = offset_into_cluster(s, dst_offset);
|
|
|
26ba25 |
+ cur_bytes = MIN(bytes, INT_MAX);
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ /* TODO:
|
|
|
26ba25 |
+ * If src->bs == dst->bs, we could simply copy by incrementing
|
|
|
26ba25 |
+ * the refcnt, without copying user data.
|
|
|
26ba25 |
+ * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */
|
|
|
26ba25 |
+ ret = qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes,
|
|
|
26ba25 |
+ &cluster_offset, &l2meta);
|
|
|
26ba25 |
+ if (ret < 0) {
|
|
|
26ba25 |
+ goto fail;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ assert((cluster_offset & 511) == 0);
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ ret = qcow2_pre_write_overlap_check(bs, 0,
|
|
|
26ba25 |
+ cluster_offset + offset_in_cluster, cur_bytes);
|
|
|
26ba25 |
+ if (ret < 0) {
|
|
|
26ba25 |
+ goto fail;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ qemu_co_mutex_unlock(&s->lock);
|
|
|
26ba25 |
+ ret = bdrv_co_copy_range_to(src, src_offset,
|
|
|
26ba25 |
+ bs->file,
|
|
|
26ba25 |
+ cluster_offset + offset_in_cluster,
|
|
|
26ba25 |
+ cur_bytes, flags);
|
|
|
26ba25 |
+ qemu_co_mutex_lock(&s->lock);
|
|
|
26ba25 |
+ if (ret < 0) {
|
|
|
26ba25 |
+ goto fail;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ ret = qcow2_handle_l2meta(bs, &l2meta, true);
|
|
|
26ba25 |
+ if (ret) {
|
|
|
26ba25 |
+ goto fail;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ bytes -= cur_bytes;
|
|
|
26ba25 |
+ dst_offset += cur_bytes;
|
|
|
26ba25 |
+ }
|
|
|
26ba25 |
+ ret = 0;
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+fail:
|
|
|
26ba25 |
+ qcow2_handle_l2meta(bs, &l2meta, false);
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ qemu_co_mutex_unlock(&s->lock);
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ qemu_vfree(cluster_data);
|
|
|
26ba25 |
+ trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
|
|
|
26ba25 |
+
|
|
|
26ba25 |
+ return ret;
|
|
|
26ba25 |
+}
|
|
|
26ba25 |
+
|
|
|
26ba25 |
static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
|
|
|
26ba25 |
PreallocMode prealloc, Error **errp)
|
|
|
26ba25 |
{
|
|
|
26ba25 |
@@ -4522,6 +4689,8 @@ BlockDriver bdrv_qcow2 = {
|
|
|
26ba25 |
|
|
|
26ba25 |
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
|
|
|
26ba25 |
.bdrv_co_pdiscard = qcow2_co_pdiscard,
|
|
|
26ba25 |
+ .bdrv_co_copy_range_from = qcow2_co_copy_range_from,
|
|
|
26ba25 |
+ .bdrv_co_copy_range_to = qcow2_co_copy_range_to,
|
|
|
26ba25 |
.bdrv_truncate = qcow2_truncate,
|
|
|
26ba25 |
.bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed,
|
|
|
26ba25 |
.bdrv_make_empty = qcow2_make_empty,
|
|
|
26ba25 |
--
|
|
|
26ba25 |
1.8.3.1
|
|
|
26ba25 |
|