ostd/mm/frame/linked_list.rs
1// SPDX-License-Identifier: MPL-2.0
2
3//! Enabling linked lists of frames without heap allocation.
4//!
5//! This module leverages the customizability of the metadata system (see
6//! [super::meta]) to allow any type of frame to be used in a linked list.
7
8use core::{
9 ops::{Deref, DerefMut},
10 ptr::NonNull,
11 sync::atomic::{AtomicU64, Ordering},
12};
13
14use super::{
15 MetaSlot, mapping,
16 meta::{AnyFrameMeta, get_slot},
17 unique::UniqueFrame,
18};
19use crate::{
20 arch::mm::PagingConsts,
21 mm::{Paddr, Vaddr},
22 panic::abort,
23};
24
25/// A linked list of frames.
26///
27/// Two key features that [`LinkedList`] is different from
28/// [`alloc::collections::LinkedList`] is that:
29/// 1. It is intrusive, meaning that the links are part of the frame metadata.
30/// This allows the linked list to be used without heap allocation. But it
31/// disallows a frame to be in multiple linked lists at the same time.
32/// 2. The linked list exclusively own the frames, meaning that it takes
33/// unique pointers [`UniqueFrame`]. And other bodies cannot
34/// [`from_in_use`] a frame that is inside a linked list.
35/// 3. We also allow creating cursors at a specific frame, allowing $O(1)$
36/// removal without iterating through the list at a cost of some checks.
37///
38/// # Example
39///
40/// To create metadata types that allows linked list links, wrap the metadata
41/// type in [`Link`]:
42///
43/// ```rust
44/// use ostd::{
45/// mm::{frame::{linked_list::{Link, LinkedList}, Frame}, FrameAllocOptions},
46/// impl_untyped_frame_meta_for,
47/// };
48///
49/// #[derive(Debug)]
50/// struct MyMeta { mark: usize }
51///
52/// type MyFrame = Frame<Link<MyMeta>>;
53///
54/// impl_untyped_frame_meta_for!(MyMeta);
55///
56/// let alloc_options = FrameAllocOptions::new();
57/// let frame1 = alloc_options.alloc_frame_with(Link::new(MyMeta { mark: 1 })).unwrap();
58/// let frame2 = alloc_options.alloc_frame_with(Link::new(MyMeta { mark: 2 })).unwrap();
59///
60/// let mut list = LinkedList::new();
61/// list.push_front(frame1.try_into().unwrap());
62/// list.push_front(frame2.try_into().unwrap());
63///
64/// let mut cursor = list.cursor_front_mut();
65/// assert_eq!(cursor.current_meta().unwrap().mark, 2);
66/// cursor.move_next();
67/// assert_eq!(cursor.current_meta().unwrap().mark, 1);
68/// ```
69///
70/// [`from_in_use`]: super::Frame::from_in_use
71pub struct LinkedList<M>
72where
73 Link<M>: AnyFrameMeta,
74{
75 front: Option<NonNull<Link<M>>>,
76 back: Option<NonNull<Link<M>>>,
77 /// The number of frames in the list.
78 size: usize,
79 /// A lazily initialized ID, used to check whether a frame is in the list.
80 /// 0 means uninitialized.
81 list_id: u64,
82}
83
84// SAFETY: Only the pointers are not `Send` and `Sync`. But our interfaces
85// enforces that only with `&mut` references can we access with the pointers.
86unsafe impl<M> Send for LinkedList<M> where Link<M>: AnyFrameMeta {}
87unsafe impl<M> Sync for LinkedList<M> where Link<M>: AnyFrameMeta {}
88
89impl<M> Default for LinkedList<M>
90where
91 Link<M>: AnyFrameMeta,
92{
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98impl<M> LinkedList<M>
99where
100 Link<M>: AnyFrameMeta,
101{
102 /// Creates a new linked list.
103 pub const fn new() -> Self {
104 Self {
105 front: None,
106 back: None,
107 size: 0,
108 list_id: 0,
109 }
110 }
111
112 /// Gets the number of frames in the linked list.
113 pub fn size(&self) -> usize {
114 self.size
115 }
116
117 /// Tells if the linked list is empty.
118 pub fn is_empty(&self) -> bool {
119 let is_empty = self.size == 0;
120 debug_assert_eq!(is_empty, self.front.is_none());
121 debug_assert_eq!(is_empty, self.back.is_none());
122 is_empty
123 }
124
125 /// Pushes a frame to the front of the linked list.
126 pub fn push_front(&mut self, frame: UniqueFrame<Link<M>>) {
127 self.cursor_front_mut().insert_before(frame);
128 }
129
130 /// Pops a frame from the front of the linked list.
131 pub fn pop_front(&mut self) -> Option<UniqueFrame<Link<M>>> {
132 self.cursor_front_mut().take_current()
133 }
134
135 /// Pushes a frame to the back of the linked list.
136 pub fn push_back(&mut self, frame: UniqueFrame<Link<M>>) {
137 self.cursor_at_ghost_mut().insert_before(frame);
138 }
139
140 /// Pops a frame from the back of the linked list.
141 pub fn pop_back(&mut self) -> Option<UniqueFrame<Link<M>>> {
142 self.cursor_back_mut().take_current()
143 }
144
145 /// Tells if a frame is in the list.
146 pub fn contains(&mut self, frame: Paddr) -> bool {
147 let Ok(slot) = get_slot(frame) else {
148 return false;
149 };
150 slot.in_list.load(Ordering::Relaxed) == self.lazy_get_id()
151 }
152
153 /// Gets a cursor at the specified frame if the frame is in the list.
154 ///
155 /// This method fail if [`Self::contains`] returns `false`.
156 pub fn cursor_mut_at(&mut self, frame: Paddr) -> Option<CursorMut<'_, M>> {
157 let Ok(slot) = get_slot(frame) else {
158 return None;
159 };
160 let contains = slot.in_list.load(Ordering::Relaxed) == self.lazy_get_id();
161 if contains {
162 Some(CursorMut {
163 list: self,
164 current: Some(NonNull::new(slot.as_meta_ptr::<Link<M>>()).unwrap()),
165 })
166 } else {
167 None
168 }
169 }
170
171 /// Gets a cursor at the front that can mutate the linked list links.
172 ///
173 /// If the list is empty, the cursor points to the "ghost" non-element.
174 pub fn cursor_front_mut(&mut self) -> CursorMut<'_, M> {
175 let current = self.front;
176 CursorMut {
177 list: self,
178 current,
179 }
180 }
181
182 /// Gets a cursor at the back that can mutate the linked list links.
183 ///
184 /// If the list is empty, the cursor points to the "ghost" non-element.
185 pub fn cursor_back_mut(&mut self) -> CursorMut<'_, M> {
186 let current = self.back;
187 CursorMut {
188 list: self,
189 current,
190 }
191 }
192
193 /// Gets a cursor at the "ghost" non-element that can mutate the linked list links.
194 fn cursor_at_ghost_mut(&mut self) -> CursorMut<'_, M> {
195 CursorMut {
196 list: self,
197 current: None,
198 }
199 }
200
201 fn lazy_get_id(&mut self) -> u64 {
202 // FIXME: Self-incrementing IDs may overflow, while `core::pin::Pin`
203 // is not compatible with locks. Think about a better solution.
204 static LIST_ID_ALLOCATOR: AtomicU64 = AtomicU64::new(1);
205 const MAX_LIST_ID: u64 = i64::MAX as u64;
206
207 if self.list_id == 0 {
208 let id = LIST_ID_ALLOCATOR.fetch_add(1, Ordering::Relaxed);
209 if id >= MAX_LIST_ID {
210 log::error!("The frame list ID allocator has exhausted.");
211 abort();
212 }
213 self.list_id = id;
214 id
215 } else {
216 self.list_id
217 }
218 }
219}
220
221/// A cursor that can mutate the linked list links.
222///
223/// The cursor points to either a frame or the "ghost" non-element. It points
224/// to the "ghost" non-element when the cursor surpasses the back of the list.
225pub struct CursorMut<'a, M>
226where
227 Link<M>: AnyFrameMeta,
228{
229 list: &'a mut LinkedList<M>,
230 current: Option<NonNull<Link<M>>>,
231}
232
233impl<M> CursorMut<'_, M>
234where
235 Link<M>: AnyFrameMeta,
236{
237 /// Moves the cursor to the next frame towards the back.
238 ///
239 /// If the cursor is pointing to the "ghost" non-element then this will
240 /// move it to the first element of the [`LinkedList`]. If it is pointing
241 /// to the last element of the LinkedList then this will move it to the
242 /// "ghost" non-element.
243 pub fn move_next(&mut self) {
244 self.current = match self.current {
245 // SAFETY: The cursor is pointing to a valid element.
246 Some(current) => unsafe { current.as_ref().next },
247 None => self.list.front,
248 };
249 }
250
251 /// Moves the cursor to the previous frame towards the front.
252 ///
253 /// If the cursor is pointing to the "ghost" non-element then this will
254 /// move it to the last element of the [`LinkedList`]. If it is pointing
255 /// to the first element of the LinkedList then this will move it to the
256 /// "ghost" non-element.
257 pub fn move_prev(&mut self) {
258 self.current = match self.current {
259 // SAFETY: The cursor is pointing to a valid element.
260 Some(current) => unsafe { current.as_ref().prev },
261 None => self.list.back,
262 };
263 }
264
265 /// Gets the mutable reference to the current frame's metadata.
266 pub fn current_meta(&mut self) -> Option<&mut M> {
267 self.current.map(|current| {
268 // SAFETY: `&mut self` ensures we have exclusive access to the
269 // frame metadata.
270 let link_mut = unsafe { &mut *current.as_ptr() };
271 // We should not allow `&mut Link<M>` to modify the original links,
272 // which would break the linked list. So we just return the
273 // inner metadata `M`.
274 &mut link_mut.meta
275 })
276 }
277
278 /// Takes the current pointing frame out of the linked list.
279 ///
280 /// If successful, the frame is returned and the cursor is moved to the
281 /// next frame. If the cursor is pointing to the back of the list then it
282 /// is moved to the "ghost" non-element.
283 pub fn take_current(&mut self) -> Option<UniqueFrame<Link<M>>> {
284 let current = self.current?;
285
286 let mut frame = {
287 let meta_ptr = current.as_ptr() as *mut MetaSlot;
288 let paddr = mapping::meta_to_frame::<PagingConsts>(meta_ptr as Vaddr);
289 // SAFETY: The frame was forgotten when inserted into the linked list.
290 unsafe { UniqueFrame::<Link<M>>::from_raw(paddr) }
291 };
292
293 let next_ptr = frame.meta().next;
294 if let Some(prev) = &mut frame.meta_mut().prev {
295 // SAFETY: We own the previous node by `&mut self` and the node is
296 // initialized.
297 let prev_mut = unsafe { prev.as_mut() };
298
299 debug_assert_eq!(prev_mut.next, Some(current));
300 prev_mut.next = next_ptr;
301 } else {
302 self.list.front = next_ptr;
303 }
304 let prev_ptr = frame.meta().prev;
305 if let Some(next) = &mut frame.meta_mut().next {
306 // SAFETY: We own the next node by `&mut self` and the node is
307 // initialized.
308 let next_mut = unsafe { next.as_mut() };
309
310 debug_assert_eq!(next_mut.prev, Some(current));
311 next_mut.prev = prev_ptr;
312 self.current = Some(NonNull::from(next_mut));
313 } else {
314 self.list.back = prev_ptr;
315 self.current = None;
316 }
317
318 frame.meta_mut().next = None;
319 frame.meta_mut().prev = None;
320
321 frame.slot().in_list.store(0, Ordering::Relaxed);
322
323 self.list.size -= 1;
324
325 Some(frame)
326 }
327
328 /// Inserts a frame before the current frame.
329 ///
330 /// If the cursor is pointing at the "ghost" non-element then the new
331 /// element is inserted at the back of the [`LinkedList`].
332 pub fn insert_before(&mut self, mut frame: UniqueFrame<Link<M>>) {
333 // The frame can't possibly be in any linked lists since the list will
334 // own the frame so there can't be any unique pointers to it.
335 debug_assert!(frame.meta_mut().next.is_none());
336 debug_assert!(frame.meta_mut().prev.is_none());
337 debug_assert_eq!(frame.slot().in_list.load(Ordering::Relaxed), 0);
338
339 let frame_ptr = NonNull::from(frame.meta_mut());
340
341 if let Some(current) = &mut self.current {
342 // SAFETY: We own the current node by `&mut self` and the node is
343 // initialized.
344 let current_mut = unsafe { current.as_mut() };
345
346 if let Some(prev) = &mut current_mut.prev {
347 // SAFETY: We own the previous node by `&mut self` and the node
348 // is initialized.
349 let prev_mut = unsafe { prev.as_mut() };
350
351 debug_assert_eq!(prev_mut.next, Some(*current));
352 prev_mut.next = Some(frame_ptr);
353
354 frame.meta_mut().prev = Some(*prev);
355 frame.meta_mut().next = Some(*current);
356 *prev = frame_ptr;
357 } else {
358 debug_assert_eq!(self.list.front, Some(*current));
359 frame.meta_mut().next = Some(*current);
360 current_mut.prev = Some(frame_ptr);
361 self.list.front = Some(frame_ptr);
362 }
363 } else {
364 // We are at the "ghost" non-element.
365 if let Some(back) = &mut self.list.back {
366 // SAFETY: We have ownership of the links via `&mut self`.
367 unsafe {
368 debug_assert!(back.as_mut().next.is_none());
369 back.as_mut().next = Some(frame_ptr);
370 }
371 frame.meta_mut().prev = Some(*back);
372 self.list.back = Some(frame_ptr);
373 } else {
374 debug_assert_eq!(self.list.front, None);
375 self.list.front = Some(frame_ptr);
376 self.list.back = Some(frame_ptr);
377 }
378 }
379
380 frame
381 .slot()
382 .in_list
383 .store(self.list.lazy_get_id(), Ordering::Relaxed);
384
385 // Forget the frame to transfer the ownership to the list.
386 let _ = frame.into_raw();
387
388 self.list.size += 1;
389 }
390
391 /// Provides a reference to the linked list.
392 pub fn as_list(&self) -> &LinkedList<M> {
393 &*self.list
394 }
395}
396
397impl<M> Drop for LinkedList<M>
398where
399 Link<M>: AnyFrameMeta,
400{
401 fn drop(&mut self) {
402 let mut cursor = self.cursor_front_mut();
403 while cursor.take_current().is_some() {}
404 }
405}
406
407/// The metadata of linked list frames.
408///
409/// To allow other metadata to be customized, this type is a wrapper around the
410/// actual metadata type `M`.
411///
412/// Linked list frames can be contained in a [`LinkedList`].
413#[derive(Debug)]
414pub struct Link<M> {
415 next: Option<NonNull<Link<M>>>,
416 prev: Option<NonNull<Link<M>>>,
417 meta: M,
418}
419
420// SAFETY: `Link<M>` is `Send` and `Sync` if `M` is `Send` and `Sync` because
421// we only access these unsafe cells when the frame is not shared. This is
422// enforced by `UniqueFrame`.
423unsafe impl<M: Send> Send for Link<M> {}
424unsafe impl<M: Sync> Sync for Link<M> {}
425
426impl<M> Deref for Link<M> {
427 type Target = M;
428
429 fn deref(&self) -> &Self::Target {
430 &self.meta
431 }
432}
433
434impl<M> DerefMut for Link<M> {
435 fn deref_mut(&mut self) -> &mut Self::Target {
436 &mut self.meta
437 }
438}
439
440impl<M> Link<M> {
441 /// Creates a new linked list metadata.
442 pub const fn new(meta: M) -> Self {
443 Self {
444 next: None,
445 prev: None,
446 meta,
447 }
448 }
449}
450
451// SAFETY: If `M::on_drop` reads the page using the provided `VmReader`,
452// the safety is upheld by the one who implements `AnyFrameMeta` for `M`.
453unsafe impl<M> AnyFrameMeta for Link<M>
454where
455 M: AnyFrameMeta,
456{
457 fn on_drop(&mut self, reader: &mut crate::mm::VmReader<crate::mm::Infallible>) {
458 self.meta.on_drop(reader);
459 }
460
461 fn is_untyped(&self) -> bool {
462 self.meta.is_untyped()
463 }
464}