diff mbox series

[v7,06/11] qcow2: prepare for tracking guest io requests in data_file

Message ID 20210904162428.222008-7-vsementsov@virtuozzo.com (mailing list archive)
State New, archived
Headers show
Series qcow2: fix parallel rewrite and discard (reqlist) | expand

Commit Message

Vladimir Sementsov-Ogievskiy Sept. 4, 2021, 4:24 p.m. UTC
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(-)
diff mbox series

Patch

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,
-                                    &copy_offset, &type);
+                                    &copy_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;