buddy_slab_allocator/slab/
cache.rs1use super::page::{SlabListState, SlabPageHeader};
8use super::size_class::SizeClass;
9
10#[derive(Debug, Clone, Copy)]
12struct ListHead {
13 first: usize,
14 kind: SlabListState,
15}
16
17impl ListHead {
18 const fn empty(kind: SlabListState) -> Self {
19 Self { first: 0, kind }
20 }
21
22 fn is_empty(&self) -> bool {
23 self.first == 0
24 }
25
26 unsafe fn push_front(&mut self, base: usize) {
31 unsafe {
32 let hdr = &mut *(base as *mut SlabPageHeader);
33 debug_assert_eq!(
34 hdr.list_state,
35 SlabListState::None,
36 "pushing slab onto {:?} while it is on {:?}",
37 self.kind,
38 hdr.list_state
39 );
40 debug_assert_eq!(hdr.list_prev, 0, "pushing slab with stale list_prev");
41 debug_assert_eq!(hdr.list_next, 0, "pushing slab with stale list_next");
42 hdr.list_state = self.kind;
43 hdr.list_prev = 0;
44 hdr.list_next = self.first;
45 if self.first != 0 {
46 let old = &mut *(self.first as *mut SlabPageHeader);
47 debug_assert_eq!(
48 old.list_state, self.kind,
49 "list head points to slab with wrong membership"
50 );
51 old.list_prev = base;
52 }
53 self.first = base;
54 }
55 }
56
57 unsafe fn remove(&mut self, base: usize) {
62 unsafe {
63 let hdr = &*(base as *const SlabPageHeader);
64 debug_assert_eq!(
65 hdr.list_state, self.kind,
66 "removing slab from {:?} while it is on {:?}",
67 self.kind, hdr.list_state
68 );
69 if hdr.list_prev == 0 {
70 debug_assert_eq!(self.first, base, "front slab is not this list's head");
71 } else {
72 debug_assert_eq!(
73 (*(hdr.list_prev as *const SlabPageHeader)).list_next,
74 base,
75 "previous slab does not link back to removed slab"
76 );
77 }
78 if hdr.list_next != 0 {
79 debug_assert_eq!(
80 (*(hdr.list_next as *const SlabPageHeader)).list_prev,
81 base,
82 "next slab does not link back to removed slab"
83 );
84 }
85 let prev = hdr.list_prev;
86 let next = hdr.list_next;
87
88 if prev != 0 {
89 (*(prev as *mut SlabPageHeader)).list_next = next;
90 } else {
91 self.first = next;
92 }
93 if next != 0 {
94 (*(next as *mut SlabPageHeader)).list_prev = prev;
95 }
96 let hdr = &mut *(base as *mut SlabPageHeader);
98 hdr.list_prev = 0;
99 hdr.list_next = 0;
100 hdr.list_state = SlabListState::None;
101 }
102 }
103
104 unsafe fn pop_front(&mut self) -> usize {
106 unsafe {
107 if self.first == 0 {
108 return 0;
109 }
110 let base = self.first;
111 self.remove(base);
112 base
113 }
114 }
115}
116
117pub struct SlabCache {
119 pub size_class: SizeClass,
120 partial: ListHead,
121 full: ListHead,
122 empty: ListHead,
123 empty_count: usize,
125}
126
127pub enum CacheDeallocResult {
129 Done,
131 FreeSlab { base: usize, pages: usize },
133}
134
135impl SlabCache {
136 pub const fn new(size_class: SizeClass) -> Self {
137 Self {
138 size_class,
139 partial: ListHead::empty(SlabListState::Partial),
140 full: ListHead::empty(SlabListState::Full),
141 empty: ListHead::empty(SlabListState::Empty),
142 empty_count: 0,
143 }
144 }
145
146 pub fn alloc_object<const PAGE_SIZE: usize>(&mut self) -> Option<usize> {
148 if let Some(addr) = self.try_alloc_from_partial::<PAGE_SIZE>() {
150 return Some(addr);
151 }
152
153 if let Some(base) = self.reclaim_full_with_remote_frees() {
155 unsafe { self.partial.push_front(base) };
156 return self.try_alloc_from_partial::<PAGE_SIZE>();
157 }
158
159 if !self.empty.is_empty() {
161 let base = unsafe { self.empty.pop_front() };
162 self.empty_count -= 1;
163 unsafe { self.partial.push_front(base) };
165 return self.try_alloc_from_partial::<PAGE_SIZE>();
166 }
167
168 None
169 }
170
171 fn reclaim_full_with_remote_frees(&mut self) -> Option<usize> {
174 let mut base = self.full.first;
175 while base != 0 {
176 let next = unsafe { (*(base as *const SlabPageHeader)).list_next };
177 let hdr = unsafe { &mut *(base as *mut SlabPageHeader) };
178 if hdr.has_remote_frees() {
179 hdr.drain_remote_frees(base);
180 unsafe { self.full.remove(base) };
181 return Some(base);
182 }
183 base = next;
184 }
185 None
186 }
187
188 fn try_alloc_from_partial<const PAGE_SIZE: usize>(&mut self) -> Option<usize> {
190 let base = self.partial.first;
191 if base == 0 {
192 return None;
193 }
194
195 let hdr = unsafe { &mut *(base as *mut SlabPageHeader) };
196
197 if hdr.has_remote_frees() {
199 hdr.drain_remote_frees(base);
200 }
201
202 if let Some(idx) = hdr.local_alloc() {
203 let obj_addr = hdr.object_addr(base, idx);
204 if hdr.is_local_full() && !hdr.has_remote_frees() {
206 unsafe {
207 self.partial.remove(base);
208 self.full.push_front(base);
209 }
210 }
211 return Some(obj_addr);
212 }
213 None
214 }
215
216 pub fn dealloc_object<const PAGE_SIZE: usize>(
220 &mut self,
221 obj_addr: usize,
222 ) -> CacheDeallocResult {
223 let slab_bytes = self.size_class.slab_pages(PAGE_SIZE) * PAGE_SIZE;
224 let base = SlabPageHeader::base_from_obj_addr::<PAGE_SIZE>(obj_addr, slab_bytes);
225 let hdr = unsafe { &mut *(base as *mut SlabPageHeader) };
226 let was_full = hdr.list_state == SlabListState::Full;
227
228 let idx = hdr.object_index(base, obj_addr);
229 hdr.local_free(idx);
230
231 if was_full {
232 unsafe {
234 self.full.remove(base);
235 self.partial.push_front(base);
236 }
237 }
238
239 if hdr.has_remote_frees() {
242 hdr.drain_remote_frees(base);
243 }
244
245 if hdr.is_all_free() {
246 if self.empty_count == 0 {
247 unsafe {
249 self.partial.remove(base);
250 self.empty.push_front(base);
251 }
252 self.empty_count += 1;
253 CacheDeallocResult::Done
254 } else {
255 unsafe { self.partial.remove(base) };
257 let hdr = unsafe { &mut *(base as *mut SlabPageHeader) };
258 hdr.prepare_for_buddy_free();
259 CacheDeallocResult::FreeSlab {
260 base,
261 pages: self.size_class.slab_pages(PAGE_SIZE),
262 }
263 }
264 } else {
265 CacheDeallocResult::Done
266 }
267 }
268
269 pub fn add_slab(&mut self, base: usize, bytes: usize, owner_cpu: u16) {
271 let hdr = unsafe { &mut *(base as *mut SlabPageHeader) };
272 hdr.init(self.size_class, bytes, owner_cpu);
273 unsafe { self.partial.push_front(base) };
274 }
275}