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; } } }