From 3fb1b3ad7483326eaee1e1d7e65be6cbcdac02fe Mon Sep 17 00:00:00 2001 From: Seiya Nuta Date: Wed, 15 Dec 2021 20:14:48 +0900 Subject: [PATCH] Support freeing pages (#120) --- Cargo.lock | 41 ++++++++++++ libs/kerla_utils/Cargo.toml | 2 + libs/kerla_utils/bitmap_allocator.rs | 71 ++++++++++++++++++++ libs/kerla_utils/bump_allocator.rs | 16 ++++- libs/kerla_utils/lib.rs | 4 ++ runtime/page_allocator.rs | 96 +++++++++++++++++++++++++--- 6 files changed, 220 insertions(+), 10 deletions(-) create mode 100644 libs/kerla_utils/bitmap_allocator.rs diff --git a/Cargo.lock b/Cargo.lock index a229b0ea..94e226e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,6 +49,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5237f00a8c86130a0cc317830e558b966dd7850d48a953d998c813f01a41b527" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "buddy_system_allocator" version = "0.8.0" @@ -113,6 +125,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "funty" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e" + [[package]] name = "getrandom" version = "0.2.3" @@ -196,7 +214,9 @@ dependencies = [ name = "kerla_utils" version = "0.0.1" dependencies = [ + "bitvec", "crossbeam", + "log", "spin 0.9.2", ] @@ -276,6 +296,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + [[package]] name = "raw-cpuid" version = "10.2.0" @@ -323,6 +349,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -389,6 +421,15 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wyz" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129e027ad65ce1453680623c3fb5163cbf7107bfe1aa32257e7d0e63f9ced188" +dependencies = [ + "tap", +] + [[package]] name = "x86" version = "0.43.0" diff --git a/libs/kerla_utils/Cargo.toml b/libs/kerla_utils/Cargo.toml index 3d14071c..bb16b6f1 100644 --- a/libs/kerla_utils/Cargo.toml +++ b/libs/kerla_utils/Cargo.toml @@ -14,5 +14,7 @@ path = "lib.rs" no_std = [] [dependencies] +log = "0.4" spin = "0.9.2" crossbeam = { version = "0.8.1", default-features = false } +bitvec = { version = "0.22", default-features = false } diff --git a/libs/kerla_utils/bitmap_allocator.rs b/libs/kerla_utils/bitmap_allocator.rs new file mode 100644 index 00000000..81423430 --- /dev/null +++ b/libs/kerla_utils/bitmap_allocator.rs @@ -0,0 +1,71 @@ +use bitvec::prelude::*; + +use crate::alignment::align_up; + +const PAGE_SIZE: usize = 4096; + +pub struct BitMapAllocator { + bitmap: spin::Mutex<&'static mut BitSlice>, + base: usize, + end: usize, +} + +impl BitMapAllocator { + /// # Safety + /// + /// The caller must ensure that the memory passed to this function is + /// aligned to a page boundary. + pub unsafe fn new(base: *mut u8, base_paddr: usize, len: usize) -> BitMapAllocator { + let num_pages = align_up(len, PAGE_SIZE) / PAGE_SIZE; + let bitmap_reserved_len = align_up(num_pages / 8, PAGE_SIZE); + let bitmap_actual_len = (num_pages / 8) - (bitmap_reserved_len / PAGE_SIZE); + let bitmap = + BitSlice::from_slice_mut(core::slice::from_raw_parts_mut(base, bitmap_actual_len)) + .expect("you have too much memory"); + + debug_assert!(bitmap_reserved_len >= bitmap_actual_len); + bitmap.set_all(false); + + BitMapAllocator { + bitmap: spin::Mutex::new(bitmap), + base: base_paddr + bitmap_reserved_len, + end: base_paddr + len - bitmap_reserved_len, + } + } + + pub fn includes(&mut self, ptr: usize) -> bool { + self.base <= ptr && ptr < self.end + } + + pub fn alloc_pages(&mut self, order: usize) -> Option { + let num_pages = 1 << order; + let mut bitmap = self.bitmap.lock(); + let mut off = 0; + while let Some(first_zero) = bitmap[off..].first_zero() { + let start = off + first_zero; + let end = off + first_zero + num_pages; + if end > bitmap.len() { + break; + } + + if bitmap[start..end].not_any() { + bitmap[start..end].set_all(true); + return Some(self.base + start * PAGE_SIZE); + } + + off += first_zero + 1; + } + + None + } + + pub fn free_pages(&mut self, ptr: usize, order: usize) { + let num_pages = 1 << order; + let off = (ptr - self.base) / PAGE_SIZE; + + let mut bitmap = self.bitmap.lock(); + + debug_assert!(bitmap[off..(off + num_pages)].all(), "double free"); + bitmap[off..(off + num_pages)].set_all(false); + } +} diff --git a/libs/kerla_utils/bump_allocator.rs b/libs/kerla_utils/bump_allocator.rs index 70cfcf3f..d490f302 100644 --- a/libs/kerla_utils/bump_allocator.rs +++ b/libs/kerla_utils/bump_allocator.rs @@ -1,18 +1,28 @@ const PAGE_SIZE: usize = 4096; pub struct BumpAllocator { + base: usize, current: usize, end: usize, } impl BumpAllocator { - pub fn new(_base: *mut u8, base_paddr: usize, len: usize) -> BumpAllocator { + /// # Safety + /// + /// The caller must ensure that the memory passed to this function is + /// aligned to a page boundary. + pub unsafe fn new(_base: *mut u8, base_paddr: usize, len: usize) -> BumpAllocator { BumpAllocator { + base: base_paddr, current: base_paddr, end: base_paddr + len, } } + pub fn includes(&mut self, ptr: usize) -> bool { + self.base <= ptr && ptr < self.end + } + pub fn alloc_pages(&mut self, order: usize) -> Option { let len = PAGE_SIZE * (1 << order); if self.current + len >= self.end { @@ -23,4 +33,8 @@ impl BumpAllocator { self.current += len; Some(ptr) } + + pub fn free_pages(&mut self, _ptr: usize, _order: usize) { + // Not supported. + } } diff --git a/libs/kerla_utils/lib.rs b/libs/kerla_utils/lib.rs index 02941987..7c377db4 100644 --- a/libs/kerla_utils/lib.rs +++ b/libs/kerla_utils/lib.rs @@ -11,8 +11,12 @@ extern crate std; #[macro_use] extern crate alloc; +#[macro_use] +extern crate log; + pub mod alignment; pub mod bitmap; +pub mod bitmap_allocator; pub mod buddy_allocator; pub mod bump_allocator; pub mod byte_size; diff --git a/runtime/page_allocator.rs b/runtime/page_allocator.rs index 3cc2a31e..1f95dfaf 100644 --- a/runtime/page_allocator.rs +++ b/runtime/page_allocator.rs @@ -1,11 +1,19 @@ +use core::ops::Deref; + use crate::{address::PAddr, arch::PAGE_SIZE, bootinfo::RamArea, spinlock::SpinLock}; use arrayvec::ArrayVec; use bitflags::bitflags; -use kerla_utils::bump_allocator::BumpAllocator as Allocator; +use kerla_utils::alignment::is_aligned; use kerla_utils::byte_size::ByteSize; -// TODO: + +use kerla_utils::bitmap_allocator::BitMapAllocator as Allocator; + +// TODO: Fix bugs in use the buddy allocator. // use kerla_utils::buddy_allocator::BuddyAllocator as Allocator; +// Comment out the following line to use BumpAllocator. +// use kerla_utils::bump_allocator::BumpAllocator as Allocator; + static ZONES: SpinLock> = SpinLock::new(ArrayVec::new_const()); fn num_pages_to_order(num_pages: usize) -> usize { @@ -37,11 +45,36 @@ bitflags! { #[derive(Debug)] pub struct PageAllocError; +pub struct OwnedPages { + paddr: PAddr, + num_pages: usize, +} + +impl OwnedPages { + fn new(paddr: PAddr, num_pages: usize) -> OwnedPages { + OwnedPages { paddr, num_pages } + } +} + +impl Deref for OwnedPages { + type Target = PAddr; + + fn deref(&self) -> &Self::Target { + &self.paddr + } +} + +impl Drop for OwnedPages { + fn drop(&mut self) { + free_pages(self.paddr, self.num_pages); + } +} + pub fn alloc_pages(num_pages: usize, flags: AllocPageFlags) -> Result { let order = num_pages_to_order(num_pages); let mut zones = ZONES.lock(); - for i in 0..zones.len() { - if let Some(paddr) = zones[i].alloc_pages(order).map(PAddr::new) { + for zone in zones.iter_mut() { + if let Some(paddr) = zone.alloc_pages(order).map(PAddr::new) { if flags.contains(AllocPageFlags::ZEROED) { unsafe { paddr @@ -49,6 +82,7 @@ pub fn alloc_pages(num_pages: usize, flags: AllocPageFlags) -> Result Result Result { + let order = num_pages_to_order(num_pages); + let mut zones = ZONES.lock(); + for zone in zones.iter_mut() { + if let Some(paddr) = zone.alloc_pages(order).map(PAddr::new) { + if flags.contains(AllocPageFlags::ZEROED) { + unsafe { + paddr + .as_mut_ptr::() + .write_bytes(0, num_pages * PAGE_SIZE); + } + } + + return Ok(OwnedPages::new(paddr, num_pages)); + } + } + + Err(PageAllocError) +} + +/// The caller must ensure that the pages are not already freed. Keep holding +/// `OwnedPages` to free the pages in RAII basis. +pub fn free_pages(paddr: PAddr, num_pages: usize) { + if cfg!(debug_assertions) { + // Poison the memory. + unsafe { + paddr + .as_mut_ptr::() + .write_bytes(0xa5, num_pages * PAGE_SIZE); + } + } + + let order = num_pages_to_order(num_pages); + let mut zones = ZONES.lock(); + for zone in zones.iter_mut() { + if zone.includes(paddr.value()) { + zone.free_pages(paddr.value(), order); + return; + } + } +} + pub fn init(areas: &[RamArea]) { let mut zones = ZONES.lock(); for area in areas { @@ -65,10 +144,9 @@ pub fn init(areas: &[RamArea]) { ByteSize::new(area.len) ); - zones.push(Allocator::new( - area.base.as_mut_ptr(), - area.base.value(), - area.len, - )); + debug_assert!(is_aligned(area.base.value(), PAGE_SIZE)); + let allocator = + unsafe { Allocator::new(area.base.as_mut_ptr(), area.base.value(), area.len) }; + zones.push(allocator); } }