ostd/mm/frame/
unique.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! The unique frame pointer that is not shared with others.
4
5use core::{marker::PhantomData, mem::ManuallyDrop, sync::atomic::Ordering};
6
7use super::{
8    AnyFrameMeta, Frame, MetaSlot,
9    meta::{GetFrameError, REF_COUNT_UNIQUE},
10};
11use crate::mm::{HasPaddr, HasSize, PAGE_SIZE, Paddr, PagingConsts, PagingLevel, frame::mapping};
12
13/// An owning frame pointer.
14///
15/// Unlike [`Frame`], the frame pointed to by this pointer is not shared with
16/// others. So a mutable reference to the metadata is available for the frame.
17#[repr(transparent)]
18pub struct UniqueFrame<M: AnyFrameMeta + ?Sized> {
19    ptr: *const MetaSlot,
20    _marker: PhantomData<M>,
21}
22
23unsafe impl<M: AnyFrameMeta + ?Sized> Send for UniqueFrame<M> {}
24
25unsafe impl<M: AnyFrameMeta + ?Sized> Sync for UniqueFrame<M> {}
26
27impl<M: AnyFrameMeta + ?Sized> core::fmt::Debug for UniqueFrame<M> {
28    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29        write!(f, "UniqueFrame({:#x})", self.paddr())
30    }
31}
32
33impl<M: AnyFrameMeta> UniqueFrame<M> {
34    /// Gets a [`UniqueFrame`] with a specific usage from a raw, unused page.
35    ///
36    /// The caller should provide the initial metadata of the page.
37    pub fn from_unused(paddr: Paddr, metadata: M) -> Result<Self, GetFrameError> {
38        Ok(Self {
39            ptr: MetaSlot::get_from_unused(paddr, metadata, true)?,
40            _marker: PhantomData,
41        })
42    }
43
44    /// Gets the metadata of this page.
45    pub fn meta(&self) -> &M {
46        // SAFETY: The type is tracked by the type system.
47        unsafe { &*self.slot().as_meta_ptr::<M>() }
48    }
49
50    /// Gets the mutable metadata of this page.
51    pub fn meta_mut(&mut self) -> &mut M {
52        // SAFETY: The type is tracked by the type system.
53        // And we have the exclusive access to the metadata.
54        unsafe { &mut *self.slot().as_meta_ptr::<M>() }
55    }
56}
57
58impl<M: AnyFrameMeta + ?Sized> UniqueFrame<M> {
59    /// Gets the paging level of this page.
60    ///
61    /// This is the level of the page table entry that maps the frame,
62    /// which determines the size of the frame.
63    ///
64    /// Currently, the level is always 1, which means the frame is a regular
65    /// page frame.
66    pub const fn level(&self) -> PagingLevel {
67        1
68    }
69
70    /// Gets the dyncamically-typed metadata of this frame.
71    ///
72    /// If the type is known at compile time, use [`Frame::meta`] instead.
73    pub fn dyn_meta(&self) -> &dyn AnyFrameMeta {
74        // SAFETY: The metadata is initialized and valid.
75        unsafe { &*self.slot().dyn_meta_ptr() }
76    }
77
78    /// Gets the dyncamically-typed metadata of this frame.
79    ///
80    /// If the type is known at compile time, use [`Frame::meta`] instead.
81    pub fn dyn_meta_mut(&mut self) -> &mut dyn AnyFrameMeta {
82        // SAFETY: The metadata is initialized and valid. We have the exclusive
83        // access to the frame.
84        unsafe { &mut *self.slot().dyn_meta_ptr() }
85    }
86
87    /// Repurposes the frame with a new metadata.
88    pub fn repurpose<M1: AnyFrameMeta>(self, metadata: M1) -> UniqueFrame<M1> {
89        let this = ManuallyDrop::new(self);
90
91        // SAFETY: We are the sole owner and the metadata is initialized.
92        unsafe { this.slot().drop_meta_in_place() };
93        // SAFETY: We are the sole owner.
94        unsafe { this.slot().write_meta(metadata) };
95        // SAFETY: The metadata is initialized with type `M1`.
96        unsafe { core::mem::transmute(ManuallyDrop::into_inner(this)) }
97    }
98
99    /// Resets the frame to unused without up-calling the allocator.
100    ///
101    /// This is solely useful for the allocator implementation/testing and
102    /// is highly experimental. Usage of this function is discouraged.
103    ///
104    /// Usage of this function other than the allocator would actually leak
105    /// the frame since the allocator would not be aware of the frame.
106    //
107    // FIXME: We may have a better `Segment` and `UniqueSegment` design to
108    // allow the allocator hold the ownership of all the frames in a chunk
109    // instead of the head. Then this weird public API can be `#[cfg(ktest)]`.
110    pub fn reset_as_unused(self) {
111        let this = ManuallyDrop::new(self);
112
113        this.slot().ref_count.store(0, Ordering::Release);
114        // SAFETY: We are the sole owner and the reference count is 0.
115        // The slot is initialized.
116        unsafe { this.slot().drop_last_in_place() };
117    }
118
119    /// Converts this frame into a raw physical address.
120    pub(crate) fn into_raw(self) -> Paddr {
121        let this = ManuallyDrop::new(self);
122        this.paddr()
123    }
124
125    /// Restores a raw physical address back into a unique frame.
126    ///
127    /// # Safety
128    ///
129    /// The caller must ensure that the physical address is valid and points to
130    /// a forgotten frame that was previously casted by [`Self::into_raw`].
131    pub(crate) unsafe fn from_raw(paddr: Paddr) -> Self {
132        let vaddr = mapping::frame_to_meta::<PagingConsts>(paddr);
133        let ptr = vaddr as *const MetaSlot;
134
135        Self {
136            ptr,
137            _marker: PhantomData,
138        }
139    }
140
141    pub(super) fn slot(&self) -> &MetaSlot {
142        // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
143        // mutably borrowed, so taking an immutable reference to it is safe.
144        unsafe { &*self.ptr }
145    }
146}
147
148impl<M: AnyFrameMeta + ?Sized> HasPaddr for UniqueFrame<M> {
149    fn paddr(&self) -> Paddr {
150        self.slot().frame_paddr()
151    }
152}
153
154impl<M: AnyFrameMeta + ?Sized> HasSize for UniqueFrame<M> {
155    fn size(&self) -> usize {
156        PAGE_SIZE
157    }
158}
159
160impl<M: AnyFrameMeta + ?Sized> Drop for UniqueFrame<M> {
161    fn drop(&mut self) {
162        self.slot().ref_count.store(0, Ordering::Relaxed);
163        // SAFETY: We are the sole owner and the reference count is 0.
164        // The slot is initialized.
165        unsafe { self.slot().drop_last_in_place() };
166
167        super::allocator::get_global_frame_allocator().dealloc(self.paddr(), PAGE_SIZE);
168    }
169}
170
171impl<M: AnyFrameMeta + ?Sized> From<UniqueFrame<M>> for Frame<M> {
172    fn from(unique: UniqueFrame<M>) -> Self {
173        // The `Release` ordering make sure that previous writes are visible
174        // before the reference count is set to 1. It pairs with
175        // `MetaSlot::get_from_in_use`.
176        unique.slot().ref_count.store(1, Ordering::Release);
177        // SAFETY: The internal representation is now the same.
178        unsafe { core::mem::transmute(unique) }
179    }
180}
181
182impl<M: AnyFrameMeta + ?Sized> TryFrom<Frame<M>> for UniqueFrame<M> {
183    type Error = Frame<M>;
184
185    /// Tries to get a unique frame from a shared frame.
186    ///
187    /// If the reference count is not 1, the frame is returned back.
188    fn try_from(frame: Frame<M>) -> Result<Self, Self::Error> {
189        match frame.slot().ref_count.compare_exchange(
190            1,
191            REF_COUNT_UNIQUE,
192            Ordering::Relaxed,
193            Ordering::Relaxed,
194        ) {
195            Ok(_) => {
196                // SAFETY: The reference count is now `REF_COUNT_UNIQUE`.
197                Ok(unsafe { core::mem::transmute::<Frame<M>, UniqueFrame<M>>(frame) })
198            }
199            Err(_) => Err(frame),
200        }
201    }
202}