@@ -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,
@@ -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;
}
@@ -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;
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 <vsementsov@virtuozzo.com> --- block/qcow2.h | 11 ++++++++--- block/qcow2-cluster.c | 45 ++++++++++++++++++++++++++++++++++++++++--- block/qcow2.c | 17 ++++++++-------- 3 files changed, 59 insertions(+), 14 deletions(-)