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 <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-03-17 17:46:36 +01:00
committed by Rob Bradford
parent d609410b8b
commit 3e2e453d89

View File

@@ -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)),
}