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:
parent
e8935eefe5
commit
a35e1c177d
@ -638,9 +638,54 @@ static int get_bits_from_size(size_t size)
|
|||||||
return res;
|
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,
|
static int qcow_create2(const char *filename, int64_t total_size,
|
||||||
const char *backing_file, const char *backing_format,
|
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;
|
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_table);
|
||||||
qemu_free(s->refcount_block);
|
qemu_free(s->refcount_block);
|
||||||
close(fd);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,6 +827,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
uint64_t sectors = 0;
|
uint64_t sectors = 0;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
size_t cluster_size = 65536;
|
size_t cluster_size = 65536;
|
||||||
|
int prealloc = 0;
|
||||||
|
|
||||||
/* Read out options */
|
/* Read out options */
|
||||||
while (options && options->name) {
|
while (options && options->name) {
|
||||||
@ -787,12 +843,28 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
if (options->value.n) {
|
if (options->value.n) {
|
||||||
cluster_size = 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++;
|
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,
|
return qcow_create2(filename, sectors, backing_file, backing_fmt, flags,
|
||||||
cluster_size);
|
cluster_size, prealloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcow_make_empty(BlockDriverState *bs)
|
static int qcow_make_empty(BlockDriverState *bs)
|
||||||
@ -982,6 +1054,11 @@ static QEMUOptionParameter qcow_create_options[] = {
|
|||||||
.type = OPT_SIZE,
|
.type = OPT_SIZE,
|
||||||
.help = "qcow2 cluster size"
|
.help = "qcow2 cluster size"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = BLOCK_OPT_PREALLOC,
|
||||||
|
.type = OPT_STRING,
|
||||||
|
.help = "Preallocation mode (allowed values: off, metadata)"
|
||||||
|
},
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#define BLOCK_OPT_BACKING_FILE "backing_file"
|
#define BLOCK_OPT_BACKING_FILE "backing_file"
|
||||||
#define BLOCK_OPT_BACKING_FMT "backing_fmt"
|
#define BLOCK_OPT_BACKING_FMT "backing_fmt"
|
||||||
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
|
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
|
||||||
|
#define BLOCK_OPT_PREALLOC "preallocation"
|
||||||
|
|
||||||
typedef struct AIOPool {
|
typedef struct AIOPool {
|
||||||
void (*cancel)(BlockDriverAIOCB *acb);
|
void (*cancel)(BlockDriverAIOCB *acb);
|
||||||
|
Loading…
Reference in New Issue
Block a user