Patchwork [RFC,V4,18/30] qcow2: Behave correctly when refcount reach 0 or 2^16.

login
register
mail settings
Submitter Benoît Canet
Date Jan. 2, 2013, 4:16 p.m.
Message ID <1357143393-29832-19-git-send-email-benoit@irqsave.net>
Download mbox | patch
Permalink /patch/209098/
State New
Headers show

Comments

Benoît Canet - Jan. 2, 2013, 4:16 p.m.
When refcount reach zero we destroy the hash on disk and remove it from GTree.
When refcount is at it's maximum value we mark the hash so it won't be loaded
at next startup and remove it from GTree.

Signed-off-by: Benoit Canet <benoit@irqsave.net>
---
 block/qcow2-dedup.c    |   79 +++++++++++++++++++++++++++++++++++++++++++++---
 block/qcow2-refcount.c |    6 ++++
 block/qcow2.h          |    6 ++++
 3 files changed, 87 insertions(+), 4 deletions(-)

Patch

diff --git a/block/qcow2-dedup.c b/block/qcow2-dedup.c
index 12a2dad..28001c6 100644
--- a/block/qcow2-dedup.c
+++ b/block/qcow2-dedup.c
@@ -804,11 +804,19 @@  static inline bool is_hash_node_empty(QCowHashNode *hash_node)
     return hash_node->physical_sect & QCOW_FLAG_EMPTY;
 }
 
+static void qcow2_remove_hash_node(BlockDriverState *bs,
+                                   QCowHashNode *hash_node)
+{
+    BDRVQcowState *s = bs->opaque;
+    g_tree_remove(s->dedup_tree_by_sect, &hash_node->physical_sect);
+    g_tree_remove(s->dedup_tree_by_hash, &hash_node->hash);
+}
+
 /* This function removes a hash_node from the trees given a physical sector
  *
  * @physical_sect: The physical sector of the cluster corresponding to the hash
  */
-static void qcow_remove_hash_node_by_sector(BlockDriverState *bs,
+static void qcow2_remove_hash_node_by_sector(BlockDriverState *bs,
                                             uint64_t physical_sect)
 {
     BDRVQcowState *s = bs->opaque;
@@ -820,8 +828,7 @@  static void qcow_remove_hash_node_by_sector(BlockDriverState *bs,
         return;
     }
 
-    g_tree_remove(s->dedup_tree_by_sect, &hash_node->physical_sect);
-    g_tree_remove(s->dedup_tree_by_hash, &hash_node->hash);
+    qcow2_remove_hash_node(bs, hash_node);
 }
 
 /* This function store a dedup hash information to disk and RAM
@@ -858,7 +865,7 @@  static int qcow2_store_dedup_hash(BlockDriverState *bs,
     logical_sect = logical_sect | QCOW_FLAG_FIRST;
 
     /* remove stale hash node pointing to this physical sector from the trees */
-    qcow_remove_hash_node_by_sector(bs, physical_sect);
+    qcow2_remove_hash_node_by_sector(bs, physical_sect);
 
     /* fill the missing fields of the hash node */
     hash_node->physical_sect = physical_sect;
@@ -979,6 +986,12 @@  void coroutine_fn qcow2_co_load_dedup_hashes(void *opaque)
             continue;
         }
 
+        /* if this cluster has reached max refcount don't load it */
+        if (first_logical_sect & QCOW_FLAG_MAX_REFCOUNT) {
+            qemu_co_mutex_unlock(&s->lock);
+            continue;
+        }
+
         hash_node = qcow2_dedup_build_qcow_hash_node(&hash,
                                                      i * s->cluster_sectors,
                                                      first_logical_sect);
@@ -1002,3 +1015,61 @@  void qcow2_dedup_close(BlockDriverState *bs)
     BDRVQcowState *s = bs->opaque;
     g_free(s->dedup_table);
 }
+
+/* Clean the last reference to a given cluster when it's refcount is zero
+ *
+ * @cluster_index: the index of the physical cluster
+ */
+void qcow2_dedup_refcount_zero_reached(BlockDriverState *bs,
+                                      uint64_t cluster_index)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowHash null_hash;
+    uint64_t logical_sect = 0;
+    uint64_t physical_sect = cluster_index * s->cluster_sectors;
+
+    /* prepare null hash */
+    memset(&null_hash, 0, sizeof(null_hash));
+
+    /* clear from disk */
+    qcow2_dedup_read_write_hash(bs,
+                                &null_hash,
+                                &logical_sect,
+                                physical_sect,
+                                true);
+
+    /* remove from ram if present so we won't dedup with it anymore */
+    qcow2_remove_hash_node_by_sector(bs, physical_sect);
+}
+
+/* Force to use a new physical cluster and QCowHashNode when the refcount limit
+ * of 2^16 is about to break.
+ *
+ * @cluster_index: the index of the physical cluster
+ */
+void qcow2_dedup_refcount_max_reached(BlockDriverState *bs,
+                                      uint64_t cluster_index)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowHashNode *hash_node;
+    uint64_t physical_sect = cluster_index * s->cluster_sectors;
+
+    hash_node =  g_tree_lookup(s->dedup_tree_by_sect, &physical_sect);
+
+    if (!hash_node) {
+        return;
+    }
+
+    /* mark this hash so we won't load it anymore at startup after writing it */
+    hash_node->first_logical_sect |= QCOW_FLAG_MAX_REFCOUNT;
+
+    /* write to disk */
+    qcow2_dedup_read_write_hash(bs,
+                                &hash_node->hash,
+                                &hash_node->first_logical_sect,
+                                hash_node->physical_sect,
+                                true);
+
+    /* remove the QCowHashNode from ram so we won't use it anymore for dedup */
+    qcow2_remove_hash_node(bs, hash_node);
+}
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 75c2bde..aef280d 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -489,6 +489,12 @@  int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
             ret = -EINVAL;
             goto fail;
         }
+        if (s->has_dedup && refcount == 0) {
+            qcow2_dedup_refcount_zero_reached(bs, cluster_index);
+        }
+        if (s->has_dedup && refcount == 0xffff) {
+            qcow2_dedup_refcount_max_reached(bs, cluster_index);
+        }
         if (refcount == 0 && cluster_index < s->free_cluster_index) {
             s->free_cluster_index = cluster_index;
         }
diff --git a/block/qcow2.h b/block/qcow2.h
index 63353d9..f5576be 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -61,6 +61,8 @@ 
 #define DEFAULT_CLUSTER_SIZE 65536
 
 #define HASH_LENGTH 32
+/* indicate that this cluster refcount has reached its maximum value */
+#define QCOW_FLAG_MAX_REFCOUNT (1LL << 61)
 /* indicate that the hash structure is empty and miss offset */
 #define QCOW_FLAG_EMPTY   (1LL << 62)
 /* indicate that the cluster for this hash has QCOW_OFLAG_COPIED on disk */
@@ -478,5 +480,9 @@  int qcow2_dedup_store_new_hashes(BlockDriverState *bs,
 void coroutine_fn qcow2_co_load_dedup_hashes(void *opaque);
 int qcow2_dedup_init(BlockDriverState *bs);
 void qcow2_dedup_close(BlockDriverState *bs);
+void qcow2_dedup_refcount_zero_reached(BlockDriverState *bs,
+                                       uint64_t cluster_index);
+void qcow2_dedup_refcount_max_reached(BlockDriverState *bs,
+                                      uint64_t cluster_index);
 
 #endif