1use super::{Alloc, BucketHdr, Slab, Uma, UmaBucket, UmaFlags, UmaKeg};
2use crate::context::{CpuLocal, config, current_thread};
3use crate::lock::Mutex;
4use crate::mem::Strong;
5use crate::vm::Vm;
6use alloc::collections::VecDeque;
7use alloc::collections::linked_list::LinkedList;
8use alloc::string::String;
9use alloc::sync::Arc;
10use alloc::vec::Vec;
11use core::cell::RefCell;
12use core::cmp::min;
13use core::num::NonZero;
14use core::ops::DerefMut;
15use core::pin::Pin;
16use core::ptr::{NonNull, null_mut};
17use core::sync::atomic::{AtomicBool, Ordering};
18
19pub struct UmaZone {
21 bucket_enable: Arc<AtomicBool>,
22 bucket_keys: Arc<Vec<usize>>,
23 bucket_zones: Arc<Vec<UmaZone>>,
24 ty: ZoneType,
25 size: NonZero<usize>, slab: unsafe fn(&Arc<UmaKeg>, Alloc) -> Option<Pin<Strong<Slab>>>, init: Option<fn(*mut u8, NonZero<usize>, Alloc) -> bool>, ctor: Option<fn(*mut u8, NonZero<usize>, Alloc) -> bool>, dtor: Option<fn()>, caches: CpuLocal<RefCell<UmaCache>>, state: Mutex<ZoneState>,
32}
33
34impl UmaZone {
35 const ALIGN_CACHE: usize = 63; pub(super) fn new(
44 vm: &'static Vm,
45 bucket_enable: Arc<AtomicBool>,
46 bucket_keys: Arc<Vec<usize>>,
47 bucket_zones: Arc<Vec<UmaZone>>,
48 args: ZoneArgs,
49 ) -> Self {
50 let name = args.name;
51 let flags = args.flags;
52 let (keg, mut flags) = if flags.has_any(UmaFlags::Secondary) {
53 todo!()
54 } else {
55 let keg = match args.keg {
59 Some(v) => v,
60 None => UmaKeg::new(
61 vm,
62 args.size,
63 args.align.unwrap_or(Self::ALIGN_CACHE),
64 args.init,
65 flags,
66 ),
67 };
68
69 (keg, UmaFlags::zeroed())
70 };
71
72 let mut ty = ZoneType::Other;
74 let mut count = 0;
75
76 if !keg.flags().has_any(UmaFlags::Internal) {
77 count = if !keg.flags().has_any(UmaFlags::MaxBucket) {
78 min(keg.item_per_slab(), Uma::BUCKET_MAX)
79 } else {
80 Uma::BUCKET_MAX
81 };
82
83 match name.as_str() {
84 "mbuf_packet" => {
85 ty = ZoneType::MbufPacket;
86 count = 4;
87 }
88 "mbuf_cluster_pack" => {
89 ty = ZoneType::MbufClusterPack;
90 count = Uma::BUCKET_MAX;
91 }
92 "mbuf_jumbo_page" => {
93 ty = ZoneType::MbufJumboPage;
94 count = 1;
95 }
96 "mbuf" => {
97 ty = ZoneType::Mbuf;
98 count = 16;
99 }
100 "mbuf_cluster" => {
101 ty = ZoneType::MbufCluster;
102 count = 1;
103 }
104 _ => (),
105 }
106 }
107
108 let inherit = UmaFlags::Offpage
110 | UmaFlags::Malloc
111 | UmaFlags::Hash
112 | UmaFlags::VToSlab
113 | UmaFlags::Bucket
114 | UmaFlags::Internal
115 | UmaFlags::CacheOnly;
116
117 flags |= keg.flags() & inherit;
118
119 Self {
120 bucket_enable,
121 bucket_keys,
122 bucket_zones,
123 ty,
124 size: keg.size(),
125 slab: Self::fetch_slab,
126 init: None,
127 ctor: args.ctor,
128 dtor: args.dtor,
129 caches: CpuLocal::new(|_| RefCell::default()),
130 state: Mutex::new(ZoneState {
131 kegs: LinkedList::from([keg]),
132 full_buckets: VecDeque::default(),
133 free_buckets: VecDeque::default(),
134 alloc_count: 0,
135 free_count: 0,
136 count,
137 fills: 0,
138 flags,
139 }),
140 }
141 }
142
143 pub fn size(&self) -> NonZero<usize> {
144 self.size
145 }
146
147 pub fn alloc(&self, flags: Alloc) -> *mut u8 {
154 if flags.has_any(Alloc::Wait) {
155 let td = current_thread();
157
158 if !td.can_sleep() {
159 panic!("attempt to do waitable heap allocation in a non-sleeping context");
160 }
161 }
162
163 let m = loop {
164 let caches = self.caches.lock();
166 let mem = Self::alloc_from_cache(caches.borrow_mut().deref_mut());
167
168 if !mem.is_null() {
169 break mem;
170 }
171
172 drop(caches); let mut state = self.state.lock();
177 let caches = self.caches.lock();
178 let mut cache = caches.borrow_mut();
179 let mem = Self::alloc_from_cache(&mut cache);
180
181 if !mem.is_null() {
182 break mem;
183 }
184
185 state.alloc_count += core::mem::take(&mut cache.allocs);
187 state.free_count += core::mem::take(&mut cache.frees);
188
189 if let Some(b) = cache.alloc.take() {
190 state.free_buckets.push_front(b);
191 }
192
193 if let Some(b) = state.full_buckets.pop_front() {
194 cache.alloc = Some(b);
195
196 let m = Self::alloc_from_cache(&mut cache);
198
199 debug_assert!(!m.is_null());
200
201 break m;
202 }
203
204 drop(cache);
205 drop(caches);
206
207 if matches!(
209 self.ty,
210 ZoneType::MbufPacket
211 | ZoneType::MbufJumboPage
212 | ZoneType::Mbuf
213 | ZoneType::MbufCluster
214 ) {
215 if flags.has_any(Alloc::Wait) {
216 todo!()
217 }
218
219 todo!()
220 }
221
222 if !matches!(
224 self.ty,
225 ZoneType::MbufCluster
226 | ZoneType::Mbuf
227 | ZoneType::MbufJumboPage
228 | ZoneType::MbufPacket
229 | ZoneType::MbufClusterPack
230 ) && state.count < Uma::BUCKET_MAX
231 {
232 state.count += 1;
233 }
234
235 if self.alloc_bucket(&mut state, flags) {
236 return self.alloc_item(&mut state, flags);
237 }
238 };
239
240 if flags.has_any(Alloc::Zero) {
242 unsafe { m.write_bytes(0, self.size.get()) };
243 }
244
245 if self.ctor.is_none_or(move |f| f(m, self.size, flags)) {
246 m
247 } else {
248 todo!()
249 }
250 }
251
252 pub unsafe fn free(&self, item: *mut u8) {
262 if item.is_null() {
263 return;
264 } else if self.dtor.is_some() {
265 todo!()
266 }
267
268 let mut state = self.state.lock();
270
271 if self.ty != ZoneType::MbufPacket
272 && self.ty != ZoneType::MbufClusterPack
273 && self.ty != ZoneType::MbufJumboPage
274 && self.ty != ZoneType::Mbuf
275 && self.ty != ZoneType::MbufCluster
276 && state.flags.has_any(UmaFlags::Full)
277 {
278 todo!()
279 }
280
281 let mut caches = self.caches.lock();
284 let mut cache = caches.borrow_mut();
285
286 loop {
287 while let Some(b) = cache.free.map(|mut v| unsafe { v.as_mut() }) {
288 loop {
289 let n = if matches!(
291 self.ty,
292 ZoneType::MbufCluster
293 | ZoneType::Mbuf
294 | ZoneType::MbufJumboPage
295 | ZoneType::MbufClusterPack
296 | ZoneType::MbufPacket
297 ) {
298 todo!()
299 } else {
300 b.items.len()
301 };
302
303 if let i = b.hdr.len
304 && i < n
305 {
306 b.items[i] = item;
307 b.hdr.len = i + 1;
308
309 cache.frees += 1;
310
311 if self.ty != ZoneType::MbufPacket
312 && self.ty != ZoneType::MbufJumboPage
313 && self.ty != ZoneType::Mbuf
314 && self.ty != ZoneType::MbufCluster
315 {
316 return;
317 }
318
319 todo!()
320 }
321
322 todo!()
323 }
324 }
325
326 state.alloc_count += core::mem::take(&mut cache.allocs);
327 state.free_count += core::mem::take(&mut cache.frees);
328
329 if cache.free.take().is_some() {
330 todo!()
331 }
332
333 if let Some(b) = state.free_buckets.pop_front() {
334 cache.free = Some(b);
335 continue;
336 }
337
338 drop(cache);
339 drop(caches);
340
341 if !self.bucket_enable.load(Ordering::Relaxed) {
342 todo!()
343 }
344
345 let i = (state.count + 15) >> Uma::BUCKET_SHIFT;
347 let k = self.bucket_keys[i];
348 let b = &self.bucket_zones[k];
349 let f = Alloc::from((u32::from(state.flags) >> 31) << 9); drop(state);
352
353 let b = b.alloc_item(&mut b.state.lock(), f | Alloc::Zero | Alloc::NoWait);
356
357 if b.is_null() {
358 todo!()
359 }
360
361 let h = BucketHdr { len: 0 };
363 let b = unsafe {
364 core::ptr::write(b.cast(), h);
365 core::ptr::slice_from_raw_parts_mut(b, Uma::BUCKET_SIZES[k]) as *mut UmaBucket
366 };
367
368 state = self.state.lock();
370 state
371 .free_buckets
372 .push_front(unsafe { NonNull::new_unchecked(b) });
373
374 caches = self.caches.lock();
375 cache = caches.borrow_mut();
376 }
377 }
378
379 fn alloc_from_cache(c: &mut UmaCache) -> *mut u8 {
380 while let Some(b) = c.alloc.map(|v| v.as_ptr()) {
381 if let Some(v) = unsafe { (*b).hdr.len.checked_sub(1) } {
382 unsafe { (*b).hdr.len = v };
383
384 c.allocs += 1;
385
386 return unsafe { (*b).items[v] };
387 }
388
389 if c.free
390 .map(|v| v.as_ptr())
391 .is_some_and(|b| unsafe { (*b).hdr.len != 0 })
392 {
393 core::mem::swap(&mut c.alloc, &mut c.free);
394 continue;
395 }
396
397 break;
398 }
399
400 null_mut()
401 }
402
403 fn alloc_bucket(&self, state: &mut ZoneState, flags: Alloc) -> bool {
410 let b = match state.free_buckets.front() {
412 Some(_) => todo!(),
413 None => {
414 if self.bucket_enable.load(Ordering::Relaxed) {
415 let mut flags = flags | Alloc::Zero;
418
419 if state.flags.has_any(UmaFlags::CacheOnly) {
420 flags |= Alloc::NoVm;
421 }
422
423 let i = (state.count + 15) >> Uma::BUCKET_SHIFT;
425 let k = self.bucket_keys[i];
426 let b = &self.bucket_zones[k];
427 let b = b.alloc_item(&mut b.state.lock(), flags);
428
429 if b.is_null() {
430 todo!()
431 }
432
433 let h = BucketHdr { len: 0 };
435 let s = Uma::BUCKET_SIZES[k];
436
437 unsafe {
438 core::ptr::write(b.cast(), h);
439 core::ptr::slice_from_raw_parts_mut(b, s) as *mut UmaBucket
440 }
441 } else {
442 todo!()
443 }
444 }
445 };
446
447 let b = unsafe { &mut *b };
449
450 if state.fills < config().cpu_count().get().into() {
451 let n = min(b.items.len(), state.count);
452 let k = state.kegs.front().unwrap();
453 let mut f = flags;
454
455 state.fills += 1;
456
457 while b.hdr.len < n {
458 let s = match unsafe { (self.slab)(k, f) } {
459 Some(v) => v,
460 None => todo!(),
461 };
462
463 while b.hdr.len < n {
464 let i = s.alloc_item();
465
466 if i.is_null() {
467 break;
468 }
469
470 b.items[b.hdr.len] = i;
471 b.hdr.len += 1;
472 }
473
474 f |= Alloc::NoWait;
475 }
476
477 if self.init.is_some() {
478 todo!()
479 }
480
481 state.fills -= 1;
482
483 if b.hdr.len != 0 {
484 state
485 .full_buckets
486 .push_front(unsafe { NonNull::new_unchecked(b) });
487
488 return true;
489 }
490
491 todo!()
492 }
493
494 todo!()
495 }
496
497 fn alloc_item(&self, state: &mut ZoneState, flags: Alloc) -> *mut u8 {
504 let keg = state.kegs.front().unwrap();
506 let slab = unsafe { (self.slab)(keg, flags) };
507
508 if let Some(slab) = slab {
509 let item = slab.alloc_item();
510
511 state.alloc_count += 1;
512
513 if self.init.is_none_or(|f| f(item, self.size, flags)) {
514 if self.ctor.is_none_or(|f| f(item, self.size, flags)) {
515 if flags.has_any(Alloc::Zero) {
516 unsafe { item.write_bytes(0, self.size.get()) };
517 }
518
519 return item;
520 } else {
521 todo!()
522 }
523 } else {
524 todo!()
525 }
526 }
527
528 todo!()
529 }
530
531 unsafe fn fetch_slab(keg: &Arc<UmaKeg>, flags: Alloc) -> Option<Pin<Strong<Slab>>> {
538 if !keg.flags().has_any(UmaFlags::Bucket) || keg.recurse() == 0 {
539 loop {
540 if let Some(v) = unsafe { keg.fetch_slab(flags) } {
541 return Some(v);
542 }
543
544 if flags.has_any(Alloc::NoWait | Alloc::NoVm) {
545 break;
546 }
547 }
548 }
549
550 None
551 }
552}
553
554struct ZoneState {
556 kegs: LinkedList<Arc<UmaKeg>>, full_buckets: VecDeque<NonNull<UmaBucket>>, free_buckets: VecDeque<NonNull<UmaBucket>>, alloc_count: u64, free_count: u64, count: usize, fills: u16, flags: UmaFlags, }
565
566unsafe impl Send for ZoneState {}
567
568#[derive(Clone, Copy, PartialEq, Eq)]
570enum ZoneType {
571 Other,
572 MbufPacket,
574 MbufJumboPage,
576 Mbuf,
578 MbufCluster,
580 MbufClusterPack,
582}
583
584#[derive(Default)]
586struct UmaCache {
587 alloc: Option<NonNull<UmaBucket>>, free: Option<NonNull<UmaBucket>>, allocs: u64, frees: u64, }
593
594unsafe impl Send for UmaCache {}
595
596pub struct ZoneArgs {
598 pub name: String, pub keg: Option<Arc<UmaKeg>>, pub size: NonZero<usize>, pub align: Option<usize>, pub init: Option<fn()>, pub ctor: Option<fn(*mut u8, NonZero<usize>, Alloc) -> bool>, pub dtor: Option<fn()>, pub flags: UmaFlags, }