mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-21 16:33:24 +00:00
Separate SegmentSlice
from Segment
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
d930829866
commit
909639fd70
@ -1,9 +1,10 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use align_ext::AlignExt;
|
||||
use aster_util::segment_slice::SegmentSlice;
|
||||
use int_to_c_enum::TryFromInt;
|
||||
use ostd::{
|
||||
mm::{Frame, Infallible, Segment, VmReader, VmWriter},
|
||||
mm::{Frame, Infallible, VmReader, VmWriter},
|
||||
sync::WaitQueue,
|
||||
};
|
||||
|
||||
@ -366,7 +367,7 @@ pub enum BioStatus {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BioSegment {
|
||||
/// The contiguous pages on which this segment resides.
|
||||
pages: Segment,
|
||||
pages: SegmentSlice,
|
||||
/// The starting offset (in bytes) within the first page.
|
||||
/// The offset should always be aligned to the sector size and
|
||||
/// must not exceed the size of a single page.
|
||||
@ -381,9 +382,8 @@ const SECTOR_SIZE: u16 = super::SECTOR_SIZE as u16;
|
||||
|
||||
impl<'a> BioSegment {
|
||||
/// Constructs a new `BioSegment` from `Segment`.
|
||||
pub fn from_segment(segment: Segment, offset: usize, len: usize) -> Self {
|
||||
pub fn from_segment(segment: SegmentSlice, offset: usize, len: usize) -> Self {
|
||||
assert!(offset + len <= segment.nbytes());
|
||||
|
||||
Self {
|
||||
pages: segment.range(frame_range(&(offset..offset + len))),
|
||||
offset: AlignedUsize::<SECTOR_SIZE>::new(offset % super::BLOCK_SIZE).unwrap(),
|
||||
@ -396,7 +396,7 @@ impl<'a> BioSegment {
|
||||
assert!(offset + len <= super::BLOCK_SIZE);
|
||||
|
||||
Self {
|
||||
pages: Segment::from(frame),
|
||||
pages: SegmentSlice::from(frame),
|
||||
offset: AlignedUsize::<SECTOR_SIZE>::new(offset).unwrap(),
|
||||
len: AlignedUsize::<SECTOR_SIZE>::new(len).unwrap(),
|
||||
}
|
||||
@ -418,7 +418,7 @@ impl<'a> BioSegment {
|
||||
}
|
||||
|
||||
/// Returns the contiguous pages on which this segment resides.
|
||||
pub fn pages(&self) -> &Segment {
|
||||
pub fn pages(&self) -> &SegmentSlice {
|
||||
&self.pages
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_util::segment_slice::SegmentSlice;
|
||||
use ostd::mm::{
|
||||
FallibleVmRead, FallibleVmWrite, Frame, FrameAllocOptions, Segment, VmIo, VmReader, VmWriter,
|
||||
FallibleVmRead, FallibleVmWrite, Frame, FrameAllocOptions, VmIo, VmReader, VmWriter,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -16,7 +17,11 @@ use crate::prelude::*;
|
||||
// TODO: Add API to submit bio with multiple segments in scatter/gather manner.
|
||||
impl dyn BlockDevice {
|
||||
/// Synchronously reads contiguous blocks starting from the `bid`.
|
||||
pub fn read_blocks(&self, bid: Bid, segment: &Segment) -> Result<BioStatus, BioEnqueueError> {
|
||||
pub fn read_blocks(
|
||||
&self,
|
||||
bid: Bid,
|
||||
segment: &SegmentSlice,
|
||||
) -> Result<BioStatus, BioEnqueueError> {
|
||||
let bio = create_bio_from_segment(BioType::Read, bid, segment);
|
||||
let status = bio.submit_and_wait(self)?;
|
||||
Ok(status)
|
||||
@ -26,7 +31,7 @@ impl dyn BlockDevice {
|
||||
pub fn read_blocks_async(
|
||||
&self,
|
||||
bid: Bid,
|
||||
segment: &Segment,
|
||||
segment: &SegmentSlice,
|
||||
) -> Result<BioWaiter, BioEnqueueError> {
|
||||
let bio = create_bio_from_segment(BioType::Read, bid, segment);
|
||||
bio.submit(self)
|
||||
@ -46,7 +51,11 @@ impl dyn BlockDevice {
|
||||
}
|
||||
|
||||
/// Synchronously writes contiguous blocks starting from the `bid`.
|
||||
pub fn write_blocks(&self, bid: Bid, segment: &Segment) -> Result<BioStatus, BioEnqueueError> {
|
||||
pub fn write_blocks(
|
||||
&self,
|
||||
bid: Bid,
|
||||
segment: &SegmentSlice,
|
||||
) -> Result<BioStatus, BioEnqueueError> {
|
||||
let bio = create_bio_from_segment(BioType::Write, bid, segment);
|
||||
let status = bio.submit_and_wait(self)?;
|
||||
Ok(status)
|
||||
@ -56,7 +65,7 @@ impl dyn BlockDevice {
|
||||
pub fn write_blocks_async(
|
||||
&self,
|
||||
bid: Bid,
|
||||
segment: &Segment,
|
||||
segment: &SegmentSlice,
|
||||
) -> Result<BioWaiter, BioEnqueueError> {
|
||||
let bio = create_bio_from_segment(BioType::Write, bid, segment);
|
||||
bio.submit(self)
|
||||
@ -96,7 +105,8 @@ impl VmIo for dyn BlockDevice {
|
||||
let segment = FrameAllocOptions::new(num_blocks as usize)
|
||||
.uninit(true)
|
||||
.alloc_contiguous()?;
|
||||
let bio_segment = BioSegment::from_segment(segment, offset % BLOCK_SIZE, read_len);
|
||||
let bio_segment =
|
||||
BioSegment::from_segment(segment.into(), offset % BLOCK_SIZE, read_len);
|
||||
|
||||
(
|
||||
Bio::new(
|
||||
@ -147,7 +157,7 @@ impl VmIo for dyn BlockDevice {
|
||||
.skip(offset % BLOCK_SIZE)
|
||||
.write_fallible(reader)
|
||||
.map_err(|(e, _)| e)?;
|
||||
let bio_segment = BioSegment::from_segment(segment, offset % BLOCK_SIZE, len);
|
||||
let bio_segment = BioSegment::from_segment(segment.into(), offset % BLOCK_SIZE, len);
|
||||
Bio::new(
|
||||
BioType::Write,
|
||||
Sid::from_offset(offset),
|
||||
@ -188,7 +198,7 @@ impl dyn BlockDevice {
|
||||
.writer()
|
||||
.skip(offset % BLOCK_SIZE)
|
||||
.write(&mut buf.into());
|
||||
let bio_segment = BioSegment::from_segment(segment, offset % BLOCK_SIZE, len);
|
||||
let bio_segment = BioSegment::from_segment(segment.into(), offset % BLOCK_SIZE, len);
|
||||
Bio::new(
|
||||
BioType::Write,
|
||||
Sid::from_offset(offset),
|
||||
@ -203,7 +213,7 @@ impl dyn BlockDevice {
|
||||
}
|
||||
|
||||
// TODO: Maybe we should have a builder for `Bio`.
|
||||
fn create_bio_from_segment(type_: BioType, bid: Bid, segment: &Segment) -> Bio {
|
||||
fn create_bio_from_segment(type_: BioType, bid: Bid, segment: &SegmentSlice) -> Bio {
|
||||
let bio_segment = BioSegment::from_segment(segment.clone(), 0, segment.nbytes());
|
||||
Bio::new(
|
||||
type_,
|
||||
|
@ -152,9 +152,9 @@ impl DmaPage {
|
||||
pool: Weak<DmaPool>,
|
||||
) -> Result<Self, ostd::Error> {
|
||||
let dma_stream = {
|
||||
let vm_segment = FrameAllocOptions::new(1).alloc_contiguous()?;
|
||||
let segment = FrameAllocOptions::new(1).alloc_contiguous()?;
|
||||
|
||||
DmaStream::map(vm_segment, direction, is_cache_coherent)
|
||||
DmaStream::map(segment, direction, is_cache_coherent)
|
||||
.map_err(|_| ostd::Error::AccessDenied)?
|
||||
};
|
||||
|
||||
|
@ -419,7 +419,8 @@ impl DeviceInner {
|
||||
.flat_map(|bio| {
|
||||
bio.segments().iter().map(|segment| {
|
||||
let dma_stream =
|
||||
DmaStream::map(segment.pages().clone(), dma_direction, false).unwrap();
|
||||
DmaStream::map(segment.pages().clone().into(), dma_direction, false)
|
||||
.unwrap();
|
||||
(dma_stream, segment.offset(), segment.nbytes())
|
||||
})
|
||||
})
|
||||
|
@ -81,10 +81,8 @@ impl VirtQueue {
|
||||
let desc_size = size_of::<Descriptor>() * size as usize;
|
||||
|
||||
let (seg1, seg2) = {
|
||||
let continue_segment = FrameAllocOptions::new(2).alloc_contiguous().unwrap();
|
||||
let seg1 = continue_segment.range(0..1);
|
||||
let seg2 = continue_segment.range(1..2);
|
||||
(seg1, seg2)
|
||||
let segment = FrameAllocOptions::new(2).alloc_contiguous().unwrap();
|
||||
segment.split(ostd::mm::PAGE_SIZE)
|
||||
};
|
||||
let desc_frame_ptr: SafePtr<Descriptor, DmaCoherent> =
|
||||
SafePtr::new(DmaCoherent::map(seg1, true).unwrap(), 0);
|
||||
|
@ -10,5 +10,6 @@ extern crate alloc;
|
||||
pub mod coeff;
|
||||
pub mod dup;
|
||||
pub mod safe_ptr;
|
||||
pub mod segment_slice;
|
||||
pub mod slot_vec;
|
||||
pub mod union_read_ptr;
|
||||
|
148
kernel/libs/aster-util/src/segment_slice.rs
Normal file
148
kernel/libs/aster-util/src/segment_slice.rs
Normal file
@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Provides [`SegmentSlice`] for quick duplication and slicing over [`Segment`].
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use core::ops::Range;
|
||||
|
||||
use ostd::{
|
||||
mm::{
|
||||
FallibleVmRead, FallibleVmWrite, Frame, Infallible, Paddr, Segment, VmIo, VmReader,
|
||||
VmWriter, PAGE_SIZE,
|
||||
},
|
||||
Error, Result,
|
||||
};
|
||||
|
||||
/// A reference to a slice of a [`Segment`].
|
||||
///
|
||||
/// Cloning a [`SegmentSlice`] is cheap, as it only increments one reference
|
||||
/// count. While cloning a [`Segment`] will increment the reference count of
|
||||
/// many underlying pages.
|
||||
///
|
||||
/// The downside is that the [`SegmentSlice`] requires heap allocation. Also,
|
||||
/// if any [`SegmentSlice`] of the original [`Segment`] is alive, all pages in
|
||||
/// the original [`Segment`], including the pages that are not referenced, will
|
||||
/// not be freed.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SegmentSlice {
|
||||
inner: Arc<Segment>,
|
||||
range: Range<usize>,
|
||||
}
|
||||
|
||||
impl SegmentSlice {
|
||||
/// Returns a part of the `Segment`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `range` is not within the range of this `Segment`,
|
||||
/// then the method panics.
|
||||
pub fn range(&self, range: Range<usize>) -> Self {
|
||||
let orig_range = &self.range;
|
||||
let adj_range = (range.start + orig_range.start)..(range.end + orig_range.start);
|
||||
assert!(!adj_range.is_empty() && adj_range.end <= orig_range.end);
|
||||
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
range: adj_range,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the start physical address.
|
||||
pub fn start_paddr(&self) -> Paddr {
|
||||
self.start_frame_index() * PAGE_SIZE
|
||||
}
|
||||
|
||||
/// Returns the end physical address.
|
||||
pub fn end_paddr(&self) -> Paddr {
|
||||
(self.start_frame_index() + self.nframes()) * PAGE_SIZE
|
||||
}
|
||||
|
||||
/// Returns the number of page frames.
|
||||
pub fn nframes(&self) -> usize {
|
||||
self.range.len()
|
||||
}
|
||||
|
||||
/// Returns the number of bytes.
|
||||
pub fn nbytes(&self) -> usize {
|
||||
self.nframes() * PAGE_SIZE
|
||||
}
|
||||
|
||||
/// Gets a reader for the slice.
|
||||
pub fn reader(&self) -> VmReader<'_, Infallible> {
|
||||
self.inner
|
||||
.reader()
|
||||
.skip(self.start_paddr() - self.inner.start_paddr())
|
||||
.limit(self.nbytes())
|
||||
}
|
||||
|
||||
/// Gets a writer for the slice.
|
||||
pub fn writer(&self) -> VmWriter<'_, Infallible> {
|
||||
self.inner
|
||||
.writer()
|
||||
.skip(self.start_paddr() - self.inner.start_paddr())
|
||||
.limit(self.nbytes())
|
||||
}
|
||||
|
||||
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<Segment> for SegmentSlice {
|
||||
fn from(segment: Segment) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(segment),
|
||||
range: 0..1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SegmentSlice> for Segment {
|
||||
fn from(slice: SegmentSlice) -> Self {
|
||||
let start = slice.range.start * PAGE_SIZE;
|
||||
let end = slice.range.end * PAGE_SIZE;
|
||||
slice.inner.slice(&(start..end))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frame> for SegmentSlice {
|
||||
fn from(frame: Frame) -> Self {
|
||||
SegmentSlice::from(Segment::from(frame))
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ mod test {
|
||||
BlockDevice, BlockDeviceMeta,
|
||||
};
|
||||
use ostd::{
|
||||
mm::{FrameAllocOptions, Segment, VmIo},
|
||||
mm::{FrameAllocOptions, Segment, VmIo, PAGE_SIZE},
|
||||
prelude::*,
|
||||
};
|
||||
use rand::{rngs::SmallRng, RngCore, SeedableRng};
|
||||
@ -48,7 +48,7 @@ mod test {
|
||||
}
|
||||
|
||||
pub fn sectors_count(&self) -> usize {
|
||||
self.0.nframes() * (PAGE_SIZE / SECTOR_SIZE)
|
||||
self.0.nbytes() / SECTOR_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,13 +111,10 @@ mod test {
|
||||
|
||||
/// Read exfat disk image
|
||||
fn new_vm_segment_from_image() -> Segment {
|
||||
let vm_segment = {
|
||||
FrameAllocOptions::new(EXFAT_IMAGE.len() / PAGE_SIZE)
|
||||
.is_contiguous(true)
|
||||
.uninit(true)
|
||||
.alloc_contiguous()
|
||||
.unwrap()
|
||||
};
|
||||
let vm_segment = FrameAllocOptions::new(EXFAT_IMAGE.len().div_ceil(PAGE_SIZE))
|
||||
.uninit(true)
|
||||
.alloc_contiguous()
|
||||
.unwrap();
|
||||
|
||||
vm_segment.write_bytes(0, EXFAT_IMAGE).unwrap();
|
||||
vm_segment
|
||||
|
@ -28,7 +28,7 @@ struct BlockGroupImpl {
|
||||
impl BlockGroup {
|
||||
/// Loads and constructs a block group.
|
||||
pub fn load(
|
||||
group_descriptors_segment: &Segment,
|
||||
group_descriptors_segment: &SegmentSlice,
|
||||
idx: usize,
|
||||
block_device: &dyn BlockDevice,
|
||||
super_block: &SuperBlock,
|
||||
|
@ -23,7 +23,7 @@ pub struct Ext2 {
|
||||
blocks_per_group: Ext2Bid,
|
||||
inode_size: usize,
|
||||
block_size: usize,
|
||||
group_descriptors_segment: Segment,
|
||||
group_descriptors_segment: SegmentSlice,
|
||||
self_ref: Weak<Self>,
|
||||
}
|
||||
|
||||
@ -48,7 +48,8 @@ impl Ext2 {
|
||||
.div_ceil(BLOCK_SIZE);
|
||||
let segment = FrameAllocOptions::new(npages)
|
||||
.uninit(true)
|
||||
.alloc_contiguous()?;
|
||||
.alloc_contiguous()?
|
||||
.into();
|
||||
match block_device.read_blocks(super_block.group_descriptors_bid(0), &segment)? {
|
||||
BioStatus::Complete => (),
|
||||
err_status => {
|
||||
@ -61,7 +62,7 @@ impl Ext2 {
|
||||
// Load the block groups information
|
||||
let load_block_groups = |fs: Weak<Ext2>,
|
||||
block_device: &dyn BlockDevice,
|
||||
group_descriptors_segment: &Segment|
|
||||
group_descriptors_segment: &SegmentSlice|
|
||||
-> Result<Vec<BlockGroup>> {
|
||||
let block_groups_count = super_block.block_groups_count() as usize;
|
||||
let mut block_groups = Vec::with_capacity(block_groups_count);
|
||||
@ -297,7 +298,7 @@ impl Ext2 {
|
||||
}
|
||||
|
||||
/// Reads contiguous blocks starting from the `bid` synchronously.
|
||||
pub(super) fn read_blocks(&self, bid: Ext2Bid, segment: &Segment) -> Result<()> {
|
||||
pub(super) fn read_blocks(&self, bid: Ext2Bid, segment: &SegmentSlice) -> Result<()> {
|
||||
let status = self
|
||||
.block_device
|
||||
.read_blocks(Bid::new(bid as u64), segment)?;
|
||||
@ -308,7 +309,11 @@ impl Ext2 {
|
||||
}
|
||||
|
||||
/// Reads contiguous blocks starting from the `bid` asynchronously.
|
||||
pub(super) fn read_blocks_async(&self, bid: Ext2Bid, segment: &Segment) -> Result<BioWaiter> {
|
||||
pub(super) fn read_blocks_async(
|
||||
&self,
|
||||
bid: Ext2Bid,
|
||||
segment: &SegmentSlice,
|
||||
) -> Result<BioWaiter> {
|
||||
let waiter = self
|
||||
.block_device
|
||||
.read_blocks_async(Bid::new(bid as u64), segment)?;
|
||||
@ -333,7 +338,7 @@ impl Ext2 {
|
||||
}
|
||||
|
||||
/// Writes contiguous blocks starting from the `bid` synchronously.
|
||||
pub(super) fn write_blocks(&self, bid: Ext2Bid, segment: &Segment) -> Result<()> {
|
||||
pub(super) fn write_blocks(&self, bid: Ext2Bid, segment: &SegmentSlice) -> Result<()> {
|
||||
let status = self
|
||||
.block_device
|
||||
.write_blocks(Bid::new(bid as u64), segment)?;
|
||||
@ -344,7 +349,11 @@ impl Ext2 {
|
||||
}
|
||||
|
||||
/// Writes contiguous blocks starting from the `bid` asynchronously.
|
||||
pub(super) fn write_blocks_async(&self, bid: Ext2Bid, segment: &Segment) -> Result<BioWaiter> {
|
||||
pub(super) fn write_blocks_async(
|
||||
&self,
|
||||
bid: Ext2Bid,
|
||||
segment: &SegmentSlice,
|
||||
) -> Result<BioWaiter> {
|
||||
let waiter = self
|
||||
.block_device
|
||||
.write_blocks_async(Bid::new(bid as u64), segment)?;
|
||||
|
@ -944,7 +944,8 @@ impl Inner {
|
||||
let buf_nblocks = read_len / BLOCK_SIZE;
|
||||
let segment = FrameAllocOptions::new(buf_nblocks)
|
||||
.uninit(true)
|
||||
.alloc_contiguous()?;
|
||||
.alloc_contiguous()?
|
||||
.into();
|
||||
|
||||
self.inode_impl.read_blocks(start_bid, &segment)?;
|
||||
segment.read(0, writer)?;
|
||||
@ -986,7 +987,7 @@ impl Inner {
|
||||
.uninit(true)
|
||||
.alloc_contiguous()?;
|
||||
segment.write(0, reader)?;
|
||||
segment
|
||||
segment.into()
|
||||
};
|
||||
|
||||
self.inode_impl.write_blocks(start_bid, &segment)?;
|
||||
@ -1128,7 +1129,7 @@ impl InodeImpl_ {
|
||||
self.inode().fs()
|
||||
}
|
||||
|
||||
pub fn read_blocks_async(&self, bid: Ext2Bid, blocks: &Segment) -> Result<BioWaiter> {
|
||||
pub fn read_blocks_async(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<BioWaiter> {
|
||||
let nblocks = blocks.nframes();
|
||||
let mut segments = Vec::new();
|
||||
|
||||
@ -1183,14 +1184,14 @@ impl InodeImpl_ {
|
||||
Ok(bio_waiter)
|
||||
}
|
||||
|
||||
pub fn read_blocks(&self, bid: Ext2Bid, blocks: &Segment) -> Result<()> {
|
||||
pub fn read_blocks(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<()> {
|
||||
match self.read_blocks_async(bid, blocks)?.wait() {
|
||||
Some(BioStatus::Complete) => Ok(()),
|
||||
_ => return_errno!(Errno::EIO),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_blocks_async(&self, bid: Ext2Bid, blocks: &Segment) -> Result<BioWaiter> {
|
||||
pub fn write_blocks_async(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<BioWaiter> {
|
||||
let nblocks = blocks.nframes();
|
||||
let mut bio_waiter = BioWaiter::new();
|
||||
|
||||
@ -1214,7 +1215,7 @@ impl InodeImpl_ {
|
||||
Ok(bio_waiter)
|
||||
}
|
||||
|
||||
pub fn write_blocks(&self, bid: Ext2Bid, blocks: &Segment) -> Result<()> {
|
||||
pub fn write_blocks(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<()> {
|
||||
match self.write_blocks_async(bid, blocks)?.wait() {
|
||||
Some(BioStatus::Complete) => Ok(()),
|
||||
_ => return_errno!(Errno::EIO),
|
||||
@ -1873,20 +1874,20 @@ impl InodeImpl {
|
||||
}
|
||||
|
||||
/// Reads one or multiple blocks to the segment start from `bid` asynchronously.
|
||||
pub fn read_blocks_async(&self, bid: Ext2Bid, blocks: &Segment) -> Result<BioWaiter> {
|
||||
pub fn read_blocks_async(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<BioWaiter> {
|
||||
self.0.read().read_blocks_async(bid, blocks)
|
||||
}
|
||||
|
||||
pub fn read_blocks(&self, bid: Ext2Bid, blocks: &Segment) -> Result<()> {
|
||||
pub fn read_blocks(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<()> {
|
||||
self.0.read().read_blocks(bid, blocks)
|
||||
}
|
||||
|
||||
/// Writes one or multiple blocks from the segment start from `bid` asynchronously.
|
||||
pub fn write_blocks_async(&self, bid: Ext2Bid, blocks: &Segment) -> Result<BioWaiter> {
|
||||
pub fn write_blocks_async(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<BioWaiter> {
|
||||
self.0.read().write_blocks_async(bid, blocks)
|
||||
}
|
||||
|
||||
pub fn write_blocks(&self, bid: Ext2Bid, blocks: &Segment) -> Result<()> {
|
||||
pub fn write_blocks(&self, bid: Ext2Bid, blocks: &SegmentSlice) -> Result<()> {
|
||||
self.0.read().write_blocks(bid, blocks)
|
||||
}
|
||||
|
||||
@ -1962,7 +1963,9 @@ impl InodeImpl {
|
||||
|
||||
// TODO: If we can persist the `blocks_hole_desc`, Can we avoid zeroing all the holes on the device?
|
||||
debug_assert!(max_batch_len > 0);
|
||||
let zeroed_segment = FrameAllocOptions::new(max_batch_len).alloc_contiguous()?;
|
||||
let zeroed_segment: SegmentSlice = FrameAllocOptions::new(max_batch_len)
|
||||
.alloc_contiguous()?
|
||||
.into();
|
||||
for (start_bid, batch_len) in data_hole_batches {
|
||||
inner.write_blocks(start_bid, &zeroed_segment.range(0..batch_len))?;
|
||||
}
|
||||
@ -2001,12 +2004,12 @@ impl InodeImpl {
|
||||
impl PageCacheBackend for InodeImpl {
|
||||
fn read_page_async(&self, idx: usize, frame: &Frame) -> Result<BioWaiter> {
|
||||
let bid = idx as Ext2Bid;
|
||||
self.read_blocks_async(bid, &Segment::from(frame.clone()))
|
||||
self.read_blocks_async(bid, &SegmentSlice::from(frame.clone()))
|
||||
}
|
||||
|
||||
fn write_page_async(&self, idx: usize, frame: &Frame) -> Result<BioWaiter> {
|
||||
let bid = idx as Ext2Bid;
|
||||
self.write_blocks_async(bid, &Segment::from(frame.clone()))
|
||||
self.write_blocks_async(bid, &SegmentSlice::from(frame.clone()))
|
||||
}
|
||||
|
||||
fn npages(&self) -> usize {
|
||||
|
@ -12,8 +12,9 @@ pub(super) use aster_block::{
|
||||
BlockDevice, BLOCK_SIZE,
|
||||
};
|
||||
pub(super) use aster_rights::Full;
|
||||
pub(super) use aster_util::segment_slice::SegmentSlice;
|
||||
pub(super) use ostd::{
|
||||
mm::{Frame, FrameAllocOptions, Segment, VmIo},
|
||||
mm::{Frame, FrameAllocOptions, VmIo},
|
||||
sync::{RwMutex, RwMutexReadGuard, RwMutexWriteGuard},
|
||||
};
|
||||
pub(super) use static_assertions::const_assert;
|
||||
|
@ -35,13 +35,15 @@ pub fn main() {
|
||||
}
|
||||
|
||||
fn create_user_space(program: &[u8]) -> UserSpace {
|
||||
let nframes = program.len().align_up(PAGE_SIZE) / PAGE_SIZE;
|
||||
let nbytes = program.len().align_up(PAGE_SIZE);
|
||||
let user_pages = {
|
||||
let vm_frames = FrameAllocOptions::new(nframes).alloc().unwrap();
|
||||
let segment = FrameAllocOptions::new(nbytes / PAGE_SIZE)
|
||||
.alloc_contiguous()
|
||||
.unwrap();
|
||||
// Physical memory pages can be only accessed
|
||||
// via the Frame abstraction.
|
||||
vm_frames.write_bytes(0, program).unwrap();
|
||||
vm_frames
|
||||
// via the `Frame` or `Segment` abstraction.
|
||||
segment.write_bytes(0, program).unwrap();
|
||||
segment
|
||||
};
|
||||
let user_address_space = {
|
||||
const MAP_ADDR: Vaddr = 0x0040_0000; // The map addr for statically-linked executable
|
||||
@ -50,9 +52,7 @@ fn create_user_space(program: &[u8]) -> UserSpace {
|
||||
// created and manipulated safely through
|
||||
// the `VmSpace` abstraction.
|
||||
let vm_space = VmSpace::new();
|
||||
let mut cursor = vm_space
|
||||
.cursor_mut(&(MAP_ADDR..MAP_ADDR + nframes * PAGE_SIZE))
|
||||
.unwrap();
|
||||
let mut cursor = vm_space.cursor_mut(&(MAP_ADDR..MAP_ADDR + nbytes)).unwrap();
|
||||
let map_prop = PageProperty::new(PageFlags::RWX, CachePolicy::Writeback);
|
||||
for frame in user_pages {
|
||||
cursor.map(frame, map_prop);
|
||||
|
@ -57,7 +57,7 @@ impl DmaCoherent {
|
||||
vm_segment: Segment,
|
||||
is_cache_coherent: bool,
|
||||
) -> core::result::Result<Self, DmaError> {
|
||||
let frame_count = vm_segment.nframes();
|
||||
let frame_count = vm_segment.nbytes() / PAGE_SIZE;
|
||||
let start_paddr = vm_segment.start_paddr();
|
||||
if !check_and_insert_dma_mapping(start_paddr, frame_count) {
|
||||
return Err(DmaError::AlreadyMapped);
|
||||
@ -109,6 +109,11 @@ impl DmaCoherent {
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the number of bytes in the DMA mapping.
|
||||
pub fn nbytes(&self) -> usize {
|
||||
self.inner.vm_segment.nbytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasDaddr for DmaCoherent {
|
||||
@ -126,7 +131,7 @@ impl Deref for DmaCoherent {
|
||||
|
||||
impl Drop for DmaCoherentInner {
|
||||
fn drop(&mut self) {
|
||||
let frame_count = self.vm_segment.nframes();
|
||||
let frame_count = self.vm_segment.nbytes() / PAGE_SIZE;
|
||||
let start_paddr = self.vm_segment.start_paddr();
|
||||
// Ensure that the addresses used later will not overflow
|
||||
start_paddr.checked_add(frame_count * PAGE_SIZE).unwrap();
|
||||
@ -244,7 +249,7 @@ mod test {
|
||||
.is_contiguous(true)
|
||||
.alloc_contiguous()
|
||||
.unwrap();
|
||||
let vm_segment_child = vm_segment_parent.range(0..1);
|
||||
let vm_segment_child = vm_segment_parent.slice(&(0..PAGE_SIZE));
|
||||
let _dma_coherent_parent = DmaCoherent::map(vm_segment_parent, false);
|
||||
let dma_coherent_child = DmaCoherent::map(vm_segment_child, false);
|
||||
assert!(dma_coherent_child.is_err());
|
||||
|
@ -63,7 +63,7 @@ impl DmaStream {
|
||||
direction: DmaDirection,
|
||||
is_cache_coherent: bool,
|
||||
) -> Result<Self, DmaError> {
|
||||
let frame_count = vm_segment.nframes();
|
||||
let frame_count = vm_segment.nbytes() / PAGE_SIZE;
|
||||
let start_paddr = vm_segment.start_paddr();
|
||||
if !check_and_insert_dma_mapping(start_paddr, frame_count) {
|
||||
return Err(DmaError::AlreadyMapped);
|
||||
@ -119,7 +119,7 @@ impl DmaStream {
|
||||
|
||||
/// Returns the number of frames
|
||||
pub fn nframes(&self) -> usize {
|
||||
self.inner.vm_segment.nframes()
|
||||
self.inner.vm_segment.nbytes() / PAGE_SIZE
|
||||
}
|
||||
|
||||
/// Returns the number of bytes
|
||||
@ -171,7 +171,7 @@ impl HasDaddr for DmaStream {
|
||||
|
||||
impl Drop for DmaStreamInner {
|
||||
fn drop(&mut self) {
|
||||
let frame_count = self.vm_segment.nframes();
|
||||
let frame_count = self.vm_segment.nbytes() / PAGE_SIZE;
|
||||
let start_paddr = self.vm_segment.start_paddr();
|
||||
// Ensure that the addresses used later will not overflow
|
||||
start_paddr.checked_add(frame_count * PAGE_SIZE).unwrap();
|
||||
@ -333,7 +333,7 @@ mod test {
|
||||
.is_contiguous(true)
|
||||
.alloc_contiguous()
|
||||
.unwrap();
|
||||
let vm_segment_child = vm_segment_parent.range(0..1);
|
||||
let vm_segment_child = vm_segment_parent.slice(&(0..PAGE_SIZE));
|
||||
let dma_stream_parent =
|
||||
DmaStream::map(vm_segment_parent, DmaDirection::Bidirectional, false);
|
||||
let dma_stream_child = DmaStream::map(vm_segment_child, DmaDirection::Bidirectional, false);
|
||||
|
@ -9,23 +9,20 @@
|
||||
//! read and written by the kernel or the user.
|
||||
|
||||
pub mod options;
|
||||
pub mod segment;
|
||||
mod segment;
|
||||
|
||||
use core::mem::ManuallyDrop;
|
||||
|
||||
pub use segment::Segment;
|
||||
|
||||
use super::{
|
||||
page::{
|
||||
meta::{FrameMeta, MetaSlot, PageMeta, PageUsage},
|
||||
DynPage, Page,
|
||||
},
|
||||
Infallible,
|
||||
use super::page::{
|
||||
meta::{FrameMeta, MetaSlot, PageMeta, PageUsage},
|
||||
DynPage, Page,
|
||||
};
|
||||
use crate::{
|
||||
mm::{
|
||||
io::{FallibleVmRead, FallibleVmWrite, VmIo, VmReader, VmWriter},
|
||||
paddr_to_vaddr, HasPaddr, Paddr, PAGE_SIZE,
|
||||
paddr_to_vaddr, HasPaddr, Infallible, Paddr, PAGE_SIZE,
|
||||
},
|
||||
Error, Result,
|
||||
};
|
||||
@ -181,54 +178,6 @@ impl VmIo for Frame {
|
||||
}
|
||||
}
|
||||
|
||||
impl VmIo for alloc::vec::Vec<Frame> {
|
||||
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()> {
|
||||
// Do bound check with potential integer overflow in mind
|
||||
let max_offset = offset.checked_add(writer.avail()).ok_or(Error::Overflow)?;
|
||||
if max_offset > self.len() * PAGE_SIZE {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
|
||||
let num_skip_pages = offset / PAGE_SIZE;
|
||||
let mut start = offset % PAGE_SIZE;
|
||||
for frame in self.iter().skip(num_skip_pages) {
|
||||
let read_len = frame
|
||||
.reader()
|
||||
.skip(start)
|
||||
.read_fallible(writer)
|
||||
.map_err(|(e, _)| e)?;
|
||||
if read_len == 0 {
|
||||
break;
|
||||
}
|
||||
start = 0;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()> {
|
||||
// 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.len() * PAGE_SIZE {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
|
||||
let num_skip_pages = offset / PAGE_SIZE;
|
||||
let mut start = offset % PAGE_SIZE;
|
||||
for frame in self.iter().skip(num_skip_pages) {
|
||||
let write_len = frame
|
||||
.writer()
|
||||
.skip(start)
|
||||
.write_fallible(reader)
|
||||
.map_err(|(e, _)| e)?;
|
||||
if write_len == 0 {
|
||||
break;
|
||||
}
|
||||
start = 0;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PageMeta for FrameMeta {
|
||||
const USAGE: PageUsage = PageUsage::Frame;
|
||||
|
||||
|
@ -1,27 +1,33 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! A contiguous range of page frames.
|
||||
//! A contiguous segment of untyped memory pages.
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use core::ops::Range;
|
||||
|
||||
use super::Frame;
|
||||
use crate::{
|
||||
mm::{
|
||||
page::{cont_pages::ContPages, meta::FrameMeta, Page},
|
||||
FallibleVmRead, FallibleVmWrite, HasPaddr, Infallible, Paddr, VmIo, VmReader, VmWriter,
|
||||
PAGE_SIZE,
|
||||
io::{FallibleVmRead, FallibleVmWrite},
|
||||
page::{meta::FrameMeta, ContPages},
|
||||
Frame, HasPaddr, Infallible, Paddr, VmIo, VmReader, VmWriter,
|
||||
},
|
||||
Error, Result,
|
||||
};
|
||||
|
||||
/// A handle to a contiguous range of page frames (physical memory pages).
|
||||
/// A contiguous segment of untyped memory pages.
|
||||
///
|
||||
/// A cloned `Segment` refers to the same page frames as the original.
|
||||
/// As the original and cloned instances point to the same physical address,
|
||||
/// they are treated as equal to each other.
|
||||
/// A [`Segment`] object is a handle to a contiguous range of untyped memory
|
||||
/// pages, and the underlying pages can be shared among multiple threads.
|
||||
/// [`Segment::slice`] can be used to clone a slice of the segment (also can be
|
||||
/// used to clone the entire range). Reference counts are maintained for each
|
||||
/// page in the segment. So cloning the handle may not be cheap as it
|
||||
/// increments the reference count of all the cloned pages.
|
||||
///
|
||||
/// #Example
|
||||
/// Other [`Frame`] handles can also refer to the pages in the segment. And
|
||||
/// the segment can be iterated over to get all the frames in it.
|
||||
///
|
||||
/// To allocate a segment, use [`crate::mm::FrameAllocator`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let vm_segment = FrameAllocOptions::new(2)
|
||||
@ -29,88 +35,102 @@ use crate::{
|
||||
/// .alloc_contiguous()?;
|
||||
/// vm_segment.write_bytes(0, buf)?;
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct Segment {
|
||||
inner: Arc<ContPages<FrameMeta>>,
|
||||
range: Range<usize>,
|
||||
pages: ContPages<FrameMeta>,
|
||||
}
|
||||
|
||||
impl HasPaddr for Segment {
|
||||
fn paddr(&self) -> Paddr {
|
||||
self.start_paddr()
|
||||
self.pages.start_paddr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Segment {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
pages: self.pages.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Segment {
|
||||
/// Returns a part of the `Segment`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `range` is not within the range of this `Segment`,
|
||||
/// then the method panics.
|
||||
pub fn range(&self, range: Range<usize>) -> Self {
|
||||
let orig_range = &self.range;
|
||||
let adj_range = (range.start + orig_range.start)..(range.end + orig_range.start);
|
||||
assert!(!adj_range.is_empty() && adj_range.end <= orig_range.end);
|
||||
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
range: adj_range,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the start physical address.
|
||||
pub fn start_paddr(&self) -> Paddr {
|
||||
self.start_frame_index() * PAGE_SIZE
|
||||
self.pages.start_paddr()
|
||||
}
|
||||
|
||||
/// Returns the end physical address.
|
||||
pub fn end_paddr(&self) -> Paddr {
|
||||
(self.start_frame_index() + self.nframes()) * PAGE_SIZE
|
||||
self.pages.end_paddr()
|
||||
}
|
||||
|
||||
/// Returns the number of page frames.
|
||||
pub fn nframes(&self) -> usize {
|
||||
self.range.len()
|
||||
}
|
||||
|
||||
/// Returns the number of bytes.
|
||||
/// Returns the number of bytes in it.
|
||||
pub fn nbytes(&self) -> usize {
|
||||
self.nframes() * PAGE_SIZE
|
||||
self.pages.nbytes()
|
||||
}
|
||||
|
||||
fn start_frame_index(&self) -> usize {
|
||||
self.inner.start_paddr() / PAGE_SIZE + self.range.start
|
||||
/// Split the segment into two at the given byte offset from the start.
|
||||
///
|
||||
/// The resulting segments cannot be empty. So the byte offset cannot be
|
||||
/// neither zero nor the length of the segment.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function panics if the byte offset is out of bounds, at either ends, or
|
||||
/// not base-page-aligned.
|
||||
pub fn split(self, offset: usize) -> (Self, Self) {
|
||||
let (left, right) = self.pages.split(offset);
|
||||
(Self { pages: left }, Self { pages: right })
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the starting virtual address of the `Segment`.
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
super::paddr_to_vaddr(self.start_paddr()) as *const u8
|
||||
/// Get an extra handle to the segment in the byte range.
|
||||
///
|
||||
/// The sliced byte range in indexed by the offset from the start of the
|
||||
/// segment. The resulting segment holds extra reference counts.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function panics if the byte range is out of bounds, or if any of
|
||||
/// the ends of the byte range is not base-page aligned.
|
||||
pub fn slice(&self, range: &Range<usize>) -> Self {
|
||||
Self {
|
||||
pages: self.pages.slice(range),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable raw pointer to the starting virtual address of the `Segment`.
|
||||
pub fn as_mut_ptr(&self) -> *mut u8 {
|
||||
super::paddr_to_vaddr(self.start_paddr()) as *mut u8
|
||||
/// Gets a [`VmReader`] to read from the segment from the beginning to the end.
|
||||
pub fn reader(&self) -> VmReader<'_, Infallible> {
|
||||
let ptr = super::paddr_to_vaddr(self.start_paddr()) as *const u8;
|
||||
// SAFETY:
|
||||
// - The memory range points to untyped memory.
|
||||
// - The segment is alive during the lifetime `'a`.
|
||||
// - Using `VmReader` and `VmWriter` is the only way to access the segment.
|
||||
unsafe { VmReader::from_kernel_space(ptr, self.nbytes()) }
|
||||
}
|
||||
|
||||
/// Gets a [`VmWriter`] to write to the segment from the beginning to the end.
|
||||
pub fn writer(&self) -> VmWriter<'_, Infallible> {
|
||||
let ptr = super::paddr_to_vaddr(self.start_paddr()) as *mut u8;
|
||||
// SAFETY:
|
||||
// - The memory range points to untyped memory.
|
||||
// - The segment is alive during the lifetime `'a`.
|
||||
// - Using `VmReader` and `VmWriter` is the only way to access the segment.
|
||||
unsafe { VmWriter::from_kernel_space(ptr, self.nbytes()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Segment {
|
||||
/// Returns a reader to read data from it.
|
||||
pub fn reader(&'a self) -> VmReader<'a, Infallible> {
|
||||
// SAFETY:
|
||||
// - The memory range points to untyped memory.
|
||||
// - The segment is alive during the lifetime `'a`.
|
||||
// - Using `VmReader` and `VmWriter` is the only way to access the segment.
|
||||
unsafe { VmReader::from_kernel_space(self.as_ptr(), self.nbytes()) }
|
||||
impl From<Frame> for Segment {
|
||||
fn from(frame: Frame) -> Self {
|
||||
Self {
|
||||
pages: ContPages::from(frame.page),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a writer to write data into it.
|
||||
pub fn writer(&'a self) -> VmWriter<'a, Infallible> {
|
||||
// SAFETY:
|
||||
// - The memory range points to untyped memory.
|
||||
// - The segment is alive during the lifetime `'a`.
|
||||
// - Using `VmReader` and `VmWriter` is the only way to access the segment.
|
||||
unsafe { VmWriter::from_kernel_space(self.as_mut_ptr(), self.nbytes()) }
|
||||
impl From<ContPages<FrameMeta>> for Segment {
|
||||
fn from(pages: ContPages<FrameMeta>) -> Self {
|
||||
Self { pages }
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,21 +168,10 @@ impl VmIo for Segment {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frame> for Segment {
|
||||
fn from(frame: Frame) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Page::<FrameMeta>::from(frame).into()),
|
||||
range: 0..1,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Iterator for Segment {
|
||||
type Item = Frame;
|
||||
|
||||
impl From<ContPages<FrameMeta>> for Segment {
|
||||
fn from(cont_pages: ContPages<FrameMeta>) -> Self {
|
||||
let len = cont_pages.len();
|
||||
Self {
|
||||
inner: Arc::new(cont_pages),
|
||||
range: 0..len / PAGE_SIZE,
|
||||
}
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.pages.next().map(|page| Frame { page })
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
use alloc::vec::Vec;
|
||||
use core::{mem::ManuallyDrop, ops::Range};
|
||||
|
||||
use super::{meta::PageMeta, Page};
|
||||
use super::{inc_page_ref_count, meta::PageMeta, Page};
|
||||
use crate::mm::{Paddr, PAGE_SIZE};
|
||||
|
||||
/// A contiguous range of physical memory pages.
|
||||
@ -25,16 +25,31 @@ pub struct ContPages<M: PageMeta> {
|
||||
|
||||
impl<M: PageMeta> Drop for ContPages<M> {
|
||||
fn drop(&mut self) {
|
||||
for i in self.range.clone().step_by(PAGE_SIZE) {
|
||||
for paddr in self.range.clone().step_by(PAGE_SIZE) {
|
||||
// SAFETY: for each page there would be a forgotten handle
|
||||
// when creating the `ContPages` object.
|
||||
drop(unsafe { Page::<M>::from_raw(i) });
|
||||
drop(unsafe { Page::<M>::from_raw(paddr) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: PageMeta> Clone for ContPages<M> {
|
||||
fn clone(&self) -> Self {
|
||||
for paddr in self.range.clone().step_by(PAGE_SIZE) {
|
||||
// SAFETY: for each page there would be a forgotten handle
|
||||
// when creating the `ContPages` object, so we already have
|
||||
// reference counts for the pages.
|
||||
unsafe { inc_page_ref_count(paddr) };
|
||||
}
|
||||
Self {
|
||||
range: self.range.clone(),
|
||||
_marker: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: PageMeta> ContPages<M> {
|
||||
/// Create a new `ContPages` from unused pages.
|
||||
/// Creates a new `ContPages` from unused pages.
|
||||
///
|
||||
/// The caller must provide a closure to initialize metadata for all the pages.
|
||||
/// The closure receives the physical address of the page and returns the
|
||||
@ -49,8 +64,8 @@ impl<M: PageMeta> ContPages<M> {
|
||||
where
|
||||
F: FnMut(Paddr) -> M,
|
||||
{
|
||||
for i in range.clone().step_by(PAGE_SIZE) {
|
||||
let _ = ManuallyDrop::new(Page::<M>::from_unused(i, metadata_fn(i)));
|
||||
for paddr in range.clone().step_by(PAGE_SIZE) {
|
||||
let _ = ManuallyDrop::new(Page::<M>::from_unused(paddr, metadata_fn(paddr)));
|
||||
}
|
||||
Self {
|
||||
range,
|
||||
@ -58,20 +73,76 @@ impl<M: PageMeta> ContPages<M> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the start physical address of the contiguous pages.
|
||||
/// Gets the start physical address of the contiguous pages.
|
||||
pub fn start_paddr(&self) -> Paddr {
|
||||
self.range.start
|
||||
}
|
||||
|
||||
/// Get the end physical address of the contiguous pages.
|
||||
/// Gets the end physical address of the contiguous pages.
|
||||
pub fn end_paddr(&self) -> Paddr {
|
||||
self.range.end
|
||||
}
|
||||
|
||||
/// Get the length in bytes of the contiguous pages.
|
||||
pub fn len(&self) -> usize {
|
||||
/// Gets the length in bytes of the contiguous pages.
|
||||
pub fn nbytes(&self) -> usize {
|
||||
self.range.end - self.range.start
|
||||
}
|
||||
|
||||
/// Splits the pages into two at the given byte offset from the start.
|
||||
///
|
||||
/// The resulting pages cannot be empty. So the offset cannot be neither
|
||||
/// zero nor the length of the pages.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function panics if the offset is out of bounds, at either ends, or
|
||||
/// not base-page-aligned.
|
||||
pub fn split(self, offset: usize) -> (Self, Self) {
|
||||
assert!(offset % PAGE_SIZE == 0);
|
||||
assert!(0 < offset && offset < self.nbytes());
|
||||
|
||||
let old = ManuallyDrop::new(self);
|
||||
let at = old.range.start + offset;
|
||||
|
||||
(
|
||||
Self {
|
||||
range: old.range.start..at,
|
||||
_marker: core::marker::PhantomData,
|
||||
},
|
||||
Self {
|
||||
range: at..old.range.end,
|
||||
_marker: core::marker::PhantomData,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Gets an extra handle to the pages in the byte offset range.
|
||||
///
|
||||
/// The sliced byte offset range in indexed by the offset from the start of
|
||||
/// the contiguous pages. The resulting pages holds extra reference counts.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function panics if the byte offset range is out of bounds, or if
|
||||
/// any of the ends of the byte offset range is not base-page aligned.
|
||||
pub fn slice(&self, range: &Range<usize>) -> Self {
|
||||
assert!(range.start % PAGE_SIZE == 0 && range.end % PAGE_SIZE == 0);
|
||||
let start = self.range.start + range.start;
|
||||
let end = self.range.start + range.end;
|
||||
assert!(start <= end && end <= self.range.end);
|
||||
|
||||
for paddr in (start..end).step_by(PAGE_SIZE) {
|
||||
// SAFETY: We already have reference counts for the pages since
|
||||
// for each page there would be a forgotten handle when creating
|
||||
// the `ContPages` object.
|
||||
unsafe { inc_page_ref_count(paddr) };
|
||||
}
|
||||
|
||||
Self {
|
||||
range: start..end,
|
||||
_marker: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: PageMeta> From<Page<M>> for ContPages<M> {
|
||||
@ -100,3 +171,21 @@ impl<M: PageMeta> From<ContPages<M>> for Vec<Page<M>> {
|
||||
vector
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: PageMeta> Iterator for ContPages<M> {
|
||||
type Item = Page<M>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.range.start < self.range.end {
|
||||
// SAFETY: each page in the range would be a handle forgotten
|
||||
// when creating the `ContPages` object.
|
||||
let page = unsafe { Page::<M>::from_raw(self.range.start) };
|
||||
self.range.start += PAGE_SIZE;
|
||||
// The end cannot be non-page-aligned.
|
||||
debug_assert!(self.range.start <= self.range.end);
|
||||
Some(page)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
//! the handle only a pointer to the metadata.
|
||||
|
||||
pub mod allocator;
|
||||
pub mod cont_pages;
|
||||
mod cont_pages;
|
||||
pub mod meta;
|
||||
|
||||
use core::{
|
||||
|
Reference in New Issue
Block a user