mirror of https://github.com/xemu-project/xemu.git
qcow2: Zero write support
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
ab3a32ad5e
commit
621f058940
14
block.c
14
block.c
|
@ -80,6 +80,8 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
|
|||
void *opaque,
|
||||
bool is_write);
|
||||
static void coroutine_fn bdrv_co_do_rw(void *opaque);
|
||||
static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors);
|
||||
|
||||
static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors,
|
||||
bool is_write, double elapsed_time, uint64_t *wait);
|
||||
|
@ -1708,8 +1710,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
|
|||
|
||||
if (drv->bdrv_co_write_zeroes &&
|
||||
buffer_is_zero(bounce_buffer, iov.iov_len)) {
|
||||
ret = drv->bdrv_co_write_zeroes(bs, cluster_sector_num,
|
||||
cluster_nb_sectors);
|
||||
ret = bdrv_co_do_write_zeroes(bs, cluster_sector_num,
|
||||
cluster_nb_sectors);
|
||||
} else {
|
||||
ret = drv->bdrv_co_writev(bs, cluster_sector_num, cluster_nb_sectors,
|
||||
&bounce_qiov);
|
||||
|
@ -1819,9 +1821,15 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
|
|||
struct iovec iov;
|
||||
int ret;
|
||||
|
||||
/* TODO Emulate only part of misaligned requests instead of letting block
|
||||
* drivers return -ENOTSUP and emulate everything */
|
||||
|
||||
/* First try the efficient write zeroes operation */
|
||||
if (drv->bdrv_co_write_zeroes) {
|
||||
return drv->bdrv_co_write_zeroes(bs, sector_num, nb_sectors);
|
||||
ret = drv->bdrv_co_write_zeroes(bs, sector_num, nb_sectors);
|
||||
if (ret != -ENOTSUP) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fall back to bounce buffer if write zeroes is unsupported */
|
||||
|
|
|
@ -1102,3 +1102,75 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This zeroes as many clusters of nb_clusters as possible at once (i.e.
|
||||
* all clusters in the same L2 table) and returns the number of zeroed
|
||||
* clusters.
|
||||
*/
|
||||
static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
unsigned int nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table;
|
||||
int l2_index;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Limit nb_clusters to one L2 table */
|
||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||
|
||||
for (i = 0; i < nb_clusters; i++) {
|
||||
uint64_t old_offset;
|
||||
|
||||
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
|
||||
/* Update L2 entries */
|
||||
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
||||
if (old_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
|
||||
qcow2_free_any_clusters(bs, old_offset, 1);
|
||||
} else {
|
||||
l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nb_clusters;
|
||||
}
|
||||
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
unsigned int nb_clusters;
|
||||
int ret;
|
||||
|
||||
/* The zero flag is only supported by version 3 and newer */
|
||||
if (s->qcow_version < 3) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Each L2 table is handled by its own loop iteration */
|
||||
nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS);
|
||||
|
||||
while (nb_clusters > 0) {
|
||||
ret = zero_single_l2(bs, offset, nb_clusters);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
nb_clusters -= ret;
|
||||
offset += (ret * s->cluster_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1281,6 +1281,26 @@ static int qcow2_make_empty(BlockDriverState *bs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
/* Emulate misaligned zero writes */
|
||||
if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Whatever is left can use real zero clusters */
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_zero_clusters(bs, sector_num << BDRV_SECTOR_BITS,
|
||||
nb_sectors);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
|
@ -1558,6 +1578,7 @@ static BlockDriver bdrv_qcow2 = {
|
|||
.bdrv_co_writev = qcow2_co_writev,
|
||||
.bdrv_co_flush_to_os = qcow2_co_flush_to_os,
|
||||
|
||||
.bdrv_co_write_zeroes = qcow2_co_write_zeroes,
|
||||
.bdrv_co_discard = qcow2_co_discard,
|
||||
.bdrv_truncate = qcow2_truncate,
|
||||
.bdrv_write_compressed = qcow2_write_compressed,
|
||||
|
|
|
@ -283,6 +283,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
|||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
|
||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_sectors);
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
|
||||
|
||||
/* qcow2-snapshot.c functions */
|
||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
|
||||
|
|
Loading…
Reference in New Issue