block: qcow: Use aligned I/O in QcowSync

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>
This commit is contained in:
Anatol Belski
2026-04-15 12:18:06 +02:00
committed by Rob Bradford
parent f50b5ab1f2
commit fd8495b342
+49 -9
View File
@@ -22,7 +22,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, gather_from_iovecs_into,
pread_exact, pwrite_all, scatter_to_iovecs, zero_fill_iovecs,
}; };
pub struct QcowDiskSync { pub struct QcowDiskSync {
@@ -153,6 +154,8 @@ pub struct QcowSync {
/// See the backing_file field on QcowDiskSync. /// See the backing_file field on QcowDiskSync.
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,
eventfd: EventFd, eventfd: EventFd,
completion_list: VecDeque<(u64, i32)>, completion_list: VecDeque<(u64, i32)>,
} }
@@ -164,11 +167,13 @@ impl QcowSync {
backing_file: Option<Arc<dyn BackingRead>>, backing_file: Option<Arc<dyn BackingRead>>,
sparse: bool, sparse: bool,
) -> Self { ) -> Self {
let alignment = data_file.file().alignment();
QcowSync { QcowSync {
metadata, metadata,
data_file, data_file,
backing_file, backing_file,
sparse, sparse,
alignment,
eventfd: EventFd::new(libc::EFD_NONBLOCK) eventfd: EventFd::new(libc::EFD_NONBLOCK)
.expect("Failed creating EventFd for QcowSync"), .expect("Failed creating EventFd for QcowSync"),
completion_list: VecDeque::new(), completion_list: VecDeque::new(),
@@ -208,12 +213,29 @@ impl AsyncIo for QcowSync {
offset: host_offset, offset: host_offset,
length, length,
} => { } => {
let mut buf = vec![0u8; length as usize]; let len = length as usize;
pread_exact(self.data_file.as_raw_fd(), &mut buf, host_offset) if self.alignment > 0 {
// O_DIRECT, aligned buffer avoids bounce copy.
let mut abuf = AlignedBuf::new(len, self.alignment)
.map_err(AsyncIoError::ReadVectored)?;
aligned_pread(
self.data_file.as_raw_fd(),
abuf.as_mut_slice(len),
host_offset,
self.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 {
// No O_DIRECT, plain buffer is fine.
let mut buf = vec![0u8; len];
pread_exact(self.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();
@@ -287,10 +309,28 @@ impl AsyncIo for QcowSync {
ClusterWriteMapping::Allocated { ClusterWriteMapping::Allocated {
offset: host_offset, offset: host_offset,
} => { } => {
// SAFETY: iovecs point to valid guest memory buffers if self.alignment > 0 {
let buf = unsafe { gather_from_iovecs(iovecs, buf_offset, count) }; // O_DIRECT, gather directly into aligned buffer.
pwrite_all(self.data_file.as_raw_fd(), &buf, host_offset) let mut abuf = AlignedBuf::new(count, self.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(
self.data_file.as_raw_fd(),
abuf.as_slice(count),
host_offset,
self.alignment,
)
.map_err(AsyncIoError::WriteVectored)?; .map_err(AsyncIoError::WriteVectored)?;
} else {
// No O_DIRECT, plain buffer is fine.
// SAFETY: iovecs point to valid guest memory buffers
let buf = unsafe { gather_from_iovecs(iovecs, buf_offset, count) };
pwrite_all(self.data_file.as_raw_fd(), &buf, host_offset)
.map_err(AsyncIoError::WriteVectored)?;
}
} }
} }
buf_offset += count; buf_offset += count;