1use core::{
4 alloc::{GlobalAlloc, Layout},
5 ptr::NonNull,
6};
7
8use ax_sync::SpinLock;
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: SpinLock<TlsfInfo>,
44 usages: SpinLock<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: SpinLock::new(TlsfInfo::new()),
58 usages: SpinLock::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_irqsave();
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_irqsave();
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 =
98 crate::retry_after_registered_reclaim(crate::layout_reclaim_pages(layout), || {
99 self.inner
100 .lock_irqsave()
101 .tlsf
102 .allocate(layout)
103 .ok_or(crate::AllocError::NoMemory)
104 })?;
105 self.inner.lock_irqsave().used_bytes += layout.size();
106 self.usages
107 .lock_irqsave()
108 .alloc(UsageKind::RustHeap, layout.size());
109 Ok(ptr)
110 }
111
112 pub fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
114 unsafe {
115 self.inner
116 .lock_irqsave()
117 .tlsf
118 .deallocate(pos, layout.align());
119 }
120 self.inner.lock_irqsave().used_bytes -= layout.size();
121 self.usages
122 .lock_irqsave()
123 .dealloc(UsageKind::RustHeap, layout.size());
124 }
125
126 pub fn alloc_pages(
128 &self,
129 num_pages: usize,
130 alignment: usize,
131 kind: UsageKind,
132 ) -> AllocResult<usize> {
133 let size = num_pages
134 .checked_mul(PAGE_SIZE)
135 .ok_or(crate::AllocError::InvalidParam)?;
136 let align = alignment.max(PAGE_SIZE);
137 let layout =
138 Layout::from_size_align(size, align).map_err(|_| crate::AllocError::InvalidParam)?;
139 let ptr = crate::retry_after_registered_reclaim(num_pages, || {
140 self.inner
141 .lock_irqsave()
142 .tlsf
143 .allocate(layout)
144 .ok_or(crate::AllocError::NoMemory)
145 })?;
146 self.inner.lock_irqsave().used_bytes += size;
147 if !matches!(kind, UsageKind::RustHeap) {
148 self.usages.lock_irqsave().alloc(kind, size);
149 }
150 Ok(ptr.as_ptr() as usize)
151 }
152
153 pub fn alloc_dma32_pages(
155 &self,
156 _num_pages: usize,
157 _alignment: usize,
158 _kind: UsageKind,
159 ) -> AllocResult<usize> {
160 unimplemented!("TLSF allocator does not support alloc_dma32_pages")
161 }
162
163 pub fn alloc_pages_at(
165 &self,
166 _start: usize,
167 _num_pages: usize,
168 _alignment: usize,
169 _kind: UsageKind,
170 ) -> AllocResult<usize> {
171 unimplemented!("TLSF allocator does not support alloc_pages_at")
172 }
173
174 pub fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
176 let size = num_pages * PAGE_SIZE;
177 let ptr = NonNull::new(pos as *mut u8).expect("dealloc_pages null ptr");
178 unsafe {
179 self.inner.lock_irqsave().tlsf.deallocate(ptr, PAGE_SIZE);
180 }
181 self.inner.lock_irqsave().used_bytes -= size;
182 self.usages.lock_irqsave().dealloc(kind, size);
183 }
184
185 pub fn used_bytes(&self) -> usize {
187 self.inner.lock_irqsave().used_bytes
188 }
189
190 pub fn available_bytes(&self) -> usize {
192 let inner = self.inner.lock_irqsave();
193 inner.total_bytes.saturating_sub(inner.used_bytes)
194 }
195
196 pub fn used_pages(&self) -> usize {
198 self.used_bytes() / PAGE_SIZE
199 }
200
201 pub fn available_pages(&self) -> usize {
203 self.available_bytes() / PAGE_SIZE
204 }
205
206 pub fn usages(&self) -> Usages {
208 *self.usages.lock_irqsave()
209 }
210}
211
212impl AllocatorOps for GlobalAllocator {
213 fn name(&self) -> &'static str {
214 GlobalAllocator::name(self)
215 }
216
217 fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
218 GlobalAllocator::init(self, start_vaddr, size)
219 }
220
221 fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
222 GlobalAllocator::add_memory(self, start_vaddr, size)
223 }
224
225 fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
226 GlobalAllocator::alloc(self, layout)
227 }
228
229 fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
230 GlobalAllocator::dealloc(self, pos, layout)
231 }
232
233 fn alloc_pages(
234 &self,
235 num_pages: usize,
236 alignment: usize,
237 kind: UsageKind,
238 ) -> AllocResult<usize> {
239 GlobalAllocator::alloc_pages(self, num_pages, alignment, kind)
240 }
241
242 fn alloc_dma32_pages(
243 &self,
244 num_pages: usize,
245 alignment: usize,
246 kind: UsageKind,
247 ) -> AllocResult<usize> {
248 GlobalAllocator::alloc_dma32_pages(self, num_pages, alignment, kind)
249 }
250
251 fn alloc_pages_at(
252 &self,
253 start: usize,
254 num_pages: usize,
255 alignment: usize,
256 kind: UsageKind,
257 ) -> AllocResult<usize> {
258 GlobalAllocator::alloc_pages_at(self, start, num_pages, alignment, kind)
259 }
260
261 fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
262 GlobalAllocator::dealloc_pages(self, pos, num_pages, kind)
263 }
264
265 fn used_bytes(&self) -> usize {
266 GlobalAllocator::used_bytes(self)
267 }
268
269 fn available_bytes(&self) -> usize {
270 GlobalAllocator::available_bytes(self)
271 }
272
273 fn used_pages(&self) -> usize {
274 GlobalAllocator::used_pages(self)
275 }
276
277 fn available_pages(&self) -> usize {
278 GlobalAllocator::available_pages(self)
279 }
280
281 fn usages(&self) -> Usages {
282 GlobalAllocator::usages(self)
283 }
284}
285
286pub fn global_allocator() -> &'static GlobalAllocator {
288 &GLOBAL_ALLOCATOR
289}
290
291pub fn init_percpu_slab(_cpu_id: usize) {}
295
296pub fn global_init(start_vaddr: usize, size: usize) -> AllocResult {
298 debug!(
299 "initialize global allocator at: [{:#x}, {:#x})",
300 start_vaddr,
301 start_vaddr + size
302 );
303 GLOBAL_ALLOCATOR.init(start_vaddr, size)
304}
305
306pub fn global_add_memory(start_vaddr: usize, size: usize) -> AllocResult {
308 debug!(
309 "add a memory region to global allocator: [{:#x}, {:#x})",
310 start_vaddr,
311 start_vaddr + size
312 );
313 GLOBAL_ALLOCATOR.add_memory(start_vaddr, size)
314}
315
316unsafe impl GlobalAlloc for GlobalAllocator {
317 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
318 let inner = move || {
319 if let Ok(ptr) = GlobalAllocator::alloc(self, layout) {
320 ptr.as_ptr()
321 } else {
322 core::ptr::null_mut()
326 }
327 };
328
329 #[cfg(feature = "tracking")]
330 {
331 crate::tracking::with_state(|state| match state {
332 None => inner(),
333 Some(state) => {
334 let ptr = inner();
335 if ptr.is_null() {
336 return ptr;
337 }
338 let generation = state.generation;
339 state.generation += 1;
340 state.map.insert(
341 ptr as usize,
342 crate::tracking::AllocationInfo {
343 layout,
344 backtrace: axbacktrace::Backtrace::capture(),
345 generation,
346 },
347 );
348 ptr
349 }
350 })
351 }
352
353 #[cfg(not(feature = "tracking"))]
354 inner()
355 }
356
357 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
358 let ptr = NonNull::new(ptr).expect("dealloc null ptr");
359 let inner = || GlobalAllocator::dealloc(self, ptr, layout);
360
361 #[cfg(feature = "tracking")]
362 crate::tracking::with_state(|state| match state {
363 None => inner(),
364 Some(state) => {
365 let address = ptr.as_ptr() as usize;
366 state.map.remove(&address);
367 inner()
368 }
369 });
370
371 #[cfg(not(feature = "tracking"))]
372 inner();
373 }
374}