From 3e603e344b81b3ecfea6fb9589ba91f70a22139d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 8 Jun 2020 15:01:33 +0100 Subject: [PATCH 05/17] qcow2: Support BDRV_REQ_ZERO_WRITE for truncate RH-Author: Kevin Wolf Message-id: <20200608150140.38218-5-kwolf@redhat.com> Patchwork-id: 97449 O-Subject: [RHEL-AV-8.2.1 qemu-kvm PATCH 04/11] qcow2: Support BDRV_REQ_ZERO_WRITE for truncate Bugzilla: 1780574 RH-Acked-by: Sergio Lopez Pascual RH-Acked-by: Eric Blake RH-Acked-by: Max Reitz 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 Message-Id: <20200424125448.63318-5-kwolf@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Max Reitz Signed-off-by: Kevin Wolf (cherry picked from commit f01643fb8b47e8a70c04bbf45e0f12a9e5bc54de) Signed-off-by: Kevin Wolf Signed-off-by: Danilo C. L. de Paula --- block/qcow2-cluster.c | 2 +- block/qcow2.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index dc3c270..9d04f8d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1784,7 +1784,7 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, /* Caller must pass aligned values, except at image end */ assert(QEMU_IS_ALIGNED(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 */ if (s->qcow_version < 3) { diff --git a/block/qcow2.c b/block/qcow2.c index 86aa74a..f3d6cb0 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1726,6 +1726,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, } bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0; + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; /* Repair image if dirty */ if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only && @@ -4197,6 +4198,39 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, 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) { /* Flush metadata before actually changing the image size */ ret = qcow2_write_caches(bs); -- 1.8.3.1