mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: raw: Add vectored positioned I/O to AlignedFile
Add read_vectored_at and write_vectored_at to AlignedFile. They take the aligned fast path with a single preadv or pwritev when the offset and every iovec base and length satisfy the O_DIRECT alignment, and otherwise bounce through an AlignedBuffer, scattering on read and gathering with a read-modify-write on write. Convert the raw sync engine to these methods and drop its raw preadv and pwritev block. The methods are unsafe because their soundness depends on the caller passing iovecs that describe valid memory for iov_len bytes. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
3cd8abcd8c
commit
206cb1e100
@@ -3,10 +3,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fs::{File, Metadata};
|
||||
use std::io;
|
||||
use std::os::fd::{AsFd, BorrowedFd};
|
||||
use std::os::unix::fs::FileExt;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::{io, slice};
|
||||
|
||||
use vmm_sys_util::file_traits::FileSync;
|
||||
use vmm_sys_util::seek_hole::SeekHole;
|
||||
@@ -24,6 +24,17 @@ fn is_aligned(alignment: usize, buf_ptr: usize, len: usize, offset: u64) -> bool
|
||||
&& offset.is_multiple_of(alignment as u64))
|
||||
}
|
||||
|
||||
/// True when `offset` and every iovec base/length satisfy `alignment`
|
||||
/// (`alignment == 0` means no O_DIRECT, so everything is "aligned").
|
||||
fn iovecs_are_aligned(alignment: usize, iovecs: &[libc::iovec], offset: u64) -> bool {
|
||||
alignment == 0
|
||||
|| (offset.is_multiple_of(alignment as u64)
|
||||
&& iovecs.iter().all(|iov| {
|
||||
(iov.iov_base as usize).is_multiple_of(alignment)
|
||||
&& iov.iov_len.is_multiple_of(alignment)
|
||||
}))
|
||||
}
|
||||
|
||||
/// A `File` that transparently satisfies O_DIRECT alignment requirements.
|
||||
///
|
||||
/// `alignment == 0` means no O_DIRECT (all I/O passes straight through).
|
||||
@@ -128,6 +139,92 @@ impl AlignedFile {
|
||||
abuf.write_to(&self.file)?;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
/// Read into the buffers described by `iovecs`, starting at `offset`.
|
||||
///
|
||||
/// # Safety
|
||||
/// Every iovec must describe valid, writable memory of `iov_len` bytes.
|
||||
pub(crate) unsafe fn read_vectored_at(
|
||||
&self,
|
||||
iovecs: &[libc::iovec],
|
||||
offset: u64,
|
||||
) -> io::Result<usize> {
|
||||
if iovecs.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
if iovecs_are_aligned(self.alignment, iovecs, offset) {
|
||||
// SAFETY: upheld by this fn contract.
|
||||
let ret = unsafe {
|
||||
libc::preadv(
|
||||
self.file.as_raw_fd(),
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as libc::c_int,
|
||||
offset as libc::off_t,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
return Ok(ret as usize);
|
||||
}
|
||||
let total_len = iovecs.iter().map(|iov| iov.iov_len).sum();
|
||||
self.read_unaligned(offset, total_len, |mut data| {
|
||||
for iov in iovecs {
|
||||
if data.is_empty() {
|
||||
break;
|
||||
}
|
||||
let n = data.len().min(iov.iov_len);
|
||||
// SAFETY: upheld by this fn contract.
|
||||
let dst = unsafe { slice::from_raw_parts_mut(iov.iov_base as *mut u8, n) };
|
||||
dst.copy_from_slice(&data[..n]);
|
||||
data = &data[n..];
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Write the buffers described by `iovecs`, starting at `offset`.
|
||||
///
|
||||
/// # Safety
|
||||
/// Every iovec must describe valid, readable memory of `iov_len` bytes.
|
||||
pub(crate) unsafe fn write_vectored_at(
|
||||
&self,
|
||||
iovecs: &[libc::iovec],
|
||||
offset: u64,
|
||||
) -> io::Result<usize> {
|
||||
if iovecs.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
if iovecs_are_aligned(self.alignment, iovecs, offset) {
|
||||
// SAFETY: upheld by this fn contract.
|
||||
let ret = unsafe {
|
||||
libc::pwritev(
|
||||
self.file.as_raw_fd(),
|
||||
iovecs.as_ptr(),
|
||||
iovecs.len() as libc::c_int,
|
||||
offset as libc::off_t,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
return Ok(ret as usize);
|
||||
}
|
||||
let total_len = iovecs.iter().map(|iov| iov.iov_len).sum();
|
||||
self.write_unaligned(offset, total_len, |mut dst| {
|
||||
for iov in iovecs {
|
||||
if dst.is_empty() {
|
||||
break;
|
||||
}
|
||||
let n = dst.len().min(iov.iov_len);
|
||||
// SAFETY: upheld by this fn contract.
|
||||
let src = unsafe { slice::from_raw_parts(iov.iov_base as *const u8, n) };
|
||||
dst[..n].copy_from_slice(src);
|
||||
dst = &mut dst[n..];
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FileExt for AlignedFile {
|
||||
|
||||
Reference in New Issue
Block a user