mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-27 21:40:49 +00:00
qcow2: Allow reading both COW regions with only one request
Reading both COW regions requires two separate requests, but it's perfectly possible to merge them and perform only one. This generally improves performance, particularly on rotating disk drives. The downside is that the data in the middle region is read but discarded. This patch takes a conservative approach and only merges reads when the size of the middle region is <= 16KB. Signed-off-by: Alberto Garcia <berto@igalia.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
672f0f2c4b
commit
b3cf1c7cf8
@ -777,20 +777,38 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
|
||||
Qcow2COWRegion *start = &m->cow_start;
|
||||
Qcow2COWRegion *end = &m->cow_end;
|
||||
unsigned buffer_size;
|
||||
unsigned data_bytes = end->offset - (start->offset + start->nb_bytes);
|
||||
bool merge_reads;
|
||||
uint8_t *start_buffer, *end_buffer;
|
||||
int ret;
|
||||
|
||||
assert(start->nb_bytes <= UINT_MAX - end->nb_bytes);
|
||||
assert(start->nb_bytes + end->nb_bytes <= UINT_MAX - data_bytes);
|
||||
assert(start->offset + start->nb_bytes <= end->offset);
|
||||
|
||||
if (start->nb_bytes == 0 && end->nb_bytes == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reserve a buffer large enough to store the data from both the
|
||||
* start and end COW regions. Add some padding in the middle if
|
||||
* necessary to make sure that the end region is optimally aligned */
|
||||
buffer_size = QEMU_ALIGN_UP(start->nb_bytes, bdrv_opt_mem_align(bs)) +
|
||||
end->nb_bytes;
|
||||
/* If we have to read both the start and end COW regions and the
|
||||
* middle region is not too large then perform just one read
|
||||
* operation */
|
||||
merge_reads = start->nb_bytes && end->nb_bytes && data_bytes <= 16384;
|
||||
if (merge_reads) {
|
||||
buffer_size = start->nb_bytes + data_bytes + end->nb_bytes;
|
||||
} else {
|
||||
/* If we have to do two reads, add some padding in the middle
|
||||
* if necessary to make sure that the end region is optimally
|
||||
* aligned. */
|
||||
size_t align = bdrv_opt_mem_align(bs);
|
||||
assert(align > 0 && align <= UINT_MAX);
|
||||
assert(QEMU_ALIGN_UP(start->nb_bytes, align) <=
|
||||
UINT_MAX - end->nb_bytes);
|
||||
buffer_size = QEMU_ALIGN_UP(start->nb_bytes, align) + end->nb_bytes;
|
||||
}
|
||||
|
||||
/* Reserve a buffer large enough to store all the data that we're
|
||||
* going to read */
|
||||
start_buffer = qemu_try_blockalign(bs, buffer_size);
|
||||
if (start_buffer == NULL) {
|
||||
return -ENOMEM;
|
||||
@ -799,7 +817,13 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
|
||||
end_buffer = start_buffer + buffer_size - end->nb_bytes;
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
/* First we read the existing data from both COW regions */
|
||||
/* First we read the existing data from both COW regions. We
|
||||
* either read the whole region in one go, or the start and end
|
||||
* regions separately. */
|
||||
if (merge_reads) {
|
||||
ret = do_perform_cow_read(bs, m->offset, start->offset,
|
||||
start_buffer, buffer_size);
|
||||
} else {
|
||||
ret = do_perform_cow_read(bs, m->offset, start->offset,
|
||||
start_buffer, start->nb_bytes);
|
||||
if (ret < 0) {
|
||||
@ -808,6 +832,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
|
||||
|
||||
ret = do_perform_cow_read(bs, m->offset, end->offset,
|
||||
end_buffer, end->nb_bytes);
|
||||
}
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user