qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH][RFC][2/2] qcow2: ref+ optimization


From: Frediano Ziglio
Subject: [Qemu-devel] [PATCH][RFC][2/2] qcow2: ref+ optimization
Date: Tue, 13 Sep 2011 09:53:08 +0200

preallocate multiple refcount increment in order to collapse
allocation writes. This cause leaks in case of Qemu crash but
no corruptions.

Signed-off-by: Frediano Ziglio <address@hidden>
---
 block/qcow2-refcount.c |  128 ++++++++++++++++++++++++++++++++++++++++++++---
 block/qcow2.c          |    1 +
 block/qcow2.h          |    2 +
 3 files changed, 122 insertions(+), 9 deletions(-)

diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 7d59b68..3792cda 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -30,6 +30,7 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, 
int64_t size);
 static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
                             int64_t offset, int64_t length,
                             int addend);
+static void qcow2_refp_enable(BlockDriverState *bs);
 
 
 /*********************************************************/
@@ -117,6 +118,12 @@ static int get_refcount(BlockDriverState *bs, int64_t 
cluster_index)
         ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
     refcount = be16_to_cpu(refcount_block[block_index]);
 
+    /* ignore preallocation */
+    if (cluster_index >= s->refp_prealloc_begin
+        && cluster_index < s->refp_prealloc_end) {
+        --refcount;
+    }
+
     ret = qcow2_cache_put(bs, s->refcount_block_cache,
         (void**) &refcount_block);
     if (ret < 0) {
@@ -207,6 +214,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
      *   refcount block into the cache
      */
 
+    uint64_t old_free_cluster_index = s->free_cluster_index;
+    qcow2_refp_flush(bs);
+    s->free_cluster_index = old_free_cluster_index;
+
     *refcount_block = NULL;
 
     /* We write to the refcount table, so we might depend on L2 tables */
@@ -215,6 +226,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
     /* Allocate the refcount block itself and mark it as used */
     int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
     if (new_block < 0) {
+        qcow2_refp_enable(bs);
         return new_block;
     }
 
@@ -279,6 +291,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
         }
 
         s->refcount_table[refcount_table_index] = new_block;
+        qcow2_refp_enable(bs);
         return 0;
     }
 
@@ -400,10 +413,11 @@ static int alloc_refcount_block(BlockDriverState *bs,
     s->refcount_table_offset = table_offset;
 
     /* Free old table. Remember, we must not change free_cluster_index */
-    uint64_t old_free_cluster_index = s->free_cluster_index;
+    old_free_cluster_index = s->free_cluster_index;
     qcow2_free_clusters(bs, old_table_offset, old_table_size * 
sizeof(uint64_t));
     s->free_cluster_index = old_free_cluster_index;
 
+    qcow2_refp_enable(bs);
     ret = load_refcount_block(bs, new_block, (void**) refcount_block);
     if (ret < 0) {
         return ret;
@@ -417,6 +431,7 @@ fail_block:
     if (*refcount_block != NULL) {
         qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
     }
+    qcow2_refp_enable(bs);
     return ret;
 }
 
@@ -529,9 +544,23 @@ static int update_cluster_refcount(BlockDriverState *bs,
     BDRVQcowState *s = bs->opaque;
     int ret;
 
-    ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend);
-    if (ret < 0) {
-        return ret;
+    /* handle preallocation */
+    if (cluster_index >= s->refp_prealloc_begin
+        && cluster_index < s->refp_prealloc_end) {
+
+        /* free previous (should never happen) */
+        int64_t index = s->refp_prealloc_begin;
+        for (; index < cluster_index; ++index) {
+            qcow2_refm_add(bs, index << s->cluster_bits);
+        }
+        addend--;
+        s->refp_prealloc_begin = cluster_index + 1;
+    }
+    if (addend) {
+        ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend);
+        if (ret < 0) {
+            return ret;
+        }
     }
 
     bdrv_flush(bs->file);
@@ -572,20 +601,94 @@ retry:
     return (s->free_cluster_index - nb_clusters) << s->cluster_bits;
 }
 
+static void qcow2_refp_enable(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+
+    if (s->refp_prealloc_end < 0) {
+        /* enable again ? */
+        if (++s->refp_prealloc_end == 0) {
+            s->refp_prealloc_end =
+                s->refp_prealloc_begin;
+        }
+    }
+}
+
+int qcow2_refp_flush(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t index, end = s->refp_prealloc_end;
+
+    if (end < 0) {
+        s->refp_prealloc_end = end - 1;
+        return 0;
+    }
+
+    index = s->refp_prealloc_begin;
+    /* this disable next allocations */
+    s->refp_prealloc_end = -1;
+    for (; index < end; ++index) {
+        qcow2_refm_add(bs, index << s->cluster_bits);
+    }
+    qcow2_refm_flush(bs);
+    return 0;
+}
+
 int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
 {
-    int64_t offset;
-    int ret;
+    BDRVQcowState *s = bs->opaque;
+    int64_t offset, cluster_index;
+    int ret, nb_clusters;
+    uint32_t n_prealloc = 0;
 
     BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
+    nb_clusters = size_to_clusters(s, size);
     offset = alloc_clusters_noref(bs, size);
     if (offset < 0) {
         return offset;
     }
 
-    ret = update_refcount(bs, offset, size, 1);
-    if (ret < 0) {
-        return ret;
+    /* preallocation */
+    cluster_index = offset >> s->cluster_bits;
+    if (cluster_index >= s->refp_prealloc_begin &&
+        cluster_index < s->refp_prealloc_end) {
+
+        /* free previous (should never happen) */
+        int64_t index = s->refp_prealloc_begin;
+        for (; index < cluster_index; ++index) {
+            qcow2_refm_add(bs, index << s->cluster_bits);
+        }
+        while (cluster_index < s->refp_prealloc_end
+            && nb_clusters > 0) {
+            --nb_clusters;
+            ++cluster_index;
+        }
+        s->refp_prealloc_begin = cluster_index;
+    }
+
+    /* try to allocate new space for preallocation */
+    if (s->refp_prealloc_begin == s->refp_prealloc_end) {
+        s->refp_prealloc_begin =
+            s->refp_prealloc_end = cluster_index + nb_clusters;
+        while (nb_clusters < 1024
+            && get_refcount(bs, s->refp_prealloc_begin + n_prealloc) == 0) {
+            ++nb_clusters;
+            ++n_prealloc;
+            s->refp_prealloc_end = -1;
+        }
+    }
+
+    if (nb_clusters) {
+        ret = update_refcount(bs, cluster_index << s->cluster_bits,
+                              nb_clusters << s->cluster_bits, 1);
+        if (ret < 0) {
+            return ret;
+        }
+        if (n_prealloc) {
+            assert(s->refp_prealloc_end == -1);
+            s->refp_prealloc_end =
+                s->refp_prealloc_begin + n_prealloc;
+        }
     }
 
     return offset;
@@ -739,6 +842,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
         l1_allocated = 0;
     }
 
+    /* disable preallocation */
+    qcow2_refp_flush(bs);
+
     for(i = 0; i < l1_size; i++) {
         l2_offset = l1_table[i];
         if (l2_offset) {
@@ -761,6 +867,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
                                        s->csize_mask) + 1;
                         if (addend != 0) {
                             int ret;
+                            /* XXX preallocation ??? */
                             ret = update_refcount(bs,
                                 (offset & s->cluster_offset_mask) & ~511,
                                 nb_csectors * 512, addend);
@@ -836,6 +943,9 @@ fail:
     qcow2_cache_set_writethrough(bs, s->refcount_block_cache,
         old_refcount_writethrough);
 
+    /* enable preallocation again */
+    qcow2_refp_enable(bs);
+
     if (l1_modified) {
         for(i = 0; i < l1_size; i++)
             cpu_to_be64s(&l1_table[i]);
diff --git a/block/qcow2.c b/block/qcow2.c
index 89ae765..51014e1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -622,6 +622,7 @@ static void qcow2_close(BlockDriverState *bs)
     g_free(s->l1_table);
 
     qcow2_cache_flush(bs, s->l2_table_cache);
+    qcow2_refp_flush(bs);
     qcow2_refm_flush(bs);
     qcow2_cache_flush(bs, s->refcount_block_cache);
 
diff --git a/block/qcow2.h b/block/qcow2.h
index 49d3d55..98b1ab5 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -105,6 +105,7 @@ typedef struct BDRVQcowState {
     Qcow2Cache* refcount_block_cache;
     int refm_cache_len, refm_cache_index;
     uint64_t *refm_cache;
+    int64_t refp_prealloc_begin, refp_prealloc_end;
 
     uint8_t *cluster_cache;
     uint8_t *cluster_data;
@@ -185,6 +186,7 @@ void qcow2_refcount_close(BlockDriverState *bs);
 
 int qcow2_refm_add_any(BlockDriverState *bs, int64_t offset);
 int qcow2_refm_flush(BlockDriverState *bs);
+int qcow2_refp_flush(BlockDriverState *bs);
 static inline int qcow2_refm_add(BlockDriverState *bs, int64_t offset)
 {
     BDRVQcowState *s = bs->opaque;
-- 
1.7.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]