mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
feat: allocator: add block allocator
This commit is contained in:
parent
ced2b85b63
commit
0bb447ca28
153
kernel/src/vspace/allocator/block.rs
Normal file
153
kernel/src/vspace/allocator/block.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use crate::vspace::addr::{AddressOps, PhysAddr};
|
||||
use core::alloc::Layout;
|
||||
use core::cmp::min;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Block {
|
||||
start: PhysAddr,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
fn start_addr(&self) -> PhysAddr {
|
||||
self.start
|
||||
}
|
||||
|
||||
fn end_addr(&self) -> PhysAddr {
|
||||
self.start + self.size
|
||||
}
|
||||
|
||||
fn could_merge(&self, start: PhysAddr, size: usize) -> bool {
|
||||
self.start_addr() == start + size || self.end_addr() == start
|
||||
}
|
||||
|
||||
fn merge(&mut self, start: PhysAddr, size: usize) -> bool {
|
||||
if self.could_merge(start, size) {
|
||||
self.start = min(self.start, start);
|
||||
self.size += size;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn could_fit(&self, layout: Layout) -> bool {
|
||||
self.start_addr().align_up(layout.align()) + layout.size() <= self.end_addr()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RamBlock<const N: usize> {
|
||||
blocks: [Option<Block>; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> RamBlock<N> {
|
||||
pub const fn new() -> Self {
|
||||
Self { blocks: [None; N] }
|
||||
}
|
||||
|
||||
fn insert(&mut self, start: PhysAddr, size: usize) -> Result<&mut Option<Block>, ()> {
|
||||
for block in self.blocks.iter_mut() {
|
||||
if block.is_none() {
|
||||
*block = Some(Block { start, size });
|
||||
return Ok(block);
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
pub fn dealloc(&mut self, start: PhysAddr, size: usize) {
|
||||
/* NOTE: blocks will not be fully merged, for example:
|
||||
* we have [(0-10), (15-20)], if we dealloc (10, 15),
|
||||
* we will have [(0-15), (15, 20)] instead of [(0-20)]
|
||||
*/
|
||||
|
||||
// check whether we could combine with the previous block
|
||||
for block in self.blocks.iter_mut().flatten() {
|
||||
if block.merge(start, size) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// insert into a new slot
|
||||
self.insert(start, size).expect("No free slot");
|
||||
}
|
||||
|
||||
pub fn alloc(&mut self, layout: Layout) -> Option<PhysAddr> {
|
||||
let victim = self
|
||||
.blocks
|
||||
.iter_mut()
|
||||
.find(|block| block.is_some_and(|b| b.could_fit(layout)));
|
||||
|
||||
victim.as_ref()?;
|
||||
let victim = victim.unwrap();
|
||||
|
||||
if let Some(block) = victim.take() {
|
||||
let start = block.start_addr().align_up(layout.align());
|
||||
let end = start + layout.size();
|
||||
|
||||
if block.end_addr() > end {
|
||||
*victim = Some(Block {
|
||||
start: end,
|
||||
size: (block.end_addr() - end).as_usize(),
|
||||
})
|
||||
}
|
||||
|
||||
if block.start_addr() < start {
|
||||
let _ = self.insert(block.start_addr(), (start - block.start_addr()).as_usize());
|
||||
}
|
||||
|
||||
return Some(start);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use log::debug;
|
||||
|
||||
#[test_case]
|
||||
fn test_block() {
|
||||
let mut blk = RamBlock::<4>::new();
|
||||
blk.dealloc(PhysAddr(0), 100);
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(5, 8).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(0)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(5, 8).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(8)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(5, 1).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(0xd)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(5, 0x20).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(0x20)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(5, 0x40).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(0x40)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(5, 0x80).unwrap());
|
||||
assert_eq!(ptr, None);
|
||||
|
||||
blk.dealloc(PhysAddr(0), 5);
|
||||
blk.dealloc(PhysAddr(32), 4);
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(31, 1).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(0x45)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(8, 1).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(0)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(18, 1).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(0x12)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(27, 1).unwrap());
|
||||
assert_eq!(ptr, Some(PhysAddr(0x25)));
|
||||
|
||||
let ptr = blk.alloc(Layout::from_size_align(1, 1).unwrap());
|
||||
assert_eq!(ptr, None);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
mod bitmap;
|
||||
mod block;
|
||||
mod freelist;
|
||||
|
||||
pub use bitmap::*;
|
||||
|
Loading…
Reference in New Issue
Block a user