From 46e6ecddfe6c689f5c80ef3d9a6ee826edf8f121 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Mon, 2 Feb 2026 00:04:20 +0100 Subject: [PATCH] block: Add supports_zero_flag trait method Add supports_zero_flag() to DiskFile trait to indicate whether a disk format can mark clusters/blocks as reading zeros without deallocating storage. QCOW2 supports this via the zero flag in L2 entries. VHDX also has PAYLOAD_BLOCK_ZERO state for this, though it's not yet implemented in cloud-hypervisor. This enables DISCARD to be advertised even with sparse=false for formats with zero-flag support, since they can mark regions as zeros (keeps storage allocated) instead of requiring full deallocation. Signed-off-by: Anatol Belski --- block/src/async_io.rs | 6 ++++++ block/src/qcow_sync.rs | 4 ++++ virtio-devices/src/block.rs | 15 +++++++++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/block/src/async_io.rs b/block/src/async_io.rs index 3727f0d95..a1e8fa3e4 100644 --- a/block/src/async_io.rs +++ b/block/src/async_io.rs @@ -84,6 +84,12 @@ pub trait DiskFile: Send { false } + /// Indicates support for zero flag optimization in WRITE_ZEROES. Override + /// to return true when supported. + fn supports_zero_flag(&self) -> bool { + false + } + /// Returns the file descriptor of the underlying disk image file. /// /// The file descriptor is supposed to be used for `fcntl()` calls but no diff --git a/block/src/qcow_sync.rs b/block/src/qcow_sync.rs index 189254bfd..6730fccb0 100644 --- a/block/src/qcow_sync.rs +++ b/block/src/qcow_sync.rs @@ -80,6 +80,10 @@ impl DiskFile for QcowDiskSync { true } + fn supports_zero_flag(&self) -> bool { + true + } + fn fd(&mut self) -> BorrowedDiskFd<'_> { BorrowedDiskFd::new(self.qcow_file.lock().unwrap().as_raw_fd()) } diff --git a/virtio-devices/src/block.rs b/virtio-devices/src/block.rs index e05900b5a..a7da43e6c 100644 --- a/virtio-devices/src/block.rs +++ b/virtio-devices/src/block.rs @@ -672,7 +672,7 @@ impl Block { exit_evt: EventFd, state: Option, queue_affinity: BTreeMap>, - _sparse: bool, + sparse: bool, ) -> io::Result { let (disk_nsectors, avail_features, acked_features, config, paused) = if let Some(state) = state { @@ -704,10 +704,17 @@ impl Block { | (1u64 << VIRTIO_RING_F_EVENT_IDX) | (1u64 << VIRTIO_RING_F_INDIRECT_DESC); - // Only advertise discard/write zeroes if the backend supports sparse operations + // When backend supports sparse operations: + // - Always advertise WRITE_ZEROES + // - Advertise DISCARD only if sparse=true OR format supports marking + // clusters as zero without deallocating if disk_image.supports_sparse_operations() { - avail_features |= - (1u64 << VIRTIO_BLK_F_DISCARD) | (1u64 << VIRTIO_BLK_F_WRITE_ZEROES); + avail_features |= 1u64 << VIRTIO_BLK_F_WRITE_ZEROES; + if sparse || disk_image.supports_zero_flag() { + avail_features |= 1u64 << VIRTIO_BLK_F_DISCARD; + } + } else if sparse { + warn!("sparse=on requested but backend does not support sparse operations"); } if iommu {