Replace `write_vals` by `fill_zeros` in `VmIo`

This commit is contained in:
Ruihan Li 2025-07-27 23:56:31 +08:00 committed by Tate, Hongliang Tian
parent 2700d88bef
commit cdd28787ed
8 changed files with 109 additions and 91 deletions

View File

@ -6,7 +6,7 @@ use alloc::{
sync::Arc,
vec::Vec,
};
use core::{fmt::Debug, iter, mem};
use core::{fmt::Debug, mem};
use aster_input::{
key::{Key, KeyStatus},
@ -18,8 +18,9 @@ use log::{debug, info};
use ostd::{
arch::trap::TrapFrame,
io::IoMem,
mm::{DmaDirection, DmaStream, FrameAllocOptions, HasDaddr, VmIo, PAGE_SIZE},
mm::{DmaDirection, DmaStream, FrameAllocOptions, HasDaddr, PAGE_SIZE},
sync::{LocalIrqDisabled, RwLock, SpinLock},
Pod,
};
use super::{InputConfigSelect, VirtioInputConfig, VirtioInputEvent, QUEUE_EVENT, QUEUE_STATUS};
@ -260,12 +261,14 @@ impl EventTable {
fn new(num_events: usize) -> Self {
assert!(num_events * mem::size_of::<VirtioInputEvent>() <= PAGE_SIZE);
let segment = FrameAllocOptions::new().alloc_segment(1).unwrap();
let default_event = VirtioInputEvent::default();
let iter = iter::repeat_n(&default_event, EVENT_SIZE);
let nr_written = segment.write_vals(0, iter, 0).unwrap();
assert_eq!(nr_written, EVENT_SIZE);
let segment = FrameAllocOptions::new()
.zeroed(true)
.alloc_segment(1)
.unwrap();
debug_assert!(VirtioInputEvent::default()
.as_bytes()
.iter()
.all(|b| *b == 0));
let stream = DmaStream::map(segment.into(), DmaDirection::FromDevice, false).unwrap();
Self { stream, num_events }

View File

@ -3,7 +3,6 @@
#![expect(dead_code)]
use core::{
iter,
ops::Range,
sync::atomic::{AtomicU8, Ordering},
};
@ -14,7 +13,7 @@ use aster_rights::Full;
use lru::LruCache;
use ostd::{
impl_untyped_frame_meta_for,
mm::{Frame, FrameAllocOptions, UFrame, VmIo},
mm::{Frame, FrameAllocOptions, UFrame, VmIoFill},
};
use crate::{
@ -101,21 +100,18 @@ impl PageCache {
let first_page_end = start.align_up(PAGE_SIZE);
if first_page_end > start {
let zero_len = first_page_end.min(end) - start;
self.pages()
.write_vals(start, iter::repeat_n(&0, zero_len), 0)?;
self.pages().fill_zeros(start, zero_len)?;
}
// Write zeros to the last partial page if any
let last_page_start = end.align_down(PAGE_SIZE);
if last_page_start < end && last_page_start >= start {
let zero_len = end - last_page_start;
self.pages()
.write_vals(last_page_start, iter::repeat_n(&0, zero_len), 0)?;
self.pages().fill_zeros(last_page_start, zero_len)?;
}
for offset in (first_page_end..last_page_start).step_by(PAGE_SIZE) {
self.pages()
.write_vals(offset, iter::repeat_n(&0, PAGE_SIZE), 0)?;
self.pages().fill_zeros(offset, PAGE_SIZE)?;
}
Ok(())
}

View File

@ -3,7 +3,7 @@
use core::ops::Range;
use aster_rights::{Rights, TRights};
use ostd::mm::{UFrame, VmIo};
use ostd::mm::{UFrame, VmIo, VmIoFill};
use super::{CommitFlags, Vmo, VmoCommitError, VmoRightsOp};
use crate::prelude::*;
@ -141,7 +141,23 @@ impl VmIo for Vmo<Rights> {
self.0.write(offset, reader)?;
Ok(())
}
// TODO: Support efficient `write_vals()`
}
impl VmIoFill for Vmo<Rights> {
fn fill_zeros(
&self,
offset: usize,
len: usize,
) -> core::result::Result<(), (ostd::Error, usize)> {
// TODO: Support efficient `fill_zeros()`.
for i in 0..len {
match self.write_slice(offset + i, &[0u8]) {
Ok(()) => continue,
Err(err) => return Err((err, i)),
}
}
Ok(())
}
}
impl VmoRightsOp for Vmo<Rights> {

View File

@ -4,7 +4,7 @@ use core::ops::Range;
use aster_rights::{Dup, Rights, TRightSet, TRights, Write};
use aster_rights_proc::require;
use ostd::mm::{UFrame, VmIo};
use ostd::mm::{UFrame, VmIo, VmIoFill};
use super::{CommitFlags, Vmo, VmoCommitError, VmoRightsOp};
use crate::prelude::*;
@ -139,6 +139,23 @@ impl<R: TRights> VmIo for Vmo<TRightSet<R>> {
}
}
impl<R: TRights> VmIoFill for Vmo<TRightSet<R>> {
fn fill_zeros(
&self,
offset: usize,
len: usize,
) -> core::result::Result<(), (ostd::Error, usize)> {
// TODO: Support efficient `fill_zeros()`.
for i in 0..len {
match self.write_slice(offset + i, &[0u8]) {
Ok(()) => continue,
Err(err) => return Err((err, i)),
}
}
Ok(())
}
}
impl<R: TRights> VmoRightsOp for Vmo<TRightSet<R>> {
fn rights(&self) -> Rights {
Rights::from_bits(R::BITS).unwrap()

View File

@ -42,8 +42,6 @@
use core::{marker::PhantomData, mem::MaybeUninit};
use align_ext::AlignExt;
use crate::{
arch::mm::{__memcpy_fallible, __memset_fallible},
mm::{
@ -171,67 +169,37 @@ pub trait VmIo: Send + Sync {
let buf = unsafe { core::slice::from_raw_parts(ptr, len_in_bytes) };
self.write_bytes(offset, buf)
}
}
/// Writes a sequence of values given by an iterator (`iter`) from the specified offset (`offset`).
/// A trait that enables filling bytes (e.g., filling zeros) to a VM object.
pub trait VmIoFill {
/// Writes `len` zeros at a specified offset.
///
/// The write process stops until the VM object does not have enough remaining space
/// or the iterator returns `None`. If any value is written, the function returns `Ok(nr_written)`,
/// where `nr_written` is the number of the written values.
/// Unlike the methods in [`VmIo`], this method allows for short writes because `len` can be
/// effectively unbounded. However, if not all bytes can be written successfully, an `Err(_)`
/// will be returned with the error and the number of zeros that have been written thus far.
///
/// The offset of every value written by this method is aligned to the `align`-byte boundary.
/// Naturally, when `align` equals to `0` or `1`, then the argument takes no effect:
/// the values will be written in the most compact way.
/// # A slow, general implementation
///
/// # Example
/// Suppose that [`VmIo`] has already been implemented for the type,
/// this method can be implemented in the following general way.
///
/// Initializes an VM object with the same value can be done easily with `write_values`.
///
/// ```
/// use core::iter::self;
///
/// let _nr_values = vm_obj.write_vals(0, iter::repeat(0_u32), 0).unwrap();
/// ```rust
/// fn fill_zeros(&self, offset: usize, len: usize) -> core::result::Result<(), (Error, usize)> {
/// for i in 0..len {
/// match self.write_slice(offset + i, &[0u8]) {
/// Ok(()) => continue,
/// Err(err) => return Err((err, i)),
/// }
/// }
/// Ok(())
/// }
/// ```
///
/// # Panics
///
/// This method panics if `align` is greater than two,
/// but not a power of two, in release mode.
fn write_vals<'a, T: Pod + 'a, I: Iterator<Item = &'a T>>(
&self,
offset: usize,
iter: I,
align: usize,
) -> Result<usize> {
let mut nr_written = 0;
let (mut offset, item_size) = if (align >> 1) == 0 {
// align is 0 or 1
(offset, core::mem::size_of::<T>())
} else {
// align is more than 2
(
offset.align_up(align),
core::mem::size_of::<T>().align_up(align),
)
};
for item in iter {
match self.write_val(offset, item) {
Ok(_) => {
offset += item_size;
nr_written += 1;
}
Err(e) => {
if nr_written > 0 {
return Ok(nr_written);
}
return Err(e);
}
}
}
Ok(nr_written)
}
/// But we choose not to provide a general, default implementation
/// because doing so would make it too easy for a concrete type of `VmIoFill`
/// to settle with a slower implementation for such a performance-sensitive operation.
fn fill_zeros(&self, offset: usize, len: usize) -> core::result::Result<(), (Error, usize)>;
}
/// A trait that enables reading/writing data from/to a VM object using one non-tearing memory

View File

@ -5,7 +5,7 @@
use inherit_methods_macro::inherit_methods;
use ostd_pod::Pod;
use super::{Infallible, PodOnce, VmIo, VmIoOnce, VmReader, VmWriter};
use super::{Infallible, PodOnce, VmIo, VmIoFill, VmIoOnce, VmReader, VmWriter};
use crate::{
mm::{FallibleVmRead, FallibleVmWrite},
prelude::*,
@ -16,7 +16,7 @@ use crate::{
///
/// 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`].
/// [`VmIo`], [`VmIoFill`], and [`VmIoOnce`].
pub trait HasVmReaderWriter {
/// A marker type that denotes the return types of [`Self::reader`] and [`Self::writer`].
///
@ -161,6 +161,24 @@ impl<S: HasVmReaderWriter + Send + Sync> VmIo for S {
}
}
impl<S: HasVmReaderWriter> VmIoFill for S {
fn fill_zeros(&self, offset: usize, len: usize) -> core::result::Result<(), (Error, usize)> {
let mut writer = <Self as HasVmReaderWriter>::Types::to_writer_result(self.writer())
.map_err(|err| (err, 0))?;
if offset > writer.avail() {
return Err((Error::InvalidArgs, 0));
}
let filled_len = writer.skip(offset).fill_zeros(len);
if filled_len == len {
Ok(())
} else {
Err((Error::InvalidArgs, filled_len))
}
}
}
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())?;

View File

@ -34,8 +34,8 @@ pub use self::{
Frame,
},
io::{
Fallible, FallibleVmRead, FallibleVmWrite, Infallible, PodOnce, VmIo, VmIoOnce, VmReader,
VmWriter,
Fallible, FallibleVmRead, FallibleVmWrite, Infallible, PodOnce, VmIo, VmIoFill, VmIoOnce,
VmReader, VmWriter,
},
page_prop::{CachePolicy, PageFlags, PageProperty},
vm_space::VmSpace,

View File

@ -7,7 +7,7 @@ use ostd_pod::Pod;
use crate::{
mm::{
io::{VmIo, VmReader, VmWriter},
io::{VmIo, VmIoFill, VmReader, VmWriter},
tlb::TlbFlushOp,
vm_space::get_activated_vm_space,
CachePolicy, FallibleVmRead, FallibleVmWrite, FrameAllocOptions, PageFlags, PageProperty,
@ -381,19 +381,19 @@ mod io {
assert_eq!(result, Err(Error::InvalidArgs));
}
/// Tests the `write_vals` method in VmIO.
/// Tests the `fill_zeros` method in VmIO.
#[ktest]
fn write_vals_segment() {
let mut buffer = [0u8; 12];
fn fill_zeros_segment() {
let mut buffer = [0u8; 5];
let segment = FrameAllocOptions::new().alloc_segment(1).unwrap();
let values = [1u32, 2, 3];
let nr_written = segment.write_vals(0, values.iter(), 4).unwrap();
assert_eq!(nr_written, 3);
let values = [1u8, 2, 3, 4, 5];
segment.write_slice(0, &values).unwrap();
segment.fill_zeros(1, 3).unwrap();
segment.read_bytes(0, &mut buffer[..]).unwrap();
assert_eq!(buffer, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);
assert_eq!(buffer, [1, 0, 0, 0, 5]);
// Writes with error offset
let result = segment.write_vals(8192, values.iter(), 4);
assert_eq!(result, Err(Error::InvalidArgs));
let result = segment.fill_zeros(8192, 3);
assert_eq!(result, Err((Error::InvalidArgs, 0)));
}
/// Tests the `write_slice` method in VmIO.
@ -412,7 +412,7 @@ mod io {
fn read_val_segment() {
let segment = FrameAllocOptions::new().alloc_segment(1).unwrap();
let values = [1u32, 2, 3];
segment.write_vals(0, values.iter(), 4).unwrap();
segment.write_slice(0, &values).unwrap();
let val: u32 = segment.read_val(0).unwrap();
assert_eq!(val, 1);
}
@ -422,7 +422,7 @@ mod io {
fn read_slice_segment() {
let segment = FrameAllocOptions::new().alloc_segment(1).unwrap();
let values = [1u32, 2, 3];
segment.write_vals(0, values.iter(), 4).unwrap();
segment.write_slice(0, &values).unwrap();
let mut read_buffer = [0u8; 12];
segment.read_slice(0, &mut read_buffer[..]).unwrap();
assert_eq!(read_buffer, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);