xref: /DragonOS/kernel/crates/rust-slabmalloc/src/zone.rs (revision fae6e9ade46a52976ad5d099643d51cc20876448)
1 //! A ZoneAllocator to allocate arbitrary object sizes (up to `ZoneAllocator::MAX_ALLOC_SIZE`)
2 //!
3 //! The ZoneAllocator achieves this by having many `SCAllocator`
4 
5 use crate::*;
6 
7 /// Creates an instance of a zone, we do this in a macro because we
8 /// re-use the code in const and non-const functions
9 ///
10 /// We can get rid of this once the const fn feature is fully stabilized.
11 macro_rules! new_zone {
12     () => {
13         ZoneAllocator {
14             // TODO(perf): We should probably pick better classes
15             // rather than powers-of-two (see SuperMalloc etc.)
16             small_slabs: [
17                 SCAllocator::new(1 << 3),  // 8
18                 SCAllocator::new(1 << 4),  // 16
19                 SCAllocator::new(1 << 5),  // 32
20                 SCAllocator::new(1 << 6),  // 64
21                 SCAllocator::new(1 << 7),  // 128
22                 SCAllocator::new(1 << 8),  // 256
23                 SCAllocator::new(1 << 9),  // 512
24                 SCAllocator::new(1 << 10), // 1024
25                 SCAllocator::new(1 << 11), // 2048            ],
26             ],
27             total: 0,
28         }
29     };
30 }
31 
32 /// A zone allocator for arbitrary sized allocations.
33 ///
34 /// Has a bunch of `SCAllocator` and through that can serve allocation
35 /// requests for many different object sizes up to (MAX_SIZE_CLASSES) by selecting
36 /// the right `SCAllocator` for allocation and deallocation.
37 ///
38 /// The allocator provides to refill functions `refill` and `refill_large`
39 /// to provide the underlying `SCAllocator` with more memory in case it runs out.
40 pub struct ZoneAllocator<'a> {
41     small_slabs: [SCAllocator<'a, ObjectPage<'a>>; ZoneAllocator::MAX_BASE_SIZE_CLASSES],
42     total: u64,
43 }
44 
45 impl<'a> Default for ZoneAllocator<'a> {
46     fn default() -> ZoneAllocator<'a> {
47         new_zone!()
48     }
49 }
50 
51 enum Slab {
52     Base(usize),
53     Unsupported,
54 }
55 
56 impl<'a> ZoneAllocator<'a> {
57     /// Maximum size which is allocated with ObjectPages (4 KiB pages).
58     ///
59     /// e.g. this is 4 KiB - 80 bytes of meta-data.
60     pub const MAX_BASE_ALLOC_SIZE: usize = 1 << 11;
61 
62     /// How many allocators of type SCAllocator<ObjectPage> we have.
63     pub const MAX_BASE_SIZE_CLASSES: usize = 9;
64 
65     #[cfg(feature = "unstable")]
66     pub const fn new() -> ZoneAllocator<'a> {
67         new_zone!()
68     }
69 
70     #[cfg(not(feature = "unstable"))]
71     pub fn new() -> ZoneAllocator<'a> {
72         new_zone!()
73     }
74 
75     /// Return maximum size an object of size `current_size` can use.
76     ///
77     /// Used to optimize `realloc`.
78     pub fn get_max_size(current_size: usize) -> Option<usize> {
79         match current_size {
80             0..=8 => Some(8),
81             9..=16 => Some(16),
82             17..=32 => Some(32),
83             33..=64 => Some(64),
84             65..=128 => Some(128),
85             129..=256 => Some(256),
86             257..=512 => Some(512),
87             513..=1024 => Some(1024),
88             1025..=2048 => Some(2048),
89             _ => None,
90         }
91     }
92 
93     /// Figure out index into zone array to get the correct slab allocator for that size.
94     fn get_slab(requested_size: usize) -> Slab {
95         match requested_size {
96             0..=8 => Slab::Base(0),
97             9..=16 => Slab::Base(1),
98             17..=32 => Slab::Base(2),
99             33..=64 => Slab::Base(3),
100             65..=128 => Slab::Base(4),
101             129..=256 => Slab::Base(5),
102             257..=512 => Slab::Base(6),
103             513..=1024 => Slab::Base(7),
104             1025..=2048 => Slab::Base(8),
105             _ => Slab::Unsupported,
106         }
107     }
108 
109     /// Reclaims empty pages by calling `dealloc` on it and removing it from the
110     /// empty lists in the [`SCAllocator`].
111     ///
112     /// The `dealloc` function is called at most `reclaim_base_max` times for
113     /// base pages, and at most `reclaim_large_max` for large pages.
114     pub fn try_reclaim_base_pages<F>(&mut self, mut to_reclaim: usize, mut dealloc: F)
115     where
116         F: Fn(*mut ObjectPage),
117     {
118         for i in 0..ZoneAllocator::MAX_BASE_SIZE_CLASSES {
119             let slab = &mut self.small_slabs[i];
120             // reclaim的page数
121             let just_reclaimed = slab.try_reclaim_pages(to_reclaim, &mut dealloc);
122             self.total -= (just_reclaimed * OBJECT_PAGE_SIZE) as u64;
123             to_reclaim = to_reclaim.saturating_sub(just_reclaimed);
124             if to_reclaim == 0 {
125                 break;
126             }
127         }
128     }
129 
130     /// 获取scallocator中的还未被分配的空间
131     pub fn free_space(&mut self) -> u64 {
132         // 记录空闲空间
133         let mut free = 0;
134         // 遍历所有scallocator
135         for count in 0..ZoneAllocator::MAX_BASE_SIZE_CLASSES {
136             // 获取scallocator
137             let scallocator = &mut self.small_slabs[count];
138 
139             // 遍历scallocator中的部分分配的page(partial_page)
140             for slab_page in scallocator.slabs.iter_mut() {
141                 // 剩余可分配object数乘上page中规定的每个object的大小,即空闲空间
142                 free += slab_page.free_obj_count() * scallocator.size();
143             }
144             // 遍历scallocator中的empty_page,把空页空间也加上去
145             free +=
146                 scallocator.empty_slabs.elements * (scallocator.obj_per_page * scallocator.size());
147         }
148         free as u64
149     }
150 
151     pub fn usage(&mut self) -> SlabUsage {
152         let free_num = self.free_space();
153         SlabUsage::new(self.total, free_num)
154     }
155 }
156 
157 unsafe impl<'a> crate::Allocator<'a> for ZoneAllocator<'a> {
158     /// Allocate a pointer to a block of memory described by `layout`.
159     fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError> {
160         match ZoneAllocator::get_slab(layout.size()) {
161             Slab::Base(idx) => self.small_slabs[idx].allocate(layout),
162             Slab::Unsupported => Err(AllocationError::InvalidLayout),
163         }
164     }
165 
166     /// Deallocates a pointer to a block of memory, which was
167     /// previously allocated by `allocate`.
168     ///
169     /// # Arguments
170     ///  * `ptr` - Address of the memory location to free.
171     ///  * `layout` - Memory layout of the block pointed to by `ptr`.
172     ///  * `slab_callback` - The callback function to free slab_page in buddy.
173     unsafe fn deallocate(
174         &mut self,
175         ptr: NonNull<u8>,
176         layout: Layout,
177         slab_callback: &'static dyn CallBack,
178     ) -> Result<(), AllocationError> {
179         match ZoneAllocator::get_slab(layout.size()) {
180             Slab::Base(idx) => self.small_slabs[idx].deallocate(ptr, layout, slab_callback),
181             Slab::Unsupported => Err(AllocationError::InvalidLayout),
182         }
183     }
184 
185     /// Refills the SCAllocator for a given Layout with an ObjectPage.
186     ///
187     /// # Safety
188     /// ObjectPage needs to be emtpy etc.
189     unsafe fn refill(
190         &mut self,
191         layout: Layout,
192         new_page: &'a mut ObjectPage<'a>,
193     ) -> Result<(), AllocationError> {
194         match ZoneAllocator::get_slab(layout.size()) {
195             Slab::Base(idx) => {
196                 self.small_slabs[idx].refill(new_page);
197                 // 每refill一个page就为slab的总空间统计加上4KB
198                 self.total += OBJECT_PAGE_SIZE as u64;
199                 Ok(())
200             }
201             Slab::Unsupported => Err(AllocationError::InvalidLayout),
202         }
203     }
204 }
205 
206 /// Slab内存空间使用情况
207 pub struct SlabUsage {
208     // slab总共使用的内存空间
209     total: u64,
210     // slab的空闲空间
211     free: u64,
212 }
213 
214 impl SlabUsage {
215     /// 初始化SlabUsage
216     pub fn new(total: u64, free: u64) -> Self {
217         Self { total, free }
218     }
219 
220     pub fn total(&self) -> u64 {
221         self.total
222     }
223 
224     pub fn used(&self) -> u64 {
225         self.total - self.free
226     }
227 
228     pub fn free(&self) -> u64 {
229         self.free
230     }
231 }
232