提交 564a6b69 编写于 作者: M Max Reitz 提交者: Kevin Wolf

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: NEric Blake <eblake@redhat.com>
Signed-off-by: NMax Reitz <mreitz@redhat.com>
Reviewed-by: NEric Blake <eblake@redhat.com>
Signed-off-by: NKevin Wolf <kwolf@redhat.com>
上级 92413c16
...@@ -309,14 +309,20 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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);
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); 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);
/* Allocate, if necessary at a given offset in the image file */ nb_clusters = preallocated_nb_clusters;
alloc_cluster_offset = start_of_cluster(s, *host_offset); alloc_cluster_offset = entry & L2E_OFFSET_MASK;
ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
&nb_clusters);
if (ret < 0) {
goto fail;
}
/* Can't extend contiguous allocation */ /* We want to reuse these clusters, so qcow2_alloc_cluster_link_l2()
if (nb_clusters == 0) { * should not free them. */
*bytes = 0; keep_old_clusters = true;
return 0;
} }
/* !*host_offset would overwrite the image header and is reserved for "no qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
* 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, ...@@ -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 ...@@ -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.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册