Skip to content

Commit

Permalink
feat(kernel): put back bump pointer region (#367)
Browse files Browse the repository at this point in the history
At one point in time, Mycelium contained a bump pointer allocator that
could be used for very early allocations before a better heap allocator
is initialized with information from the BIOS memory map. This was
removed in #130, since it was possible to use the buddy-block allocator
for all heap allocations.

In order to add a scrollback capability to framebuffer logging (#364),
though, we want to be able to allocate a dynamically sized scrollback
buffer. This is because we don't know the actual size of the framebuffer
at compile time, and need to determine how long the lines in the
scrollback ring are based on the dimensions of the framebuffer. However,
we want to preallocate the entire scrollback ring, since we want to be
able to store logs from inside the allocator in the scrollback ring.
Also, if the scrollback ring isn't allocated until after the heap is
initialized, we won't include early log messages in the scrollback ring.
Therefore, it would be nice to be able to allocate the scrollback ring
before we initialize the heap.

This PR puts back the bump pointer allocator so that it can be used for
allocating the scrollback ring. We could potentially use it for other
very early dynamic allocations as well, if we can guarantee that they
will live for the entire lifetime of the kernel.

We may want to emit a warning, or even panic, if we try to deallocate a
bump-allocated address?
  • Loading branch information
hawkw committed Nov 5, 2022
1 parent 2f2bb52 commit 16206f1
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 43 deletions.
81 changes: 77 additions & 4 deletions src/allocator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub use alloc::alloc::{GlobalAlloc, Layout};
use core::sync::atomic::{AtomicUsize, Ordering};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use hal_core::{
boot::BootInfo,
mem::{
Expand All @@ -8,27 +8,39 @@ use hal_core::{
},
PAddr,
};
use mycelium_alloc::buddy;
use mycelium_alloc::{buddy, bump};
use mycelium_util::{fmt, math::Logarithm};

#[derive(Debug)]
pub struct Allocator {
bump: bump::Alloc<BUMP_REGION_SIZE>,
allocator: buddy::Alloc<32>,
/// If true, only the bump region is active.
bump_mode: AtomicBool,
allocating: AtomicUsize,
deallocating: AtomicUsize,
}

/// 1k is enough for anyone.
const BUMP_REGION_SIZE: usize = 1024;

#[derive(Debug, Copy, Clone)]
pub struct State {
pub(crate) allocating: usize,
pub(crate) deallocating: usize,
pub(crate) heap_size: usize,
pub(crate) allocated: usize,
pub(crate) min_size: usize,
pub(crate) bump_mode: bool,
pub(crate) bump_allocated: usize,
pub(crate) bump_size: usize,
}

impl Allocator {
pub const fn new() -> Self {
Self {
bump: bump::Alloc::new(),
bump_mode: AtomicBool::new(true),
allocator: buddy::Alloc::new(32),
allocating: AtomicUsize::new(0),
deallocating: AtomicUsize::new(0),
Expand All @@ -39,9 +51,12 @@ impl Allocator {
State {
allocating: self.allocating.load(Ordering::Acquire),
deallocating: self.deallocating.load(Ordering::Acquire),
bump_mode: self.bump_mode.load(Ordering::Acquire),
heap_size: self.allocator.total_size(),
allocated: self.allocator.allocated_size(),
min_size: self.allocator.min_size(),
bump_allocated: self.bump.allocated_size(),
bump_size: self.bump.total_size(),
}
}

Expand All @@ -58,6 +73,9 @@ impl Allocator {
let added = self.allocator.add_region(region).is_ok();
tracing::trace!(added);
self.deallocating.fetch_sub(1, Ordering::Release);
if self.bump_mode.swap(false, Ordering::Release) {
tracing::debug!("disabled bump allocator mode");
}
}

#[inline]
Expand All @@ -70,15 +88,21 @@ unsafe impl GlobalAlloc for Allocator {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.allocating.fetch_add(1, Ordering::Release);
let ptr = GlobalAlloc::alloc(&self.allocator, layout);
let ptr = if self.bump_mode.load(Ordering::Acquire) {
GlobalAlloc::alloc(&self.bump, layout)
} else {
GlobalAlloc::alloc(&self.allocator, layout)
};
self.allocating.fetch_sub(1, Ordering::Release);
ptr
}

#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.deallocating.fetch_add(1, Ordering::Release);
GlobalAlloc::dealloc(&self.allocator, ptr, layout);
if !self.bump.owns(ptr) {
GlobalAlloc::dealloc(&self.allocator, ptr, layout);
}
self.deallocating.fetch_sub(1, Ordering::Release);
}
}
Expand Down Expand Up @@ -119,6 +143,55 @@ impl State {
}
}

impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let &Self {
allocating,
deallocating,
heap_size,
allocated,
min_size,
bump_mode,
bump_allocated,
bump_size,
} = self;
f.write_str("heap stats:\n")?;
writeln!(f, " {allocating} cores allocating")?;
writeln!(f, " {deallocating} cores deallocating")?;

if bump_mode {
writeln!(f, " bump allocator mode only")?;
} else {
let digits = (heap_size).checked_ilog(10).unwrap_or(0) + 1;
let free = heap_size - allocated;
writeln!(f, "buddy heap:")?;

writeln!(f, " {free:>digits$} B free", digits = digits)?;

writeln!(f, " {heap_size:>digits$} B total", digits = digits)?;
writeln!(f, " {free:>digits$} B free", digits = digits)?;
writeln!(f, " {allocated:>digits$} B busy", digits = digits)?;
writeln!(
f,
" {min_size:>digits$} B minimum allocation",
digits = digits
)?;
}

writeln!(f, "bump region:")?;
let bump_digits = (bump_size).checked_ilog(10).unwrap_or(0) + 1;
let bump_free = bump_size - bump_allocated;

writeln!(f, " {bump_free:>digits$} B free", digits = bump_digits)?;
writeln!(
f,
" {bump_allocated:>digits$} B used",
digits = bump_digits
)?;
Ok(())
}
}

#[cfg(test)]
mod tests {
use mycotest::*;
Expand Down
47 changes: 8 additions & 39 deletions src/arch/x86_64/oops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,60 +162,29 @@ pub fn oops(oops: Oops<'_>) -> ! {

// we were in the allocator, so dump the allocator's free list
if oops.involves_allocator() {
use mycelium_util::math::Logarithm;
let crate::allocator::State {
allocating,
deallocating,
heap_size,
min_size,
allocated,
} = oops.alloc;
let alloc_state = oops.alloc;

let mut writer = mk_writer.make_writer();
if allocating > 0 {
if alloc_state.allocating > 0 {
writeln!(
&mut writer,
"...while allocating ({allocating} allocations in progress)!"
"...while allocating ({} allocations in progress)!",
alloc_state.allocating,
)
.unwrap();
}

if deallocating > 0 {
if alloc_state.deallocating > 0 {
writeln!(
&mut writer,
"...while deallocating ({deallocating} deallocations in progress)!"
"...while deallocating ({} deallocations in progress)!",
alloc_state.deallocating
)
.unwrap();
}

writer.write_char('\n').unwrap();
let digits = (heap_size).checked_ilog(10).unwrap_or(0) + 1;
writeln!(&mut writer, "heap stats:").unwrap();
writeln!(
&mut writer,
" {heap_size:>digits$} B total",
digits = digits
)
.unwrap();
writeln!(
&mut writer,
" {allocated:>digits$} B busy",
digits = digits
)
.unwrap();
writeln!(
&mut writer,
" {:>digits$} B free",
heap_size - allocated,
digits = digits
)
.unwrap();
writeln!(
&mut writer,
" {min_size:>digits$} B minimum allocation",
digits = digits
)
.unwrap();
writeln!(&mut writer, "{alloc_state}").unwrap();

crate::ALLOC.dump_free_lists();
}
Expand Down

0 comments on commit 16206f1

Please sign in to comment.