rustubs/mm/allocator/
buddy.rs1use core::{
22 fmt,
23 ops::Range,
24 ptr::{self, null_mut},
25};
26
27use crate::defs;
28use defs::Mem::*;
29
30#[derive(Copy, Clone)]
31struct Node {
32 next: *const Node,
33 prev: *const Node,
34}
35
36type FreeList = Node;
37
38pub struct FrameDesc {
43 pfn: u64, order: usize, }
46
47impl FrameDesc {
48 pub fn get_size(&self) -> usize {
49 return (PAGE_SIZE << self.order) as usize;
50 }
51 pub fn get_paddr(&self) -> u64 { return self.pfn << PAGE_SHIFT; }
52 pub fn from_paddr(paddr: u64, order: usize) -> FrameDesc {
53 return Self { pfn: paddr >> PAGE_SHIFT, order };
54 }
55}
56
57#[derive(Debug)]
58pub struct Zone {
64 pa_range: Range<u64>,
65 free_areas: [FreeArea; PFA_NR_PAGE_ORDER],
66}
67
68impl Zone {
71 pub const fn default() -> Self {
72 Self {
73 pa_range: Range::<u64> { start: 0, end: 0 },
74 free_areas: [FreeArea::default(); PFA_NR_PAGE_ORDER],
75 }
76 }
77
78 pub fn get_buddy(&self, frame: &FrameDesc) -> Option<FrameDesc> {
80 let pa: u64 = frame.get_paddr();
83 let offset_pfn = (pa - self.pa_range.start) >> PAGE_SHIFT;
84 let buddy_pfn = offset_pfn ^ (1 << frame.order);
85 let buddy = FrameDesc { pfn: buddy_pfn, order: frame.order };
86
87 if !self.contains(&buddy) {
91 return None;
92 }
93 Some(FrameDesc { pfn: buddy_pfn, order: frame.order })
94 }
95
96 pub fn get_buddy_by_pa(&self, pa: u64, order: usize) -> Option<u64> {
99 let offset_pfn = (pa - self.pa_range.start) >> PAGE_SHIFT;
100 let buddy_offset_pfn = offset_pfn ^ (1 << order);
101 let buddy_offset_pa = buddy_offset_pfn << PAGE_SHIFT;
102 let buddy_pa = buddy_offset_pa + self.pa_range.start;
103 if !self.pa_range.contains(&buddy_pa) {
104 return None;
105 }
106 return Some(buddy_pa);
107 }
108
109 #[inline]
110 pub fn contains(&self, frame: &FrameDesc) -> bool {
111 let pa: u64 = frame.get_paddr();
112 let pa_end = pa + frame.get_size() as u64;
113 return self.pa_range.contains(&pa) && self.pa_range.contains(&pa_end);
114 }
115}
116
117#[derive(Copy, Clone)]
118struct FreeArea {
119 list_head: FreeList,
120 nr_free: usize,
121 order: usize,
122}
123
124impl fmt::Debug for FreeArea {
125 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126 write!(f, "free: {}", self.nr_free)
127 }
128}
129
130impl FreeArea {
131 pub const fn default() -> FreeArea {
132 FreeArea {
133 list_head: FreeList {
134 next: ptr::null_mut::<Node>(),
135 prev: ptr::null_mut::<Node>(),
136 },
137 nr_free: 0,
138 order: 0,
139 }
140 }
141
142 pub fn init_empty(&mut self) {
144 self.list_head.next = &self.list_head as *const Node;
145 self.list_head.prev = self.list_head.next;
146 }
147
148 pub unsafe fn push(&mut self, n: *mut Node) {
152 (*n).next = self.list_head.next;
153 (*n).prev = &(self.list_head);
154 self.list_head.next = n;
155 (*(self.list_head.next as *mut Node)).prev = n;
156 self.nr_free += 1;
157 }
158
159 pub fn pop(&mut self) -> Option<*const Node> {
160 if self.nr_free == 0 {
161 return None;
162 }
163
164 let n = self.list_head.next;
165 unsafe {
166 self.list_head.next = (*n).next;
167 (*((*n).next as *mut Node)).prev = &self.list_head;
168 }
169 self.nr_free -= 1;
170 return Some(n);
171 }
172}
173
174#[derive(Debug)]
176pub struct BuddyAllocator {
177 zone: Zone,
178}
179
180impl BuddyAllocator {
183 pub const fn default() -> Self { BuddyAllocator { zone: Zone::default() } }
184
185 pub unsafe fn free_frame_by_pa(&mut self, paddr: u64, order: usize) {
194 if let Some(va) = defs::P2V(paddr) {
195 let n = va as *mut Node;
196 self.zone.free_areas[order].push(n);
197 } else {
198 return;
199 }
200 }
201
202 pub unsafe fn free_frame_by_desc(&mut self, frame: &FrameDesc) {
203 self.free_frame_by_pa(frame.get_paddr(), frame.order);
204 }
205
206 pub fn print_debug_info(&self) {
207 for i in &self.zone.free_areas {
208 print!("{}:{} ;", i.order, i.nr_free);
209 }
210 print!("\n");
211 }
212
213 pub fn alloc_frame(&mut self, order: usize) -> Option<FrameDesc> {
214 let n = self.recursive_split_down(order);
215 n.map(|n| FrameDesc {
216 pfn: defs::V2P(n as u64).unwrap() >> PAGE_SHIFT,
217 order,
218 })
219 }
220
221 fn split_buddy_block(&mut self, n: *mut Node, order: usize) {
223 let pa = defs::V2P(n as u64).unwrap();
224 let buddy_pa = self.zone.get_buddy_by_pa(pa, order - 1);
225
226 if buddy_pa.is_none() {
227 return;
228 }
229
230 let buddy_pa = buddy_pa.unwrap();
231 let buddy_node = defs::P2V(buddy_pa).unwrap() as *mut Node;
232
233 let fa_down = &mut self.zone.free_areas[order - 1];
234 unsafe {
235 fa_down.push(buddy_node);
236 }
237 }
238
239 fn recursive_split_down(&mut self, order: usize) -> Option<*const Node> {
241 if order > PFA_MAX_PAGE_ORDER {
242 return None;
243 }
244 let n = self.zone.free_areas[order].pop();
245
246 if let Some(n) = n {
247 Some(n)
248 } else {
249 let n_higher = self.recursive_split_down(order + 1)?;
250 self.split_buddy_block(n_higher as *mut Node, order + 1);
251 return Some(n_higher);
252 }
253 }
254
255 pub unsafe fn init(&mut self, start: u64, end: u64) {
260 let start = defs::roundup_4k(start);
261 let end = defs::rounddown_4k(end);
262 let len = end - start;
263 assert!(
265 len >= MIN_PHY_MEM,
266 "TO LITTLE RAM ...{:X}-{:X} = {}",
267 start,
268 end,
269 end - start
270 );
271
272 const MAX_ORDER_BLKSIZE: u64 = PAGE_SIZE << PFA_MAX_PAGE_ORDER;
275 const {
276 assert!(MAX_ORDER_BLKSIZE == 4 * M, "sancheck failed");
277 }
278
279 self.zone.pa_range = start..end;
280
281 for i in PFA_ORDER_RANGE {
283 let fa = &mut self.zone.free_areas[i];
284 fa.order = i;
285 fa.init_empty();
286 }
287
288 let list_head = &(self.zone.free_areas[PFA_MAX_PAGE_ORDER].list_head)
290 as *const Node;
291
292 let mut prev_node_ptr = list_head;
293 let mut curr = start; let mut n: *mut Node = null_mut::<Node>();
295 let mut free_blocks: usize = 0;
296
297 while curr + MAX_ORDER_BLKSIZE <= end {
298 n = defs::P2V(curr).unwrap() as *mut Node;
299
300 unsafe {
301 (*n).prev = prev_node_ptr;
302 (*(prev_node_ptr as *mut Node)).next = n as *const Node;
303 }
304
305 prev_node_ptr = n;
306 free_blocks += 1;
307 curr += MAX_ORDER_BLKSIZE;
308 }
309
310 if !n.is_null() {
312 unsafe {
313 (*n).next = list_head;
314 (*(list_head as *mut Node)).prev = n;
315 }
316 }
317
318 self.zone.free_areas[PFA_MAX_PAGE_ORDER].nr_free = free_blocks;
319 }
320}