Thanks to visit codestin.com
Credit goes to github.com

Skip to content

block: qcow: Fix O_DIRECT EINVAL in async io_uring read path - #8051

Merged
rbradford merged 7 commits into
cloud-hypervisor:mainfrom
weltling:qcow-odirect-async-alignment
Apr 17, 2026
Merged

rbradford merged 7 commits into
cloud-hypervisor:mainfrom
weltling:qcow-odirect-async-alignment

Conversation

@weltling

Copy link
Copy Markdown
Member

Fixes EINVAL errors when booting UEFI guests from QCOW2 images with direct=on on devices where O_DIRECT requires alignment larger than 512 bytes.

QcowAsync::resolve_read() has a fast path that submits guest iovecs directly to io_uring. When O_DIRECT is active on a device with 4096 byte sectors, guest reads smaller than the sector size (for example, 512 byte UEFI firmware reads) are rejected by the kernel because the I/O length is not a multiple of the required alignment.

The AsyncIo::alignment() trait method was also not overridden, so execute_async() in the virtio block layer used the default 512, which is not enough to correctly bounce misaligned guest memory pointers on 4K sector devices.

Fix:

  • Override AsyncIo::alignment() on QcowAsync to report the actual device alignment (max(file_alignment, SECTOR_SIZE)), so that execute_async() correctly handles pointer alignment.
  • Guard the io_uring fast path in resolve_read() with an alignment check. When O_DIRECT is active, reads are routed through scatter_read_sync() which uses AlignedBuf and aligned_pread to satisfy all three O_DIRECT constraints (buffer address, offset, and length alignment).

Tests:

  • Four unit tests covering alignment reporting and O_DIRECT read/write correctness
  • One integration test that boots a UEFI Linux guest from a QCOW2 image with direct=on

Ref #8007.

See #8050 for a broader discussion on centralizing alignment handling across all block backends.

@weltling
weltling requested a review from a team as a code owner April 16, 2026 22:41
@weltling
weltling force-pushed the qcow-odirect-async-alignment branch from 5a5a253 to 08d551a Compare April 17, 2026 06:54
Comment thread cloud-hypervisor/tests/integration.rs Outdated
"truncate failed"
);

let loop_dev = exec_host_command_output(&format!(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude tells me there is a create_loop_device function that does this using ioctls rather than needing to shell out. Maybe you could use that (and change the existing use too.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Switched to create_loop_device now and fixed the other test. I was considering it but the other use is what actually misled me.

Thanks

Boot a UEFI guest from a QCOW2 image with direct=on to exercise
the aligned I/O write path during early firmware operations.

Signed-off-by: Anatol Belski <[email protected]>
Verify that QcowAsync reports the default SECTOR_SIZE alignment
when O_DIRECT is not active.

Signed-off-by: Anatol Belski <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Use the ioctl based create_loop_device() helper instead of
shelling out to losetup in the file backed 4K alignment test.

Signed-off-by: Anatol Belski <[email protected]>
@weltling
weltling force-pushed the qcow-odirect-async-alignment branch from 08d551a to 250cd64 Compare April 17, 2026 07:29
@rbradford
rbradford enabled auto-merge April 17, 2026 07:35
@rbradford
rbradford added this pull request to the merge queue Apr 17, 2026
Merged via the queue into cloud-hypervisor:main with commit e38f006 Apr 17, 2026
38 checks passed
@weltling
weltling deleted the qcow-odirect-async-alignment branch April 17, 2026 10:28
@rbradford rbradford moved this to ✅ Done in Cloud Hypervisor Roadmap Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants