1use super::{Alloc, BucketHdr, Slab, Uma, UmaBucket, UmaFlags, UmaKeg};
2use crate::context::{CpuLocal, config, current_thread};
3use crate::lock::Mutex;
4use crate::vm::Vm;
5use alloc::collections::VecDeque;
6use alloc::collections::linked_list::LinkedList;
7use alloc::string::String;
8use alloc::sync::Arc;
9use alloc::vec::Vec;
10use core::cell::RefCell;
11use core::cmp::min;
12use core::num::NonZero;
13use core::ops::DerefMut;
14use core::ptr::{NonNull, null_mut};
15use core::sync::atomic::{AtomicBool, Ordering};
16
17pub struct UmaZone {
19 bucket_enable: Arc<AtomicBool>,
20 bucket_keys: Arc<Vec<usize>>,
21 bucket_zones: Arc<Vec<UmaZone>>,
22 ty: ZoneType,
23 size: NonZero<usize>, slab: unsafe fn(&mut UmaKeg, Alloc) -> Option<NonNull<Slab>>, init: Option<fn(*mut u8, NonZero<usize>, Alloc) -> bool>, ctor: Option<fn(*mut u8, NonZero<usize>, Alloc) -> bool>, caches: CpuLocal<RefCell<UmaCache>>, flags: UmaFlags, state: Mutex<ZoneState>,
30}
31
32impl UmaZone {
33 const ALIGN_CACHE: usize = 63; #[allow(clippy::too_many_arguments)] pub(super) fn new(
43 vm: &'static Vm,
44 bucket_enable: Arc<AtomicBool>,
45 bucket_keys: Arc<Vec<usize>>,
46 bucket_zones: Arc<Vec<UmaZone>>,
47 name: impl Into<String>,
48 keg: Option<UmaKeg>,
49 size: NonZero<usize>,
50 align: Option<usize>,
51 init: Option<fn()>,
52 flags: impl Into<UmaFlags>,
53 ) -> Self {
54 let name = name.into();
55 let flags = flags.into();
56 let (keg, mut flags) = if flags.has_any(UmaFlags::Secondary) {
57 todo!()
58 } else {
59 let keg = match keg {
63 Some(v) => v,
64 None => UmaKeg::new(vm, size, align.unwrap_or(Self::ALIGN_CACHE), init, flags),
65 };
66
67 (keg, UmaFlags::zeroed())
68 };
69
70 let mut ty = ZoneType::Other;
72 let mut count = 0;
73
74 if !keg.flags().has_any(UmaFlags::Internal) {
75 count = if !keg.flags().has_any(UmaFlags::MaxBucket) {
76 min(keg.item_per_slab(), Uma::BUCKET_MAX)
77 } else {
78 Uma::BUCKET_MAX
79 };
80
81 match name.as_str() {
82 "mbuf_packet" => {
83 ty = ZoneType::MbufPacket;
84 count = 4;
85 }
86 "mbuf_cluster_pack" => {
87 ty = ZoneType::MbufClusterPack;
88 count = Uma::BUCKET_MAX;
89 }
90 "mbuf_jumbo_page" => {
91 ty = ZoneType::MbufJumboPage;
92 count = 1;
93 }
94 "mbuf" => {
95 ty = ZoneType::Mbuf;
96 count = 16;
97 }
98 "mbuf_cluster" => {
99 ty = ZoneType::MbufCluster;
100 count = 1;
101 }
102 _ => (),
103 }
104 }
105
106 let inherit = UmaFlags::Offpage
108 | UmaFlags::Malloc
109 | UmaFlags::Hash
110 | UmaFlags::VToSlab
111 | UmaFlags::Bucket
112 | UmaFlags::Internal
113 | UmaFlags::CacheOnly;
114
115 flags |= keg.flags() & inherit;
116
117 Self {
118 bucket_enable,
119 bucket_keys,
120 bucket_zones,
121 ty,
122 size: keg.size(),
123 slab: Self::fetch_slab,
124 init: None,
125 ctor: None,
126 caches: CpuLocal::new(|_| RefCell::default()),
127 flags,
128 state: Mutex::new(ZoneState {
129 kegs: LinkedList::from([keg]),
130 full_buckets: VecDeque::default(),
131 free_buckets: VecDeque::default(),
132 alloc_count: 0,
133 free_count: 0,
134 count,
135 fills: 0,
136 }),
137 }
138 }
139
140 pub fn size(&self) -> NonZero<usize> {
141 self.size
142 }
143
144 pub fn alloc(&self, flags: Alloc) -> *mut u8 {
151 if flags.has_any(Alloc::Wait) {
152 let td = current_thread();
154
155 if !td.can_sleep() {
156 panic!("attempt to do waitable heap allocation in a non-sleeping context");
157 }
158 }
159
160 loop {
161 let caches = self.caches.lock();
163 let mem = Self::alloc_from_cache(caches.borrow_mut().deref_mut());
164
165 if !mem.is_null() {
166 return mem;
167 }
168
169 drop(caches); let mut state = self.state.lock();
174 let caches = self.caches.lock();
175 let mut cache = caches.borrow_mut();
176 let mem = Self::alloc_from_cache(&mut cache);
177
178 if !mem.is_null() {
179 return mem;
180 }
181
182 state.alloc_count += core::mem::take(&mut cache.allocs);
184 state.free_count += core::mem::take(&mut cache.frees);
185
186 if let Some(b) = cache.alloc.take() {
187 state.free_buckets.push_front(b);
188 }
189
190 if let Some(b) = state.full_buckets.pop_front() {
191 cache.alloc = Some(b);
192
193 let m = Self::alloc_from_cache(&mut cache);
195
196 assert!(!m.is_null());
197
198 return m;
199 }
200
201 drop(cache);
202 drop(caches);
203
204 if matches!(
206 self.ty,
207 ZoneType::MbufPacket
208 | ZoneType::MbufJumboPage
209 | ZoneType::Mbuf
210 | ZoneType::MbufCluster
211 ) {
212 if flags.has_any(Alloc::Wait) {
213 todo!()
214 }
215
216 todo!()
217 }
218
219 if !matches!(
221 self.ty,
222 ZoneType::MbufCluster
223 | ZoneType::Mbuf
224 | ZoneType::MbufJumboPage
225 | ZoneType::MbufPacket
226 | ZoneType::MbufClusterPack
227 ) && state.count < Uma::BUCKET_MAX
228 {
229 state.count += 1;
230 }
231
232 if self.alloc_bucket(&mut state, flags) {
233 return self.alloc_item(&mut state, flags);
234 }
235 }
236 }
237
238 fn alloc_from_cache(c: &mut UmaCache) -> *mut u8 {
239 while let Some(b) = c.alloc.map(|v| v.as_ptr()) {
240 if unsafe { (*b).hdr.len != 0 } {
241 todo!()
242 }
243
244 if c.free
245 .map(|v| v.as_ptr())
246 .is_some_and(|b| unsafe { (*b).hdr.len != 0 })
247 {
248 core::mem::swap(&mut c.alloc, &mut c.free);
249 continue;
250 }
251
252 break;
253 }
254
255 null_mut()
256 }
257
258 fn alloc_bucket(&self, state: &mut ZoneState, flags: Alloc) -> bool {
265 let b = match state.free_buckets.front() {
267 Some(_) => todo!(),
268 None => {
269 if self.bucket_enable.load(Ordering::Relaxed) {
270 let mut flags = flags | Alloc::Zero;
273
274 if self.flags.has_any(UmaFlags::CacheOnly) {
275 flags |= Alloc::NoVm;
276 }
277
278 let i = (state.count + 15) >> Uma::BUCKET_SHIFT;
280 let k = self.bucket_keys[i];
281 let b = &self.bucket_zones[k];
282 let b = b.alloc_item(&mut b.state.lock(), flags);
283
284 if b.is_null() {
285 todo!()
286 }
287
288 let h = BucketHdr { len: 0 };
290
291 unsafe { core::ptr::write(b.cast(), h) };
292
293 core::ptr::slice_from_raw_parts_mut(b, Uma::BUCKET_SIZES[k]) as *mut UmaBucket
294 } else {
295 todo!()
296 }
297 }
298 };
299
300 let b = unsafe { &mut *b };
302
303 if state.fills < config().cpu_count().get().into() {
304 let n = min(b.items.len(), state.count);
305 let k = state.kegs.front_mut().unwrap();
306 let mut f = flags;
307
308 state.fills += 1;
309
310 while b.hdr.len < n {
311 let s = match unsafe { (self.slab)(k, f) } {
312 Some(v) => v.as_ptr(),
313 None => todo!(),
314 };
315
316 while b.hdr.len < n {
317 let i = unsafe { (*s).alloc_item(k) };
318
319 if i.is_null() {
320 break;
321 }
322
323 b.items[b.hdr.len] = i;
324 b.hdr.len += 1;
325 }
326
327 f |= Alloc::NoWait;
328 }
329
330 if self.init.is_some() {
331 todo!()
332 }
333
334 state.fills -= 1;
335
336 if b.hdr.len != 0 {
337 state
338 .full_buckets
339 .push_front(unsafe { NonNull::new_unchecked(b) });
340
341 return true;
342 }
343
344 todo!()
345 }
346
347 todo!()
348 }
349
350 fn alloc_item(&self, state: &mut ZoneState, flags: Alloc) -> *mut u8 {
357 let keg = state.kegs.front_mut().unwrap();
359 let slab = unsafe { (self.slab)(keg, flags) };
360
361 if let Some(slab) = slab {
362 let item = unsafe { slab.as_ref().alloc_item(keg) };
363
364 state.alloc_count += 1;
365
366 if self.init.is_none_or(|f| f(item, self.size, flags)) {
367 if self.ctor.is_none_or(|f| f(item, self.size, flags)) {
368 if flags.has_any(Alloc::Zero) {
369 unsafe { item.write_bytes(0, self.size.get()) };
370 }
371
372 return item;
373 } else {
374 todo!()
375 }
376 } else {
377 todo!()
378 }
379 }
380
381 todo!()
382 }
383
384 unsafe fn fetch_slab(keg: &mut UmaKeg, flags: Alloc) -> Option<NonNull<Slab>> {
391 if !keg.flags().has_any(UmaFlags::Bucket) || keg.recurse() == 0 {
392 loop {
393 if let Some(v) = unsafe { keg.fetch_slab(flags) } {
394 return Some(v);
395 }
396
397 if flags.has_any(Alloc::NoWait | Alloc::NoVm) {
398 break;
399 }
400 }
401 }
402
403 None
404 }
405}
406
407struct ZoneState {
409 kegs: LinkedList<UmaKeg>, full_buckets: VecDeque<NonNull<UmaBucket>>, free_buckets: VecDeque<NonNull<UmaBucket>>, alloc_count: u64, free_count: u64, count: usize, fills: u16, }
417
418unsafe impl Send for ZoneState {}
419
420#[derive(Clone, Copy)]
422enum ZoneType {
423 Other,
424 MbufPacket,
426 MbufJumboPage,
428 Mbuf,
430 MbufCluster,
432 MbufClusterPack,
434}
435
436#[derive(Default)]
438struct UmaCache {
439 alloc: Option<NonNull<UmaBucket>>, free: Option<NonNull<UmaBucket>>, allocs: u64, frees: u64, }
444
445unsafe impl Send for UmaCache {}