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}