• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::prev_power_of_two;
2 use alloc::collections::BTreeSet;
3 use core::alloc::Layout;
4 use core::array;
5 use core::cmp::{max, min};
6 use core::ops::Range;
7 
8 #[cfg(feature = "use_spin")]
9 use core::ops::Deref;
10 #[cfg(feature = "use_spin")]
11 use spin::Mutex;
12 
13 /// A frame allocator that uses buddy system, requiring a global allocator.
14 ///
15 /// The max order of the allocator is specified via the const generic parameter `ORDER`. The frame
16 /// allocator will only be able to allocate ranges of size up to 2<sup>ORDER</sup>, out of a total
17 /// range of size at most 2<sup>ORDER + 1</sup> - 1.
18 ///
19 /// # Usage
20 ///
21 /// Create a frame allocator and add some frames to it:
22 /// ```
23 /// use buddy_system_allocator::*;
24 /// let mut frame = FrameAllocator::<32>::new();
25 /// assert!(frame.alloc(1).is_none());
26 ///
27 /// frame.add_frame(0, 3);
28 /// let num = frame.alloc(1);
29 /// assert_eq!(num, Some(2));
30 /// let num = frame.alloc(2);
31 /// assert_eq!(num, Some(0));
32 /// ```
33 pub struct FrameAllocator<const ORDER: usize = 32> {
34     // buddy system with max order of ORDER
35     free_list: [BTreeSet<usize>; ORDER],
36 
37     // statistics
38     allocated: usize,
39     total: usize,
40 }
41 
42 impl<const ORDER: usize> FrameAllocator<ORDER> {
43     /// Create an empty frame allocator
new() -> Self44     pub fn new() -> Self {
45         Self {
46             free_list: array::from_fn(|_| BTreeSet::default()),
47             allocated: 0,
48             total: 0,
49         }
50     }
51 
52     /// Add a range of frame number [start, end) to the allocator
add_frame(&mut self, start: usize, end: usize)53     pub fn add_frame(&mut self, start: usize, end: usize) {
54         assert!(start <= end);
55 
56         let mut total = 0;
57         let mut current_start = start;
58 
59         while current_start < end {
60             let lowbit = if current_start > 0 {
61                 current_start & (!current_start + 1)
62             } else {
63                 32
64             };
65             let size = min(
66                 min(lowbit, prev_power_of_two(end - current_start)),
67                 1 << (ORDER - 1),
68             );
69             total += size;
70 
71             self.free_list[size.trailing_zeros() as usize].insert(current_start);
72             current_start += size;
73         }
74 
75         self.total += total;
76     }
77 
78     /// Add a range of frames to the allocator.
insert(&mut self, range: Range<usize>)79     pub fn insert(&mut self, range: Range<usize>) {
80         self.add_frame(range.start, range.end);
81     }
82 
83     /// Allocate a range of frames from the allocator, returning the first frame of the allocated
84     /// range.
alloc(&mut self, count: usize) -> Option<usize>85     pub fn alloc(&mut self, count: usize) -> Option<usize> {
86         let size = count.next_power_of_two();
87         self.alloc_power_of_two(size)
88     }
89 
90     /// Allocate a range of frames with the given size and alignment from the allocator, returning
91     /// the first frame of the allocated range.
alloc_aligned(&mut self, layout: Layout) -> Option<usize>92     pub fn alloc_aligned(&mut self, layout: Layout) -> Option<usize> {
93         let size = max(layout.size().next_power_of_two(), layout.align());
94         self.alloc_power_of_two(size)
95     }
96 
97     /// Allocate a range of frames of the given size from the allocator. The size must be a power of
98     /// two. The allocated range will have alignment equal to the size.
alloc_power_of_two(&mut self, size: usize) -> Option<usize>99     fn alloc_power_of_two(&mut self, size: usize) -> Option<usize> {
100         let class = size.trailing_zeros() as usize;
101         for i in class..self.free_list.len() {
102             // Find the first non-empty size class
103             if !self.free_list[i].is_empty() {
104                 // Split buffers
105                 for j in (class + 1..i + 1).rev() {
106                     if let Some(block_ref) = self.free_list[j].iter().next() {
107                         let block = *block_ref;
108                         self.free_list[j - 1].insert(block + (1 << (j - 1)));
109                         self.free_list[j - 1].insert(block);
110                         self.free_list[j].remove(&block);
111                     } else {
112                         return None;
113                     }
114                 }
115 
116                 let result = self.free_list[class].iter().next().clone();
117                 if let Some(result_ref) = result {
118                     let result = *result_ref;
119                     self.free_list[class].remove(&result);
120                     self.allocated += size;
121                     return Some(result);
122                 } else {
123                     return None;
124                 }
125             }
126         }
127         None
128     }
129 
130     /// Deallocate a range of frames [frame, frame+count) from the frame allocator.
131     ///
132     /// The range should be exactly the same when it was allocated, as in heap allocator
dealloc(&mut self, start_frame: usize, count: usize)133     pub fn dealloc(&mut self, start_frame: usize, count: usize) {
134         let size = count.next_power_of_two();
135         self.dealloc_power_of_two(start_frame, size)
136     }
137 
138     /// Deallocate a range of frames which was previously allocated by [`alloc_aligned`].
139     ///
140     /// The layout must be exactly the same as when it was allocated.
dealloc_aligned(&mut self, start_frame: usize, layout: Layout)141     pub fn dealloc_aligned(&mut self, start_frame: usize, layout: Layout) {
142         let size = max(layout.size().next_power_of_two(), layout.align());
143         self.dealloc_power_of_two(start_frame, size)
144     }
145 
146     /// Deallocate a range of frames with the given size from the allocator. The size must be a
147     /// power of two.
dealloc_power_of_two(&mut self, start_frame: usize, size: usize)148     fn dealloc_power_of_two(&mut self, start_frame: usize, size: usize) {
149         let class = size.trailing_zeros() as usize;
150 
151         // Merge free buddy lists
152         let mut current_ptr = start_frame;
153         let mut current_class = class;
154         while current_class < self.free_list.len() {
155             let buddy = current_ptr ^ (1 << current_class);
156             if self.free_list[current_class].remove(&buddy) == true {
157                 // Free buddy found
158                 current_ptr = min(current_ptr, buddy);
159                 current_class += 1;
160             } else {
161                 self.free_list[current_class].insert(current_ptr);
162                 break;
163             }
164         }
165 
166         self.allocated -= size;
167     }
168 }
169 
170 /// A locked version of `FrameAllocator`
171 ///
172 /// # Usage
173 ///
174 /// Create a locked frame allocator and add frames to it:
175 /// ```
176 /// use buddy_system_allocator::*;
177 /// let mut frame = LockedFrameAllocator::<32>::new();
178 /// assert!(frame.lock().alloc(1).is_none());
179 ///
180 /// frame.lock().add_frame(0, 3);
181 /// let num = frame.lock().alloc(1);
182 /// assert_eq!(num, Some(2));
183 /// let num = frame.lock().alloc(2);
184 /// assert_eq!(num, Some(0));
185 /// ```
186 #[cfg(feature = "use_spin")]
187 pub struct LockedFrameAllocator<const ORDER: usize = 32>(Mutex<FrameAllocator<ORDER>>);
188 
189 #[cfg(feature = "use_spin")]
190 impl<const ORDER: usize> LockedFrameAllocator<ORDER> {
191     /// Creates an empty heap
new() -> Self192     pub fn new() -> Self {
193         Self(Mutex::new(FrameAllocator::new()))
194     }
195 }
196 
197 #[cfg(feature = "use_spin")]
198 impl<const ORDER: usize> Deref for LockedFrameAllocator<ORDER> {
199     type Target = Mutex<FrameAllocator<ORDER>>;
200 
deref(&self) -> &Mutex<FrameAllocator<ORDER>>201     fn deref(&self) -> &Mutex<FrameAllocator<ORDER>> {
202         &self.0
203     }
204 }
205