Skip to content

Commit 7261d1f

Browse files
committed
test(block): add pytest integration tests for VIRTIO_BLK_F_WRITE_ZEROES
Two tests, parametrised over Sync and Async IO engines: - test_write_zeroes: boot VM with a writable block device, write random data to a 1 MiB region of /dev/vdb, run `blkdiscard -z` (BLKZEROOUT ioctl), then verify: * sysfs /queue/write_zeroes_max_bytes is non-zero (feature negotiated) * the region reads back as zeros after the operation * no requests failed - test_write_zeroes_not_advertised_for_read_only: read-only device reports write_zeroes_max_bytes=0 in sysfs. The test deliberately does not assert that the write_zeroes_count metric incremented. The Linux kernel's blkdev_issue_zeroout() may legitimately fall back to issuing plain zero-page writes (REQ_OP_WRITE) instead of REQ_OP_WRITE_ZEROES even when the feature is advertised, depending on internal heuristics. Both paths leave the device reading as zeros, so a metric assertion would be flaky in CI without indicating any actual bug. Direct WRITE_ZEROES request-handling correctness is covered by the unit tests. Use cmp -n /dev/vdb /dev/zero for the zero check (od collapses repeated lines with *, which makes naive byte-comparison fragile). Add write_zeroes_count to host_tools/fcmetrics.py so the metrics schema validation passes. Signed-off-by: Nikita Kalyazin <nikita.kalyazin@e2b.dev>
1 parent 17d83b9 commit 7261d1f

2 files changed

Lines changed: 84 additions & 0 deletions

File tree

tests/host_tools/fcmetrics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def validate_fc_metrics(metrics):
9696
"io_engine_throttled_events",
9797
"remaining_reqs_count",
9898
"discard_count",
99+
"write_zeroes_count",
99100
{"read_agg": latency_agg_metrics_fields},
100101
{"write_agg": latency_agg_metrics_fields},
101102
]

tests/integration_tests/functional/test_drive_virtio.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,86 @@ def test_discard_not_advertised_for_read_only(uvm_plain_any, io_engine):
447447
), f"Expected discard_max_bytes=0 for read-only device, got: {stdout.strip()}"
448448

449449

450+
def _exercise_write_zeroes(ssh):
451+
"""Write random data, issue blkdiscard -z, verify zeros on /dev/vdb."""
452+
# Sysfs check: the kernel populates write_zeroes_max_bytes from the
453+
# negotiated feature; a non-zero value proves the feature is advertised.
454+
_, stdout, _ = ssh.check_output(
455+
"cat /sys/block/vdb/queue/write_zeroes_max_bytes"
456+
)
457+
assert int(stdout.strip()) > 0, (
458+
f"Expected non-zero write_zeroes_max_bytes, got: {stdout.strip()}"
459+
)
460+
# Write random non-zero data so we can tell zeroing apart from
461+
# "the device was already zero".
462+
ssh.check_output("dd if=/dev/urandom of=/dev/vdb bs=1M count=1 conv=fsync")
463+
ssh.check_output("sync && echo 3 > /proc/sys/vm/drop_caches")
464+
# Issue zero-out via blkdiscard -z (BLKZEROOUT ioctl).
465+
ssh.check_output("blkdiscard -z --offset 0 --length $((1024*1024)) /dev/vdb")
466+
ssh.check_output("sync && echo 3 > /proc/sys/vm/drop_caches")
467+
# Verify the range now reads as zeros.
468+
ssh.check_output("cmp -n 1048576 /dev/vdb /dev/zero")
469+
470+
471+
def test_write_zeroes(uvm_plain_any, microvm_factory, io_engine):
472+
"""
473+
Verify VIRTIO_BLK_F_WRITE_ZEROES on a fresh boot and after snapshot/restore.
474+
475+
Writes random data to a 1 MiB region of /dev/vdb, then issues
476+
`blkdiscard -z` (BLKZEROOUT ioctl), and asserts that:
477+
- sysfs /queue/write_zeroes_max_bytes is non-zero (feature negotiated)
478+
- the region reads back as zeros after the operation
479+
The same workload is then run against a snapshot-restored VM, which
480+
catches `persist::restore()` populating the write-zeroes ConfigSpace
481+
fields wrong (the restored guest would otherwise see a zero
482+
write_zeroes_max_bytes and the workload would silently regress).
483+
484+
Note: we deliberately do NOT assert that the `write_zeroes_count` metric
485+
increased. The Linux kernel's `blkdev_issue_zeroout()` may issue either
486+
`REQ_OP_WRITE_ZEROES` (counted) or fall back to plain zero-page writes
487+
(counted as `write_count`) depending on internal heuristics; both
488+
result in the device contents reading as zeros. Direct WRITE_ZEROES
489+
request handling is covered by the unit tests.
490+
"""
491+
vm = uvm_plain_any
492+
vm.spawn()
493+
vm.basic_config()
494+
vm.add_net_iface()
495+
fs = drive_tools.FilesystemFile(os.path.join(vm.fsfiles, "wz_test"), size=64)
496+
vm.add_drive("wz_disk", fs.path, is_read_only=False, io_engine=io_engine)
497+
vm.start()
498+
499+
_exercise_write_zeroes(vm.ssh)
500+
501+
snapshot = vm.snapshot_full()
502+
vm = microvm_factory.build_from_snapshot(snapshot)
503+
_exercise_write_zeroes(vm.ssh)
504+
505+
metrics = vm.flush_metrics()
506+
assert metrics["block"]["execute_fails"] == 0
507+
508+
509+
def test_write_zeroes_not_advertised_for_read_only(uvm_plain_any, io_engine):
510+
"""
511+
Verify VIRTIO_BLK_F_WRITE_ZEROES is NOT advertised for read-only devices.
512+
513+
The kernel exposes the negotiated feature via
514+
/sys/block/<dev>/queue/write_zeroes_max_bytes; 0 means not supported.
515+
"""
516+
vm = uvm_plain_any
517+
vm.spawn()
518+
vm.basic_config()
519+
vm.add_net_iface()
520+
521+
fs = drive_tools.FilesystemFile(os.path.join(vm.fsfiles, "ro_wz_disk"), size=64)
522+
vm.add_drive("ro_wz_disk", fs.path, is_read_only=True, io_engine=io_engine)
523+
vm.start()
524+
525+
_, stdout, _ = vm.ssh.check_output(
526+
"cat /sys/block/vdb/queue/write_zeroes_max_bytes"
527+
)
528+
assert stdout.strip() == "0", (
529+
f"Expected write_zeroes_max_bytes=0 for read-only device, got: {stdout.strip()}"
530+
)
531+
532+

0 commit comments

Comments
 (0)