block/qcow2: falloc/full preallocating growth
Implement the preallocation modes falloc and full for growing qcow2 images. Signed-off-by: Max Reitz <mreitz@redhat.com> Message-id: 20170613202107.10125-15-mreitz@redhat.com Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
60c48a29b7
commit
772d1f973f
|
@ -34,10 +34,6 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
|
||||||
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||||
int64_t offset, int64_t length, uint64_t addend,
|
int64_t offset, int64_t length, uint64_t addend,
|
||||||
bool decrease, enum qcow2_discard_type type);
|
bool decrease, enum qcow2_discard_type type);
|
||||||
static int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t offset,
|
|
||||||
uint64_t additional_clusters,
|
|
||||||
bool exact_size, int new_refblock_index,
|
|
||||||
uint64_t new_refblock_offset);
|
|
||||||
|
|
||||||
static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index);
|
static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index);
|
||||||
static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index);
|
static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index);
|
||||||
|
@ -517,10 +513,10 @@ fail:
|
||||||
* Returns: The offset after the new refcount structures (i.e. where the
|
* Returns: The offset after the new refcount structures (i.e. where the
|
||||||
* @additional_clusters may be placed) on success, -errno on error.
|
* @additional_clusters may be placed) on success, -errno on error.
|
||||||
*/
|
*/
|
||||||
static int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset,
|
int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset,
|
||||||
uint64_t additional_clusters,
|
uint64_t additional_clusters, bool exact_size,
|
||||||
bool exact_size, int new_refblock_index,
|
int new_refblock_index,
|
||||||
uint64_t new_refblock_offset)
|
uint64_t new_refblock_offset)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t total_refblock_count_u64, additional_refblock_count;
|
uint64_t total_refblock_count_u64, additional_refblock_count;
|
||||||
|
|
100
block/qcow2.c
100
block/qcow2.c
|
@ -3093,7 +3093,9 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
int64_t new_l1_size;
|
int64_t new_l1_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA) {
|
if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA &&
|
||||||
|
prealloc != PREALLOC_MODE_FALLOC && prealloc != PREALLOC_MODE_FULL)
|
||||||
|
{
|
||||||
error_setg(errp, "Unsupported preallocation mode '%s'",
|
error_setg(errp, "Unsupported preallocation mode '%s'",
|
||||||
PreallocMode_lookup[prealloc]);
|
PreallocMode_lookup[prealloc]);
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
@ -3144,6 +3146,102 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PREALLOC_MODE_FALLOC:
|
||||||
|
case PREALLOC_MODE_FULL:
|
||||||
|
{
|
||||||
|
int64_t allocation_start, host_offset, guest_offset;
|
||||||
|
int64_t clusters_allocated;
|
||||||
|
int64_t old_file_size, new_file_size;
|
||||||
|
uint64_t nb_new_data_clusters, nb_new_l2_tables;
|
||||||
|
|
||||||
|
old_file_size = bdrv_getlength(bs->file->bs);
|
||||||
|
if (old_file_size < 0) {
|
||||||
|
error_setg_errno(errp, -old_file_size,
|
||||||
|
"Failed to inquire current file length");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
nb_new_data_clusters = DIV_ROUND_UP(offset - old_length,
|
||||||
|
s->cluster_size);
|
||||||
|
|
||||||
|
/* This is an overestimation; we will not actually allocate space for
|
||||||
|
* these in the file but just make sure the new refcount structures are
|
||||||
|
* able to cover them so we will not have to allocate new refblocks
|
||||||
|
* while entering the data blocks in the potentially new L2 tables.
|
||||||
|
* (We do not actually care where the L2 tables are placed. Maybe they
|
||||||
|
* are already allocated or they can be placed somewhere before
|
||||||
|
* @old_file_size. It does not matter because they will be fully
|
||||||
|
* allocated automatically, so they do not need to be covered by the
|
||||||
|
* preallocation. All that matters is that we will not have to allocate
|
||||||
|
* new refcount structures for them.) */
|
||||||
|
nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
|
||||||
|
s->cluster_size / sizeof(uint64_t));
|
||||||
|
/* The cluster range may not be aligned to L2 boundaries, so add one L2
|
||||||
|
* table for a potential head/tail */
|
||||||
|
nb_new_l2_tables++;
|
||||||
|
|
||||||
|
allocation_start = qcow2_refcount_area(bs, old_file_size,
|
||||||
|
nb_new_data_clusters +
|
||||||
|
nb_new_l2_tables,
|
||||||
|
true, 0, 0);
|
||||||
|
if (allocation_start < 0) {
|
||||||
|
error_setg_errno(errp, -allocation_start,
|
||||||
|
"Failed to resize refcount structures");
|
||||||
|
return -allocation_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start,
|
||||||
|
nb_new_data_clusters);
|
||||||
|
if (clusters_allocated < 0) {
|
||||||
|
error_setg_errno(errp, -clusters_allocated,
|
||||||
|
"Failed to allocate data clusters");
|
||||||
|
return -clusters_allocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(clusters_allocated == nb_new_data_clusters);
|
||||||
|
|
||||||
|
/* Allocate the data area */
|
||||||
|
new_file_size = allocation_start +
|
||||||
|
nb_new_data_clusters * s->cluster_size;
|
||||||
|
ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_prepend(errp, "Failed to resize underlying file: ");
|
||||||
|
qcow2_free_clusters(bs, allocation_start,
|
||||||
|
nb_new_data_clusters * s->cluster_size,
|
||||||
|
QCOW2_DISCARD_OTHER);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the necessary L2 entries */
|
||||||
|
host_offset = allocation_start;
|
||||||
|
guest_offset = old_length;
|
||||||
|
while (nb_new_data_clusters) {
|
||||||
|
int64_t guest_cluster = guest_offset >> s->cluster_bits;
|
||||||
|
int64_t nb_clusters = MIN(nb_new_data_clusters,
|
||||||
|
s->l2_size - guest_cluster % s->l2_size);
|
||||||
|
QCowL2Meta allocation = {
|
||||||
|
.offset = guest_offset,
|
||||||
|
.alloc_offset = host_offset,
|
||||||
|
.nb_clusters = nb_clusters,
|
||||||
|
};
|
||||||
|
qemu_co_queue_init(&allocation.dependent_requests);
|
||||||
|
|
||||||
|
ret = qcow2_alloc_cluster_link_l2(bs, &allocation);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to update L2 tables");
|
||||||
|
qcow2_free_clusters(bs, host_offset,
|
||||||
|
nb_new_data_clusters * s->cluster_size,
|
||||||
|
QCOW2_DISCARD_OTHER);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
guest_offset += nb_clusters * s->cluster_size;
|
||||||
|
host_offset += nb_clusters * s->cluster_size;
|
||||||
|
nb_new_data_clusters -= nb_clusters;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
|
@ -549,6 +549,11 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
|
||||||
uint64_t addend, bool decrease,
|
uint64_t addend, bool decrease,
|
||||||
enum qcow2_discard_type type);
|
enum qcow2_discard_type type);
|
||||||
|
|
||||||
|
int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t offset,
|
||||||
|
uint64_t additional_clusters, bool exact_size,
|
||||||
|
int new_refblock_index,
|
||||||
|
uint64_t new_refblock_offset);
|
||||||
|
|
||||||
int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size);
|
int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size);
|
||||||
int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||||
int64_t nb_clusters);
|
int64_t nb_clusters);
|
||||||
|
|
Loading…
Reference in New Issue