mirror of https://github.com/xemu-project/xemu.git
qcow2: Reuse preallocated zero clusters
Instead of just freeing preallocated zero clusters and completely allocating them from scratch, reuse them. We cannot do this in handle_copied(), however, since this is a COW operation. Therefore, we have to add the new logic to handle_alloc() and simply return the existing offset if it exists. The only catch is that we have to convince qcow2_alloc_cluster_link_l2() not to free the old clusters (because we have reused them). Reported-by: Eric Blake <eblake@redhat.com> Signed-off-by: Max Reitz <mreitz@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
92413c16be
commit
564a6b6938
|
@ -309,14 +309,20 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
||||||
uint64_t *l2_table, uint64_t stop_flags)
|
uint64_t *l2_table, uint64_t stop_flags)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
int first_cluster_type;
|
||||||
uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED;
|
uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED;
|
||||||
uint64_t first_entry = be64_to_cpu(l2_table[0]);
|
uint64_t first_entry = be64_to_cpu(l2_table[0]);
|
||||||
uint64_t offset = first_entry & mask;
|
uint64_t offset = first_entry & mask;
|
||||||
|
|
||||||
if (!offset)
|
if (!offset) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL);
|
/* must be allocated */
|
||||||
|
first_cluster_type = qcow2_get_cluster_type(first_entry);
|
||||||
|
assert(first_cluster_type == QCOW2_CLUSTER_NORMAL ||
|
||||||
|
(first_cluster_type == QCOW2_CLUSTER_ZERO &&
|
||||||
|
(first_entry & L2E_OFFSET_MASK) != 0));
|
||||||
|
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
|
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
|
||||||
|
@ -835,7 +841,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||||
* Don't discard clusters that reach a refcount of 0 (e.g. compressed
|
* Don't discard clusters that reach a refcount of 0 (e.g. compressed
|
||||||
* clusters), the next write will reuse them anyway.
|
* clusters), the next write will reuse them anyway.
|
||||||
*/
|
*/
|
||||||
if (j != 0) {
|
if (!m->keep_old_clusters && j != 0) {
|
||||||
for (i = 0; i < j; i++) {
|
for (i = 0; i < j; i++) {
|
||||||
qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1,
|
qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1,
|
||||||
QCOW2_DISCARD_NEVER);
|
QCOW2_DISCARD_NEVER);
|
||||||
|
@ -1132,8 +1138,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
uint64_t entry;
|
uint64_t entry;
|
||||||
uint64_t nb_clusters;
|
uint64_t nb_clusters;
|
||||||
int ret;
|
int ret;
|
||||||
|
bool keep_old_clusters = false;
|
||||||
|
|
||||||
uint64_t alloc_cluster_offset;
|
uint64_t alloc_cluster_offset = 0;
|
||||||
|
|
||||||
trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset,
|
trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset,
|
||||||
*bytes);
|
*bytes);
|
||||||
|
@ -1170,31 +1177,54 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
* wrong with our code. */
|
* wrong with our code. */
|
||||||
assert(nb_clusters > 0);
|
assert(nb_clusters > 0);
|
||||||
|
|
||||||
|
if (qcow2_get_cluster_type(entry) == QCOW2_CLUSTER_ZERO &&
|
||||||
|
(entry & L2E_OFFSET_MASK) != 0 && (entry & QCOW_OFLAG_COPIED) &&
|
||||||
|
(!*host_offset ||
|
||||||
|
start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK)))
|
||||||
|
{
|
||||||
|
/* Try to reuse preallocated zero clusters; contiguous normal clusters
|
||||||
|
* would be fine, too, but count_cow_clusters() above has limited
|
||||||
|
* nb_clusters already to a range of COW clusters */
|
||||||
|
int preallocated_nb_clusters =
|
||||||
|
count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||||
|
&l2_table[l2_index], QCOW_OFLAG_COPIED);
|
||||||
|
assert(preallocated_nb_clusters > 0);
|
||||||
|
|
||||||
|
nb_clusters = preallocated_nb_clusters;
|
||||||
|
alloc_cluster_offset = entry & L2E_OFFSET_MASK;
|
||||||
|
|
||||||
|
/* We want to reuse these clusters, so qcow2_alloc_cluster_link_l2()
|
||||||
|
* should not free them. */
|
||||||
|
keep_old_clusters = true;
|
||||||
|
}
|
||||||
|
|
||||||
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
|
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
|
||||||
|
|
||||||
/* Allocate, if necessary at a given offset in the image file */
|
|
||||||
alloc_cluster_offset = start_of_cluster(s, *host_offset);
|
|
||||||
ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
|
|
||||||
&nb_clusters);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Can't extend contiguous allocation */
|
|
||||||
if (nb_clusters == 0) {
|
|
||||||
*bytes = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* !*host_offset would overwrite the image header and is reserved for "no
|
|
||||||
* host offset preferred". If 0 was a valid host offset, it'd trigger the
|
|
||||||
* following overlap check; do that now to avoid having an invalid value in
|
|
||||||
* *host_offset. */
|
|
||||||
if (!alloc_cluster_offset) {
|
if (!alloc_cluster_offset) {
|
||||||
ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
|
/* Allocate, if necessary at a given offset in the image file */
|
||||||
nb_clusters * s->cluster_size);
|
alloc_cluster_offset = start_of_cluster(s, *host_offset);
|
||||||
assert(ret < 0);
|
ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
|
||||||
goto fail;
|
&nb_clusters);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can't extend contiguous allocation */
|
||||||
|
if (nb_clusters == 0) {
|
||||||
|
*bytes = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* !*host_offset would overwrite the image header and is reserved for
|
||||||
|
* "no host offset preferred". If 0 was a valid host offset, it'd
|
||||||
|
* trigger the following overlap check; do that now to avoid having an
|
||||||
|
* invalid value in *host_offset. */
|
||||||
|
if (!alloc_cluster_offset) {
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
|
||||||
|
nb_clusters * s->cluster_size);
|
||||||
|
assert(ret < 0);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1225,6 +1255,8 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
.offset = start_of_cluster(s, guest_offset),
|
.offset = start_of_cluster(s, guest_offset),
|
||||||
.nb_clusters = nb_clusters,
|
.nb_clusters = nb_clusters,
|
||||||
|
|
||||||
|
.keep_old_clusters = keep_old_clusters,
|
||||||
|
|
||||||
.cow_start = {
|
.cow_start = {
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.nb_bytes = offset_into_cluster(s, guest_offset),
|
.nb_bytes = offset_into_cluster(s, guest_offset),
|
||||||
|
|
|
@ -322,6 +322,9 @@ typedef struct QCowL2Meta
|
||||||
/** Number of newly allocated clusters */
|
/** Number of newly allocated clusters */
|
||||||
int nb_clusters;
|
int nb_clusters;
|
||||||
|
|
||||||
|
/** Do not free the old clusters */
|
||||||
|
bool keep_old_clusters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that overlap with this allocation and wait to be restarted
|
* Requests that overlap with this allocation and wait to be restarted
|
||||||
* when the allocating request has completed.
|
* when the allocating request has completed.
|
||||||
|
|
Loading…
Reference in New Issue