summaryrefslogtreecommitdiff
path: root/crates/buddy_allocator/src/free_list.rs
blob: 2e3afe2f010cdd018dcc7a0596386b2157825dbe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! A doubly-linked list of power-of-two-sized subregions.

use core::{
    fmt,
    marker::PhantomPinned,
    pin::Pin,
    ptr::{self, NonNull},
};

/// The magic number a non-sentinel node should have.
const FREE_NODE_MAGIC: u64 = u64::from_ne_bytes(*b"FREENODE");

/// The magic number a sentinel node should have.
const SENTINEL_MAGIC: u64 = u64::from_ne_bytes(*b"SENTINEL");

/// A doubly-linked list of power-of-two-sized subregions.
///
/// This type is valid when zero-initialized.
///
/// # Safety
///
/// - This type is valid only if it is zero-initialized or if it contains a valid doubly-linked
///   list of subregions of the correct size, which it owns.
pub struct FreeList {
    sentinel: FreeListNode,
}

impl FreeList {
    /// Pushes a subregion into the free-list.
    ///
    /// # Safety
    ///
    /// - `subregion_ptr` must convey ownership over untyped memory of the correct size.
    pub unsafe fn push(mut self: Pin<&mut FreeList>, subregion_ptr: NonNull<u8>) {
        assert_ne!(self.sentinel.next, ptr::null_mut());
        assert_ne!(self.sentinel.prev, ptr::null_mut());

        // Write the header the node should have to the node.
        let sentinel = &self.sentinel as *const FreeListNode as *mut FreeListNode;
        let next = *self.as_mut().sentinel().next_mut();
        let node = subregion_ptr.cast();
        node.write(FreeListNode {
            next,
            prev: sentinel,
            magic: FREE_NODE_MAGIC,
            _phantom: PhantomPinned,
        });

        // Link the node into the list.
        *self.sentinel().next_mut() = node.as_ptr();
        (*next).prev = node.as_ptr();
    }

    /// Performs self-linking. This must be done before the free list is used in any other way.
    pub fn self_link(self: Pin<&mut FreeList>) {
        assert_eq!(self.sentinel.next, ptr::null_mut());
        assert_eq!(self.sentinel.prev, ptr::null_mut());

        let mut sentinel = self.sentinel_unchecked();
        // SAFETY: We do not use this pointer to move out of the sentinel.
        let sentinel_ptr = unsafe { sentinel.as_mut().get_unchecked_mut() as *mut FreeListNode };
        *sentinel.as_mut().next_mut() = sentinel_ptr;
        *sentinel.as_mut().prev_mut() = sentinel_ptr;
        *sentinel.as_mut().magic_mut() = SENTINEL_MAGIC;
    }

    /// Projects the sentinel node out of the list.
    fn sentinel(self: Pin<&mut FreeList>) -> Pin<&mut FreeListNode> {
        let mut sentinel = self.sentinel_unchecked();
        assert_eq!(*sentinel.as_mut().magic_mut(), SENTINEL_MAGIC);
        sentinel
    }

    /// Projects the sentinel node out of the list, without checking the magic number.
    fn sentinel_unchecked(self: Pin<&mut FreeList>) -> Pin<&mut FreeListNode> {
        // SAFETY: sentinel is structurally pinned.
        unsafe { self.map_unchecked_mut(|list| &mut list.sentinel) }
    }
}

/// This impl will panic until the sentinel has been self-linked.
impl fmt::Debug for FreeList {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        assert_ne!(self.sentinel.next, ptr::null_mut());
        assert_ne!(self.sentinel.prev, ptr::null_mut());

        let sentinel = &self.sentinel as *const FreeListNode;

        // SAFETY: The invariants of FreeList guarantee that the list is valid. In a valid list,
        // the next pointer is always valid and non-null.
        let mut ptr = unsafe { sentinel.as_ref().unwrap().next as *const FreeListNode };
        let mut fmt = fmt.debug_list();
        while ptr != sentinel {
            fmt.entry(&ptr);
            // SAFETY: The invariants of FreeList guarantee that the list is valid. In a valid
            // list, the next pointer is always valid and non-null.
            ptr = unsafe { ptr.as_ref().unwrap().next as *const FreeListNode };
        }
        fmt.finish()
    }
}

#[derive(Debug)]
struct FreeListNode {
    next: *mut FreeListNode,
    prev: *mut FreeListNode,
    magic: u64,
    _phantom: PhantomPinned,
}

impl FreeListNode {
    /// Projects the `next` pointer out of the node.
    fn next_mut(self: Pin<&mut FreeListNode>) -> &mut *mut FreeListNode {
        // SAFETY: next is not structurally pinned.
        unsafe { &mut self.get_unchecked_mut().next }
    }

    /// Projects the `prev` pointer out of the node.
    fn prev_mut(self: Pin<&mut FreeListNode>) -> &mut *mut FreeListNode {
        // SAFETY: prev is structurally pinned.
        unsafe { &mut self.get_unchecked_mut().prev }
    }

    /// Projects the `magic` number out of the node.
    fn magic_mut(self: Pin<&mut FreeListNode>) -> &mut u64 {
        // SAFETY: magic is structurally pinned.
        unsafe { &mut self.get_unchecked_mut().magic }
    }
}