os/mm/frame_allocator/
frame_allocator.rs1use crate::config::PAGE_SIZE;
8use crate::mm::address::{ConvertablePaddr, PageNum, Ppn, PpnRange, UsizeConvert};
9use crate::sync::SpinLock;
10use alloc::vec::Vec;
11use lazy_static::lazy_static;
12
13#[derive(Debug)]
16pub struct FrameTracker(Ppn);
17
18impl FrameTracker {
19 pub fn new(ppn: Ppn) -> Self {
22 clear_frame(ppn);
23 FrameTracker(ppn)
24 }
25
26 pub fn ppn(&self) -> Ppn {
28 self.0
29 }
30}
31
32fn clear_frame(ppn: Ppn) {
34 unsafe {
35 let va = ppn.start_addr().to_vaddr().as_mut_ptr::<u8>();
37 core::ptr::write_bytes(va, 0, PAGE_SIZE);
39 }
40}
41
42impl Drop for FrameTracker {
43 fn drop(&mut self) {
45 super::dealloc_frame(self);
46 }
47}
48
49#[derive(Debug)]
52pub struct FrameRangeTracker {
53 range: PpnRange,
54}
55
56impl FrameRangeTracker {
57 pub fn new(range: PpnRange) -> Self {
60 for ppn in range {
61 clear_frame(ppn);
62 }
63 FrameRangeTracker { range }
64 }
65
66 pub fn start_ppn(&self) -> Ppn {
68 self.range.start()
69 }
70
71 pub fn end_ppn(&self) -> Ppn {
73 self.range.end()
74 }
75
76 pub fn len(&self) -> usize {
78 self.range.len()
79 }
80
81 pub fn range(&self) -> &PpnRange {
83 &self.range
84 }
85}
86
87impl Drop for FrameRangeTracker {
88 fn drop(&mut self) {
90 super::dealloc_contig_frames(self);
91 }
92}
93
94#[derive(Debug)]
97pub enum TrackedFrames {
98 Single(FrameTracker),
100 Multiple(Vec<FrameTracker>),
102 Contiguous(FrameRangeTracker),
104}
105
106lazy_static! {
107 pub static ref FRAME_ALLOCATOR: SpinLock<FrameAllocator> = SpinLock::new(FrameAllocator::new());
109}
110
111pub struct FrameAllocator {
114 start: Ppn,
116 end: Ppn,
118 cur: Ppn,
120 recycled: Vec<Ppn>,
122}
123
124impl FrameAllocator {
126 pub fn new() -> Self {
128 FrameAllocator {
129 start: Ppn::from_usize(usize::MAX),
131 end: Ppn::from_usize(usize::MAX),
132 cur: Ppn::from_usize(usize::MAX),
133 recycled: Vec::new(),
134 }
135 }
136
137 pub fn init(&mut self, start: Ppn, end: Ppn) {
139 self.start = start;
140 self.end = end;
141 self.cur = start;
142 }
143
144 pub fn alloc_frame(&mut self) -> Option<FrameTracker> {
147 if let Some(ppn) = self.recycled.pop() {
148 Some(FrameTracker::new(ppn))
150 } else if self.cur < self.end {
151 let ppn = self.cur;
153 self.cur.step(); Some(FrameTracker::new(ppn))
155 } else {
156 None
158 }
159 }
160
161 pub fn alloc_frames(&mut self, num: usize) -> Option<Vec<FrameTracker>> {
163 let mut frames = Vec::with_capacity(num);
164 for _ in 0..num {
165 if let Some(frame) = self.alloc_frame() {
166 frames.push(frame);
167 } else {
168 return None;
171 }
172 }
173 Some(frames)
174 }
175
176 pub fn alloc_contig_frames(&mut self, num: usize) -> Option<FrameRangeTracker> {
178 if num == 0 {
179 return None;
180 }
181
182 let required_end = self.cur + num;
184 if required_end <= self.end {
185 let start = self.cur;
186 self.cur = required_end;
188 let range = PpnRange::from_start_len(start, num);
189 Some(FrameRangeTracker::new(range))
190 } else {
191 None
193 }
194 }
195
196 pub fn alloc_contig_frames_aligned(
198 &mut self,
199 num: usize,
200 align_pages: usize,
201 ) -> Option<FrameRangeTracker> {
202 if num == 0 {
203 return None;
204 }
205
206 debug_assert!(
207 align_pages.is_power_of_two(),
208 "Alignment must be power of 2" );
210
211 let aligned_cur_val =
213 (self.cur.as_usize() + align_pages - 1).div_ceil(align_pages) * align_pages;
214 let aligned_cur = Ppn::from_usize(aligned_cur_val);
215
216 let required_end = aligned_cur + num;
218 if required_end <= self.end {
219 for ppn_val in self.cur.as_usize()..aligned_cur.as_usize() {
221 self.recycled.push(Ppn::from_usize(ppn_val));
222 }
223
224 self.cur = required_end;
226 let range = PpnRange::from_start_len(aligned_cur, num);
227 Some(FrameRangeTracker::new(range))
228 } else {
229 None
231 }
232 }
233
234 pub fn dealloc_frame(&mut self, frame: &FrameTracker) {
237 debug_assert!(
239 frame.ppn() >= self.start && frame.ppn() < self.end,
240 "dealloc_frame: frame out of range" );
242 debug_assert!(
244 frame.ppn() < self.cur && self.recycled.iter().all(|&ppn| ppn != frame.ppn()),
245 );
246
247 let ppn = frame.ppn();
248 self.recycled.push(ppn);
249 self.recycled.sort_unstable();
251
252 if let Some(&last) = self.recycled.last() {
253 if last + 1 == self.cur {
255 let mut new_cur = last;
257 self.recycled.pop();
258 while let Some(&top) = self.recycled.last() {
259 if top + 1 == new_cur {
260 new_cur = top;
261 self.recycled.pop();
262 } else {
263 break;
264 }
265 }
266 self.cur = new_cur;
267 }
268 }
269 }
270
271 pub fn dealloc_contig_frames(&mut self, frame_range: &FrameRangeTracker) {
274 let start = frame_range.start_ppn();
275 let end = frame_range.end_ppn();
276 debug_assert!(
278 start >= self.start && end <= self.end,
279 "dealloc_contig_frames: frame range out of range" );
281 debug_assert!(
283 end <= self.cur,
284 "dealloc_contig_frames: frame range not allocated" );
286
287 for ppn in frame_range.range().into_iter() {
289 self.recycled.push(ppn);
290 }
291 self.recycled.sort_unstable();
293
294 if let Some(&last) = self.recycled.last() {
295 if last + 1 == self.cur {
297 let mut new_cur = last;
299 self.recycled.pop();
300 while let Some(&top) = self.recycled.last() {
301 if top + 1 == new_cur {
302 new_cur = top;
303 self.recycled.pop();
304 } else {
305 break;
306 }
307 }
308 self.cur = new_cur;
309 }
310 }
311 }
312
313 pub fn total_frames(&self) -> usize {
315 self.end.as_usize() - self.start.as_usize()
316 }
317
318 pub fn allocated_frames(&self) -> usize {
320 let allocated = self.cur.as_usize() - self.start.as_usize();
321 let recycled = self.recycled.len();
322 allocated - recycled
323 }
324
325 pub fn free_frames(&self) -> usize {
327 let total = self.total_frames();
328 let allocated = self.allocated_frames();
329 total - allocated
330 }
331
332 pub fn get_stats(&self) -> (usize, usize, usize, usize, usize) {
340 (
341 self.cur.as_usize(),
342 self.end.as_usize(),
343 self.recycled.len(),
344 self.allocated_frames(),
345 self.free_frames(),
346 )
347 }
348}