Re: [Qemu-devel] [PATCH v4 2/5] qcow2-cluster: Expand zero clusters
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
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
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 =