block: Drop the middle buffer on the unaligned O_DIRECT path

run_unaligned_operation staged every unaligned request in a plain Vec
and then handed it to AlignedFile, which bounced again through an
aligned buffer. That Vec only gave the operation a contiguous range to
scatter into or gather from, which the aligned buffer already is, so
each slow path request paid for an extra allocation and a full length
copy.

Add read_unaligned and write_unaligned on AlignedFile that own the
single aligned bounce and scatter or gather through a closure over the
staging slice. run_unaligned_operation and the FileExt read_at and
write_at impls both route through them, so the staging and
read-modify-write logic lives in one place. The closures keep
AlignedFile free of any AsyncIoOperation dependency.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-06-25 11:58:18 +02:00
committed by Bo Chen
parent f62e2615a9
commit 55b3bad2c3
2 changed files with 37 additions and 18 deletions

View File

@@ -7,8 +7,6 @@
//! Each backend implements the [`AsyncIo`](crate::async_io::AsyncIo)
//! trait.
use std::os::unix::fs::FileExt;
use crate::AlignedFile;
use crate::async_io::{AsyncIoError, AsyncIoOperation, AsyncIoResult};
@@ -40,20 +38,15 @@ pub(crate) fn run_unaligned_operation(
) -> AsyncIoResult<i32> {
let offset = op.offset() as u64;
let total_len = op.total_len();
let mut buf = vec![0u8; total_len];
if op.is_read() {
let n = aligned_file
.read_at(&mut buf, offset)
.map_err(AsyncIoError::ReadVectored)?;
op.write_bytes_at(0, &buf[..n])
.read_unaligned(offset, total_len, |data| op.write_bytes_at(0, data))
.map_err(AsyncIoError::ReadVectored)?;
Ok(n as i32)
} else {
op.read_bytes_at(0, &mut buf)
.map_err(AsyncIoError::WriteVectored)?;
let n = aligned_file
.write_at(&buf, offset)
.write_unaligned(offset, total_len, |data| op.read_bytes_at(0, data))
.map_err(AsyncIoError::WriteVectored)?;
Ok(n as i32)
}