Skip to content

Commit 763e21c

Browse files
committed
feat(block): advertise VIRTIO_BLK_F_WRITE_ZEROES for non-read-only devices
Set VIRTIO_BLK_F_WRITE_ZEROES in avail_features for any non-read-only block device, alongside VIRTIO_BLK_F_DISCARD. Populate the max_write_zeroes_sectors, max_write_zeroes_seg, and write_zeroes_may_unmap config fields in both VirtioBlock::new() and the persist::restore() path so the values match what the guest sees on a fresh boot vs after a snapshot restore. write_zeroes_may_unmap=1 lets the guest set the UNMAP flag on individual segments, which we then translate to fallocate's PUNCH_HOLE mode (UNMAP=0 uses ZERO_RANGE). Update the test_virtio_features and test_virtio_read_config expectations to account for the new feature bit and config fields. Signed-off-by: Nikita Kalyazin <nikita.kalyazin@e2b.dev>
1 parent bde344a commit 763e21c

2 files changed

Lines changed: 33 additions & 4 deletions

File tree

src/vmm/src/devices/virtio/block/virtio/device.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ use crate::devices::virtio::block::CacheType;
2727
use crate::devices::virtio::block::virtio::metrics::{BlockDeviceMetrics, BlockMetricsPerDevice};
2828
use crate::devices::virtio::device::{ActiveState, DeviceState, VirtioDevice};
2929
use crate::devices::virtio::generated::virtio_blk::{
30-
VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_RO, VIRTIO_BLK_ID_BYTES,
30+
VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_WRITE_ZEROES,
31+
VIRTIO_BLK_ID_BYTES,
3132
};
3233
use crate::devices::virtio::generated::virtio_config::VIRTIO_F_VERSION_1;
3334
use crate::devices::virtio::generated::virtio_ids::VIRTIO_ID_BLOCK;
@@ -360,6 +361,7 @@ impl VirtioBlock {
360361
avail_features |= 1u64 << VIRTIO_BLK_F_RO;
361362
} else {
362363
avail_features |= 1u64 << VIRTIO_BLK_F_DISCARD;
364+
avail_features |= 1u64 << VIRTIO_BLK_F_WRITE_ZEROES;
363365
}
364366

365367
let queue_evts = [EventFd::new(libc::EFD_NONBLOCK).map_err(VirtioBlockError::EventFd)?];
@@ -385,6 +387,25 @@ impl VirtioBlock {
385387
// serialising them). max_discard_sectors is set to the full disk
386388
// so contiguous ranges are never split, regardless of this limit.
387389
max_discard_seg: if !config.is_read_only { 1 } else { 0 },
390+
max_write_zeroes_sectors: if !config.is_read_only {
391+
discard_sectors
392+
} else {
393+
0
394+
},
395+
// max_write_zeroes_seg = 1: each VIRTIO_BLK_T_WRITE_ZEROES
396+
// request carries exactly one (sector, num_sectors, flags) tuple.
397+
// Raising this would let the guest batch disjoint zero ranges
398+
// (e.g. mkfs zeroing several inode tables) into a single
399+
// multi-segment request, saving virtqueue round-trips. We keep
400+
// it at 1 in this iteration because the async io_uring engine
401+
// currently produces exactly one SQE per virtio request;
402+
// multi-segment would require submitting N SQEs and only
403+
// completing the virtio request after all N CQEs return (or
404+
// serialising them). max_write_zeroes_sectors is set to the
405+
// full disk so contiguous ranges are never split, regardless of
406+
// this limit.
407+
max_write_zeroes_seg: if !config.is_read_only { 1 } else { 0 },
408+
write_zeroes_may_unmap: if !config.is_read_only { 1 } else { 0 },
388409
..Default::default()
389410
};
390411

@@ -912,10 +933,12 @@ mod tests {
912933

913934
assert_eq!(block.device_type(), VIRTIO_ID_BLOCK);
914935

915-
// default_block is non-read-only, so VIRTIO_BLK_F_DISCARD is advertised.
936+
// default_block is non-read-only, so VIRTIO_BLK_F_DISCARD and
937+
// VIRTIO_BLK_F_WRITE_ZEROES are advertised.
916938
let features: u64 = (1u64 << VIRTIO_F_VERSION_1)
917939
| (1u64 << VIRTIO_RING_F_EVENT_IDX)
918-
| (1u64 << VIRTIO_BLK_F_DISCARD);
940+
| (1u64 << VIRTIO_BLK_F_DISCARD)
941+
| (1u64 << VIRTIO_BLK_F_WRITE_ZEROES);
919942

920943
assert_eq!(
921944
block.avail_features_by_page(0),
@@ -942,11 +965,14 @@ mod tests {
942965
let mut actual_config_space = ConfigSpace::default();
943966
block.read_config(0, actual_config_space.as_mut_slice());
944967
// The block's backing file size is 0x1000, so there are 8 (4096/512) sectors.
945-
// default_block is non-read-only, so discard fields are populated.
968+
// default_block is non-read-only, so discard and write-zeroes fields are populated.
946969
let expected_config_space = ConfigSpace {
947970
capacity: 8,
948971
max_discard_sectors: 8,
949972
max_discard_seg: 1,
973+
max_write_zeroes_sectors: 8,
974+
max_write_zeroes_seg: 1,
975+
write_zeroes_may_unmap: 1,
950976
..Default::default()
951977
};
952978
assert_eq!(actual_config_space, expected_config_space);

src/vmm/src/devices/virtio/block/virtio/persist.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ impl Persist<'_> for VirtioBlock {
116116
capacity: disk_properties.nsectors.to_le(),
117117
max_discard_sectors: if !is_read_only { discard_sectors } else { 0 },
118118
max_discard_seg: if !is_read_only { 1 } else { 0 },
119+
max_write_zeroes_sectors: if !is_read_only { discard_sectors } else { 0 },
120+
max_write_zeroes_seg: if !is_read_only { 1 } else { 0 },
121+
write_zeroes_may_unmap: if !is_read_only { 1 } else { 0 },
119122
..Default::default()
120123
};
121124

0 commit comments

Comments
 (0)