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