diff --git a/src/allocator/frame/buddy.rs b/src/allocator/frame/buddy.rs
index 2b9a6685ea..6153b19d4b 100644
--- a/src/allocator/frame/buddy.rs
+++ b/src/allocator/frame/buddy.rs
@@ -12,29 +12,61 @@ use crate::{
VirtualAddress,
};
+#[repr(transparent)]
+struct BuddyUsage(u8);
+
#[derive(Clone, Copy, Debug)]
#[repr(packed)]
-struct BuddyEntry {
+struct BuddyEntry {
base: PhysicalAddress,
size: usize,
- map: PhysicalAddress,
+ // Number of first free page
+ skip: usize,
+ // Count of used pages
+ used: usize,
+ phantom: PhantomData,
}
-impl BuddyEntry {
- pub fn empty() -> Self {
+impl BuddyEntry {
+ fn empty() -> Self {
Self {
base: PhysicalAddress::new(0),
size: 0,
- map: PhysicalAddress::new(0),
+ skip: 0,
+ used: 0,
+ phantom: PhantomData,
}
}
-}
-#[derive(Clone, Copy, Debug)]
-#[repr(packed)]
-struct BuddyMapFooter {
- next: PhysicalAddress,
- skip: usize,
+ #[inline(always)]
+ fn pages(&self) -> usize {
+ self.size >> A::PAGE_SHIFT
+ }
+
+ fn usage_pages(&self) -> usize {
+ let bytes = self.pages() * mem::size_of::();
+ // Round bytes used for usage to next page
+ (bytes + A::PAGE_OFFSET_MASK) >> A::PAGE_SHIFT
+ }
+
+ unsafe fn usage_addr(&self, page: usize) -> Option {
+ if page < self.pages() {
+ let phys = self.base.add(page * mem::size_of::());
+ Some(A::phys_to_virt(phys))
+ } else {
+ None
+ }
+ }
+
+ unsafe fn usage(&self, page: usize) -> Option {
+ let addr = self.usage_addr(page)?;
+ Some(A::read(addr))
+ }
+
+ unsafe fn set_usage(&self, page: usize, usage: BuddyUsage) -> Option<()> {
+ let addr = self.usage_addr(page)?;
+ Some(A::write(addr, usage))
+ }
}
pub struct BuddyAllocator {
@@ -43,17 +75,15 @@ pub struct BuddyAllocator {
}
impl BuddyAllocator {
- const BUDDY_ENTRIES: usize = A::PAGE_SIZE / mem::size_of::();
- const MAP_PAGE_BYTES: usize = (A::PAGE_SIZE - mem::size_of::());
- const MAP_PAGE_BITS: usize = Self::MAP_PAGE_BYTES * 8;
+ const BUDDY_ENTRIES: usize = A::PAGE_SIZE / mem::size_of::>();
pub unsafe fn new(mut bump_allocator: BumpAllocator) -> Option {
// Allocate buddy table
let table_phys = bump_allocator.allocate_one()?;
let table_virt = A::phys_to_virt(table_phys);
- for i in 0 .. (A::PAGE_SIZE / mem::size_of::()) {
- let virt = table_virt.add(i * mem::size_of::());
- A::write(virt, BuddyEntry::empty());
+ for i in 0 .. (A::PAGE_SIZE / mem::size_of::>()) {
+ let virt = table_virt.add(i * mem::size_of::>());
+ A::write(virt, BuddyEntry::::empty());
}
let mut allocator = Self {
@@ -61,11 +91,22 @@ impl BuddyAllocator {
phantom: PhantomData,
};
- // Add areas to buddy table, combining areas when possible
- for area in bump_allocator.areas().iter() {
- for i in 0 .. (A::PAGE_SIZE / mem::size_of::()) {
- let virt = table_virt.add(i * mem::size_of::());
- let mut entry = A::read::(virt);
+ // Add areas to buddy table, combining areas when possible, and skipping frames used
+ // by the bump allocator
+ let mut offset = bump_allocator.offset();
+ for old_area in bump_allocator.areas().iter() {
+ let mut area = old_area.clone();
+ if offset >= area.size {
+ offset -= area.size;
+ continue;
+ } else if offset > 0 {
+ area.base = area.base.add(offset);
+ area.size -= offset;
+ offset = 0;
+ }
+ for i in 0 .. (A::PAGE_SIZE / mem::size_of::>()) {
+ let virt = table_virt.add(i * mem::size_of::>());
+ let mut entry = A::read::>(virt);
let inserted = if area.base.add(area.size) == entry.base {
// Combine entry at start
entry.base = area.base;
@@ -94,36 +135,32 @@ impl BuddyAllocator {
// Allocate buddy maps
for i in 0 .. Self::BUDDY_ENTRIES {
- let virt = table_virt.add(i * mem::size_of::());
- let mut entry = A::read::(virt);
- if entry.size > 0 {
- let pages = entry.size / A::PAGE_SIZE;
- let map_pages = (pages + (Self::MAP_PAGE_BITS - 1)) / Self::MAP_PAGE_BITS;
- for _ in 0 .. map_pages {
- let map_phys = bump_allocator.allocate_one()?;
- let map_virt = A::phys_to_virt(map_phys);
- A::write(map_virt.add(Self::MAP_PAGE_BYTES), BuddyMapFooter {
- next: entry.map,
- skip: Self::MAP_PAGE_BYTES,
- });
- entry.map = map_phys;
+ let virt = table_virt.add(i * mem::size_of::>());
+ let mut entry = A::read::>(virt);
+
+ // Only set up entries that have enough space for their own usage map
+ let usage_pages = entry.usage_pages();
+ if entry.pages() > usage_pages {
+ // Mark all usage bytes as unused
+ let usage_start = entry.usage_addr(0)?;
+ for page in 0..usage_pages {
+ A::write_bytes(usage_start.add(page << A::PAGE_SHIFT), 0, A::PAGE_SIZE);
}
- A::write(virt, entry);
+ // Mark bytes used for usage as used
+ for page in 0..usage_pages {
+ entry.set_usage(page, BuddyUsage(1))?;
+ }
}
- }
- // Mark unused areas as free
- let mut area_offset = bump_allocator.offset();
- for area in bump_allocator.areas().iter() {
- if area_offset < area.size {
- let area_base = area.base.add(area_offset);
- let area_size = area.size - area_offset;
- allocator.free(area_base, FrameCount::new(area_size / A::PAGE_SIZE));
- area_offset = 0;
- } else {
- area_offset -= area.size;
- }
+ // Skip the pages used for usage
+ entry.skip = usage_pages;
+
+ // Set used pages to pages used for usage
+ entry.used = usage_pages;
+
+ // Write updated entry
+ A::write(virt, entry);
}
Some(allocator)
@@ -137,109 +174,53 @@ impl FrameAllocator for BuddyAllocator {
}
for entry_i in 0 .. Self::BUDDY_ENTRIES {
- let virt = self.table_virt.add(entry_i * mem::size_of::());
- let entry = A::read::(virt);
+ let virt = self.table_virt.add(entry_i * mem::size_of::>());
+ let mut entry = A::read::>(virt);
- let mut start_map_phys = entry.map;
- let mut start_offset = 0;
- let mut start_i = 0;
- let mut start_bit = 0;
- let mut start_page_phys = PhysicalAddress::new(0);
- let mut found = 0;
+ let mut free_page = entry.skip;
+ let mut free_count = 0;
+ for page in entry.skip .. entry.pages() {
+ let mut usage = entry.usage(page)?;
+ if usage.0 == 0 {
+ free_count += 1;
- //TODO: improve performance
- let mut map_phys = start_map_phys;
- let mut offset = start_offset;
- 'find: while map_phys.data() != 0 {
- let map_virt = A::phys_to_virt(map_phys);
- let footer_virt = map_virt.add(Self::MAP_PAGE_BYTES);
- let mut footer = A::read::(footer_virt);
- offset += footer.skip * A::PAGE_SIZE * 8;
-
- for i in footer.skip .. Self::MAP_PAGE_BYTES {
- let map_byte_virt = map_virt.add(i);
- let value: u8 = A::read(map_byte_virt);
- if (value & u8::MAX) != 0 {
- for bit in 0..8 {
- if (value & (1 << bit)) != 0 {
- if found == 0 {
- start_map_phys = map_phys;
- start_offset = offset;
- start_i = i;
- start_bit = bit;
- start_page_phys = entry.base.add(offset + bit * A::PAGE_SIZE);
- }
- found += 1;
- if found == count.data() {
- // If skip is set to the start of the newly allocated frames
- if footer.skip == start_i && footer.skip != i {
- // Set it to the end of the newly allocated frames
- footer.skip = i;
- A::write(footer_virt, footer);
- }
- break 'find;
- }
- } else {
- found = 0;
- }
- }
-
- } else {
- // If skip is set to the current 8 frames, and they are already used
- if footer.skip == i {
- // Set skip to the next current frame
- footer.skip = i + 1;
- A::write(footer_virt, footer);
- }
- found = 0;
+ if free_count == count.data() {
+ break;
}
- offset += A::PAGE_SIZE * 8;
+ } else {
+ free_page = page + 1;
+ free_count = 0;
}
-
- map_phys = footer.next;
}
- if found == count.data() {
- map_phys = start_map_phys;
- offset = start_offset;
- found = 0;
- while map_phys.data() != 0 {
- let map_virt = A::phys_to_virt(map_phys);
- for i in start_i .. Self::MAP_PAGE_BYTES {
- let map_byte_virt = map_virt.add(i);
- let mut value: u8 = A::read(map_byte_virt);
- if (value & u8::MAX) != 0 {
- for bit in start_bit..8 {
- if (value & (1 << bit)) != 0 {
- value &= !(1 << bit);
- A::write(map_byte_virt, value);
+ if free_count == count.data() {
+ for page in free_page .. free_page + free_count {
+ // Update usage
+ let mut usage = entry.usage(page)?;
+ usage.0 += 1;
+ entry.set_usage(page, usage);
- let page_phys = entry.base.add(offset + bit * A::PAGE_SIZE);
- let page_virt = A::phys_to_virt(page_phys);
- A::write_bytes(page_virt, 0, A::PAGE_SIZE);
-
- found += 1;
- if found == count.data() {
- return Some(start_page_phys);
- }
- } else {
- panic!("check your logic, bit was set");
- }
- }
- start_bit = 0;
- } else {
- panic!("check your logic, byte was set");
- }
- offset += A::PAGE_SIZE * 8;
- }
- start_i = 0;
-
- let footer_virt = map_virt.add(Self::MAP_PAGE_BYTES);
- let footer = A::read::(footer_virt);
- map_phys = footer.next;
+ // Zero page
+ let page_phys = entry.base.add(page << A::PAGE_SHIFT);
+ let page_virt = A::phys_to_virt(page_phys);
+ A::write_bytes(page_virt, 0, A::PAGE_SIZE);
}
+
+ // Update skip if necessary
+ if entry.skip == free_page {
+ entry.skip = free_page + free_count;
+ }
+
+ // Update used page count
+ entry.used += free_count;
+
+ // Write updated entry
+ A::write(virt, entry);
+
+ return Some(entry.base.add(free_page << A::PAGE_SHIFT));
}
}
+
None
}
@@ -250,40 +231,40 @@ impl FrameAllocator for BuddyAllocator {
let size = count.data() * A::PAGE_SIZE;
for i in 0 .. Self::BUDDY_ENTRIES {
- let virt = self.table_virt.add(i * mem::size_of::());
- let entry = A::read::(virt);
- if base >= entry.base && base.add(size) <= entry.base.add(entry.size) {
- //TODO: Correct logic
- for page in 0 .. count.data() {
- let page_base = base.add(page * A::PAGE_SIZE);
- let index = (page_base.data() - entry.base.data()) / A::PAGE_SIZE;
- let mut map_page = index / Self::MAP_PAGE_BITS;
- let map_bit = index % Self::MAP_PAGE_BITS;
+ let virt = self.table_virt.add(i * mem::size_of::>());
+ let mut entry = A::read::>(virt);
- //TODO: improve performance
- let mut map_phys = entry.map;
- loop {
- if map_phys.data() == 0 { unimplemented!("map_phys.data() == 0") }
- let map_virt = A::phys_to_virt(map_phys);
- let footer_virt = map_virt.add(Self::MAP_PAGE_BYTES);
- let mut footer = A::read::(footer_virt);
- if map_page == 0 {
- let map_byte = map_bit / 8;
- if map_byte < footer.skip {
- footer.skip = map_byte;
- A::write(footer_virt, footer);
- }
- let map_byte_virt = map_virt.add(map_byte);
- let mut value: u8 = A::read(map_byte_virt);
- value |= 1 << (map_bit % 8);
- A::write(map_byte_virt, value);
- break;
- } else {
- map_phys = footer.next;
- map_page -= 1;
- }
+ if base >= entry.base && base.add(size) <= entry.base.add(entry.size) {
+ let start_page = (base.data() - entry.base.data()) >> A::PAGE_SHIFT;
+ let end_page = start_page + count.data();
+
+ for page in start_page..start_page + count.data() {
+ let mut usage = entry.usage(page).expect("failed to get usage during free");
+
+ if usage.0 > 0 {
+ usage.0 -= 1;
+ } else {
+ panic!("tried to free already free frame");
}
+
+ // If page was freed
+ if usage.0 == 0 {
+ // Update skip if necessary
+ if page < entry.skip {
+ entry.skip = page;
+ }
+
+ // Update used page count
+ entry.used -= 1;
+ }
+
+ entry.set_usage(page, usage).expect("failed to set usage during free");
}
+
+ // Write updated entry
+ A::write(virt, entry);
+
+ return;
}
}
}