Re: [Qemu-devel] [PATCH v4 2/5] qcow2-cluster: Expand zero clusters

2013-09-03 Thread Max Reitz

Am 02.09.2013 17:13, schrieb Kevin Wolf:

Am 02.09.2013 um 12:04 hat Max Reitz geschrieben:

Add functionality for expanding zero clusters. This is necessary for
downgrading the image version to one without zero cluster support.

For non-backed images, this function may also just discard zero clusters
instead of truly expanding them.

Signed-off-by: Max Reitz mre...@redhat.com
---
  block/qcow2-cluster.c  | 228 +
  block/qcow2-refcount.c |  29 ---
  block/qcow2.h  |   5 ++
  3 files changed, 248 insertions(+), 14 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 2d5aa92..c90fb51 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -1497,3 +1497,231 @@ fail:
  
  return ret;

  }
+
+/*
+ * Expands all zero clusters in a specific L1 table (or deallocates them, for
+ * non-backed non-pre-allocated zero clusters).
+ *
+ * expanded_clusters is a bitmap where every bit corresponds to one cluster in
+ * the image file; a bit gets set if the corresponding cluster has been used 
for
+ * zero expansion (i.e., has been filled with zeroes and is referenced from an
+ * L2 table). nb_clusters contains the total cluster count of the image file,
+ * i.e., the number of bits in expanded_clusters.
+ */
+static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
+  int l1_size, uint8_t *expanded_clusters,
+  uint64_t nb_clusters)
+{
+BDRVQcowState *s = bs-opaque;
+bool is_active_l1 = (l1_table == s-l1_table);
+uint64_t *l2_table = NULL;
+int ret;
+int i, j;
+
+if (!is_active_l1) {
+/* inactive L2 tables require a buffer to be stored in when loading
+ * them from disk */
+l2_table = qemu_blockalign(bs, s-cluster_size);
+}
+
+for (i = 0; i  l1_size; i++) {
+uint64_t l2_offset = l1_table[i]  L1E_OFFSET_MASK;
+bool l2_dirty = false;
+
+if (!l2_offset) {
+/* unallocated */
+continue;
+}
+
+if (is_active_l1) {
+/* get active L2 tables from cache */
+ret = qcow2_cache_get(bs, s-l2_table_cache, l2_offset,
+(void **)l2_table);
+} else {
+/* load inactive L2 tables from disk */
+ret = bdrv_read(bs-file, l2_offset / BDRV_SECTOR_SIZE,
+(void *)l2_table, s-cluster_sectors);
+}
+if (ret  0) {
+goto fail;
+}
+
+for (j = 0; j  s-l2_size; j++) {
+uint64_t l2_entry = be64_to_cpu(l2_table[j]);
+int64_t offset = l2_entry  L2E_OFFSET_MASK, cluster_index;
+int cluster_type = qcow2_get_cluster_type(l2_entry);
+
+if (cluster_type == QCOW2_CLUSTER_NORMAL) {
+cluster_index = offset  s-cluster_bits;
+assert((cluster_index = 0)  (cluster_index  nb_clusters));
+if (expanded_clusters[cluster_index / 8] 
+(1  (cluster_index % 8))) {
+/* Probably a shared L2 table; this cluster was a zero
+ * cluster which has been expanded, its refcount
+ * therefore most likely requires an update. */
+ret = qcow2_update_cluster_refcount(bs, cluster_index, 1,
+QCOW2_DISCARD_NEVER);
+if (ret  0) {
+goto fail;
+}
+/* Since we just increased the refcount, the COPIED flag 
may
+ * no longer be set. */
+l2_table[j] = cpu_to_be64(l2_entry  ~QCOW_OFLAG_COPIED);
+l2_dirty = true;
+}
+continue;
+}
+else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) {
+continue;
+}
+
+if (!offset) {
+/* not preallocated */
+if (!bs-backing_hd) {
+/* not backed; therefore we can simply deallocate the
+ * cluster */
+l2_table[j] = 0;
+l2_dirty = true;
+continue;
+}
+
+offset = qcow2_alloc_clusters(bs, s-cluster_size);
+if (offset  0) {
+ret = offset;
+goto fail;
+}
+}
+
+ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
+offset, s-cluster_size);
+if (ret  0) {
+qcow2_free_clusters(bs, offset, s-cluster_size,
+QCOW2_DISCARD_ALWAYS);
+goto fail;
+}
+
+ret = bdrv_write_zeroes(bs-file, offset / BDRV_SECTOR_SIZE,
+

[Qemu-devel] [PATCH v4 2/5] qcow2-cluster: Expand zero clusters

2013-09-02 Thread Max Reitz
Add functionality for expanding zero clusters. This is necessary for
downgrading the image version to one without zero cluster support.

For non-backed images, this function may also just discard zero clusters
instead of truly expanding them.

Signed-off-by: Max Reitz mre...@redhat.com
---
 block/qcow2-cluster.c  | 228 +
 block/qcow2-refcount.c |  29 ---
 block/qcow2.h  |   5 ++
 3 files changed, 248 insertions(+), 14 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 2d5aa92..c90fb51 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -1497,3 +1497,231 @@ fail:
 
 return ret;
 }
+
+/*
+ * Expands all zero clusters in a specific L1 table (or deallocates them, for
+ * non-backed non-pre-allocated zero clusters).
+ *
+ * expanded_clusters is a bitmap where every bit corresponds to one cluster in
+ * the image file; a bit gets set if the corresponding cluster has been used 
for
+ * zero expansion (i.e., has been filled with zeroes and is referenced from an
+ * L2 table). nb_clusters contains the total cluster count of the image file,
+ * i.e., the number of bits in expanded_clusters.
+ */
+static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
+  int l1_size, uint8_t *expanded_clusters,
+  uint64_t nb_clusters)
+{
+BDRVQcowState *s = bs-opaque;
+bool is_active_l1 = (l1_table == s-l1_table);
+uint64_t *l2_table = NULL;
+int ret;
+int i, j;
+
+if (!is_active_l1) {
+/* inactive L2 tables require a buffer to be stored in when loading
+ * them from disk */
+l2_table = qemu_blockalign(bs, s-cluster_size);
+}
+
+for (i = 0; i  l1_size; i++) {
+uint64_t l2_offset = l1_table[i]  L1E_OFFSET_MASK;
+bool l2_dirty = false;
+
+if (!l2_offset) {
+/* unallocated */
+continue;
+}
+
+if (is_active_l1) {
+/* get active L2 tables from cache */
+ret = qcow2_cache_get(bs, s-l2_table_cache, l2_offset,
+(void **)l2_table);
+} else {
+/* load inactive L2 tables from disk */
+ret = bdrv_read(bs-file, l2_offset / BDRV_SECTOR_SIZE,
+(void *)l2_table, s-cluster_sectors);
+}
+if (ret  0) {
+goto fail;
+}
+
+for (j = 0; j  s-l2_size; j++) {
+uint64_t l2_entry = be64_to_cpu(l2_table[j]);
+int64_t offset = l2_entry  L2E_OFFSET_MASK, cluster_index;
+int cluster_type = qcow2_get_cluster_type(l2_entry);
+
+if (cluster_type == QCOW2_CLUSTER_NORMAL) {
+cluster_index = offset  s-cluster_bits;
+assert((cluster_index = 0)  (cluster_index  nb_clusters));
+if (expanded_clusters[cluster_index / 8] 
+(1  (cluster_index % 8))) {
+/* Probably a shared L2 table; this cluster was a zero
+ * cluster which has been expanded, its refcount
+ * therefore most likely requires an update. */
+ret = qcow2_update_cluster_refcount(bs, cluster_index, 1,
+QCOW2_DISCARD_NEVER);
+if (ret  0) {
+goto fail;
+}
+/* Since we just increased the refcount, the COPIED flag 
may
+ * no longer be set. */
+l2_table[j] = cpu_to_be64(l2_entry  ~QCOW_OFLAG_COPIED);
+l2_dirty = true;
+}
+continue;
+}
+else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) {
+continue;
+}
+
+if (!offset) {
+/* not preallocated */
+if (!bs-backing_hd) {
+/* not backed; therefore we can simply deallocate the
+ * cluster */
+l2_table[j] = 0;
+l2_dirty = true;
+continue;
+}
+
+offset = qcow2_alloc_clusters(bs, s-cluster_size);
+if (offset  0) {
+ret = offset;
+goto fail;
+}
+}
+
+ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
+offset, s-cluster_size);
+if (ret  0) {
+qcow2_free_clusters(bs, offset, s-cluster_size,
+QCOW2_DISCARD_ALWAYS);
+goto fail;
+}
+
+ret = bdrv_write_zeroes(bs-file, offset / BDRV_SECTOR_SIZE,
+s-cluster_sectors);
+if (ret  0) {
+qcow2_free_clusters(bs, offset, 

Re: [Qemu-devel] [PATCH v4 2/5] qcow2-cluster: Expand zero clusters

2013-09-02 Thread Kevin Wolf
Am 02.09.2013 um 12:04 hat Max Reitz geschrieben:
 Add functionality for expanding zero clusters. This is necessary for
 downgrading the image version to one without zero cluster support.
 
 For non-backed images, this function may also just discard zero clusters
 instead of truly expanding them.
 
 Signed-off-by: Max Reitz mre...@redhat.com
 ---
  block/qcow2-cluster.c  | 228 
 +
  block/qcow2-refcount.c |  29 ---
  block/qcow2.h  |   5 ++
  3 files changed, 248 insertions(+), 14 deletions(-)
 
 diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
 index 2d5aa92..c90fb51 100644
 --- a/block/qcow2-cluster.c
 +++ b/block/qcow2-cluster.c
 @@ -1497,3 +1497,231 @@ fail:
  
  return ret;
  }
 +
 +/*
 + * Expands all zero clusters in a specific L1 table (or deallocates them, for
 + * non-backed non-pre-allocated zero clusters).
 + *
 + * expanded_clusters is a bitmap where every bit corresponds to one cluster 
 in
 + * the image file; a bit gets set if the corresponding cluster has been used 
 for
 + * zero expansion (i.e., has been filled with zeroes and is referenced from 
 an
 + * L2 table). nb_clusters contains the total cluster count of the image file,
 + * i.e., the number of bits in expanded_clusters.
 + */
 +static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t 
 *l1_table,
 +  int l1_size, uint8_t 
 *expanded_clusters,
 +  uint64_t nb_clusters)
 +{
 +BDRVQcowState *s = bs-opaque;
 +bool is_active_l1 = (l1_table == s-l1_table);
 +uint64_t *l2_table = NULL;
 +int ret;
 +int i, j;
 +
 +if (!is_active_l1) {
 +/* inactive L2 tables require a buffer to be stored in when loading
 + * them from disk */
 +l2_table = qemu_blockalign(bs, s-cluster_size);
 +}
 +
 +for (i = 0; i  l1_size; i++) {
 +uint64_t l2_offset = l1_table[i]  L1E_OFFSET_MASK;
 +bool l2_dirty = false;
 +
 +if (!l2_offset) {
 +/* unallocated */
 +continue;
 +}
 +
 +if (is_active_l1) {
 +/* get active L2 tables from cache */
 +ret = qcow2_cache_get(bs, s-l2_table_cache, l2_offset,
 +(void **)l2_table);
 +} else {
 +/* load inactive L2 tables from disk */
 +ret = bdrv_read(bs-file, l2_offset / BDRV_SECTOR_SIZE,
 +(void *)l2_table, s-cluster_sectors);
 +}
 +if (ret  0) {
 +goto fail;
 +}
 +
 +for (j = 0; j  s-l2_size; j++) {
 +uint64_t l2_entry = be64_to_cpu(l2_table[j]);
 +int64_t offset = l2_entry  L2E_OFFSET_MASK, cluster_index;
 +int cluster_type = qcow2_get_cluster_type(l2_entry);
 +
 +if (cluster_type == QCOW2_CLUSTER_NORMAL) {
 +cluster_index = offset  s-cluster_bits;
 +assert((cluster_index = 0)  (cluster_index  
 nb_clusters));
 +if (expanded_clusters[cluster_index / 8] 
 +(1  (cluster_index % 8))) {
 +/* Probably a shared L2 table; this cluster was a zero
 + * cluster which has been expanded, its refcount
 + * therefore most likely requires an update. */
 +ret = qcow2_update_cluster_refcount(bs, cluster_index, 1,
 +QCOW2_DISCARD_NEVER);
 +if (ret  0) {
 +goto fail;
 +}
 +/* Since we just increased the refcount, the COPIED flag 
 may
 + * no longer be set. */
 +l2_table[j] = cpu_to_be64(l2_entry  ~QCOW_OFLAG_COPIED);
 +l2_dirty = true;
 +}
 +continue;
 +}
 +else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) 
 {
 +continue;
 +}
 +
 +if (!offset) {
 +/* not preallocated */
 +if (!bs-backing_hd) {
 +/* not backed; therefore we can simply deallocate the
 + * cluster */
 +l2_table[j] = 0;
 +l2_dirty = true;
 +continue;
 +}
 +
 +offset = qcow2_alloc_clusters(bs, s-cluster_size);
 +if (offset  0) {
 +ret = offset;
 +goto fail;
 +}
 +}
 +
 +ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
 +offset, s-cluster_size);
 +if (ret  0) {
 +qcow2_free_clusters(bs, offset, s-cluster_size,
 +QCOW2_DISCARD_ALWAYS);
 +goto fail;
 +}
 +
 +ret =