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}