[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-block] [PATCH for-2.10 13/16] block/qcow2: qcow2_calc_size_usage()
From: |
Max Reitz |
Subject: |
[Qemu-block] [PATCH for-2.10 13/16] block/qcow2: qcow2_calc_size_usage() for truncate |
Date: |
Mon, 13 Mar 2017 22:41:14 +0100 |
This patch extends qcow2_calc_size_usage() so it can calculate the
additional space needed for preallocating image growth.
Signed-off-by: Max Reitz <address@hidden>
---
block/qcow2.c | 137 +++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 98 insertions(+), 39 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index 21b2b3cd53..80fb815b15 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2101,7 +2101,15 @@ done:
return ret;
}
-static uint64_t qcow2_calc_size_usage(uint64_t new_size,
+/**
+ * Returns the number of bytes that must be allocated in the underlying file
+ * to accomodate an image growth from @current_size to @new_size.
+ *
+ * @current_size must be 0 when creating a new image. In that case, @bs is
+ * ignored; otherwise it must be valid.
+ */
+static uint64_t qcow2_calc_size_usage(BlockDriverState *bs,
+ uint64_t current_size, uint64_t new_size,
int cluster_bits, int refcount_order)
{
size_t cluster_size = 1u << cluster_bits;
@@ -2122,47 +2130,97 @@ static uint64_t qcow2_calc_size_usage(uint64_t new_size,
refblock_bits = cluster_bits - (refcount_order - 3);
refblock_size = 1 << refblock_bits;
- /* header: 1 cluster */
- meta_size += cluster_size;
-
- /* total size of L2 tables */
- nl2e = aligned_total_size / cluster_size;
- nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
- meta_size += nl2e * sizeof(uint64_t);
+ if (!current_size) {
+ /* header: 1 cluster */
+ meta_size += cluster_size;
+
+ /* total size of L2 tables */
+ nl2e = aligned_total_size / cluster_size;
+ nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
+ meta_size += nl2e * sizeof(uint64_t);
+
+ /* total size of L1 tables */
+ nl1e = nl2e * sizeof(uint64_t) / cluster_size;
+ nl1e = align_offset(nl1e, cluster_size / sizeof(uint64_t));
+ meta_size += nl1e * sizeof(uint64_t);
+
+ /* total size of refcount blocks
+ *
+ * note: every host cluster is reference-counted, including metadata
+ * (even refcount blocks are recursively included).
+ * Let:
+ * a = total_size (this is the guest disk size)
+ * m = meta size not including refcount blocks and refcount tables
+ * c = cluster size
+ * y1 = number of refcount blocks entries
+ * y2 = meta size including everything
+ * rces = refcount entry size in bytes
+ * then,
+ * y1 = (y2 + a)/c
+ * y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m
+ * we can get y1:
+ * y1 = (a + m) / (c - rces - rces * sizeof(u64) / c)
+ */
+ nrefblocke = (aligned_total_size + meta_size + cluster_size)
+ / (cluster_size - rces - rces * sizeof(uint64_t)
+ / cluster_size);
+ meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size;
- /* total size of L1 tables */
- nl1e = nl2e * sizeof(uint64_t) / cluster_size;
- nl1e = align_offset(nl1e, cluster_size / sizeof(uint64_t));
- meta_size += nl1e * sizeof(uint64_t);
+ /* total size of refcount tables */
+ nreftablee = nrefblocke / refblock_size;
+ nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t));
+ meta_size += nreftablee * sizeof(uint64_t);
- /* total size of refcount blocks
- *
- * note: every host cluster is reference-counted, including metadata
- * (even refcount blocks are recursively included).
- * Let:
- * a = total_size (this is the guest disk size)
- * m = meta size not including refcount blocks and refcount tables
- * c = cluster size
- * y1 = number of refcount blocks entries
- * y2 = meta size including everything
- * rces = refcount entry size in bytes
- * then,
- * y1 = (y2 + a)/c
- * y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m
- * we can get y1:
- * y1 = (a + m) / (c - rces - rces * sizeof(u64) / c)
- */
- nrefblocke = (aligned_total_size + meta_size + cluster_size)
- / (cluster_size - rces - rces * sizeof(uint64_t)
- / cluster_size);
- meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size;
+ return aligned_total_size + meta_size;
+ } else {
+ BDRVQcow2State *s = bs->opaque;
+ uint64_t aligned_cur_size = align_offset(current_size, cluster_size);
+ uint64_t creftable_length;
+ uint64_t i;
+
+ /* new total size of L2 tables */
+ nl2e = aligned_total_size / cluster_size;
+ nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
+ meta_size += nl2e * sizeof(uint64_t);
+
+ /* Subtract L2 tables which are already present */
+ for (i = 0; i < s->l1_size; i++) {
+ if (s->l1_table[i] & L1E_OFFSET_MASK) {
+ meta_size -= cluster_size;
+ }
+ }
- /* total size of refcount tables */
- nreftablee = nrefblocke / refblock_size;
- nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t));
- meta_size += nreftablee * sizeof(uint64_t);
+ /* Do not add L1 table size because the only caller of this path
+ * (qcow2_truncate) has increased its size already. */
- return aligned_total_size + meta_size;
+ /* Calculate size of the additional refblocks (this assumes that all of
+ * the existing image is covered by refblocks, which is extremely
+ * likely); this may result in overallocation because parts of the
newly
+ * added space may be covered by existing refblocks, but that is fine.
+ *
+ * This only considers the newly added space. Since we cannot update
the
+ * reftable in-place, we will have to able to store both the old and
the
+ * new one at the same time, though. Therefore, we need to add the size
+ * of the old reftable here.
+ */
+ creftable_length = ROUND_UP(s->refcount_table_size * sizeof(uint64_t),
+ cluster_size);
+ nrefblocke = ((aligned_total_size - aligned_cur_size) + meta_size +
+ creftable_length + cluster_size)
+ / (cluster_size - rces -
+ rces * sizeof(uint64_t) / cluster_size);
+ meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size;
+
+ /* total size of the new refcount table (again, may be too much because
+ * it assumes that the new area is not covered by any refcount blocks
+ * yet) */
+ nreftablee = s->max_refcount_table_index + 1 +
+ nrefblocke / refblock_size;
+ nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t));
+ meta_size += nreftablee * sizeof(uint64_t);
+
+ return (aligned_total_size - aligned_cur_size) + meta_size;
+ }
}
static int qcow2_create2(const char *filename, int64_t total_size,
@@ -2203,7 +2261,8 @@ static int qcow2_create2(const char *filename, int64_t
total_size,
int ret;
if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
- uint64_t file_size = qcow2_calc_size_usage(total_size, cluster_bits,
+ uint64_t file_size = qcow2_calc_size_usage(NULL, 0, total_size,
+ cluster_bits,
refcount_order);
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, file_size, &error_abort);
--
2.12.0
[Qemu-block] [PATCH for-2.10 15/16] iotests: Add preallocated resize test for raw, Max Reitz, 2017/03/13
[Qemu-block] [PATCH for-2.10 16/16] iotests: Add preallocated growth test for qcow2, Max Reitz, 2017/03/13