]> git.proxmox.com Git - qemu.git/blobdiff - block/stream.c
stream: tweak usage of bdrv_co_is_allocated
[qemu.git] / block / stream.c
index 8e5832273bdd28c73530b6b3533d7463be253520..4490a25ba2787efe42ab4c35eb6841c86f2e676c 100644 (file)
@@ -101,45 +101,33 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
 /*
  * Given an image chain: [BASE] -> [INTER1] -> [INTER2] -> [TOP]
  *
- * Return true if the given sector is allocated in top.
- * Return false if the given sector is allocated in intermediate images.
- * Return true otherwise.
+ * Return true if the given sector is allocated in any image between
+ * BASE and TOP (inclusive).  BASE can be NULL to check if the given
+ * sector is allocated in any image of the chain.  Return false otherwise.
  *
  * 'pnum' is set to the number of sectors (including and immediately following
  *  the specified sector) that are known to be in the same
  *  allocated/unallocated state.
  *
  */
-static int coroutine_fn is_allocated_base(BlockDriverState *top,
-                                          BlockDriverState *base,
-                                          int64_t sector_num,
-                                          int nb_sectors, int *pnum)
+static int coroutine_fn is_allocated_above(BlockDriverState *top,
+                                           BlockDriverState *base,
+                                           int64_t sector_num,
+                                           int nb_sectors, int *pnum)
 {
     BlockDriverState *intermediate;
-    int ret, n;
-
-    ret = bdrv_co_is_allocated(top, sector_num, nb_sectors, &n);
-    if (ret) {
-        *pnum = n;
-        return ret;
-    }
-
-    /*
-     * Is the unallocated chunk [sector_num, n] also
-     * unallocated between base and top?
-     */
-    intermediate = top->backing_hd;
+    int ret, n = nb_sectors;
 
+    intermediate = top;
     while (intermediate != base) {
         int pnum_inter;
-
         ret = bdrv_co_is_allocated(intermediate, sector_num, nb_sectors,
                                    &pnum_inter);
         if (ret < 0) {
             return ret;
         } else if (ret) {
             *pnum = pnum_inter;
-            return 0;
+            return 1;
         }
 
         /*
@@ -156,7 +144,7 @@ static int coroutine_fn is_allocated_base(BlockDriverState *top,
     }
 
     *pnum = n;
-    return 1;
+    return 0;
 }
 
 static void coroutine_fn stream_run(void *opaque)
@@ -189,6 +177,7 @@ static void coroutine_fn stream_run(void *opaque)
 
     for (sector_num = 0; sector_num < end; sector_num += n) {
         uint64_t delay_ns = 0;
+        bool copy;
 
 wait:
         /* Note that even when no rate limit is applied we need to yield
@@ -199,10 +188,20 @@ wait:
             break;
         }
 
-        ret = is_allocated_base(bs, base, sector_num,
-                                STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
+        ret = bdrv_co_is_allocated(bs, sector_num,
+                                   STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
+        if (ret == 1) {
+            /* Allocated in the top, no need to copy.  */
+            copy = false;
+        } else {
+            /* Copy if allocated in the intermediate images.  Limit to the
+             * known-unallocated area [sector_num, sector_num+n).  */
+            ret = is_allocated_above(bs->backing_hd, base, sector_num, n, &n);
+            copy = (ret == 1);
+        }
+
         trace_stream_one_iteration(s, sector_num, n, ret);
-        if (ret == 0) {
+        if (ret >= 0 && copy) {
             if (s->common.speed) {
                 delay_ns = ratelimit_calculate_delay(&s->limit, n);
                 if (delay_ns > 0) {