qcow2: Support BDRV_REQ_ZERO_WRITE for truncate
If BDRV_REQ_ZERO_WRITE is set and we're extending the image, calling qcow2_cluster_zeroize() with flags=0 does the right thing: It doesn't undo any previous preallocation, but just adds the zero flag to all relevant L2 entries. If an external data file is in use, a write_zeroes request to the data file is made instead. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Message-Id: <20200424125448.63318-5-kwolf@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
8c6242b6f3
commit
f01643fb8b
|
@ -1795,7 +1795,7 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
|
||||||
/* Caller must pass aligned values, except at image end */
|
/* Caller must pass aligned values, except at image end */
|
||||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||||
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
|
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
|
||||||
end_offset == bs->total_sectors << BDRV_SECTOR_BITS);
|
end_offset >= bs->total_sectors << BDRV_SECTOR_BITS);
|
||||||
|
|
||||||
/* The zero flag is only supported by version 3 and newer */
|
/* The zero flag is only supported by version 3 and newer */
|
||||||
if (s->qcow_version < 3) {
|
if (s->qcow_version < 3) {
|
||||||
|
|
|
@ -1726,6 +1726,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||||
|
|
||||||
bs->supported_zero_flags = header.version >= 3 ?
|
bs->supported_zero_flags = header.version >= 3 ?
|
||||||
BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK : 0;
|
BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK : 0;
|
||||||
|
bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
|
||||||
|
|
||||||
/* Repair image if dirty */
|
/* Repair image if dirty */
|
||||||
if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
|
if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
|
||||||
|
@ -4214,6 +4215,39 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((flags & BDRV_REQ_ZERO_WRITE) && offset > old_length) {
|
||||||
|
uint64_t zero_start = QEMU_ALIGN_UP(old_length, s->cluster_size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use zero clusters as much as we can. qcow2_cluster_zeroize()
|
||||||
|
* requires a cluster-aligned start. The end may be unaligned if it is
|
||||||
|
* at the end of the image (which it is here).
|
||||||
|
*/
|
||||||
|
ret = qcow2_cluster_zeroize(bs, zero_start, offset - zero_start, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to zero out new clusters");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write explicit zeros for the unaligned head */
|
||||||
|
if (zero_start > old_length) {
|
||||||
|
uint64_t len = zero_start - old_length;
|
||||||
|
uint8_t *buf = qemu_blockalign0(bs, len);
|
||||||
|
QEMUIOVector qiov;
|
||||||
|
qemu_iovec_init_buf(&qiov, buf, len);
|
||||||
|
|
||||||
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
ret = qcow2_co_pwritev_part(bs, old_length, len, &qiov, 0, 0);
|
||||||
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
|
||||||
|
qemu_vfree(buf);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to zero out the new area");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (prealloc != PREALLOC_MODE_OFF) {
|
if (prealloc != PREALLOC_MODE_OFF) {
|
||||||
/* Flush metadata before actually changing the image size */
|
/* Flush metadata before actually changing the image size */
|
||||||
ret = qcow2_write_caches(bs);
|
ret = qcow2_write_caches(bs);
|
||||||
|
|
Loading…
Reference in a new issue