Provide efficient `VmIo` with VM readers/writers

This commit is contained in:
Ruihan Li 2025-07-29 14:32:05 +08:00 committed by Tate, Hongliang Tian
parent 894b942a79
commit 2700d88bef
28 changed files with 295 additions and 306 deletions

View File

@ -5,8 +5,9 @@ use bitvec::array::BitArray;
use int_to_c_enum::TryFromInt;
use ostd::{
mm::{
DmaDirection, DmaStream, DmaStreamSlice, FrameAllocOptions, Infallible, USegment, VmIo,
VmReader, VmWriter,
io_util::{HasVmReaderWriter, VmReaderWriterResult},
DmaDirection, DmaStream, DmaStreamSlice, FrameAllocOptions, Infallible, USegment, VmReader,
VmWriter,
},
sync::{SpinLock, WaitQueue},
Error,
@ -391,7 +392,7 @@ pub enum BioDirection {
ToDevice,
}
impl<'a> BioSegment {
impl BioSegment {
/// Allocates a new `BioSegment` with the wanted blocks count and
/// the bio direction.
pub fn alloc(nblocks: usize, direction: BioDirection) -> Self {
@ -484,28 +485,20 @@ impl<'a> BioSegment {
pub fn inner_segment(&self) -> &USegment {
self.inner.dma_slice.stream().segment()
}
}
/// Returns a reader to read data from it.
pub fn reader(&'a self) -> Result<VmReader<'a, Infallible>, Error> {
impl HasVmReaderWriter for BioSegment {
type Types = VmReaderWriterResult;
fn reader(&self) -> Result<VmReader<'_, Infallible>, Error> {
self.inner.dma_slice.reader()
}
/// Returns a writer to write data into it.
pub fn writer(&'a self) -> Result<VmWriter<'a, Infallible>, Error> {
fn writer(&self) -> Result<VmWriter<'_, Infallible>, Error> {
self.inner.dma_slice.writer()
}
}
impl VmIo for BioSegment {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<(), Error> {
self.inner.dma_slice.read(offset, writer)
}
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<(), Error> {
self.inner.dma_slice.write(offset, reader)
}
}
// The timing for free the segment to the pool.
impl Drop for BioSegmentInner {
fn drop(&mut self) {

View File

@ -25,7 +25,10 @@ use aster_block::{
BlockDevice, SECTOR_SIZE,
};
use component::{init_component, ComponentInitError};
use ostd::{mm::VmIo, prelude::*};
use ostd::{
mm::{io_util::HasVmReaderWriter, VmIo},
prelude::*,
};
pub use self::{
error::{Errno, Error},

View File

@ -5,8 +5,8 @@ use alloc::{collections::linked_list::LinkedList, sync::Arc};
use aster_softirq::BottomHalfDisabled;
use ostd::{
mm::{
Daddr, DmaDirection, DmaStream, FrameAllocOptions, HasDaddr, Infallible, VmReader,
VmWriter, PAGE_SIZE,
io_util::HasVmReaderWriter, Daddr, DmaDirection, DmaStream, FrameAllocOptions, HasDaddr,
Infallible, VmReader, VmWriter, PAGE_SIZE,
},
sync::SpinLock,
Pod,

View File

@ -12,8 +12,8 @@ use aster_softirq::BottomHalfDisabled;
use bitvec::{array::BitArray, prelude::Lsb0};
use ostd::{
mm::{
Daddr, DmaDirection, DmaStream, FrameAllocOptions, HasDaddr, Infallible, VmReader,
VmWriter, PAGE_SIZE,
io_util::HasVmReaderWriter, Daddr, DmaDirection, DmaStream, FrameAllocOptions, HasDaddr,
Infallible, VmReader, VmWriter, PAGE_SIZE,
},
sync::{RwLock, SpinLock},
};

View File

@ -7,7 +7,10 @@ use aster_console::{AnyConsoleDevice, ConsoleCallback};
use log::debug;
use ostd::{
arch::trap::TrapFrame,
mm::{DmaDirection, DmaStream, DmaStreamSlice, FrameAllocOptions, VmReader},
mm::{
io_util::HasVmReaderWriter, DmaDirection, DmaStream, DmaStreamSlice, FrameAllocOptions,
VmReader,
},
sync::{Rcu, SpinLock},
};

View File

@ -7,12 +7,9 @@
use alloc::sync::Arc;
use core::ops::Range;
use ostd::{
mm::{
FallibleVmRead, FallibleVmWrite, Infallible, Paddr, UFrame, USegment, UntypedMem, VmIo,
VmReader, VmWriter, PAGE_SIZE,
},
Error, Result,
use ostd::mm::{
io_util::{HasVmReaderWriter, VmReaderWriterIdentity},
Infallible, Paddr, UFrame, USegment, VmReader, VmWriter, PAGE_SIZE,
};
/// A reference to a slice of a [`USegment`].
@ -69,8 +66,15 @@ impl SegmentSlice {
self.nframes() * PAGE_SIZE
}
/// Gets a reader for the slice.
pub fn reader(&self) -> VmReader<'_, Infallible> {
fn start_frame_index(&self) -> usize {
self.inner.start_paddr() / PAGE_SIZE + self.range.start
}
}
impl HasVmReaderWriter for SegmentSlice {
type Types = VmReaderWriterIdentity;
fn reader(&self) -> VmReader<'_, Infallible> {
let mut reader = self.inner.reader();
reader
.skip(self.start_paddr() - self.inner.start_paddr())
@ -78,52 +82,13 @@ impl SegmentSlice {
reader
}
/// Gets a writer for the slice.
pub fn writer(&self) -> VmWriter<'_, Infallible> {
fn writer(&self) -> VmWriter<'_, Infallible> {
let mut writer = self.inner.writer();
writer
.skip(self.start_paddr() - self.inner.start_paddr())
.limit(self.nbytes());
writer
}
fn start_frame_index(&self) -> usize {
self.inner.start_paddr() / PAGE_SIZE + self.range.start
}
}
impl VmIo for SegmentSlice {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()> {
let read_len = writer.avail();
// Do bound check with potential integer overflow in mind
let max_offset = offset.checked_add(read_len).ok_or(Error::Overflow)?;
if max_offset > self.nbytes() {
return Err(Error::InvalidArgs);
}
let len = self
.reader()
.skip(offset)
.read_fallible(writer)
.map_err(|(e, _)| e)?;
debug_assert!(len == read_len);
Ok(())
}
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()> {
let write_len = reader.remain();
// Do bound check with potential integer overflow in mind
let max_offset = offset.checked_add(reader.remain()).ok_or(Error::Overflow)?;
if max_offset > self.nbytes() {
return Err(Error::InvalidArgs);
}
let len = self
.writer()
.skip(offset)
.write_fallible(reader)
.map_err(|(e, _)| e)?;
debug_assert!(len == write_len);
Ok(())
}
}
impl From<USegment> for SegmentSlice {

View File

@ -13,7 +13,7 @@ use aster_block::{
BLOCK_SIZE,
};
use aster_rights::Full;
use ostd::mm::{Segment, VmIo};
use ostd::mm::{io_util::HasVmReaderWriter, Segment, VmIo};
use super::{
constants::*,

View File

@ -31,7 +31,7 @@ mod test {
BlockDevice, BlockDeviceMeta,
};
use ostd::{
mm::{FrameAllocOptions, Segment, VmIo, PAGE_SIZE},
mm::{io_util::HasVmReaderWriter, FrameAllocOptions, Segment, VmIo, PAGE_SIZE},
prelude::*,
};
use rand::{rngs::SmallRng, RngCore, SeedableRng};

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
use id_alloc::IdAlloc;
use ostd::{const_assert, mm::UntypedMem};
use ostd::{const_assert, mm::io_util::HasVmReaderWriter};
use super::{
block_ptr::Ext2Bid,

View File

@ -7,7 +7,7 @@ use alloc::{borrow::ToOwned, rc::Rc};
use core::sync::atomic::{AtomicUsize, Ordering};
use inherit_methods_macro::inherit_methods;
use ostd::{const_assert, mm::UntypedMem};
use ostd::{const_assert, mm::io_util::HasVmReaderWriter};
use super::{
block_ptr::{BidPath, BlockPtrs, Ext2Bid, BID_SIZE, MAX_BLOCK_PTRS},

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use ostd::mm::UntypedMem;
use ostd::mm::io_util::HasVmReaderWriter;
use super::{block_ptr::Ext2Bid, prelude::*, Ext2, Inode};
use crate::fs::utils::{XattrName, XattrNamespace, XattrSetFlags, XATTR_NAME_MAX_LEN};

View File

@ -13,7 +13,7 @@ use aster_block::BLOCK_SIZE;
use aster_rights::Full;
use hashbrown::HashSet;
use inherit_methods_macro::inherit_methods;
use ostd::mm::{FrameAllocOptions, UntypedMem};
use ostd::mm::{io_util::HasVmReaderWriter, FrameAllocOptions};
use crate::{
fs::{

View File

@ -11,7 +11,7 @@ use aster_rights::Full;
use aster_util::slot_vec::SlotVec;
use hashbrown::HashMap;
use ostd::{
mm::{UntypedMem, VmIo},
mm::{io_util::HasVmReaderWriter, VmIo},
sync::{PreemptDisabled, RwLockWriteGuard},
};

View File

@ -21,7 +21,7 @@ use core::{
use align_ext::AlignExt;
use aster_rights::Full;
use ostd::{
mm::{UntypedMem, VmIo, MAX_USERSPACE_VADDR},
mm::{io_util::HasVmReaderWriter, VmIo, MAX_USERSPACE_VADDR},
task::disable_preempt,
};

View File

@ -8,7 +8,7 @@ use core::{
};
use inherit_methods_macro::inherit_methods;
use ostd::mm::{FrameAllocOptions, Segment, UntypedMem, VmIo};
use ostd::mm::{io_util::HasVmReaderWriter, FrameAllocOptions, Segment, VmIo};
use super::{MultiRead, MultiWrite};
use crate::prelude::*;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use ostd::mm::{Frame, FrameAllocOptions, UFrame, UntypedMem};
use ostd::mm::{io_util::HasVmReaderWriter, Frame, FrameAllocOptions, UFrame};
use crate::prelude::*;

View File

@ -13,7 +13,7 @@ use core::{
use align_ext::AlignExt;
use aster_rights::Rights;
use ostd::{
mm::{FrameAllocOptions, UFrame, UntypedMem, VmReader, VmWriter},
mm::{io_util::HasVmReaderWriter, FrameAllocOptions, UFrame, VmReader, VmWriter},
task::disable_preempt,
};
use xarray::{Cursor, LockedXArray, XArray};

View File

@ -8,7 +8,7 @@ use int_to_c_enum::TryFromInt;
use super::IrtEntryHandle;
use crate::{
mm::{FrameAllocOptions, Segment, UntypedMem, PAGE_SIZE},
mm::{io_util::HasVmReaderWriter, FrameAllocOptions, Segment, PAGE_SIZE},
sync::{LocalIrqDisabled, SpinLock},
};

View File

@ -12,10 +12,10 @@ pub(super) use self::allocator::init;
pub(crate) use self::allocator::IoMemAllocatorBuilder;
use crate::{
mm::{
io_util::{HasVmReaderWriter, VmReaderWriterIdentity},
kspace::kvirt_area::KVirtArea,
page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags},
FallibleVmRead, FallibleVmWrite, HasPaddr, Infallible, Paddr, PodOnce, VmIo, VmIoOnce,
VmReader, VmWriter, PAGE_SIZE,
HasPaddr, Infallible, Paddr, VmReader, VmWriter, PAGE_SIZE,
},
prelude::*,
Error,
@ -31,12 +31,6 @@ pub struct IoMem {
pa: Paddr,
}
impl HasPaddr for IoMem {
fn paddr(&self) -> Paddr {
self.pa
}
}
impl IoMem {
/// Acquires an `IoMem` instance for the given range.
pub fn acquire(range: Range<Paddr>) -> Result<IoMem> {
@ -143,7 +137,9 @@ impl IoMem {
// is in OSTD, so we can rely on the implementation details of `VmReader` and `VmWriter`, which we
// know are also suitable for accessing I/O memory.
impl IoMem {
impl HasVmReaderWriter for IoMem {
type Types = VmReaderWriterIdentity;
fn reader(&self) -> VmReader<'_, Infallible> {
// SAFETY: The constructor of the `IoMem` structure has already ensured the
// safety of reading from the mapped physical address, and the mapping is valid.
@ -167,53 +163,9 @@ impl IoMem {
}
}
impl VmIo for IoMem {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()> {
let offset = offset + self.offset;
if self
.limit
.checked_sub(offset)
.is_none_or(|remain| remain < writer.avail())
{
return Err(Error::InvalidArgs);
}
self.reader()
.skip(offset)
.read_fallible(writer)
.map_err(|(e, _)| e)?;
debug_assert!(!writer.has_avail());
Ok(())
}
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()> {
let offset = offset + self.offset;
if self
.limit
.checked_sub(offset)
.is_none_or(|remain| remain < reader.remain())
{
return Err(Error::InvalidArgs);
}
self.writer()
.skip(offset)
.write_fallible(reader)
.map_err(|(e, _)| e)?;
debug_assert!(!reader.has_remain());
Ok(())
}
}
impl VmIoOnce for IoMem {
fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T> {
self.reader().skip(offset).read_once()
}
fn write_once<T: PodOnce>(&self, offset: usize, new_val: &T) -> Result<()> {
self.writer().skip(offset).write_once(new_val)
impl HasPaddr for IoMem {
fn paddr(&self) -> Paddr {
self.pa
}
}

View File

@ -10,13 +10,11 @@ use crate::{
arch::iommu,
mm::{
dma::{dma_type, Daddr, DmaType},
io::VmIoOnce,
io_util::{HasVmReaderWriter, VmReaderWriterIdentity},
kspace::{paddr_to_vaddr, KERNEL_PAGE_TABLE},
page_prop::CachePolicy,
HasPaddr, Infallible, Paddr, PodOnce, USegment, UntypedMem, VmIo, VmReader, VmWriter,
PAGE_SIZE,
HasPaddr, Infallible, Paddr, USegment, VmReader, VmWriter, PAGE_SIZE,
},
prelude::*,
};
cfg_if! {
@ -176,34 +174,14 @@ impl Drop for DmaCoherentInner {
}
}
impl VmIo for DmaCoherent {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()> {
self.inner.segment.read(offset, writer)
}
impl HasVmReaderWriter for DmaCoherent {
type Types = VmReaderWriterIdentity;
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()> {
self.inner.segment.write(offset, reader)
}
}
impl VmIoOnce for DmaCoherent {
fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T> {
self.inner.segment.reader().skip(offset).read_once()
}
fn write_once<T: PodOnce>(&self, offset: usize, new_val: &T) -> Result<()> {
self.inner.segment.writer().skip(offset).write_once(new_val)
}
}
impl<'a> DmaCoherent {
/// Returns a reader to read data from it.
pub fn reader(&'a self) -> VmReader<'a, Infallible> {
fn reader(&self) -> VmReader<'_, Infallible> {
self.inner.segment.reader()
}
/// Returns a writer to write data into it.
pub fn writer(&'a self) -> VmWriter<'a, Infallible> {
fn writer(&self) -> VmWriter<'_, Infallible> {
self.inner.segment.writer()
}
}

View File

@ -9,7 +9,8 @@ use crate::{
error::Error,
mm::{
dma::{dma_type, Daddr, DmaType},
HasPaddr, Infallible, Paddr, USegment, UntypedMem, VmIo, VmReader, VmWriter, PAGE_SIZE,
io_util::{HasVmReaderWriter, VmReaderWriterResult},
HasPaddr, Infallible, Paddr, USegment, VmReader, VmWriter, PAGE_SIZE,
},
};
@ -135,8 +136,8 @@ impl DmaStream {
/// (e.g., using [`write_bytes`]).
/// Before the CPU side notifies the device side to read, it must call the `sync` method first.
///
/// [`read_bytes`]: Self::read_bytes
/// [`write_bytes`]: Self::write_bytes
/// [`read_bytes`]: crate::mm::VmIo::read_bytes
/// [`write_bytes`]: crate::mm::VmIo::write_bytes
pub fn sync(&self, _byte_range: Range<usize>) -> Result<(), Error> {
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")]{
@ -203,35 +204,17 @@ impl Drop for DmaStreamInner {
}
}
impl VmIo for DmaStream {
/// Reads data into the buffer.
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<(), Error> {
if self.inner.direction == DmaDirection::ToDevice {
return Err(Error::AccessDenied);
}
self.inner.segment.read(offset, writer)
}
impl HasVmReaderWriter for DmaStream {
type Types = VmReaderWriterResult;
/// Writes data from the buffer.
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<(), Error> {
if self.inner.direction == DmaDirection::FromDevice {
return Err(Error::AccessDenied);
}
self.inner.segment.write(offset, reader)
}
}
impl<'a> DmaStream {
/// Returns a reader to read data from it.
pub fn reader(&'a self) -> Result<VmReader<'a, Infallible>, Error> {
fn reader(&self) -> Result<VmReader<'_, Infallible>, Error> {
if self.inner.direction == DmaDirection::ToDevice {
return Err(Error::AccessDenied);
}
Ok(self.inner.segment.reader())
}
/// Returns a writer to write data into it.
pub fn writer(&'a self) -> Result<VmWriter<'a, Infallible>, Error> {
fn writer(&self) -> Result<VmWriter<'_, Infallible>, Error> {
if self.inner.direction == DmaDirection::FromDevice {
return Err(Error::AccessDenied);
}
@ -300,38 +283,24 @@ impl<Dma: AsRef<DmaStream>> DmaStreamSlice<Dma> {
.as_ref()
.sync(self.offset..self.offset + self.len)
}
}
/// Returns a reader to read data from it.
pub fn reader(&self) -> Result<VmReader<Infallible>, Error> {
impl<Dma: AsRef<DmaStream>> HasVmReaderWriter for DmaStreamSlice<Dma> {
type Types = VmReaderWriterResult;
fn reader(&self) -> Result<VmReader<'_, Infallible>, Error> {
let mut stream_reader = self.stream.as_ref().reader()?;
stream_reader.skip(self.offset).limit(self.len);
Ok(stream_reader)
}
/// Returns a writer to write data into it.
pub fn writer(&self) -> Result<VmWriter<Infallible>, Error> {
fn writer(&self) -> Result<VmWriter<'_, Infallible>, Error> {
let mut stream_writer = self.stream.as_ref().writer()?;
stream_writer.skip(self.offset).limit(self.len);
Ok(stream_writer)
}
}
impl<Dma: AsRef<DmaStream> + Send + Sync> VmIo for DmaStreamSlice<Dma> {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<(), Error> {
if writer.avail() + offset > self.len {
return Err(Error::InvalidArgs);
}
self.stream.as_ref().read(self.offset + offset, writer)
}
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<(), Error> {
if reader.remain() + offset > self.len {
return Err(Error::InvalidArgs);
}
self.stream.as_ref().write(self.offset + offset, reader)
}
}
impl<Dma: AsRef<DmaStream>> HasDaddr for DmaStreamSlice<Dma> {
fn daddr(&self) -> Daddr {
self.stream.as_ref().daddr() + self.offset

View File

@ -6,6 +6,7 @@ use crate::{
mm::{
dma::*,
io::{VmIo, VmIoOnce},
io_util::HasVmReaderWriter,
kspace::KERNEL_PAGE_TABLE,
paddr_to_vaddr, CachePolicy, FrameAllocOptions, HasPaddr, VmReader, VmWriter, PAGE_SIZE,
},

View File

@ -1,7 +1,9 @@
// SPDX-License-Identifier: MPL-2.0
use super::{allocator::FrameAllocOptions, *};
use crate::{impl_frame_meta_for, impl_untyped_frame_meta_for, prelude::*};
use crate::{
impl_frame_meta_for, impl_untyped_frame_meta_for, mm::io_util::HasVmReaderWriter, prelude::*,
};
/// Typed mock metadata struct for testing
#[derive(Debug, Default)]

View File

@ -6,14 +6,14 @@
//! relaxed rules but we cannot create references to them. This module provides
//! the declaration of untyped frames and segments, and the implementation of
//! extra functionalities (such as [`VmIo`]) for them.
//!
//! [`VmIo`]: crate::mm::VmIo
use super::{meta::AnyFrameMeta, Frame, Segment};
use crate::{
mm::{
io::{FallibleVmRead, FallibleVmWrite, VmIo, VmReader, VmWriter},
paddr_to_vaddr, Infallible,
},
Error, Result,
use crate::mm::{
io::{VmReader, VmWriter},
io_util::{HasVmReaderWriter, VmReaderWriterIdentity},
paddr_to_vaddr, Infallible,
};
/// The metadata of untyped frame.
@ -66,66 +66,29 @@ macro_rules! impl_untyped_frame_meta_for {
// A special case of untyped metadata is the unit type.
impl_untyped_frame_meta_for!(());
/// A physical memory range that is untyped.
///
/// Untyped frames or segments can be safely read and written by the kernel or
/// the user.
pub trait UntypedMem {
/// Borrows a reader that can read the untyped memory.
fn reader(&self) -> VmReader<'_, Infallible>;
/// Borrows a writer that can write the untyped memory.
fn writer(&self) -> VmWriter<'_, Infallible>;
}
macro_rules! impl_untyped_for {
($t:ident) => {
impl<UM: AnyUFrameMeta + ?Sized> UntypedMem for $t<UM> {
impl<UM: AnyUFrameMeta + ?Sized> HasVmReaderWriter for $t<UM> {
type Types = VmReaderWriterIdentity;
fn reader(&self) -> VmReader<'_, Infallible> {
let ptr = paddr_to_vaddr(self.start_paddr()) as *const u8;
// SAFETY: Only untyped frames are allowed to be read.
// SAFETY:
// - The memory range points to untyped memory.
// - The frame/segment is alive during the lifetime `'_`.
// - Using `VmReader` and `VmWriter` is the only way to access the frame/segment.
unsafe { VmReader::from_kernel_space(ptr, self.size()) }
}
fn writer(&self) -> VmWriter<'_, Infallible> {
let ptr = paddr_to_vaddr(self.start_paddr()) as *mut u8;
// SAFETY: Only untyped frames are allowed to be written.
// SAFETY:
// - The memory range points to untyped memory.
// - The frame/segment is alive during the lifetime `'_`.
// - Using `VmReader` and `VmWriter` is the only way to access the frame/segment.
unsafe { VmWriter::from_kernel_space(ptr, self.size()) }
}
}
impl<UM: AnyUFrameMeta + ?Sized> VmIo for $t<UM> {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()> {
let read_len = writer.avail().min(self.size().saturating_sub(offset));
// Do bound check with potential integer overflow in mind
let max_offset = offset.checked_add(read_len).ok_or(Error::Overflow)?;
if max_offset > self.size() {
return Err(Error::InvalidArgs);
}
let len = self
.reader()
.skip(offset)
.read_fallible(writer)
.map_err(|(e, _)| e)?;
debug_assert!(len == read_len);
Ok(())
}
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()> {
let write_len = reader.remain().min(self.size().saturating_sub(offset));
// Do bound check with potential integer overflow in mind
let max_offset = offset.checked_add(write_len).ok_or(Error::Overflow)?;
if max_offset > self.size() {
return Err(Error::InvalidArgs);
}
let len = self
.writer()
.skip(offset)
.write_fallible(reader)
.map_err(|(e, _)| e)?;
debug_assert!(len == write_len);
Ok(())
}
}
};
}

View File

@ -43,7 +43,6 @@
use core::{marker::PhantomData, mem::MaybeUninit};
use align_ext::AlignExt;
use inherit_methods_macro::inherit_methods;
use crate::{
arch::mm::{__memcpy_fallible, __memset_fallible},
@ -256,42 +255,6 @@ pub trait VmIoOnce {
fn write_once<T: PodOnce>(&self, offset: usize, new_val: &T) -> Result<()>;
}
macro_rules! impl_vm_io_pointer {
($typ:ty,$from:tt) => {
#[inherit_methods(from = $from)]
impl<T: VmIo> VmIo for $typ {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()>;
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()>;
fn read_val<F: Pod>(&self, offset: usize) -> Result<F>;
fn read_slice<F: Pod>(&self, offset: usize, slice: &mut [F]) -> Result<()>;
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()>;
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()>;
fn write_val<F: Pod>(&self, offset: usize, new_val: &F) -> Result<()>;
fn write_slice<F: Pod>(&self, offset: usize, slice: &[F]) -> Result<()>;
}
};
}
impl_vm_io_pointer!(&T, "(**self)");
impl_vm_io_pointer!(&mut T, "(**self)");
impl_vm_io_pointer!(Box<T>, "(**self)");
impl_vm_io_pointer!(Arc<T>, "(**self)");
macro_rules! impl_vm_io_once_pointer {
($typ:ty,$from:tt) => {
#[inherit_methods(from = $from)]
impl<T: VmIoOnce> VmIoOnce for $typ {
fn read_once<F: PodOnce>(&self, offset: usize) -> Result<F>;
fn write_once<F: PodOnce>(&self, offset: usize, new_val: &F) -> Result<()>;
}
};
}
impl_vm_io_once_pointer!(&T, "(**self)");
impl_vm_io_once_pointer!(&mut T, "(**self)");
impl_vm_io_once_pointer!(Box<T>, "(**self)");
impl_vm_io_once_pointer!(Arc<T>, "(**self)");
/// A marker type used for [`VmReader`] and [`VmWriter`],
/// representing whether reads or writes on the underlying memory region are fallible.
pub enum Fallible {}

196
ostd/src/mm/io_util.rs Normal file
View File

@ -0,0 +1,196 @@
// SPDX-License-Identifier: MPL-2.0
//! Utilities for types in [`super::io`].
use inherit_methods_macro::inherit_methods;
use ostd_pod::Pod;
use super::{Infallible, PodOnce, VmIo, VmIoOnce, VmReader, VmWriter};
use crate::{
mm::{FallibleVmRead, FallibleVmWrite},
prelude::*,
Error,
};
/// A helper trait that denotes types that can provide [`VmReader`]s and [`VmWriter`]s.
///
/// Having the reader and writer means that the type is capable of performing a range of VM
/// operations. Thus, several traits will be automatically and efficiently implemented, such as
/// [`VmIo`] and [`VmIoOnce`].
pub trait HasVmReaderWriter {
/// A marker type that denotes the return types of [`Self::reader`] and [`Self::writer`].
///
/// This can be either [`VmReaderWriterIdentity`] or [`VmReaderWriterResult`].
//
// TODO: This exists because `DmaStream` and related types track the DMA direction at runtime.
// The goal is to achieve this at compile time, which would eliminate the need for
// `VmReaderWriterTypes`. See the discussion at
// <https://github.com/asterinas/asterinas/pull/2289#discussion_r2261801694>.
type Types: VmReaderWriterTypes;
/// Returns a reader to read data from it.
fn reader(&self) -> <Self::Types as VmReaderWriterTypes>::Reader<'_>;
/// Returns a writer to write data to it.
fn writer(&self) -> <Self::Types as VmReaderWriterTypes>::Writer<'_>;
}
/// A marker trait that denotes the return types for [`HasVmReaderWriter`].
pub trait VmReaderWriterTypes {
/// The return type of [`HasVmReaderWriter::reader`].
type Reader<'a>;
/// The return type of [`HasVmReaderWriter::writer`].
type Writer<'a>;
/// Converts [`Self::Reader`] to [`Result<VmReader<Infallible>>`].
fn to_reader_result(reader: Self::Reader<'_>) -> Result<VmReader<'_, Infallible>>;
/// Converts [`Self::Writer`] to [`Result<VmWriter<Infallible>>`].
fn to_writer_result(writer: Self::Writer<'_>) -> Result<VmWriter<'_, Infallible>>;
}
/// A marker type that denotes reader and writer identities as [`HasVmReaderWriter`] return types.
pub enum VmReaderWriterIdentity {}
impl VmReaderWriterTypes for VmReaderWriterIdentity {
type Reader<'a> = VmReader<'a, Infallible>;
type Writer<'a> = VmWriter<'a, Infallible>;
fn to_reader_result(reader: Self::Reader<'_>) -> Result<VmReader<'_, Infallible>> {
Ok(reader)
}
fn to_writer_result(writer: Self::Writer<'_>) -> Result<VmWriter<'_, Infallible>> {
Ok(writer)
}
}
/// A marker type that denotes reader and writer results as [`HasVmReaderWriter`] return types.
pub enum VmReaderWriterResult {}
impl VmReaderWriterTypes for VmReaderWriterResult {
type Reader<'a> = Result<VmReader<'a, Infallible>>;
type Writer<'a> = Result<VmWriter<'a, Infallible>>;
fn to_reader_result(reader: Self::Reader<'_>) -> Result<VmReader<'_, Infallible>> {
reader
}
fn to_writer_result(writer: Self::Writer<'_>) -> Result<VmWriter<'_, Infallible>> {
writer
}
}
impl<S: HasVmReaderWriter + Send + Sync> VmIo for S {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()> {
let mut reader = <Self as HasVmReaderWriter>::Types::to_reader_result(self.reader())?;
let limit = offset.checked_add(writer.avail()).ok_or(Error::Overflow)?;
if limit > reader.remain() {
return Err(Error::InvalidArgs);
}
reader.skip(offset);
let _len = reader
.to_fallible()
.read_fallible(writer)
.map_err(|(err, _)| err)?;
debug_assert!(!writer.has_avail());
Ok(())
}
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
let mut reader = <Self as HasVmReaderWriter>::Types::to_reader_result(self.reader())?;
let limit = offset.checked_add(buf.len()).ok_or(Error::Overflow)?;
if limit > reader.remain() {
return Err(Error::InvalidArgs);
}
let len = reader.skip(offset).read(&mut VmWriter::from(&mut *buf));
debug_assert_eq!(len, buf.len());
Ok(())
}
// No need to implement `read_slice`. Its default implementation is efficient enough by relying
// on `read_bytes`.
fn read_val<T: Pod>(&self, offset: usize) -> Result<T> {
let mut reader = <Self as HasVmReaderWriter>::Types::to_reader_result(self.reader())?;
if offset > reader.remain() {
return Err(Error::InvalidArgs);
}
reader.skip(offset).read_val()
}
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()> {
let mut writer = <Self as HasVmReaderWriter>::Types::to_writer_result(self.writer())?;
let limit = offset.checked_add(reader.remain()).ok_or(Error::Overflow)?;
if limit > writer.avail() {
return Err(Error::InvalidArgs);
}
writer.skip(offset);
let _len = writer
.to_fallible()
.write_fallible(reader)
.map_err(|(err, _)| err)?;
debug_assert!(!reader.has_remain());
Ok(())
}
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
let mut writer = <Self as HasVmReaderWriter>::Types::to_writer_result(self.writer())?;
let limit = offset.checked_add(buf.len()).ok_or(Error::Overflow)?;
if limit > writer.avail() {
return Err(Error::InvalidArgs);
}
let len = writer.skip(offset).write(&mut VmReader::from(buf));
debug_assert_eq!(len, buf.len());
Ok(())
}
// No need to implement `write_slice`. Its default implementation is efficient enough by
// relying on `write_bytes`.
fn write_val<T: Pod>(&self, offset: usize, new_val: &T) -> Result<()> {
let mut writer = <Self as HasVmReaderWriter>::Types::to_writer_result(self.writer())?;
if offset > writer.avail() {
return Err(Error::InvalidArgs);
}
writer.skip(offset).write_val(new_val)
}
}
impl<S: HasVmReaderWriter> VmIoOnce for S {
fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T> {
let mut reader = <Self as HasVmReaderWriter>::Types::to_reader_result(self.reader())?;
reader.skip(offset).read_once()
}
fn write_once<T: PodOnce>(&self, offset: usize, new_val: &T) -> Result<()> {
let mut writer = <Self as HasVmReaderWriter>::Types::to_writer_result(self.writer())?;
writer.skip(offset).write_once(new_val)
}
}
// The pointer implementations below (i.e., `impl_vm_io_pointer`/`impl_vm_io_once_pointer`) should
// apply to the `VmIo`/`VmIoOnce` traits themselves, instead of these helper traits.
//
// However, there are some unexpected compiler errors that complain that downstream crates can
// implement `HasVmReaderWriter` to cause conflict implementations.
macro_rules! impl_vm_io_pointer {
($typ:ty,$from:tt) => {
#[inherit_methods(from = $from)]
impl<T: HasVmReaderWriter> HasVmReaderWriter for $typ {
type Types = T::Types;
fn reader(&self) -> <Self::Types as VmReaderWriterTypes>::Reader<'_>;
fn writer(&self) -> <Self::Types as VmReaderWriterTypes>::Writer<'_>;
}
};
}
impl_vm_io_pointer!(&T, "(**self)");
impl_vm_io_pointer!(&mut T, "(**self)");
impl_vm_io_pointer!(Box<T>, "(**self)");
impl_vm_io_pointer!(Arc<T>, "(**self)");

View File

@ -12,6 +12,7 @@ pub(crate) mod dma;
pub mod frame;
pub mod heap;
pub mod io;
pub mod io_util;
pub(crate) mod kspace;
pub(crate) mod page_prop;
pub(crate) mod page_table;
@ -29,7 +30,7 @@ pub use self::{
allocator::FrameAllocOptions,
segment::{Segment, USegment},
unique::UniqueFrame,
untyped::{AnyUFrameMeta, UFrame, UntypedMem},
untyped::{AnyUFrameMeta, UFrame},
Frame,
},
io::{

View File

@ -14,6 +14,6 @@ pub use ostd_macros::ktest;
pub use crate::{
early_print as print, early_println as println,
mm::{Paddr, UntypedMem, Vaddr},
mm::{Paddr, Vaddr},
panic::abort,
};