qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Frediano Ziglio <freddy77@gmail.com>
To: kwolf@redhat.com
Cc: qemu-devel@nongnu.org, Frediano Ziglio <freddy77@gmail.com>
Subject: [Qemu-devel] [PATCH][RFC][2/2] qcow2: ref+ optimization
Date: Tue, 13 Sep 2011 09:53:08 +0200	[thread overview]
Message-ID: <1315900388-6448-3-git-send-email-freddy77@gmail.com> (raw)
In-Reply-To: <1315900388-6448-1-git-send-email-freddy77@gmail.com>

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 <freddy77@gmail.com>
---
 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

  parent reply	other threads:[~2011-09-13  7:53 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-09-13  7:53 [Qemu-devel] [PATCH][RFC][0/2] REF+/REF- optimization Frediano Ziglio
2011-09-13  7:53 ` [Qemu-devel] [PATCH][RFC][1/2] qcow2: optimize refminus updates Frediano Ziglio
2011-09-13  7:53 ` Frediano Ziglio [this message]
2011-09-13 10:37 ` [Qemu-devel] [PATCH][RFC][0/2] REF+/REF- optimization Kevin Wolf
2011-09-13 13:36   ` Frediano Ziglio
2011-09-14  9:10     ` Kevin Wolf
2011-09-14  9:52       ` Frediano Ziglio
2011-09-14 10:21         ` Kevin Wolf
2011-09-14 11:49           ` Frediano Ziglio
2011-09-15  7:24           ` Frediano Ziglio
2011-09-13 14:55   ` Frediano Ziglio

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1315900388-6448-3-git-send-email-freddy77@gmail.com \
    --to=freddy77@gmail.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).