Skip to main content

ax_alloc/
lib.rs

1//! [ArceOS](https://github.com/arceos-org/arceos) global memory allocator.
2//!
3//! It provides [`GlobalAllocator`], which implements the trait
4//! [`core::alloc::GlobalAlloc`]. A static global variable of type
5//! [`GlobalAllocator`] is defined with the `#[global_allocator]` attribute, to
6//! be registered as the standard library's default allocator.
7
8#![no_std]
9
10#[macro_use]
11extern crate log;
12extern crate alloc;
13
14use core::{alloc::Layout, fmt, ptr::NonNull};
15
16use ax_errno::AxError;
17use strum::{IntoStaticStr, VariantArray};
18
19const PAGE_SIZE: usize = 0x1000;
20
21mod page;
22pub use page::GlobalPage;
23
24/// Tracking of memory usage, enabled with the `tracking` feature.
25#[cfg(feature = "tracking")]
26pub mod tracking;
27
28/// Kinds of memory usage for tracking.
29#[repr(u8)]
30#[derive(Debug, Clone, Copy, PartialEq, Eq, VariantArray, IntoStaticStr)]
31pub enum UsageKind {
32    /// Heap allocations made by kernel Rust code.
33    RustHeap,
34    /// Virtual memory, usually used for user space.
35    VirtMem,
36    /// Page cache for file systems.
37    PageCache,
38    /// Page tables.
39    PageTable,
40    /// DMA memory.
41    Dma,
42    /// Memory used by [`GlobalPage`].
43    Global,
44}
45
46/// Statistics of memory usages.
47#[derive(Clone, Copy)]
48pub struct Usages([usize; UsageKind::VARIANTS.len()]);
49
50impl Usages {
51    const fn new() -> Self {
52        Self([0; UsageKind::VARIANTS.len()])
53    }
54
55    fn alloc(&mut self, kind: UsageKind, size: usize) {
56        self.0[kind as usize] += size;
57    }
58
59    fn dealloc(&mut self, kind: UsageKind, size: usize) {
60        self.0[kind as usize] -= size;
61    }
62
63    /// Get the memory usage for a specific kind.
64    pub fn get(&self, kind: UsageKind) -> usize {
65        self.0[kind as usize]
66    }
67}
68
69impl fmt::Debug for Usages {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        let mut d = f.debug_struct("UsageStats");
72        for &kind in UsageKind::VARIANTS {
73            d.field(kind.into(), &self.0[kind as usize]);
74        }
75        d.finish()
76    }
77}
78
79/// The error type used for allocation operations in `ax-alloc`.
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum AllocError {
82    /// Invalid size, alignment, or other input parameter.
83    InvalidParam,
84    /// The allocator has already been initialized.
85    AlreadyInitialized,
86    /// A region overlaps with an existing managed region.
87    MemoryOverlap,
88    /// Not enough memory is available to satisfy the request.
89    NoMemory,
90    /// Attempted to deallocate memory that was not allocated.
91    NotAllocated,
92    /// The allocator has not been initialized.
93    NotInitialized,
94    /// The requested address or entity was not found.
95    NotFound,
96}
97
98/// A [`Result`] alias with [`AllocError`] as the error type.
99pub type AllocResult<T = ()> = Result<T, AllocError>;
100
101impl From<AllocError> for AxError {
102    fn from(value: AllocError) -> Self {
103        match value {
104            AllocError::NoMemory => AxError::NoMemory,
105            AllocError::NotFound => AxError::NotFound,
106            AllocError::NotInitialized | AllocError::AlreadyInitialized => AxError::BadState,
107            AllocError::MemoryOverlap => AxError::AlreadyExists,
108            AllocError::InvalidParam | AllocError::NotAllocated => AxError::InvalidInput,
109        }
110    }
111}
112
113/// Unified allocator operations provided by all `ax-alloc` backends.
114pub trait AllocatorOps {
115    /// Returns the allocator name.
116    fn name(&self) -> &'static str;
117
118    /// Initializes the allocator with the given region.
119    fn init(&self, start_vaddr: usize, size: usize) -> AllocResult;
120
121    /// Adds an extra memory region to the allocator.
122    fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult;
123
124    /// Allocates arbitrary bytes.
125    fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>>;
126
127    /// Deallocates a prior byte allocation.
128    fn dealloc(&self, pos: NonNull<u8>, layout: Layout);
129
130    /// Allocates contiguous pages.
131    ///
132    /// `align` is the requested byte alignment, not a log2/exponent.
133    /// It must be a power-of-two byte alignment accepted by the backend page allocator.
134    fn alloc_pages(&self, num_pages: usize, align: usize, kind: UsageKind) -> AllocResult<usize>;
135
136    /// Allocates contiguous DMA32 pages.
137    ///
138    /// `align` is the requested byte alignment, not a log2/exponent.
139    /// It must be a power-of-two byte alignment accepted by the backend page allocator.
140    fn alloc_dma32_pages(
141        &self,
142        num_pages: usize,
143        align: usize,
144        kind: UsageKind,
145    ) -> AllocResult<usize>;
146
147    /// Allocates contiguous pages starting from the given address.
148    ///
149    /// `align` is the requested byte alignment, not a log2/exponent.
150    /// It must be a power-of-two byte alignment accepted by the backend page allocator.
151    fn alloc_pages_at(
152        &self,
153        start: usize,
154        num_pages: usize,
155        align: usize,
156        kind: UsageKind,
157    ) -> AllocResult<usize>;
158
159    /// Deallocates a prior page allocation.
160    fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind);
161
162    /// Returns used byte count.
163    fn used_bytes(&self) -> usize;
164
165    /// Returns available byte count.
166    fn available_bytes(&self) -> usize;
167
168    /// Returns used page count.
169    fn used_pages(&self) -> usize;
170
171    /// Returns available page count.
172    fn available_pages(&self) -> usize;
173
174    /// Returns usage statistics.
175    fn usages(&self) -> Usages;
176}
177
178// Select implementation based on features.
179#[cfg(feature = "buddy-slab")]
180mod buddy_slab;
181#[cfg(feature = "buddy-slab")]
182use buddy_slab as imp;
183
184#[cfg(not(feature = "buddy-slab"))]
185mod default_impl;
186#[cfg(not(feature = "buddy-slab"))]
187use default_impl as imp;
188#[cfg(feature = "buddy-slab")]
189pub use imp::init_percpu_slab;
190pub use imp::{DefaultByteAllocator, GlobalAllocator, global_add_memory, global_init};
191
192/// Returns the reference to the global allocator.
193pub fn global_allocator() -> &'static GlobalAllocator {
194    imp::global_allocator()
195}