Some block devices (ZFS volume) may require BLKDISCARD and BLKZEROOUT
ioctls for discard and write_zeroes operations respectively.
There is no good way to probe whether fallocate is supported on a block
device. Arguably, punch_hole and write_zeroes are rare. Instead of
having a complex scheme for the IO uring backend, we force it to always
use ioctls. The code can be changed if the synchronized ioctls become a
performance issue.
Changes:
- Detect block devices at construction time
- Use BLKDISCARD ioctl for punch_hole (discard) on block devices
- Use BLKZEROOUT ioctl for write_zeroes on block devices
- Add BLKDISCARD/BLKZEROOUT to VirtioBlock seccomp whitelist
- Keep fallocate() path for regular files (no behavior change)
- Consolidate some helper functions to the new sparse module
Signed-off-by: Wei Liu <liuwe@microsoft.com>
probe_sparse_support() and DiskTopology::is_block_device() each carry
their own copy of the same fstat()+S_IFMT dance to ask "is this fd a
block device?". Hoist a single pub helper
pub(crate) fn is_block_device(fd: RawFd) -> bool
into block::lib and route both call sites through it. Drop the
MaybeUninit gymnastics in favour of mem::zeroed() since libc::stat is
POD.
Drop DiskTopology::is_block_device since it is now just a one line
wrapper around the new helper function.
Pure refactor in preparation for the BLKDISCARD/BLKZEROOUT support,
which needs the same probe in three more backends.
Signed-off-by: Wei Liu <liuwe@microsoft.com>
submit_batch_requests pushed each BatchRequest into the io_uring SQ in
turn and used `?` to bail on the first push failure.
Leaving the initial SQEs visible to the kernel — but submitter.submit()
was never called, and every other call site in this file gates submit()
behind a preceding sq.push() that now also fails on the full ring.
This could allow a guest to DoS it's own queue or worse if the buffer is
freed early.
Signed-off-by: Dylan Reid <dgreid@fb.com>
The bounce buffer for an unaligned descriptor was allocated in
execute_async and leaked on error paths, even though, for the sync case
the kernel already had a pointer to the buffer.
Clean this up by moving ownership of the buffer to the AlignedOperation
type. To make it actually safe, stop stashing a guest memory pointer for
the duration of the op. Instead, save the guest address and pass guest
memory back to the complete function.
Signed-off-by: Dylan Reid <dgreid@fb.com>
Keep the underlying io_uring submission queue push error in raw async
I/O paths instead of replacing it with a generic full-queue message.
Signed-off-by: Keith Adler <kadler@cloudflare.com>
Request::execute and Request::execute_async checked each data descriptor
against `disk_nsectors` using the request's fixed start sector. With
sector = disk_nsectors-1 and N descriptors of 512 bytes each, every
descriptor passed (top = disk_nsectors) but the vectored I/O
collectively read/wrote N*512 bytes starting at the last sector — N-1
sectors past EOF.
For the io_uring/aio raw backends this lets the guest extend the host
disk image beyond its provisioned size, exhausting the host filesystem.
For fixed-VHD images (footer at end of file) the same chain overwrites
the footer with guest-controlled bytes, corrupting the disk image.
Replace the per-descriptor check with a chain-wide check_data_bounds().
Pre-validating the entire request before beginning the operation avoids
having to unroll a partial submit.
Signed-off-by: Dylan Reid <dgreid@fb.com>
Verify that a freshly created sparse QCOW2 image reports a
physical size smaller than its logical size.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that try_clone preserves the backend dispatch for both
sync and io_uring backends.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that the sync backend disables batch requests and the
io_uring backend enables them.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Delete QcowDiskSync and QcowDiskAsync wrapper structs along with
their DiskFile trait impls. Only the AsyncIo worker structs
QcowSync and QcowAsync remain. Reduce module visibility of
qcow_sync and qcow_async to pub(crate).
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Replace QcowDiskSync and QcowDiskAsync constructors in the
qcow_sync and qcow_async test modules with QcowDisk::new,
passing use_io_uring=false and use_io_uring=true respectively.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Update open_qcow2 to construct QcowDisk instead of choosing
between QcowDiskAsync and QcowDiskSync. The backend decision
is now made inside QcowDisk::create_async_io.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Introduce QcowDisk, a unified DiskFile implementation for QCOW2
disk images that handles backend selection at runtime via a
use_io_uring flag, matching the pattern used by FixedVhdDisk.
The wrapper delegates to QcowSync or QcowAsync based on the flag
and includes a compile time guard that returns an error when
io_uring is requested but the feature is not enabled.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
The physical size of a fixed VHD is the data region plus the 512
byte footer. Verify it differs from the logical size.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Fixed VHDs do not support resize. Verify the error is returned.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that a cloned disk produces the same async I/O backend
as the original for both sync and io_uring paths.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that create_async_io dispatches to the correct backend
depending on use_io_uring. The sync backend does not support
batch requests, while the io_uring backend does.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that FixedVhdDisk::new with the sync backend reads the VHD
footer and reports the correct logical size.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Update open_fixed_vhd to construct FixedVhdDisk instead of choosing
between FixedVhdDiskAsync and FixedVhdDiskSync. The io_uring decision
is now made inside FixedVhdDisk::create_async_io().
Remove FixedVhdDiskSync and FixedVhdDiskAsync DiskFile wrapper structs
from fixed_vhd_sync.rs and fixed_vhd_async.rs. Only the FixedVhdSync
and FixedVhdAsync AsyncIo worker structs remain in those files.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Introduce FixedVhdDisk as a single DiskFile wrapper around FixedVhd.
It accepts a use_io_uring flag at construction time and dispatches to
FixedVhdSync or FixedVhdAsync inside create_async_io() accordingly.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that physical size of a sparse file is less than
logical size.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that try_clone preserves the backend variant for
each RawBackend: sync, AIO and io_uring.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify each RawBackend variant stores correctly and
create_async_io dispatches to the matching constructor.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that RawDisk with the sync backend reports the correct
logical size for a temporary file. Introduce make_raw_file helper
and TEST_SIZE constant for reuse in subsequent tests.
Assisted-by: Claude:Opus-4.6
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Update open_raw to construct RawDisk instead of choosing between
RawFileDisk, RawFileDiskSync and RawFileDiskAio. The backend decision
is now made inside RawDisk::create_async_io.
Remove the DiskFile wrapper structs from raw_sync.rs, raw_async.rs
and raw_async_aio.rs. Only the AsyncIo worker structs remain in those
files. Reduce their module visibility to pub(crate).
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Add the DiskFile marker and AsyncDiskFile with try_clone and
create_async_io. The dispatch creates RawFileSync, RawFileAsync,
or RawFileAsyncAio depending on the backend selected at
construction. Alignment handling is left to the workers as is,
to be centralized separately per #8050.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Use the block device aware resize from RawFileDisk. For block
devices, verify the externally set size matches instead of
calling ftruncate. For regular files, truncate as usual.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Introduce the unified DiskFile wrapper for raw disk images. The
RawBackend enum selects between sync, io_uring, and AIO backends
at construction time.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
The trait has no implementations. All format backends now implement
the composable disk_file traits instead. The DiskFileError and
AsyncIo types remain in async_io.rs as they are still used by
format workers.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
All disk format backends now implement AsyncFullDiskFile directly.
The DiskBackend enum that dispatched between Legacy and Next arms
is no longer needed since the factory returns trait objects and vmm
no longer constructs format types manually.
Replace DiskBackend with Box<dyn AsyncFullDiskFile> in the Block
struct and its constructor. Remove the DiskBackend::Next wrapping
in device_manager and the fuzz target.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that open_disk() falls back to synchronous backend when both
io_uring and AIO are disabled, and that the returned disk reports
the correct logical size.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Create a minimal QCOW2 temp file via QcowFile::new() and verify
that open_disk() detects it as ImageType::Qcow2.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that open_disk() detects a plain temporary file as RAW and
returns a working backend with synchronous fallback.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that open_disk() returns BlockErrorKind::Io when the disk
image file does not exist.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Introduce block::factory with open_disk() as the single entry point
for opening disk images. It handles file opening, format detection,
async I/O probing, and backend construction.
Per format helpers (open_fixed_vhd, open_raw, open_qcow2, open_vhdx)
prefer io_uring over AIO over synchronous fallback. Warnings only
fire when a backend was eligible but its runtime probe failed, not
when the user intentionally disabled it.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
There is no reason for most of the Request struct to be writable from
anywhere in the codebase. Encapsulate it.
Use getter functions for access outside the request module. Replace the
trivial setter for the writeback field with direct assignment.
No functional change intended.
Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
The new_ prefix in Rust conventionally denotes constructors that return
Self (e.g. Vec::new(), File::new()). AsyncDiskFile::new_async_io does
not return Self. It is a factory method that constructs and returns a
Box<dyn AsyncIo> worker bound to the disk file descriptor and
metadata. The create_ prefix communicates this: the caller receives
a freshly constructed object of a different type.
This rename touches every format backend in block plus two external
callers in virtio-devices and performance-metrics. Every change is a
mechanical s/new_async_io/create_async_io/ substitution. No functional
change.
Ref: #7877 (task 3.2.8)
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Change RawFileAsync::new() from std::io::Result to BlockResult,
aligning it with RawFileAsyncAio::new(). Each fallible call inside
the constructor now maps to BlockErrorKind::Io explicitly.
FixedVhdAsync::new() follows the same change since its only
fallible operation is constructing a RawFileAsync. The intermediate
DiskFileError::NewAsyncIo wrapping in both new_async_io() call
sites is no longer needed and is removed.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Replace the blanket BlockErrorKind::Io mapping with an explicit
match on all VhdxError variants:
NotVhdx, ParseVhdxHeader, ParseVhdxMetadata,
ParseVhdxRegionEntry => InvalidFormat
ReadBatEntry => CorruptImage
ReadFailed, WriteFailed => Io
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Vhdx::physical_size() can only return Error::GetFileMetadata.
Replace the catch-all arm with unreachable!() so future error
variants are not silently mapped to a generic Io classification.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Replace .unwrap() on FixedVhd::logical_size() with map_err in
DiskSize::logical_size() and new_async_io() for both FixedVhdDiskSync
and FixedVhdDiskAsync. The call is infallible today but unwrap hides
that assumption from callers and would panic if it ever changed.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
FixedVhd::physical_size() can only return Error::GetFileMetadata.
Replace the catch-all arm with unreachable!() so future error
variants are not silently mapped to a generic Io classification.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Write a known data pattern through QcowFile, compress all clusters
in place, reopen and read back via the seek based file_read path.
This covers the decompress_l2_cluster code path used by QcowFile
which is separate from the pread based path in QcowSync/QcowAsync.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Write a known data pattern, compress all clusters in place, reopen
through QcowDiskAsync, and read back from four concurrent queues
on separate threads. Each queue independently decompresses and
returns the correct data, validating the Arc<dyn Decoder> sharing.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>