yeahuh / rpms / qemu-kvm

Forked from rpms/qemu-kvm 2 years ago
Clone
9ae3a8
From 1d6333da24b0175691d25b06c92a3eb7717a8a78 Mon Sep 17 00:00:00 2001
9ae3a8
From: Max Reitz <mreitz@redhat.com>
9ae3a8
Date: Tue, 7 Jan 2014 21:57:11 +0100
9ae3a8
Subject: [PATCH 06/14] qcow2-cluster: Expand zero clusters
9ae3a8
9ae3a8
RH-Author: Max Reitz <mreitz@redhat.com>
9ae3a8
Message-id: <1389131839-12920-7-git-send-email-mreitz@redhat.com>
9ae3a8
Patchwork-id: 56542
9ae3a8
O-Subject: [RHEL-7.0 qemu-kvm PATCH v2 06/14] qcow2-cluster: Expand zero clusters
9ae3a8
Bugzilla: 1033490
9ae3a8
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
RH-Acked-by: Fam Zheng <famz@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
9ae3a8
BZ: 1033490
9ae3a8
9ae3a8
Add functionality for expanding zero clusters. This is necessary for
9ae3a8
downgrading the image version to one without zero cluster support.
9ae3a8
9ae3a8
For non-backed images, this function may also just discard zero clusters
9ae3a8
instead of truly expanding them.
9ae3a8
9ae3a8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
(cherry picked from commit 32b6444d23d0ff618d73e5b766600cd258066169)
9ae3a8
9ae3a8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9ae3a8
9ae3a8
Conflicts:
9ae3a8
	block/qcow2-cluster.c
9ae3a8
9ae3a8
Conflicts since the changes omitted from downstream commit
9ae3a8
ca635f6c3ae10562a2165590bb84667aa61ad12f (because this particular commit
9ae3a8
had not been backported then) have to be included here.
9ae3a8
9ae3a8
The same applies to the change in qcow2-cluster.c omitted from
9ae3a8
downstream commit 0f795e4e9e128f9ff88a128ed8590d2357228b80.
9ae3a8
---
9ae3a8
 block/qcow2-cluster.c  | 232 +++++++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 block/qcow2-refcount.c |  29 ++++---
9ae3a8
 block/qcow2.h          |   5 ++
9ae3a8
 3 files changed, 252 insertions(+), 14 deletions(-)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 block/qcow2-cluster.c  |  232 ++++++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 block/qcow2-refcount.c |   29 +++---
9ae3a8
 block/qcow2.h          |    5 +
9ae3a8
 3 files changed, 252 insertions(+), 14 deletions(-)
9ae3a8
9ae3a8
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
9ae3a8
index 5d13515..796d7c5 100644
9ae3a8
--- a/block/qcow2-cluster.c
9ae3a8
+++ b/block/qcow2-cluster.c
9ae3a8
@@ -1499,3 +1499,235 @@ fail:
9ae3a8
 
9ae3a8
     return ret;
9ae3a8
 }
9ae3a8
+
9ae3a8
+/*
9ae3a8
+ * Expands all zero clusters in a specific L1 table (or deallocates them, for
9ae3a8
+ * non-backed non-pre-allocated zero clusters).
9ae3a8
+ *
9ae3a8
+ * expanded_clusters is a bitmap where every bit corresponds to one cluster in
9ae3a8
+ * the image file; a bit gets set if the corresponding cluster has been used for
9ae3a8
+ * zero expansion (i.e., has been filled with zeroes and is referenced from an
9ae3a8
+ * L2 table). nb_clusters contains the total cluster count of the image file,
9ae3a8
+ * i.e., the number of bits in expanded_clusters.
9ae3a8
+ */
9ae3a8
+static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
9ae3a8
+                                      int l1_size, uint8_t *expanded_clusters,
9ae3a8
+                                      uint64_t nb_clusters)
9ae3a8
+{
9ae3a8
+    BDRVQcowState *s = bs->opaque;
9ae3a8
+    bool is_active_l1 = (l1_table == s->l1_table);
9ae3a8
+    uint64_t *l2_table = NULL;
9ae3a8
+    int ret;
9ae3a8
+    int i, j;
9ae3a8
+
9ae3a8
+    if (!is_active_l1) {
9ae3a8
+        /* inactive L2 tables require a buffer to be stored in when loading
9ae3a8
+         * them from disk */
9ae3a8
+        l2_table = qemu_blockalign(bs, s->cluster_size);
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    for (i = 0; i < l1_size; i++) {
9ae3a8
+        uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK;
9ae3a8
+        bool l2_dirty = false;
9ae3a8
+
9ae3a8
+        if (!l2_offset) {
9ae3a8
+            /* unallocated */
9ae3a8
+            continue;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        if (is_active_l1) {
9ae3a8
+            /* get active L2 tables from cache */
9ae3a8
+            ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
9ae3a8
+                    (void **)&l2_table);
9ae3a8
+        } else {
9ae3a8
+            /* load inactive L2 tables from disk */
9ae3a8
+            ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE,
9ae3a8
+                    (void *)l2_table, s->cluster_sectors);
9ae3a8
+        }
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        for (j = 0; j < s->l2_size; j++) {
9ae3a8
+            uint64_t l2_entry = be64_to_cpu(l2_table[j]);
9ae3a8
+            int64_t offset = l2_entry & L2E_OFFSET_MASK, cluster_index;
9ae3a8
+            int cluster_type = qcow2_get_cluster_type(l2_entry);
9ae3a8
+
9ae3a8
+            if (cluster_type == QCOW2_CLUSTER_NORMAL) {
9ae3a8
+                cluster_index = offset >> s->cluster_bits;
9ae3a8
+                assert((cluster_index >= 0) && (cluster_index < nb_clusters));
9ae3a8
+                if (expanded_clusters[cluster_index / 8] &
9ae3a8
+                    (1 << (cluster_index % 8))) {
9ae3a8
+                    /* Probably a shared L2 table; this cluster was a zero
9ae3a8
+                     * cluster which has been expanded, its refcount
9ae3a8
+                     * therefore most likely requires an update. */
9ae3a8
+                    ret = qcow2_update_cluster_refcount(bs, cluster_index, 1,
9ae3a8
+                                                        QCOW2_DISCARD_NEVER);
9ae3a8
+                    if (ret < 0) {
9ae3a8
+                        goto fail;
9ae3a8
+                    }
9ae3a8
+                    /* Since we just increased the refcount, the COPIED flag may
9ae3a8
+                     * no longer be set. */
9ae3a8
+                    l2_table[j] = cpu_to_be64(l2_entry & ~QCOW_OFLAG_COPIED);
9ae3a8
+                    l2_dirty = true;
9ae3a8
+                }
9ae3a8
+                continue;
9ae3a8
+            }
9ae3a8
+            else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) {
9ae3a8
+                continue;
9ae3a8
+            }
9ae3a8
+
9ae3a8
+            if (!offset) {
9ae3a8
+                /* not preallocated */
9ae3a8
+                if (!bs->backing_hd) {
9ae3a8
+                    /* not backed; therefore we can simply deallocate the
9ae3a8
+                     * cluster */
9ae3a8
+                    l2_table[j] = 0;
9ae3a8
+                    l2_dirty = true;
9ae3a8
+                    continue;
9ae3a8
+                }
9ae3a8
+
9ae3a8
+                offset = qcow2_alloc_clusters(bs, s->cluster_size);
9ae3a8
+                if (offset < 0) {
9ae3a8
+                    ret = offset;
9ae3a8
+                    goto fail;
9ae3a8
+                }
9ae3a8
+            }
9ae3a8
+
9ae3a8
+            ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
9ae3a8
+            if (ret < 0) {
9ae3a8
+                qcow2_free_clusters(bs, offset, s->cluster_size,
9ae3a8
+                        QCOW2_DISCARD_ALWAYS);
9ae3a8
+                goto fail;
9ae3a8
+            }
9ae3a8
+
9ae3a8
+            ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE,
9ae3a8
+                                    s->cluster_sectors, 0);
9ae3a8
+            if (ret < 0) {
9ae3a8
+                qcow2_free_clusters(bs, offset, s->cluster_size,
9ae3a8
+                        QCOW2_DISCARD_ALWAYS);
9ae3a8
+                goto fail;
9ae3a8
+            }
9ae3a8
+
9ae3a8
+            l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
9ae3a8
+            l2_dirty = true;
9ae3a8
+
9ae3a8
+            cluster_index = offset >> s->cluster_bits;
9ae3a8
+            assert((cluster_index >= 0) && (cluster_index < nb_clusters));
9ae3a8
+            expanded_clusters[cluster_index / 8] |= 1 << (cluster_index % 8);
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        if (is_active_l1) {
9ae3a8
+            if (l2_dirty) {
9ae3a8
+                qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
9ae3a8
+                qcow2_cache_depends_on_flush(s->l2_table_cache);
9ae3a8
+            }
9ae3a8
+            ret = qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
9ae3a8
+            if (ret < 0) {
9ae3a8
+                l2_table = NULL;
9ae3a8
+                goto fail;
9ae3a8
+            }
9ae3a8
+        } else {
9ae3a8
+            if (l2_dirty) {
9ae3a8
+                ret = qcow2_pre_write_overlap_check(bs,
9ae3a8
+                        QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, l2_offset,
9ae3a8
+                        s->cluster_size);
9ae3a8
+                if (ret < 0) {
9ae3a8
+                    goto fail;
9ae3a8
+                }
9ae3a8
+
9ae3a8
+                ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE,
9ae3a8
+                        (void *)l2_table, s->cluster_sectors);
9ae3a8
+                if (ret < 0) {
9ae3a8
+                    goto fail;
9ae3a8
+                }
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = 0;
9ae3a8
+
9ae3a8
+fail:
9ae3a8
+    if (l2_table) {
9ae3a8
+        if (!is_active_l1) {
9ae3a8
+            qemu_vfree(l2_table);
9ae3a8
+        } else {
9ae3a8
+            if (ret < 0) {
9ae3a8
+                qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
9ae3a8
+            } else {
9ae3a8
+                ret = qcow2_cache_put(bs, s->l2_table_cache,
9ae3a8
+                        (void **)&l2_table);
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/*
9ae3a8
+ * For backed images, expands all zero clusters on the image. For non-backed
9ae3a8
+ * images, deallocates all non-pre-allocated zero clusters (and claims the
9ae3a8
+ * allocation for pre-allocated ones). This is important for downgrading to a
9ae3a8
+ * qcow2 version which doesn't yet support metadata zero clusters.
9ae3a8
+ */
9ae3a8
+int qcow2_expand_zero_clusters(BlockDriverState *bs)
9ae3a8
+{
9ae3a8
+    BDRVQcowState *s = bs->opaque;
9ae3a8
+    uint64_t *l1_table = NULL;
9ae3a8
+    int cluster_to_sector_bits = s->cluster_bits - BDRV_SECTOR_BITS;
9ae3a8
+    uint64_t nb_clusters;
9ae3a8
+    uint8_t *expanded_clusters;
9ae3a8
+    int ret;
9ae3a8
+    int i, j;
9ae3a8
+
9ae3a8
+    nb_clusters = (bs->total_sectors + (1 << cluster_to_sector_bits) - 1)
9ae3a8
+            >> cluster_to_sector_bits;
9ae3a8
+    expanded_clusters = g_malloc0((nb_clusters + 7) / 8);
9ae3a8
+
9ae3a8
+    ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size,
9ae3a8
+                                     expanded_clusters, nb_clusters);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto fail;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* Inactive L1 tables may point to active L2 tables - therefore it is
9ae3a8
+     * necessary to flush the L2 table cache before trying to access the L2
9ae3a8
+     * tables pointed to by inactive L1 entries (else we might try to expand
9ae3a8
+     * zero clusters that have already been expanded); furthermore, it is also
9ae3a8
+     * necessary to empty the L2 table cache, since it may contain tables which
9ae3a8
+     * are now going to be modified directly on disk, bypassing the cache.
9ae3a8
+     * qcow2_cache_empty() does both for us. */
9ae3a8
+    ret = qcow2_cache_empty(bs, s->l2_table_cache);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto fail;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    for (i = 0; i < s->nb_snapshots; i++) {
9ae3a8
+        int l1_sectors = (s->snapshots[i].l1_size * sizeof(uint64_t) +
9ae3a8
+                BDRV_SECTOR_SIZE - 1) / BDRV_SECTOR_SIZE;
9ae3a8
+
9ae3a8
+        l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
9ae3a8
+
9ae3a8
+        ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset /
9ae3a8
+                BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        for (j = 0; j < s->snapshots[i].l1_size; j++) {
9ae3a8
+            be64_to_cpus(&l1_table[j]);
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size,
9ae3a8
+                                         expanded_clusters, nb_clusters);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = 0;
9ae3a8
+
9ae3a8
+fail:
9ae3a8
+    g_free(expanded_clusters);
9ae3a8
+    g_free(l1_table);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
9ae3a8
index 2b72d5e..389a837 100644
9ae3a8
--- a/block/qcow2-refcount.c
9ae3a8
+++ b/block/qcow2-refcount.c
9ae3a8
@@ -601,10 +601,10 @@ fail:
9ae3a8
  * If the return value is non-negative, it is the new refcount of the cluster.
9ae3a8
  * If it is negative, it is -errno and indicates an error.
9ae3a8
  */
9ae3a8
-static int update_cluster_refcount(BlockDriverState *bs,
9ae3a8
-                                   int64_t cluster_index,
9ae3a8
-                                   int addend,
9ae3a8
-                                   enum qcow2_discard_type type)
9ae3a8
+int qcow2_update_cluster_refcount(BlockDriverState *bs,
9ae3a8
+                                  int64_t cluster_index,
9ae3a8
+                                  int addend,
9ae3a8
+                                  enum qcow2_discard_type type)
9ae3a8
 {
9ae3a8
     BDRVQcowState *s = bs->opaque;
9ae3a8
     int ret;
9ae3a8
@@ -733,8 +733,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
9ae3a8
         if (free_in_cluster == 0)
9ae3a8
             s->free_byte_offset = 0;
9ae3a8
         if ((offset & (s->cluster_size - 1)) != 0)
9ae3a8
-            update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
9ae3a8
-                                    QCOW2_DISCARD_NEVER);
9ae3a8
+            qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
9ae3a8
+                                          QCOW2_DISCARD_NEVER);
9ae3a8
     } else {
9ae3a8
         offset = qcow2_alloc_clusters(bs, s->cluster_size);
9ae3a8
         if (offset < 0) {
9ae3a8
@@ -744,8 +744,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
9ae3a8
         if ((cluster_offset + s->cluster_size) == offset) {
9ae3a8
             /* we are lucky: contiguous data */
9ae3a8
             offset = s->free_byte_offset;
9ae3a8
-            update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
9ae3a8
-                                    QCOW2_DISCARD_NEVER);
9ae3a8
+            qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
9ae3a8
+                                          QCOW2_DISCARD_NEVER);
9ae3a8
             s->free_byte_offset += size;
9ae3a8
         } else {
9ae3a8
             s->free_byte_offset = offset;
9ae3a8
@@ -754,8 +754,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
9ae3a8
     }
9ae3a8
 
9ae3a8
     /* The cluster refcount was incremented, either by qcow2_alloc_clusters()
9ae3a8
-     * or explicitly by update_cluster_refcount().  Refcount blocks must be
9ae3a8
-     * flushed before the caller's L2 table updates.
9ae3a8
+     * or explicitly by qcow2_update_cluster_refcount().  Refcount blocks must
9ae3a8
+     * be flushed before the caller's L2 table updates.
9ae3a8
      */
9ae3a8
     qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
9ae3a8
     return offset;
9ae3a8
@@ -896,8 +896,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
9ae3a8
                             break;
9ae3a8
                         }
9ae3a8
                         if (addend != 0) {
9ae3a8
-                            refcount = update_cluster_refcount(bs, cluster_index, addend,
9ae3a8
-                                                               QCOW2_DISCARD_SNAPSHOT);
9ae3a8
+                            refcount = qcow2_update_cluster_refcount(bs,
9ae3a8
+                                    cluster_index, addend,
9ae3a8
+                                    QCOW2_DISCARD_SNAPSHOT);
9ae3a8
                         } else {
9ae3a8
                             refcount = get_refcount(bs, cluster_index);
9ae3a8
                         }
9ae3a8
@@ -936,8 +937,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
9ae3a8
 
9ae3a8
 
9ae3a8
             if (addend != 0) {
9ae3a8
-                refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend,
9ae3a8
-                                                   QCOW2_DISCARD_SNAPSHOT);
9ae3a8
+                refcount = qcow2_update_cluster_refcount(bs, l2_offset >>
9ae3a8
+                        s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT);
9ae3a8
             } else {
9ae3a8
                 refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
9ae3a8
             }
9ae3a8
diff --git a/block/qcow2.h b/block/qcow2.h
9ae3a8
index 5ca6b78..2660483 100644
9ae3a8
--- a/block/qcow2.h
9ae3a8
+++ b/block/qcow2.h
9ae3a8
@@ -422,6 +422,9 @@ int qcow2_update_header(BlockDriverState *bs);
9ae3a8
 int qcow2_refcount_init(BlockDriverState *bs);
9ae3a8
 void qcow2_refcount_close(BlockDriverState *bs);
9ae3a8
 
9ae3a8
+int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
9ae3a8
+                                  int addend, enum qcow2_discard_type type);
9ae3a8
+
9ae3a8
 int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
9ae3a8
 int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
9ae3a8
     int nb_clusters);
9ae3a8
@@ -469,6 +472,8 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
9ae3a8
     int nb_sectors);
9ae3a8
 int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
9ae3a8
 
9ae3a8
+int qcow2_expand_zero_clusters(BlockDriverState *bs);
9ae3a8
+
9ae3a8
 /* qcow2-snapshot.c functions */
9ae3a8
 int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
9ae3a8
 int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8