From 3e2e453d89813d5bef2e51e2b3673451ba6fd946 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Tue, 17 Mar 2026 17:46:36 +0100 Subject: [PATCH] block: Honor unmap flag in write zeroes requests The write zeroes segment descriptor (struct virtio_blk_discard_write_zeroes, virtio spec v1.2 section 5.2.6) includes a flags field with an unmap bit. Per section 5.2.6.2, if unmap is set, the device MAY deallocate the specified range of sectors in the device backend storage, as if the discard command had been sent. Read the flags field and when the unmap bit is set, use punch_hole to deallocate the range. Otherwise continue using write_zeroes via ZERO_RANGE which preserves allocation. This allows the guest to reclaim host disk space through write zeroes requests on thin provisioned images. Signed-off-by: Anatol Belski --- block/src/lib.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/block/src/lib.rs b/block/src/lib.rs index c02b315cc..2c8556c39 100644 --- a/block/src/lib.rs +++ b/block/src/lib.rs @@ -621,15 +621,18 @@ impl Request { } let mut wz_sector = [0u8; 8]; - let mut wz_num_sectors = [0u8; 4]; + let mut wz_flags = [0u8; 4]; mem.read_slice(&mut wz_sector, data_addr) .map_err(ExecuteError::Read)?; mem.read_slice(&mut wz_num_sectors, data_addr.checked_add(8).unwrap()) .map_err(ExecuteError::Read)?; + mem.read_slice(&mut wz_flags, data_addr.checked_add(12).unwrap()) + .map_err(ExecuteError::Read)?; let wz_sector = u64::from_le_bytes(wz_sector); let wz_num_sectors = u32::from_le_bytes(wz_num_sectors); + let wz_flags = u32::from_le_bytes(wz_flags); let wz_offset = wz_sector * SECTOR_SIZE; if wz_offset == 0 && disable_sector0_writes { @@ -637,9 +640,15 @@ impl Request { } let wz_length = (wz_num_sectors as u64) * SECTOR_SIZE; - disk_image - .write_zeroes(wz_offset, wz_length, user_data) - .map_err(ExecuteError::AsyncWriteZeroes)?; + if wz_flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP != 0 { + disk_image + .punch_hole(wz_offset, wz_length, user_data) + .map_err(ExecuteError::AsyncPunchHole)?; + } else { + disk_image + .write_zeroes(wz_offset, wz_length, user_data) + .map_err(ExecuteError::AsyncWriteZeroes)?; + } } RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)), }