1use crate::arch::xshim::PhysAddr;
4use core::{ptr, slice};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[repr(u8)]
9pub enum ZoneType {
10 DMA = 0,
12 Normal = 1,
14 HighMem = 2,
16}
17
18impl ZoneType {
19 pub const COUNT: usize = 3;
21}
22
23pub const MAX_ORDER: usize = 11;
25
26pub const PAGEBLOCK_ORDER: usize = 9;
32
33pub const PAGEBLOCK_PAGES: usize = 1 << PAGEBLOCK_ORDER;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[repr(u8)]
44pub enum Migratetype {
45 Unmovable = 0,
47 Movable = 1,
49}
50
51impl Migratetype {
52 pub const COUNT: usize = 2;
54
55 pub const ALL: [Self; Self::COUNT] = [Self::Unmovable, Self::Movable];
57
58 #[inline]
60 pub const fn index(self) -> usize {
61 self as usize
62 }
63
64 #[inline]
72 pub const fn fallback_order(self) -> [Self; Self::COUNT] {
73 match self {
74 Self::Unmovable => [Self::Unmovable, Self::Movable],
75 Self::Movable => [Self::Movable, Self::Unmovable],
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy)]
85pub struct BuddyBitmap {
86 pub data: *mut u8,
87 pub num_bits: usize,
88}
89
90impl BuddyBitmap {
91 pub const fn empty() -> Self {
93 Self {
94 data: core::ptr::null_mut(),
95 num_bits: 0,
96 }
97 }
98
99 #[inline]
101 pub fn is_empty(&self) -> bool {
102 self.data.is_null() || self.num_bits == 0
103 }
104
105 #[inline]
107 pub fn toggle(&self, idx: usize) -> bool {
108 debug_assert!(idx < self.num_bits);
109 let byte_idx = idx >> 3;
110 let mask = 1u8 << (idx & 7);
111 unsafe {
112 let byte = self.data.add(byte_idx);
113 let new_val = *byte ^ mask;
114 *byte = new_val;
115 (new_val & mask) != 0
116 }
117 }
118
119 #[inline]
121 pub fn test(&self, idx: usize) -> bool {
122 if idx >= self.num_bits || self.is_empty() {
123 return false;
124 }
125 let byte_idx = idx >> 3;
126 let mask = 1u8 << (idx & 7);
127 unsafe { (*self.data.add(byte_idx) & mask) != 0 }
128 }
129
130 #[inline]
132 pub fn set(&self, idx: usize) {
133 debug_assert!(idx < self.num_bits);
134 let byte_idx = idx >> 3;
135 let mask = 1u8 << (idx & 7);
136 unsafe {
137 *self.data.add(byte_idx) |= mask;
138 }
139 }
140
141 #[inline]
143 pub fn clear(&self, idx: usize) {
144 debug_assert!(idx < self.num_bits);
145 let byte_idx = idx >> 3;
146 let mask = 1u8 << (idx & 7);
147 unsafe {
148 *self.data.add(byte_idx) &= !mask;
149 }
150 }
151}
152
153#[derive(Clone, Copy)]
155pub struct ZoneSegment {
156 pub base: PhysAddr,
158
159 pub page_count: usize,
161
162 pub free_lists: [[u64; MAX_ORDER + 1]; Migratetype::COUNT],
164
165 pub buddy_bitmaps: [BuddyBitmap; MAX_ORDER + 1],
167
168 pub pageblock_tags: *mut u8,
170
171 pub pageblock_count: usize,
173
174 #[cfg(debug_assertions)]
176 pub alloc_bitmap: BuddyBitmap,
177}
178
179impl ZoneSegment {
180 pub const fn empty() -> Self {
182 Self {
183 base: PhysAddr::new(0),
184 page_count: 0,
185 free_lists: [[0; MAX_ORDER + 1]; Migratetype::COUNT],
186 buddy_bitmaps: [BuddyBitmap::empty(); MAX_ORDER + 1],
187 pageblock_tags: core::ptr::null_mut(),
188 pageblock_count: 0,
189 #[cfg(debug_assertions)]
190 alloc_bitmap: BuddyBitmap::empty(),
191 }
192 }
193
194 #[inline]
196 pub fn is_populated(&self) -> bool {
197 self.page_count != 0
198 }
199
200 #[inline]
202 pub fn contains_address(&self, addr: PhysAddr) -> bool {
203 if !self.is_populated() {
204 return false;
205 }
206 let start = self.base.as_u64();
207 let end = start + (self.page_count as u64 * 4096);
208 let value = addr.as_u64();
209 value >= start && value < end
210 }
211
212 #[inline]
214 pub fn end_address(&self) -> u64 {
215 self.base.as_u64() + (self.page_count as u64 * 4096)
216 }
217
218 #[inline]
220 pub fn base_cmp(&self, addr: u64) -> core::cmp::Ordering {
221 self.base.as_u64().cmp(&addr)
222 }
223
224 pub fn free_list_count(&self, order: u8) -> usize {
226 Migratetype::ALL
227 .into_iter()
228 .map(|migratetype| self.free_list_count_for(order, migratetype))
229 .sum()
230 }
231
232 pub fn free_list_count_for(&self, order: u8, migratetype: Migratetype) -> usize {
234 let mut count = 0usize;
235 let mut phys = self.free_lists[migratetype.index()][order as usize];
236 while phys != 0 {
237 count += 1;
238 let meta = crate::memory::frame::get_meta(PhysAddr::new(phys));
239 phys = if meta.next() == crate::memory::frame::FRAME_META_LINK_NONE {
240 0
241 } else {
242 meta.next()
243 };
244 }
245 count
246 }
247}
248
249pub struct Zone {
251 pub zone_type: ZoneType,
253
254 pub base: PhysAddr,
256
257 pub page_count: usize,
259
260 pub present_pages: usize,
262
263 pub span_pages: usize,
267
268 pub allocated: usize,
270
271 pub reserved_pages: usize,
273
274 pub lowmem_reserve_pages: usize,
276
277 pub watermark_min: usize,
279
280 pub watermark_low: usize,
282
283 pub watermark_high: usize,
285
286 pub segment_count: usize,
288
289 pub segment_capacity: usize,
291
292 pub segments: *mut ZoneSegment,
294}
295
296impl Zone {
297 pub const fn new(zone_type: ZoneType) -> Self {
299 Zone {
300 zone_type,
301 base: PhysAddr::new(0),
302 page_count: 0,
303 present_pages: 0,
304 span_pages: 0,
305 allocated: 0,
306 reserved_pages: 0,
307 lowmem_reserve_pages: 0,
308 watermark_min: 0,
309 watermark_low: 0,
310 watermark_high: 0,
311 segment_count: 0,
312 segment_capacity: 0,
313 segments: ptr::null_mut(),
314 }
315 }
316
317 #[inline]
319 pub fn segments(&self) -> &[ZoneSegment] {
320 if self.segment_capacity == 0 || self.segments.is_null() {
321 &[]
322 } else {
323 unsafe { slice::from_raw_parts(self.segments, self.segment_capacity) }
324 }
325 }
326
327 #[inline]
329 pub fn segments_mut(&mut self) -> &mut [ZoneSegment] {
330 if self.segment_capacity == 0 || self.segments.is_null() {
331 &mut []
332 } else {
333 unsafe { slice::from_raw_parts_mut(self.segments, self.segment_capacity) }
334 }
335 }
336
337 #[inline]
339 pub fn clear_segments(&mut self) {
340 self.segment_count = 0;
341 self.segment_capacity = 0;
342 self.segments = ptr::null_mut();
343 }
344
345 pub fn contains_address(&self, addr: PhysAddr) -> bool {
347 self.segments()
348 .iter()
349 .take(self.segment_count)
350 .any(|segment| segment.contains_address(addr))
351 }
352
353 pub fn available_pages(&self) -> usize {
355 self.page_count.saturating_sub(self.allocated)
356 }
357
358 pub fn free_list_count(&self, order: u8) -> usize {
363 self.segments()
364 .iter()
365 .take(self.segment_count)
366 .map(|segment| segment.free_list_count(order))
367 .sum()
368 }
369
370 pub fn free_list_count_for(&self, order: u8, migratetype: Migratetype) -> usize {
372 self.segments()
373 .iter()
374 .take(self.segment_count)
375 .map(|segment| segment.free_list_count_for(order, migratetype))
376 .sum()
377 }
378
379 pub fn free_pages_by_migratetype(&self) -> [usize; Migratetype::COUNT] {
381 let mut totals = [0usize; Migratetype::COUNT];
382 for migratetype in Migratetype::ALL {
383 let idx = migratetype.index();
384 for order in 0..=MAX_ORDER {
385 let blocks = self.free_list_count_for(order as u8, migratetype);
386 totals[idx] = totals[idx].saturating_add(blocks << order);
387 }
388 }
389 totals
390 }
391
392 pub fn free_pages_at_or_above_order(&self, order: u8) -> usize {
398 let mut pages = 0usize;
399 for current_order in order as usize..=MAX_ORDER {
400 pages =
401 pages.saturating_add(self.free_list_count(current_order as u8) << current_order);
402 }
403 pages
404 }
405
406 pub fn fragmentation_score(&self, order: u8, cached_order0_pages: usize) -> usize {
413 if order == 0 {
414 return 0;
415 }
416
417 let total_free = self.available_pages().saturating_add(cached_order0_pages);
418 if total_free == 0 {
419 return 0;
420 }
421
422 let usable = self.free_pages_at_or_above_order(order);
423 let fragmented = total_free.saturating_sub(usable);
424 fragmented.saturating_mul(100) / total_free
425 }
426
427 pub fn usable_pages_and_fragmentation(
432 &self,
433 order: u8,
434 cached_order0_pages: usize,
435 ) -> (usize, usize) {
436 if order == 0 {
437 return (
438 self.available_pages().saturating_add(cached_order0_pages),
439 0,
440 );
441 }
442
443 let total_free = self.available_pages().saturating_add(cached_order0_pages);
444 if total_free == 0 {
445 return (0, 0);
446 }
447
448 let usable = self.free_pages_at_or_above_order(order);
449 let fragmented = total_free.saturating_sub(usable);
450 let score = fragmented.saturating_mul(100) / total_free;
451 (usable, score)
452 }
453
454 pub fn largest_free_order(&self) -> Option<u8> {
456 for order in (0..=MAX_ORDER).rev() {
457 if self.free_list_count(order as u8) > 0 {
458 return Some(order as u8);
459 }
460 }
461 None
462 }
463}
464
465unsafe impl Send for BuddyBitmap {}
467unsafe impl Send for Zone {}