Write a known data pattern to a QCOW2 image, convert all allocated
clusters to compressed format using compress_allocated_clusters,
reopen the image through QcowDiskSync, and verify that reading back
the full cluster returns the original data.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that decompress_cluster returns EIO when given invalid
compressed data that the decoder cannot process.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Compress a known 64K buffer with raw deflate, pass it through
decompress_cluster with ZlibDecoder, and verify the output matches
the original data.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that pread_alloc returns the correct data for a full read
from the start and a partial read at an arbitrary offset. Also
confirm that reading past the end of file produces an error.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Add a test utility that converts standard uncompressed clusters in
a QCOW2 image into compressed clusters in place. It walks the L1/L2
tables, compresses each allocated cluster with raw deflate, appends
the compressed payload at the end of the file, and rewrites the L2
entry with the compressed layout.
This enables end to end testing of the compressed read path without
external tools like qemu-img.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
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>
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>
Add two reusable helpers for the compressed cluster read path:
- pread_alloc(fd, offset, len) allocates a buffer and fills it with
pread_exact, returning the owned Vec.
- decompress_cluster(compressed, cluster_size, decoder) allocates the
output buffer, decodes via the Decoder trait, and validates that the
decoder produced exactly cluster_size bytes.
These will be used by QcowSync, QcowAsync, Qcow2Backing, and the
legacy QcowFile.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
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>
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>
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>
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>
Verify that QcowAsync reports the default SECTOR_SIZE alignment
when O_DIRECT is not active.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Block devices (LVM volumes, loop devices, RBD, etc.) cannot be resized
via ftruncate - they are resized externally. When vm.resize-disk is
called for a block device backend, verify the device size matches the
requested size instead of attempting ftruncate.
This enables the resize-disk API to work with block device backends by
validating the externally-resized device matches the expected size.
Signed-off-by: Vincent Thomas <vincent@v-thomas.com>
Verify that AlignedBuf rounds the allocation size up to the
requested alignment. Passes under miri.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Test AlignedBuf with 512 and 4096 byte alignment. Verify pointer
alignment, zero initialization, and write/read round trip. Passes
under miri.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Exercise both aligned_pread and aligned_pwrite with 4096 byte
alignment instead of 512. Verify written data and that surrounding
regions are preserved.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Write at offset 100 with alignment 512 so the read modify write
path is exercised. Verify the written region and that surrounding
data is preserved.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Write 4096 bytes via plain Vec<u8> whose address is not guaranteed
to be aligned. The bounce buffer path copies data into an aligned
allocation before the syscall. Read back with pread_exact to verify
data integrity.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Write 4096 bytes of pattern data at offset 0 using AlignedBuf
and verify data integrity via plain pread_exact. All parameters
are naturally aligned to 512 so the fast path is exercised.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Test that aligned_pread handles a non aligned offset by
rounding down, reading an aligned region, and returning the
correct slice from within the bounce buffer.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Test that aligned_pread correctly uses a bounce buffer when
the caller buffer address is not aligned.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Test that aligned_pread takes the fast path when buffer
address, length, and offset are all properly aligned.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Exercise scatter/gather with multiple iovecs per operation,
covering both the standard and direct_io paths. Write uses
3 iovecs with distinct patterns, read uses 3 iovecs with
different sizes, then reassembles and compares.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Add direct_io variants for suitable tests by extracting
test bodies into _impl(direct_io: bool) functions. Each
original test calls _impl(false) and a new _direct_io test
calls _impl(true).
When direct_io is true, RawFile probes alignment and QcowSync
exercises the AlignedBuf and bounce buffer paths in
read_vectored and write_vectored.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
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>
Store the alignment from the data file in QcowSync. Use AlignedBuf
directly in read_vectored and write_vectored as the intermediate
buffer so that aligned_pread/aligned_pwrite can skip the bounce
copy when offset and length are naturally aligned.
Use gather_from_iovecs_into to gather iovec data directly into the
aligned buffer.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
When the data file is opened with O_DIRECT, buffer address, length,
and file offset must satisfy the device alignment.
Add AlignedBuf RAII wrapper and aligned_pread/aligned_pwrite helpers
in qcow_common that use bounce buffers when alignment constraints
are not met. For writes with misaligned offset, a read modify write
is performed on the aligned region.
gather_from_iovecs_into gathers iovec data directly into a caller
provided buffer, avoiding an intermediate Vec allocation.
Fixes: #8007
Signed-off-by: CMGS <ilskdw@gmail.com>
Co-authored-by: Anatol Belski <anbelski@linux.microsoft.com>
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Add pub fn alignment() to RawFile so that callers can
query the O_DIRECT buffer alignment requirement probed
at file open time.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
The old name read as 'metadata for a QCOW2 backing file' rather
than what it actually is: a QCOW2 backing file reader. Rename to
Qcow2Backing to parallel RawBacking and clarify intent.
Suggested-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
The address that is passed from the guest should be treated as
untrusted. Currently an invalid address will panic the VMM. This only
allows the guest to hurt itself, but we shouldn't have the VMM crashing.
Instead let's return an error if possible or invalidate the queue if it
happen during setup.
The data flow from guest to translate_gva/translate_gpa is:
1. Guest writes a raw u64 address into a virtio descriptor in the
shared descriptor table (guest memory).
2. The virtio-queue crate reads this descriptor via read_obj() and
returns the addr field as-is in a GuestAddress — no validation.
3. Device code calls .translate_gva(access_platform, len) on the
GuestAddress.
4. With IOMMU (access_platform is Some): the address is an IOVA that
must be translated to a GPA via the IOMMU mapping table. If the
guest provides an unmapped IOVA, translation returns Err.
Previously, .unwrap() here panicked the VMM.
5. Without IOMMU (access_platform is None): translate_gva is a no-op
(returns self). The raw address flows to GuestMemory::read_obj()
which validates it — out-of-range addresses return
Err(InvalidGuestAddress), so no host memory corruption is possible.
Signed-off-by: Dylan Reid <dgreid@fb.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Marker supertrait combining all composable capability traits.
QcowDiskAsync now satisfies the full DiskFile contract.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
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>