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 <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-02-02 00:04:20 +01:00
committed by Rob Bradford
parent 7dfcbff309
commit 46e6ecddfe
3 changed files with 21 additions and 4 deletions
+6
View File
@@ -84,6 +84,12 @@ pub trait DiskFile: Send {
false 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. /// Returns the file descriptor of the underlying disk image file.
/// ///
/// The file descriptor is supposed to be used for `fcntl()` calls but no /// The file descriptor is supposed to be used for `fcntl()` calls but no
+4
View File
@@ -80,6 +80,10 @@ impl DiskFile for QcowDiskSync {
true true
} }
fn supports_zero_flag(&self) -> bool {
true
}
fn fd(&mut self) -> BorrowedDiskFd<'_> { fn fd(&mut self) -> BorrowedDiskFd<'_> {
BorrowedDiskFd::new(self.qcow_file.lock().unwrap().as_raw_fd()) BorrowedDiskFd::new(self.qcow_file.lock().unwrap().as_raw_fd())
} }
+11 -4
View File
@@ -672,7 +672,7 @@ impl Block {
exit_evt: EventFd, exit_evt: EventFd,
state: Option<BlockState>, state: Option<BlockState>,
queue_affinity: BTreeMap<u16, Vec<usize>>, queue_affinity: BTreeMap<u16, Vec<usize>>,
_sparse: bool, sparse: bool,
) -> io::Result<Self> { ) -> io::Result<Self> {
let (disk_nsectors, avail_features, acked_features, config, paused) = let (disk_nsectors, avail_features, acked_features, config, paused) =
if let Some(state) = state { if let Some(state) = state {
@@ -704,10 +704,17 @@ impl Block {
| (1u64 << VIRTIO_RING_F_EVENT_IDX) | (1u64 << VIRTIO_RING_F_EVENT_IDX)
| (1u64 << VIRTIO_RING_F_INDIRECT_DESC); | (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() { if disk_image.supports_sparse_operations() {
avail_features |= avail_features |= 1u64 << VIRTIO_BLK_F_WRITE_ZEROES;
(1u64 << VIRTIO_BLK_F_DISCARD) | (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 { if iommu {