We are going to fix a bug of reallocating host cluster that are under guest operation. For this we need to track these operations. Guest io operations in data_file has 3 entry points:
qcow2_get_host_offset() qcow2_alloc_host_offset() qcow2_alloc_compressed_cluster_offset() These functions provides the offset in data_file. So for now, add a possibility for these function to start a BlockReq. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> --- block/qcow2.h | 11 ++++++++--- block/qcow2-cluster.c | 45 ++++++++++++++++++++++++++++++++++++++++--- block/qcow2.c | 17 ++++++++-------- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/block/qcow2.h b/block/qcow2.h index 4859ca3d0d..7b9fafc6ec 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -29,6 +29,7 @@ #include "qemu/coroutine.h" #include "qemu/units.h" #include "block/block_int.h" +#include "block/reqlist.h" //#define DEBUG_ALLOC //#define DEBUG_ALLOC2 @@ -420,6 +421,8 @@ typedef struct BDRVQcow2State { * is to convert the image with the desired compression type set. */ Qcow2CompressionType compression_type; + + BlockReqList guest_reqs; } BDRVQcow2State; typedef struct Qcow2COWRegion { @@ -906,14 +909,16 @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, unsigned int *bytes, uint64_t *host_offset, - QCow2SubclusterType *subcluster_type); + QCow2SubclusterType *subcluster_type, + BlockReq **req); int qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, unsigned int *bytes, uint64_t *host_offset, - QCowL2Meta **m); + QCowL2Meta **m, BlockReq **req); int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size, - uint64_t *host_offset); + uint64_t *host_offset, + BlockReq **req); void qcow2_parse_compressed_cluster_descriptor(BDRVQcow2State *s, uint64_t cluster_descriptor, uint64_t *coffset, diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index bd0597842f..9887f80dcc 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -567,11 +567,17 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs, * file. The subcluster type is stored in *subcluster_type. * Compressed clusters are always processed one by one. * + * On success if req is non-NULL and resulting subcluster type is + * QCOW2_SUBCLUSTER_NORMAL or QCOW2_SUBCLUSTER_COMPRESSED req is allocated and + * initialized. For other cluster types req is set to NULL. + * On failure req is untouched. + * * Returns 0 on success, -errno in error cases. */ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, unsigned int *bytes, uint64_t *host_offset, - QCow2SubclusterType *subcluster_type) + QCow2SubclusterType *subcluster_type, + BlockReq **req) { BDRVQcow2State *s = bs->opaque; unsigned int l2_index, sc_index; @@ -721,6 +727,21 @@ out: *subcluster_type = type; + if (req) { + if (type == QCOW2_SUBCLUSTER_COMPRESSED) { + uint64_t coffset; + int csize; + + qcow2_parse_compressed_cluster_descriptor(s, *host_offset, &coffset, + &csize); + *req = reqlist_new_req(&s->guest_reqs, coffset, csize); + } else if (type == QCOW2_SUBCLUSTER_NORMAL) { + *req = reqlist_new_req(&s->guest_reqs, *host_offset, *bytes); + } else { + *req = NULL; + } + } + return 0; fail: @@ -809,11 +830,16 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, * already allocated at the offset, return an error. * * Return 0 on success and -errno in error cases + * + * On success if req is non-NULL req is allocated and initialized. + * On failure req is untouched. + * */ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size, - uint64_t *host_offset) + uint64_t *host_offset, + BlockReq **req) { BDRVQcow2State *s = bs->opaque; int l2_index, ret; @@ -868,6 +894,11 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); *host_offset = cluster_offset & s->cluster_offset_mask; + + if (req) { + *req = reqlist_new_req(&s->guest_reqs, *host_offset, compressed_size); + } + return 0; } @@ -1740,10 +1771,14 @@ out: * is queued and will be reentered when the dependency has completed. * * Return 0 on success and -errno in error cases + * + * On success if req is non-NULL req is allocated and initialized. + * On failure req is untouched. + * */ int qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, unsigned int *bytes, uint64_t *host_offset, - QCowL2Meta **m) + QCowL2Meta **m, BlockReq **req) { BDRVQcow2State *s = bs->opaque; uint64_t start, remaining; @@ -1850,6 +1885,10 @@ again: assert(offset_into_cluster(s, *host_offset) == offset_into_cluster(s, offset)); + if (req) { + *req = reqlist_new_req(&s->guest_reqs, *host_offset, *bytes); + } + return 0; } diff --git a/block/qcow2.c b/block/qcow2.c index 7fbcc600da..8aa5679fe9 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1833,6 +1833,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, #endif qemu_co_queue_init(&s->thread_task_queue); + QLIST_INIT(&s->guest_reqs); return ret; @@ -2090,7 +2091,7 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, } bytes = MIN(INT_MAX, count); - ret = qcow2_get_host_offset(bs, offset, &bytes, &host_offset, &type); + ret = qcow2_get_host_offset(bs, offset, &bytes, &host_offset, &type, NULL); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { return ret; @@ -2335,7 +2336,7 @@ static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs, qemu_co_mutex_lock(&s->lock); ret = qcow2_get_host_offset(bs, offset, &cur_bytes, - &host_offset, &type); + &host_offset, &type, NULL); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { goto out; @@ -2629,7 +2630,7 @@ static coroutine_fn int qcow2_co_pwritev_part( qemu_co_mutex_lock(&s->lock); ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, - &host_offset, &l2meta); + &host_offset, &l2meta, NULL); if (ret < 0) { goto out_locked; } @@ -3170,7 +3171,7 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, while (bytes) { cur_bytes = MIN(bytes, QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size)); ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, - &host_offset, &meta); + &host_offset, &meta, NULL); if (ret < 0) { error_setg_errno(errp, -ret, "Allocating clusters failed"); goto out; @@ -3976,7 +3977,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, offset -= head; bytes = s->subcluster_size; nr = s->subcluster_size; - ret = qcow2_get_host_offset(bs, offset, &nr, &off, &type); + ret = qcow2_get_host_offset(bs, offset, &nr, &off, &type, NULL); if (ret < 0 || (type != QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && type != QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC && @@ -4051,7 +4052,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs, cur_write_flags = write_flags; ret = qcow2_get_host_offset(bs, src_offset, &cur_bytes, - ©_offset, &type); + ©_offset, &type, NULL); if (ret < 0) { goto out; } @@ -4138,7 +4139,7 @@ qcow2_co_copy_range_to(BlockDriverState *bs, * the refcnt, without copying user data. * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */ ret = qcow2_alloc_host_offset(bs, dst_offset, &cur_bytes, - &host_offset, &l2meta); + &host_offset, &l2meta, NULL); if (ret < 0) { goto fail; } @@ -4593,7 +4594,7 @@ qcow2_co_pwritev_compressed_task(BlockDriverState *bs, qemu_co_mutex_lock(&s->lock); ret = qcow2_alloc_compressed_cluster_offset(bs, offset, out_len, - &cluster_offset); + &cluster_offset, NULL); if (ret < 0) { qemu_co_mutex_unlock(&s->lock); goto fail; -- 2.29.2