diff --git a/ostd/src/mm/frame/frame_ref.rs b/ostd/src/mm/frame/frame_ref.rs new file mode 100644 index 000000000..c72f0c96e --- /dev/null +++ b/ostd/src/mm/frame/frame_ref.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MPL-2.0 + +use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::NonNull}; + +use super::{ + meta::{AnyFrameMeta, MetaSlot}, + Frame, +}; +use crate::{mm::Paddr, sync::non_null::NonNullPtr}; + +/// A struct that can work as `&'a Frame`. +pub struct FrameRef<'a, M: AnyFrameMeta + ?Sized> { + inner: ManuallyDrop>, + _marker: PhantomData<&'a Frame>, +} + +impl FrameRef<'_, M> { + /// Borrows the [`Frame`] at the physical address as a [`FrameRef`]. + /// + /// # Safety + /// + /// The caller must ensure that: + /// - the frame outlives the created reference, so that the reference can + /// be seen as borrowed from that frame. + /// - the type of the [`FrameRef`] (`M`) matches the borrowed frame. + pub(in crate::mm) unsafe fn borrow_paddr(raw: Paddr) -> Self { + Self { + // SAFETY: The caller ensures the safety. + inner: ManuallyDrop::new(unsafe { Frame::from_raw(raw) }), + _marker: PhantomData, + } + } +} + +impl Deref for FrameRef<'_, M> { + type Target = Frame; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +// SAFETY: `Frame` is essentially a `*const MetaSlot` that could be used as a non-null +// `*const` pointer. +unsafe impl NonNullPtr for Frame { + type Target = PhantomData; + + type Ref<'a> + = FrameRef<'a, M> + where + Self: 'a; + + const ALIGN_BITS: u32 = core::mem::align_of::().trailing_zeros(); + + fn into_raw(self) -> NonNull { + let ptr = NonNull::new(self.ptr.cast_mut()).unwrap(); + let _ = ManuallyDrop::new(self); + ptr.cast() + } + + unsafe fn from_raw(raw: NonNull) -> Self { + Self { + ptr: raw.as_ptr().cast_const().cast(), + _marker: PhantomData, + } + } + + unsafe fn raw_as_ref<'a>(raw: NonNull) -> Self::Ref<'a> { + Self::Ref { + inner: ManuallyDrop::new(Frame { + ptr: raw.as_ptr().cast_const().cast(), + _marker: PhantomData, + }), + _marker: PhantomData, + } + } + + fn ref_as_raw(ptr_ref: Self::Ref<'_>) -> core::ptr::NonNull { + NonNull::new(ptr_ref.inner.ptr.cast_mut()).unwrap().cast() + } +} diff --git a/ostd/src/mm/frame/mod.rs b/ostd/src/mm/frame/mod.rs index b18fcb9cb..53960fe56 100644 --- a/ostd/src/mm/frame/mod.rs +++ b/ostd/src/mm/frame/mod.rs @@ -38,6 +38,9 @@ pub mod segment; pub mod unique; pub mod untyped; +mod frame_ref; +pub use frame_ref::FrameRef; + #[cfg(ktest)] mod test; @@ -171,6 +174,12 @@ impl Frame { refcnt } + /// Borrows a reference from the given frame. + pub fn borrow(&self) -> FrameRef<'_, M> { + // SAFETY: Both the lifetime and the type matches `self`. + unsafe { FrameRef::borrow_paddr(self.start_paddr()) } + } + /// Forgets the handle to the frame. /// /// This will result in the frame being leaked without calling the custom dropper. diff --git a/ostd/src/mm/frame/test.rs b/ostd/src/mm/frame/test.rs index 644313739..dc0895b67 100644 --- a/ostd/src/mm/frame/test.rs +++ b/ostd/src/mm/frame/test.rs @@ -435,7 +435,6 @@ mod segment { // Untyped frame/segment tests mod untyped { use super::*; - use crate::mm::frame::untyped::FrameRef; #[ktest] fn untyped_frame_reader_writer() { @@ -484,11 +483,33 @@ mod untyped { reader.read(&mut buffer.as_mut_slice().into()); assert_eq!(buffer, data); } +} + +mod frame_ref { + use super::*; + use crate::sync::non_null::NonNullPtr; + + #[ktest] + fn frame_ref_preserves_refcnt() { + let init_val = 42; + let frame = FrameAllocOptions::new() + .alloc_frame_with(MockUFrameMeta { value: init_val }) + .expect("Failed to allocate frame"); + + assert_eq!(frame.reference_count(), 1); + + { + let frame_ref = frame.borrow(); + assert_eq!(frame_ref.meta().value, init_val); + assert_eq!(frame_ref.reference_count(), 1); + assert_eq!(frame.reference_count(), 1); + } + + assert_eq!(frame.reference_count(), 1); + } #[ktest] fn frame_impls_non_null_ptr() { - use crate::sync::non_null::NonNullPtr; - let init_val = 42; let frame = FrameAllocOptions::new() .alloc_frame_with(MockUFrameMeta { value: init_val }) diff --git a/ostd/src/mm/frame/untyped.rs b/ostd/src/mm/frame/untyped.rs index ec16477f9..89c999b67 100644 --- a/ostd/src/mm/frame/untyped.rs +++ b/ostd/src/mm/frame/untyped.rs @@ -7,16 +7,12 @@ //! the declaration of untyped frames and segments, and the implementation of //! extra functionalities (such as [`VmIo`]) for them. -use super::{ - meta::{AnyFrameMeta, MetaSlot}, - Frame, Segment, -}; +use super::{meta::AnyFrameMeta, Frame, Segment}; use crate::{ mm::{ io::{FallibleVmRead, FallibleVmWrite, VmIo, VmReader, VmWriter}, paddr_to_vaddr, Infallible, }, - sync::non_null::NonNullPtr, Error, Result, }; @@ -135,63 +131,3 @@ macro_rules! impl_untyped_for { impl_untyped_for!(Frame); impl_untyped_for!(Segment); - -// Here are implementations for `crate::sync::rcu`. - -use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::NonNull}; - -/// `FrameRef` is a struct that can work as `&'a Frame`. -/// -/// This is useful for [`crate::sync::rcu`]. -pub struct FrameRef<'a, M: AnyUFrameMeta + ?Sized> { - inner: ManuallyDrop>, - _marker: PhantomData<&'a Frame>, -} - -impl Deref for FrameRef<'_, M> { - type Target = Frame; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -// SAFETY: `Frame` is essentially an `*const MetaSlot` that could be used as a non-null -// `*const` pointer. -unsafe impl NonNullPtr for Frame { - type Target = PhantomData; - - type Ref<'a> - = FrameRef<'a, M> - where - Self: 'a; - - const ALIGN_BITS: u32 = core::mem::align_of::().trailing_zeros(); - - fn into_raw(self) -> NonNull { - let ptr = NonNull::new(self.ptr.cast_mut()).unwrap(); - let _ = ManuallyDrop::new(self); - ptr.cast() - } - - unsafe fn from_raw(raw: NonNull) -> Self { - Self { - ptr: raw.as_ptr().cast_const().cast(), - _marker: PhantomData, - } - } - - unsafe fn raw_as_ref<'a>(raw: NonNull) -> Self::Ref<'a> { - Self::Ref { - inner: ManuallyDrop::new(Frame { - ptr: raw.as_ptr().cast_const().cast(), - _marker: PhantomData, - }), - _marker: PhantomData, - } - } - - fn ref_as_raw(ptr_ref: Self::Ref<'_>) -> core::ptr::NonNull { - NonNull::new(ptr_ref.inner.ptr.cast_mut()).unwrap().cast() - } -}