obkrnl/uma/slab.rs
1use super::UmaKeg;
2use crate::lock::Mutex;
3use crate::mem::{RefCnt, too_many_refs};
4use alloc::sync::Arc;
5use core::marker::PhantomPinned;
6use core::ptr::null_mut;
7use core::sync::atomic::{AtomicUsize, Ordering};
8use macros::bitflag;
9
10/// Implementation of `uma_slab`.
11///
12/// Unlike Orbis, we don't support `uma_slab_refcnt`. We use [alloc::sync::Arc] for that job instead
13/// so you need to wrap the item to allocate from the slab with [alloc::sync::Arc] for any zones
14/// that going to create with `UMA_ZONE_REFCNT`.
15///
16/// We use slightly different mechanism here but has the same memory layout (except we don't support
17/// `uma_slab_refcnt` as stated on the above).
18///
19/// # Safety
20/// Adding more fields into this struct without knowing how it work can cause undefined behavior in
21/// some places.
22#[repr(C)]
23pub struct Slab {
24 pub(super) pin: PhantomPinned,
25 pub(super) hdr: SlabHdr, // us_head
26 pub(super) free: [u8], // us_freelist
27}
28
29impl Slab {
30 pub fn flags(&self) -> SlabFlags {
31 self.hdr.flags
32 }
33
34 /// Each allocated item keep a strong reference to the slab, which mean all allocated item need
35 /// to free manually otherwise the slab (and its keg) will be leak.
36 ///
37 /// Unlike Orbis, this method will return null if the slab already full instead of trigger a UB.
38 ///
39 /// See `slab_alloc_item` on the Orbis for a reference.
40 ///
41 /// # Reference offsets
42 /// | Version | Offset |
43 /// |---------|--------|
44 /// |PS4 11.00|0x141FE0|
45 pub fn alloc_item(&self) -> *mut u8 {
46 // Check if full.
47 let mut k = self.hdr.keg.state().lock();
48 let mut s = self.hdr.state.lock();
49
50 if s.free_count == 0 {
51 return null_mut();
52 }
53
54 // Allocate.
55 let f = usize::from(s.first_free);
56
57 s.first_free = self.free[f];
58 s.free_count -= 1;
59 k.free -= 1;
60
61 if s.free_count == 0 {
62 todo!()
63 }
64
65 self.hdr.refs.fetch_add(1, Ordering::Relaxed);
66
67 unsafe { self.hdr.items.add(f * self.hdr.keg.allocated_size()) }
68 }
69}
70
71impl Drop for Slab {
72 #[inline(never)]
73 fn drop(&mut self) {
74 core::sync::atomic::fence(Ordering::Acquire);
75
76 todo!()
77 }
78}
79
80unsafe impl RefCnt for Slab {
81 fn increase_ref(&self) {
82 let p = self.hdr.refs.fetch_add(1, Ordering::Relaxed);
83
84 if p == usize::MAX {
85 too_many_refs();
86 }
87 }
88
89 fn decrease_ref(&self) -> usize {
90 self.hdr.refs.fetch_sub(1, Ordering::Release)
91 }
92}
93
94unsafe impl Send for Slab {}
95unsafe impl Sync for Slab {}
96
97/// Implementation of `uma_slab_head`.
98pub(super) struct SlabHdr {
99 keg: Arc<UmaKeg>, // us_keg
100 items: *mut u8, // us_data
101 flags: SlabFlags, // us_flags
102 /// This **MUST** be locked after everything else (e.g. keg state and zone state) otherwise it
103 /// will cause a deadlock.
104 state: Mutex<SlabState>,
105 refs: AtomicUsize,
106}
107
108impl SlabHdr {
109 /// # Safety
110 /// - `items` cannot be null.
111 /// - `len` must be a number of elements of the array at `items`.
112 pub unsafe fn new(keg: Arc<UmaKeg>, flags: SlabFlags, items: *mut u8, len: usize) -> Self {
113 Self {
114 keg,
115 items,
116 flags,
117 state: Mutex::new(SlabState {
118 free_count: len,
119 first_free: 0,
120 }),
121 refs: AtomicUsize::new(0),
122 }
123 }
124}
125
126/// Flags for [Slab].
127#[bitflag(u8)]
128pub enum SlabFlags {
129 /// `UMA_SLAB_PRIV`.
130 Private = 0x08,
131 /// `UMA_SLAB_MALLOC`.
132 Malloc = 0x20,
133}
134
135/// Contains mutable data for [SlabHdr].
136struct SlabState {
137 free_count: usize, // us_freecount
138 first_free: u8, // us_firstfree
139}