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>
This commit is contained in:
Anatol Belski
2026-04-15 12:18:36 +02:00
committed by Rob Bradford
parent fd8495b342
commit cfa60a95b9
+58 -10
View File
@@ -27,7 +27,8 @@ use crate::qcow::metadata::{
use crate::qcow::qcow_raw_file::QcowRawFile; use crate::qcow::qcow_raw_file::QcowRawFile;
use crate::qcow::{MAX_NESTING_DEPTH, RawFile, parse_qcow}; use crate::qcow::{MAX_NESTING_DEPTH, RawFile, parse_qcow};
use crate::qcow_common::{ use crate::qcow_common::{
gather_from_iovecs, pread_exact, pwrite_all, scatter_to_iovecs, zero_fill_iovecs, AlignedBuf, aligned_pread, aligned_pwrite, gather_from_iovecs_into, pread_exact, pwrite_all,
scatter_to_iovecs, zero_fill_iovecs,
}; };
use crate::{BatchRequest, RequestType, disk_file}; use crate::{BatchRequest, RequestType, disk_file};
@@ -172,6 +173,8 @@ pub struct QcowAsync {
data_file: QcowRawFile, data_file: QcowRawFile,
backing_file: Option<Arc<dyn BackingRead>>, backing_file: Option<Arc<dyn BackingRead>>,
sparse: bool, sparse: bool,
/// O_DIRECT alignment requirement (0 = no alignment needed).
alignment: usize,
io_uring: IoUring, io_uring: IoUring,
eventfd: EventFd, eventfd: EventFd,
completion_list: VecDeque<(u64, i32)>, completion_list: VecDeque<(u64, i32)>,
@@ -185,6 +188,7 @@ impl QcowAsync {
sparse: bool, sparse: bool,
ring_depth: u32, ring_depth: u32,
) -> io::Result<Self> { ) -> io::Result<Self> {
let alignment = data_file.file().alignment();
let io_uring = IoUring::new(ring_depth)?; let io_uring = IoUring::new(ring_depth)?;
let eventfd = EventFd::new(libc::EFD_NONBLOCK)?; let eventfd = EventFd::new(libc::EFD_NONBLOCK)?;
io_uring.submitter().register_eventfd(eventfd.as_raw_fd())?; io_uring.submitter().register_eventfd(eventfd.as_raw_fd())?;
@@ -194,6 +198,7 @@ impl QcowAsync {
data_file, data_file,
backing_file, backing_file,
sparse, sparse,
alignment,
io_uring, io_uring,
eventfd, eventfd,
completion_list: VecDeque::new(), completion_list: VecDeque::new(),
@@ -241,6 +246,7 @@ impl AsyncIo for QcowAsync {
offset as u64, offset as u64,
iovecs, iovecs,
total_len, total_len,
self.alignment,
)? { )? {
let fd = self.data_file.as_raw_fd(); let fd = self.data_file.as_raw_fd();
let (submitter, mut sq, _) = self.io_uring.split(); let (submitter, mut sq, _) = self.io_uring.split();
@@ -285,6 +291,7 @@ impl AsyncIo for QcowAsync {
&self.metadata, &self.metadata,
&self.data_file, &self.data_file,
&self.backing_file, &self.backing_file,
self.alignment,
)?; )?;
let total_len: usize = iovecs.iter().map(|v| v.iov_len).sum(); let total_len: usize = iovecs.iter().map(|v| v.iov_len).sum();
@@ -377,6 +384,7 @@ impl AsyncIo for QcowAsync {
req.offset as u64, req.offset as u64,
&req.iovecs, &req.iovecs,
total_len, total_len,
self.alignment,
)? { )? {
let fd = self.data_file.as_raw_fd(); let fd = self.data_file.as_raw_fd();
// SAFETY: fd is valid and iovecs point to valid guest memory. // SAFETY: fd is valid and iovecs point to valid guest memory.
@@ -408,6 +416,7 @@ impl AsyncIo for QcowAsync {
&self.metadata, &self.metadata,
&self.data_file, &self.data_file,
&self.backing_file, &self.backing_file,
self.alignment,
)?; )?;
sync_completions.push((req.user_data, total_len as i32)); sync_completions.push((req.user_data, total_len as i32));
} }
@@ -448,6 +457,7 @@ impl QcowAsync {
address: u64, address: u64,
iovecs: &[libc::iovec], iovecs: &[libc::iovec],
total_len: usize, total_len: usize,
alignment: usize,
) -> AsyncIoResult<Option<u64>> { ) -> AsyncIoResult<Option<u64>> {
let has_backing = backing_file.is_some(); let has_backing = backing_file.is_some();
let mappings = metadata let mappings = metadata
@@ -464,7 +474,7 @@ impl QcowAsync {
return Ok(Some(*host_offset)); return Ok(Some(*host_offset));
} }
Self::scatter_read_sync(mappings, iovecs, data_file, backing_file)?; Self::scatter_read_sync(mappings, iovecs, data_file, backing_file, alignment)?;
Ok(None) Ok(None)
} }
@@ -474,6 +484,7 @@ impl QcowAsync {
iovecs: &[libc::iovec], iovecs: &[libc::iovec],
data_file: &QcowRawFile, data_file: &QcowRawFile,
backing_file: &Option<Arc<dyn BackingRead>>, backing_file: &Option<Arc<dyn BackingRead>>,
alignment: usize,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
let mut buf_offset = 0usize; let mut buf_offset = 0usize;
for mapping in mappings { for mapping in mappings {
@@ -489,12 +500,27 @@ impl QcowAsync {
offset: host_offset, offset: host_offset,
length, length,
} => { } => {
let mut buf = vec![0u8; length as usize]; let len = length as usize;
pread_exact(data_file.as_raw_fd(), &mut buf, host_offset) if alignment > 0 {
let mut abuf =
AlignedBuf::new(len, alignment).map_err(AsyncIoError::ReadVectored)?;
aligned_pread(
data_file.as_raw_fd(),
abuf.as_mut_slice(len),
host_offset,
alignment,
)
.map_err(AsyncIoError::ReadVectored)?; .map_err(AsyncIoError::ReadVectored)?;
// SAFETY: iovecs point to valid guest memory buffers. // SAFETY: iovecs point to valid guest memory buffers.
unsafe { scatter_to_iovecs(iovecs, buf_offset, &buf) }; unsafe { scatter_to_iovecs(iovecs, buf_offset, abuf.as_slice(len)) };
buf_offset += length as usize; } else {
let mut buf = vec![0u8; len];
pread_exact(data_file.as_raw_fd(), &mut buf, host_offset)
.map_err(AsyncIoError::ReadVectored)?;
// SAFETY: iovecs point to valid guest memory buffers.
unsafe { scatter_to_iovecs(iovecs, buf_offset, &buf) };
}
buf_offset += len;
} }
ClusterReadMapping::Compressed { data } => { ClusterReadMapping::Compressed { data } => {
let len = data.len(); let len = data.len();
@@ -528,6 +554,7 @@ impl QcowAsync {
metadata: &QcowMetadata, metadata: &QcowMetadata,
data_file: &QcowRawFile, data_file: &QcowRawFile,
backing_file: &Option<Arc<dyn BackingRead>>, backing_file: &Option<Arc<dyn BackingRead>>,
alignment: usize,
) -> AsyncIoResult<()> { ) -> AsyncIoResult<()> {
let total_len: usize = iovecs.iter().map(|v| v.iov_len).sum(); let total_len: usize = iovecs.iter().map(|v| v.iov_len).sum();
let cluster_size = metadata.cluster_size(); let cluster_size = metadata.cluster_size();
@@ -561,10 +588,31 @@ impl QcowAsync {
ClusterWriteMapping::Allocated { ClusterWriteMapping::Allocated {
offset: host_offset, offset: host_offset,
} => { } => {
// SAFETY: iovecs point to valid guest memory buffers. if alignment > 0 {
let buf = unsafe { gather_from_iovecs(iovecs, buf_offset, count) }; // O_DIRECT, gather directly into aligned buffer.
pwrite_all(data_file.as_raw_fd(), &buf, host_offset) let mut abuf = AlignedBuf::new(count, alignment)
.map_err(AsyncIoError::WriteVectored)?;
// SAFETY: iovecs point to valid guest memory buffers
unsafe {
gather_from_iovecs_into(iovecs, buf_offset, abuf.as_mut_slice(count));
}
aligned_pwrite(
data_file.as_raw_fd(),
abuf.as_slice(count),
host_offset,
alignment,
)
.map_err(AsyncIoError::WriteVectored)?; .map_err(AsyncIoError::WriteVectored)?;
} else {
// No O_DIRECT, plain buffer is fine.
let mut buf = vec![0u8; count];
// SAFETY: iovecs point to valid guest memory buffers.
unsafe {
gather_from_iovecs_into(iovecs, buf_offset, &mut buf);
}
pwrite_all(data_file.as_raw_fd(), &buf, host_offset)
.map_err(AsyncIoError::WriteVectored)?;
}
} }
} }
buf_offset += count; buf_offset += count;