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