Replace `write_vals` by `fill_zeros` in `VmIo`
This commit is contained in:
parent
2700d88bef
commit
cdd28787ed
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())?;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
Loading…
Reference in New Issue