1use core::{
4 alloc::{GlobalAlloc, Layout},
5 ptr::NonNull,
6};
7
8use ax_kspin::SpinNoIrq;
9use rlsf::Tlsf;
10
11use super::{AllocResult, AllocatorOps, UsageKind, Usages};
12
13#[cfg_attr(
15 all(any(target_os = "none", feature = "global-allocator"), not(test)),
16 global_allocator
17)]
18static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator::new();
19
20const PAGE_SIZE: usize = 0x1000;
21
22pub type DefaultByteAllocator = Tlsf<'static, u32, u32, 28, 32>;
24
25struct TlsfInfo {
26 tlsf: Tlsf<'static, u32, u32, 28, 32>,
27 total_bytes: usize,
28 used_bytes: usize,
29}
30
31impl TlsfInfo {
32 const fn new() -> Self {
33 Self {
34 tlsf: Tlsf::new(),
35 total_bytes: 0,
36 used_bytes: 0,
37 }
38 }
39}
40
41pub struct GlobalAllocator {
43 inner: SpinNoIrq<TlsfInfo>,
44 usages: SpinNoIrq<Usages>,
45}
46
47impl Default for GlobalAllocator {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl GlobalAllocator {
54 pub const fn new() -> Self {
56 Self {
57 inner: SpinNoIrq::new(TlsfInfo::new()),
58 usages: SpinNoIrq::new(Usages::new()),
59 }
60 }
61
62 pub const fn name(&self) -> &'static str {
64 "TLSF"
65 }
66
67 pub fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
69 let mut inner = self.inner.lock();
70 unsafe {
71 let pool = core::slice::from_raw_parts_mut(start_vaddr as *mut u8, size);
72 inner
73 .tlsf
74 .insert_free_block_ptr(NonNull::new(pool).unwrap())
75 .unwrap();
76 }
77 inner.total_bytes = size;
78 Ok(())
79 }
80
81 pub fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
83 let mut inner = self.inner.lock();
84 unsafe {
85 let pool = core::slice::from_raw_parts_mut(start_vaddr as *mut u8, size);
86 inner
87 .tlsf
88 .insert_free_block_ptr(NonNull::new(pool).unwrap())
89 .ok_or(crate::AllocError::InvalidParam)?;
90 }
91 inner.total_bytes += size;
92 Ok(())
93 }
94
95 pub fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
97 let ptr = self
98 .inner
99 .lock()
100 .tlsf
101 .allocate(layout)
102 .ok_or(crate::AllocError::NoMemory)?;
103 self.inner.lock().used_bytes += layout.size();
104 self.usages.lock().alloc(UsageKind::RustHeap, layout.size());
105 Ok(ptr)
106 }
107
108 pub fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
110 unsafe {
111 self.inner.lock().tlsf.deallocate(pos, layout.align());
112 }
113 self.inner.lock().used_bytes -= layout.size();
114 self.usages
115 .lock()
116 .dealloc(UsageKind::RustHeap, layout.size());
117 }
118
119 pub fn alloc_pages(
121 &self,
122 num_pages: usize,
123 alignment: usize,
124 kind: UsageKind,
125 ) -> AllocResult<usize> {
126 let size = num_pages * PAGE_SIZE;
127 let align = alignment.max(PAGE_SIZE);
128 let layout =
129 Layout::from_size_align(size, align).map_err(|_| crate::AllocError::InvalidParam)?;
130 let ptr = self
131 .inner
132 .lock()
133 .tlsf
134 .allocate(layout)
135 .ok_or(crate::AllocError::NoMemory)?;
136 self.inner.lock().used_bytes += size;
137 if !matches!(kind, UsageKind::RustHeap) {
138 self.usages.lock().alloc(kind, size);
139 }
140 Ok(ptr.as_ptr() as usize)
141 }
142
143 pub fn alloc_dma32_pages(
145 &self,
146 _num_pages: usize,
147 _alignment: usize,
148 _kind: UsageKind,
149 ) -> AllocResult<usize> {
150 unimplemented!("TLSF allocator does not support alloc_dma32_pages")
151 }
152
153 pub fn alloc_pages_at(
155 &self,
156 _start: usize,
157 _num_pages: usize,
158 _alignment: usize,
159 _kind: UsageKind,
160 ) -> AllocResult<usize> {
161 unimplemented!("TLSF allocator does not support alloc_pages_at")
162 }
163
164 pub fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
166 let size = num_pages * PAGE_SIZE;
167 let ptr = NonNull::new(pos as *mut u8).expect("dealloc_pages null ptr");
168 unsafe {
169 self.inner.lock().tlsf.deallocate(ptr, PAGE_SIZE);
170 }
171 self.inner.lock().used_bytes -= size;
172 self.usages.lock().dealloc(kind, size);
173 }
174
175 pub fn used_bytes(&self) -> usize {
177 self.inner.lock().used_bytes
178 }
179
180 pub fn available_bytes(&self) -> usize {
182 let inner = self.inner.lock();
183 inner.total_bytes.saturating_sub(inner.used_bytes)
184 }
185
186 pub fn used_pages(&self) -> usize {
188 self.used_bytes() / PAGE_SIZE
189 }
190
191 pub fn available_pages(&self) -> usize {
193 self.available_bytes() / PAGE_SIZE
194 }
195
196 pub fn usages(&self) -> Usages {
198 *self.usages.lock()
199 }
200}
201
202impl AllocatorOps for GlobalAllocator {
203 fn name(&self) -> &'static str {
204 GlobalAllocator::name(self)
205 }
206
207 fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
208 GlobalAllocator::init(self, start_vaddr, size)
209 }
210
211 fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
212 GlobalAllocator::add_memory(self, start_vaddr, size)
213 }
214
215 fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
216 GlobalAllocator::alloc(self, layout)
217 }
218
219 fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
220 GlobalAllocator::dealloc(self, pos, layout)
221 }
222
223 fn alloc_pages(
224 &self,
225 num_pages: usize,
226 alignment: usize,
227 kind: UsageKind,
228 ) -> AllocResult<usize> {
229 GlobalAllocator::alloc_pages(self, num_pages, alignment, kind)
230 }
231
232 fn alloc_dma32_pages(
233 &self,
234 num_pages: usize,
235 alignment: usize,
236 kind: UsageKind,
237 ) -> AllocResult<usize> {
238 GlobalAllocator::alloc_dma32_pages(self, num_pages, alignment, kind)
239 }
240
241 fn alloc_pages_at(
242 &self,
243 start: usize,
244 num_pages: usize,
245 alignment: usize,
246 kind: UsageKind,
247 ) -> AllocResult<usize> {
248 GlobalAllocator::alloc_pages_at(self, start, num_pages, alignment, kind)
249 }
250
251 fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
252 GlobalAllocator::dealloc_pages(self, pos, num_pages, kind)
253 }
254
255 fn used_bytes(&self) -> usize {
256 GlobalAllocator::used_bytes(self)
257 }
258
259 fn available_bytes(&self) -> usize {
260 GlobalAllocator::available_bytes(self)
261 }
262
263 fn used_pages(&self) -> usize {
264 GlobalAllocator::used_pages(self)
265 }
266
267 fn available_pages(&self) -> usize {
268 GlobalAllocator::available_pages(self)
269 }
270
271 fn usages(&self) -> Usages {
272 GlobalAllocator::usages(self)
273 }
274}
275
276pub fn global_allocator() -> &'static GlobalAllocator {
278 &GLOBAL_ALLOCATOR
279}
280
281pub fn init_percpu_slab(_cpu_id: usize) {}
285
286pub fn global_init(start_vaddr: usize, size: usize) -> AllocResult {
288 debug!(
289 "initialize global allocator at: [{:#x}, {:#x})",
290 start_vaddr,
291 start_vaddr + size
292 );
293 GLOBAL_ALLOCATOR.init(start_vaddr, size)
294}
295
296pub fn global_add_memory(start_vaddr: usize, size: usize) -> AllocResult {
298 debug!(
299 "add a memory region to global allocator: [{:#x}, {:#x})",
300 start_vaddr,
301 start_vaddr + size
302 );
303 GLOBAL_ALLOCATOR.add_memory(start_vaddr, size)
304}
305
306unsafe impl GlobalAlloc for GlobalAllocator {
307 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
308 let inner = move || {
309 if let Ok(ptr) = GlobalAllocator::alloc(self, layout) {
310 ptr.as_ptr()
311 } else {
312 alloc::alloc::handle_alloc_error(layout)
313 }
314 };
315
316 #[cfg(feature = "tracking")]
317 {
318 crate::tracking::with_state(|state| match state {
319 None => inner(),
320 Some(state) => {
321 let ptr = inner();
322 let generation = state.generation;
323 state.generation += 1;
324 state.map.insert(
325 ptr as usize,
326 crate::tracking::AllocationInfo {
327 layout,
328 backtrace: axbacktrace::Backtrace::capture(),
329 generation,
330 },
331 );
332 ptr
333 }
334 })
335 }
336
337 #[cfg(not(feature = "tracking"))]
338 inner()
339 }
340
341 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
342 let ptr = NonNull::new(ptr).expect("dealloc null ptr");
343 let inner = || GlobalAllocator::dealloc(self, ptr, layout);
344
345 #[cfg(feature = "tracking")]
346 crate::tracking::with_state(|state| match state {
347 None => inner(),
348 Some(state) => {
349 let address = ptr.as_ptr() as usize;
350 state.map.remove(&address);
351 inner()
352 }
353 });
354
355 #[cfg(not(feature = "tracking"))]
356 inner();
357 }
358}