qcow2: Metadata preallocation

This introduces a qemu-img create option for qcow2 which allows the metadata to
be preallocated, i.e. clusters are reserved in the refcount table and L1/L2
tables, but no data is written to them. Metadata is quite small, so this
happens in almost no time.

Especially with qcow2 on virtio this helps to gain a bit of performance during
the initial writes. However, as soon as create a snapshot, we're back to the
normal slow speed, obviously. So this isn't the real fix, but kind of a cheat
while we're still having trouble with qcow2 on virtio.

Note that the option is disabled by default and needs to be specified
explicitly using qemu-img create -f qcow2 -o preallocation=metadata.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Kevin Wolf 2009-08-17 15:50:10 +02:00 committed by Anthony Liguori
parent e8935eefe5
commit a35e1c177d
2 changed files with 80 additions and 2 deletions

View File

@ -638,9 +638,54 @@ static int get_bits_from_size(size_t size)
return res;
}
static int preallocate(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
uint64_t cluster_offset;
uint64_t nb_sectors;
uint64_t offset;
int num;
QCowL2Meta meta;
nb_sectors = bdrv_getlength(bs) >> 9;
offset = 0;
while (nb_sectors) {
num = MIN(nb_sectors, INT_MAX >> 9);
cluster_offset = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num,
&meta);
if (cluster_offset == 0) {
return -1;
}
if (qcow2_alloc_cluster_link_l2(bs, cluster_offset, &meta) < 0) {
qcow2_free_any_clusters(bs, cluster_offset, meta.nb_clusters);
return -1;
}
/* TODO Preallocate data if requested */
nb_sectors -= num;
offset += num << 9;
}
/*
* It is expected that the image file is large enough to actually contain
* all of the allocated clusters (otherwise we get failing reads after
* EOF). Extend the image to the last allocated sector.
*/
if (cluster_offset != 0) {
bdrv_truncate(s->hd, cluster_offset + (num << 9));
}
return 0;
}
static int qcow_create2(const char *filename, int64_t total_size,
const char *backing_file, const char *backing_format,
int flags, size_t cluster_size)
int flags, size_t cluster_size, int prealloc)
{
int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits;
@ -762,6 +807,16 @@ static int qcow_create2(const char *filename, int64_t total_size,
qemu_free(s->refcount_table);
qemu_free(s->refcount_block);
close(fd);
/* Preallocate metadata */
if (prealloc) {
BlockDriverState *bs;
bs = bdrv_new("");
bdrv_open(bs, filename, BDRV_O_CACHE_WB);
preallocate(bs);
bdrv_close(bs);
}
return 0;
}
@ -772,6 +827,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
uint64_t sectors = 0;
int flags = 0;
size_t cluster_size = 65536;
int prealloc = 0;
/* Read out options */
while (options && options->name) {
@ -787,12 +843,28 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
if (options->value.n) {
cluster_size = options->value.n;
}
} else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
if (!options->value.s || !strcmp(options->value.s, "off")) {
prealloc = 0;
} else if (!strcmp(options->value.s, "metadata")) {
prealloc = 1;
} else {
fprintf(stderr, "Invalid preallocation mode: '%s'\n",
options->value.s);
return -EINVAL;
}
}
options++;
}
if (backing_file && prealloc) {
fprintf(stderr, "Backing file and preallocation cannot be used at "
"the same time\n");
return -EINVAL;
}
return qcow_create2(filename, sectors, backing_file, backing_fmt, flags,
cluster_size);
cluster_size, prealloc);
}
static int qcow_make_empty(BlockDriverState *bs)
@ -982,6 +1054,11 @@ static QEMUOptionParameter qcow_create_options[] = {
.type = OPT_SIZE,
.help = "qcow2 cluster size"
},
{
.name = BLOCK_OPT_PREALLOC,
.type = OPT_STRING,
.help = "Preallocation mode (allowed values: off, metadata)"
},
{ NULL }
};

View File

@ -37,6 +37,7 @@
#define BLOCK_OPT_BACKING_FILE "backing_file"
#define BLOCK_OPT_BACKING_FMT "backing_fmt"
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
#define BLOCK_OPT_PREALLOC "preallocation"
typedef struct AIOPool {
void (*cancel)(BlockDriverAIOCB *acb);