diff options
Diffstat (limited to 'block/qcow2.c')
-rw-r--r-- | block/qcow2.c | 63 |
1 files changed, 53 insertions, 10 deletions
diff --git a/block/qcow2.c b/block/qcow2.c index 2d68b51f14..0daf25cb58 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1772,6 +1772,56 @@ static int qcow2_create2(const char *filename, int64_t total_size, Error *local_err = NULL; int ret; + if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { + int64_t meta_size = 0; + uint64_t nreftablee, nrefblocke, nl1e, nl2e; + int64_t aligned_total_size = align_offset(total_size, cluster_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 + * then, + * y1 = (y2 + a)/c + * y2 = y1 * sizeof(u16) + y1 * sizeof(u16) * sizeof(u64) / c + m + * we can get y1: + * y1 = (a + m) / (c - sizeof(u16) - sizeof(u16) * sizeof(u64) / c) + */ + nrefblocke = (aligned_total_size + meta_size + cluster_size) / + (cluster_size - sizeof(uint16_t) - + 1.0 * sizeof(uint16_t) * sizeof(uint64_t) / cluster_size); + nrefblocke = align_offset(nrefblocke, cluster_size / sizeof(uint16_t)); + meta_size += nrefblocke * sizeof(uint16_t); + + /* total size of refcount tables */ + nreftablee = nrefblocke * sizeof(uint16_t) / cluster_size; + nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t)); + meta_size += nreftablee * sizeof(uint64_t); + + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, + aligned_total_size + meta_size); + qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_lookup[prealloc]); + } + ret = bdrv_create_file(filename, opts, &local_err); if (ret < 0) { error_propagate(errp, local_err); @@ -1877,7 +1927,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, } /* And if we're supposed to preallocate metadata, do that now */ - if (prealloc == PREALLOC_MODE_METADATA) { + if (prealloc != PREALLOC_MODE_OFF) { BDRVQcowState *s = bs->opaque; qemu_co_mutex_lock(&s->lock); ret = preallocate(bs); @@ -1958,14 +2008,6 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) flags |= BLOCK_FLAG_LAZY_REFCOUNTS; } - if (prealloc != PREALLOC_MODE_OFF && - prealloc != PREALLOC_MODE_METADATA) { - ret = -EINVAL; - error_setg(errp, "Unsupported preallocate mode: %s", - PreallocMode_lookup[prealloc]); - goto finish; - } - if (backing_file && prealloc != PREALLOC_MODE_OFF) { error_setg(errp, "Backing file and preallocation cannot be used at " "the same time"); @@ -2526,7 +2568,8 @@ static QemuOptsList qcow2_create_opts = { { .name = BLOCK_OPT_PREALLOC, .type = QEMU_OPT_STRING, - .help = "Preallocation mode (allowed values: off, metadata)" + .help = "Preallocation mode (allowed values: off, metadata, " + "falloc, full)" }, { .name = BLOCK_OPT_LAZY_REFCOUNTS, |