Commit Graph

40 Commits

Author SHA1 Message Date
Ian Klemm
39e253ff9c block: qcow: Preserve WRITE_ZEROES with backing files
QCOW2 empty L2 entries in an overlay mean that reads fall
through to the backing file. Reusing the punch_hole path for
WRITE_ZEROES therefore turns a full-cluster zero operation on an
unallocated overlay cluster into backing data exposure.

Keep discard/punch_hole behavior unchanged, but let WRITE_ZEROES
request a logical-zero marker when the image has a backing file.
ZERO_FLAG entries now read as zeros in both the legacy QcowFile
path and the shared runtime metadata path. Partial writes after
such entries seed new clusters from zeros instead of backing data.

Treat ZERO_FLAG entries as logical holes for SEEK_HOLE/SEEK_DATA.
Empty overlay entries with a backing file still report data because
the data exists in the backing file.

Avoid cluster-sized userspace zero buffers when materializing
zero-flagged clusters by zeroing the allocated host range directly.
This keeps recycled clusters safe without making partial writes
allocate large zero-filled Vecs.

Add regression coverage for legacy QcowFile, QcowSync, direct I/O,
QcowAsync/io_uring overlay paths, and a large-cluster partial-write
case.

Assisted-by: Codex:GPT-5

Signed-off-by: Ian Klemm <hi@ianklemm.de>
2026-05-22 14:26:04 +00:00
Julian Schindel
2b6e9df4e3 block: replace as <pointer> casts with safer alternatives
`as` casts can change mutability, which quickly leads to undefined
behavior.

Signed-off-by: Julian Schindel <mail@arctic-alpaca.de>
2026-05-03 08:38:31 +00:00
Anatol Belski
587093ddfd block: qcow: Remove old wrapper structs, restrict visibility
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>
2026-04-27 11:19:37 +00:00
Anatol Belski
8a77feb813 block: qcow: Update existing tests to use QcowDisk
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>
2026-04-27 11:19:37 +00:00
Anatol Belski
27ee36449c block: qcow: Add unified QcowDisk wrapper
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>
2026-04-27 11:19:37 +00:00
Anatol Belski
21cd13df01 block: Rename AsyncDiskFile::new_async_io to create_async_io
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>
2026-04-21 10:36:52 +00:00
Anatol Belski
365ed236cc block: qcow: Test compressed cluster read via QcowDiskAsync
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>
2026-04-20 18:09:12 +00:00
Anatol Belski
5504ad753a block: qcow: Move compressed read decompression out of lock
Move decompression of compressed QCOW2 clusters out of the metadata
lock. Previously, reading a compressed cluster acquired a write lock
on metadata to perform in place decompression. Now, try_map_read
extracts the compressed layout (host offset, size) under a read lock
and returns it in the ClusterReadMapping::Compressed variant. Each
consumer (QcowSync, QcowAsync, Qcow2Backing, QcowFile) performs the
pread and decompression at the call site without holding any lock,
using the pread_alloc and decompress_cluster helpers.

Create the decoder once in QcowMetadata as Arc<dyn Decoder> and
share it via Arc::clone to QcowAsync, QcowSync, and Qcow2Backing
at construction time. This avoids per read RwLock acquisitions and
heap allocations. Add Send + Sync bounds to the Decoder trait.

This eliminates write lock contention on compressed reads, allowing
them to proceed concurrently with other read operations.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-20 18:09:12 +00:00
Anatol Belski
659f7c17e5 block: qcow: Cache cluster_size in per queue structs
Cache the immutable cluster_size value at construction time in
QcowAsync, QcowSync, and Qcow2Backing. This avoids repeated RwLock
read acquisitions on the hot write and deallocation paths.

Replace QcowMetadata::cluster_offset() calls with inline bitmask
operations using the cached cluster_size. Remove the now unused
cluster_offset() method from QcowMetadata.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-20 18:09:12 +00:00
Anatol Belski
4772235952 block: qcow: Fix O_DIRECT EINVAL in async io_uring path
Override AsyncIo::alignment() to report the actual device sector
size so that execute_async() correctly bounces misaligned guest
memory pointers.

Guard the io_uring fast path in resolve_read() with an alignment
check. When O_DIRECT is active, guest requests can have I/O sizes
smaller than the device sector size (e.g. 512 byte UEFI reads on
a 4096 byte sector device). The kernel rejects these with EINVAL.
Route such reads through scatter_read_sync() which uses AlignedBuf
and aligned_pread to satisfy O_DIRECT size and offset requirements.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-17 08:16:47 +00:00
Anatol Belski
98c533a501 block: qcow: Test O_DIRECT write and read roundtrip
Write 128K of patterned data and read it back with O_DIRECT
active to verify the aligned I/O paths produce correct results.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-17 08:16:47 +00:00
Anatol Belski
e67195ce48 block: qcow: Test sub sector O_DIRECT read
Verify that a 512 byte read from an allocated cluster succeeds
with O_DIRECT. This exercises the synchronous fallback path in
resolve_read() that is taken when alignment is nonzero.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-17 08:16:47 +00:00
Anatol Belski
dd79b1899d block: qcow: Test async alignment() with O_DIRECT
Verify that QcowAsync reports at least SECTOR_SIZE alignment
when O_DIRECT is active. Skipped on filesystems that do not
support O_DIRECT (e.g. tmpfs).

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-17 08:16:47 +00:00
Anatol Belski
854b686293 block: qcow: Test async alignment() returns SECTOR_SIZE
Verify that QcowAsync reports the default SECTOR_SIZE alignment
when O_DIRECT is not active.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-17 08:16:47 +00:00
Anatol Belski
cfa60a95b9 block: qcow: Use aligned I/O in QcowAsync
Store the alignment from the data file in QcowAsync. Use
aligned_pread in scatter_read_sync and aligned_pwrite with
gather_from_iovecs_into in cow_write_sync, matching the
QcowSync approach.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-15 17:28:58 +00:00
Anatol Belski
bb833a90e5 block: qcow_async: Add large sequential I/O test
Write a distinct byte pattern into each of eight consecutive
clusters in a single operation, then read the full range back
and verify per cluster contents.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
072b3a85d2 block: qcow_async: Add write after punch hole test
Write data, punch hole to deallocate, then rewrite the same
range and verify the new contents read back correctly.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
48155c4151 block: qcow: Test async sub cluster write
Add a QcowAsync unit test that writes 4K into the middle of a
cluster, then reads the entire cluster back. Verifies that the
written region matches and surrounding bytes remain zero. This
exercises the COW path where unwritten parts of a newly allocated
cluster must be zero filled.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
6208f3caee block: qcow: Test async read of unallocated region
Add a QcowAsync unit test that reads from a range that was never
written. Verifies the fundamental QCOW contract that unallocated
clusters return zeroes.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
34a9f7246d block: qcow: Test async batch mixed requests
Add a QcowAsync unit test that submits a batch of interleaved
write and read requests via submit_batch_requests. Verifies that
all completions arrive with the correct user_data and that the
read back data matches the written data.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
093922ff24 block: qcow: Test async read spanning cluster boundary
Add a QcowAsync unit test that writes distinct patterns into two
adjacent clusters, then issues a single read spanning the cluster
boundary. Verifies that multi mapping read resolution returns the
correct data from both clusters.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
1e66c144f4 block: qcow: Test async write and read roundtrip
Add a QcowAsync unit test that writes a byte pattern through
write_vectored, reads it back through read_vectored, and verifies
the data matches. This exercises the core async write and read
paths end to end.

Also add an async_write helper for future tests.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
e522d3a0aa block: qcow: Test async write zeroes
Add a QcowAsync unit test for write zeroes completion. The test
verifies that a write zeroes request reports successful completion
and that the zeroed range reads back as zeroes.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
8ffbe9d6fe block: qcow: Test async punch hole
Add a QcowAsync unit test for punch hole completion. The test
verifies that a punch hole request reports successful completion
and that the deallocated range reads back as zeroes.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
b1d126fdbf block: qcow: Implement batch request submission
Implement batch_requests_enabled() and submit_batch_requests() for
QcowAsync. Without batching, each read_vectored call performs its own
io_uring submit() syscall. With batching, the virtio queue handler
collects all pending requests and submits them in a single call,
pushing multiple SQEs before one submit() syscall.

Each request in the batch is classified through the metadata layer.
Requests that hit the fast path (single allocated cluster mapping)
are pushed to the io_uring submission queue. Requests that require
the slow path (compressed, backing, zero fill, or mixed mappings)
are completed synchronously and queued as synthetic completions.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
ffc579b913 block: qcow_async: impl AsyncDiskFile for QcowDiskAsync
try_clone shares the Arc wrapped metadata and backing file.
new_async_io creates a QcowAsync worker with its own io_uring
instance for the given ring depth.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
261361c1b0 block: qcow_async: impl punch_hole and write_zeroes for QcowAsync
Deallocate clusters through QcowMetadata::deallocate_bytes, then
apply the resulting DeallocAction list (punch hole or write zeroes
at host offsets). write_zeroes delegates to punch_hole since
unallocated QCOW2 clusters inherently read as zero.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
bf70c19857 block: qcow_async: impl fsync for QcowAsync
Flush dirty metadata caches and sync the underlying file via
QcowMetadata::flush, then signal synthetic completion.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
a97260c5e0 block: qcow_async: impl write_vectored for QcowAsync
Synchronous per cluster write path - gather guest data from iovecs,
map each cluster through QcowMetadata, and pwrite to the allocated
host offset. Partial cluster writes with a backing file read the
backing data first so map_cluster_for_write can perform COW.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
e7621abfbd block: qcow_async: impl read_vectored for QcowAsync
Single allocated cluster reads are submitted to io_uring for true
async completion. Mixed mapping reads (zero, compressed, backing,
multi cluster) fall back to synchronous pread64 with synthetic
completions.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
85df0fef0d block: qcow_async: impl AsyncIo scaffold for QcowAsync
Add the AsyncIo trait impl with notifier and next_completed_request
filled in. The remaining methods are stubbed with unimplemented
and will be filled in by subsequent commits.

next_completed_request drains io_uring completions first, then
falls back to the synthetic completion list.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
8d684cad98 block: qcow_async: Add QcowAsync struct and constructor
Per queue I/O worker that uses io_uring for asynchronous reads
against fully allocated clusters. The struct holds the shared
metadata, data file, optional backing reader, the io_uring
instance and a synthetic completion list.

Feature gated on io_uring in lib.rs.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
8966607898 block: qcow_async: impl DiskFile for QcowDiskAsync
Marker supertrait combining all composable capability traits.
QcowDiskAsync now satisfies the full DiskFile contract.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
8c3b6cb04b block: qcow_async: impl Resizable for QcowDiskAsync
Delegates to QcowMetadata::resize. Rejects resize when a backing
file is present, same as the sync backend.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
52cd45874a block: qcow_async: impl SparseCapable for QcowDiskAsync
QCOW2 images support both sparse operations and the zero flag.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
fa700f6493 block: qcow_async: impl Geometry for QcowDiskAsync
Uses the default geometry, same as the sync backend.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
91d6356db4 block: qcow_async: impl DiskFd for QcowDiskAsync
Returns a borrowed file descriptor for the underlying data file.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
403b75656c block: qcow_async: impl PhysicalSize for QcowDiskAsync
Queries the underlying raw file for on disk size.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
f777113818 block: qcow_async: impl DiskSize for QcowDiskAsync
Delegates to QcowMetadata::virtual_size, identical to the sync
backend.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00
Anatol Belski
3422a8b258 block: qcow: Add QcowDiskAsync struct stub
Introduce the device level handle for the async QCOW2 backend.

QcowDiskAsync mirrors QcowDiskSync. It parses the image, resolves
the backing chain and wraps QcowMetadata in an Arc for sharing
across virtio queues. No trait impls yet, just the struct,
constructor, Drop and Debug.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2026-04-14 22:11:26 +00:00