block: qcow: Drop own O_DIRECT alignment handling

The qcow workers carried their own O_DIRECT alignment requirement
and bounced unaligned cluster accesses through AlignedBuffer. Now
that the data file is an AlignedFile that handles O_DIRECT
transparently, the qcow layer can read and write through plain
buffers and let AlignedFile perform the aligned bounce.

Remove the alignment field and the per cluster AlignedBuffer RMW
branches from both the sync and async workers. The async io_uring
fast path still needs to avoid submitting unaligned guest iovecs
under O_DIRECT, so gate it on is_direct rather than on a stored
alignment value.

Drop the QcowAsync alignment override so it reports the trait
default sector size, matching QcowSync. qcow never submits guest
iovecs to the kernel under O_DIRECT, so reporting a larger value
only forced the request layer into an extra bounce buffer.

This adds one buffer copy per unaligned O_DIRECT cluster but moves
all alignment handling into a single place. The buffered path is
unchanged.

With qcow no longer the only caller, AlignedBuffer::read_exact_from
becomes dead code, so remove it and switch its tests to read_from.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-06-19 22:24:30 +02:00
committed by Rob Bradford
parent 6633072a28
commit 3f20fd0759
3 changed files with 37 additions and 116 deletions

View File

@@ -101,12 +101,6 @@ impl AlignedBuffer {
unsafe { slice::from_raw_parts_mut(self.ptr, self.aligned_len) }
}
/// Read the full aligned region from `f` into this buffer.
pub fn read_exact_from(&mut self, f: &impl FileExt) -> io::Result<()> {
let offset = self.aligned_offset;
f.read_exact_at(self.full_mut_slice(), offset)
}
/// Read into the buffer from `f`, tolerating a short read at EOF.
///
/// Returns the number of caller-logical bytes now valid in `as_slice()`,
@@ -168,7 +162,7 @@ mod tests {
let alignment = 512;
let mut abuf = AlignedBuffer::new(0, size, alignment).unwrap();
abuf.read_exact_from(tf.as_file()).unwrap();
abuf.read_from(tf.as_file()).unwrap();
let expected: Vec<u8> = (0..size).map(|i| (i % 251) as u8).collect();
assert_eq!(abuf.as_slice(), &expected[..]);
@@ -179,7 +173,7 @@ mod tests {
let tf = create_pattern_file(512);
let mut abuf = AlignedBuffer::new(100, 0, 512).unwrap();
abuf.read_exact_from(tf.as_file()).unwrap();
abuf.read_from(tf.as_file()).unwrap();
abuf.write_to(tf.as_file()).unwrap();
assert!(abuf.as_slice().is_empty());
@@ -195,7 +189,7 @@ mod tests {
let offset = 100u64;
let len = 200usize;
let mut abuf = AlignedBuffer::new(offset, len, alignment).unwrap();
abuf.read_exact_from(tf.as_file()).unwrap();
abuf.read_from(tf.as_file()).unwrap();
let expected: Vec<u8> = (offset as usize..offset as usize + len)
.map(|i| (i % 251) as u8)
@@ -230,7 +224,7 @@ mod tests {
let data: Vec<u8> = (0..len).map(|i| ((i + 1) % 239) as u8).collect();
let mut abuf = AlignedBuffer::new(offset, len, alignment).unwrap();
abuf.read_exact_from(tf.as_file()).unwrap();
abuf.read_from(tf.as_file()).unwrap();
abuf.as_mut_slice().copy_from_slice(&data);
abuf.write_to(tf.as_file()).unwrap();
@@ -256,12 +250,12 @@ mod tests {
let data: Vec<u8> = (0..len).map(|i| ((i + 1) % 239) as u8).collect();
let mut abuf = AlignedBuffer::new(offset, len, alignment).unwrap();
abuf.read_exact_from(tf.as_file()).unwrap();
abuf.read_from(tf.as_file()).unwrap();
abuf.as_mut_slice().copy_from_slice(&data);
abuf.write_to(tf.as_file()).unwrap();
let mut abuf = AlignedBuffer::new(offset, len, alignment).unwrap();
abuf.read_exact_from(tf.as_file()).unwrap();
abuf.read_from(tf.as_file()).unwrap();
assert_eq!(abuf.as_slice(), &data[..]);
let mut whole = vec![0u8; file_size];