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 --- 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, +
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 > --- > 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
[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 --- 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, offse