Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements bucket_zones #1272

Merged
merged 4 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions kernel/src/lock/gutex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ impl GutexGroup {
}
}

#[inline(never)]
fn lock(&self) -> GroupGuard {
// Acquire the lock.
let td = current_thread();
Expand All @@ -133,13 +132,18 @@ impl GutexGroup {
break;
}

todo!()
self.wait();
}

// SAFETY: This is safe because the current thread acquire the lock successfully by the
// above compare_exchange().
unsafe { GroupGuard::new(self) }
}

#[inline(never)]
fn wait(&self) {
todo!()
}
}

unsafe impl Send for GutexGroup {}
Expand All @@ -164,12 +168,17 @@ impl<'a> GroupGuard<'a> {
phantom: PhantomData,
}
}

#[inline(never)]
fn release(&mut self) {
self.group.owning.store(MTX_UNOWNED, Ordering::Release);

todo!("wakeup waiting thread");
}
}

impl Drop for GroupGuard<'_> {
#[inline(never)]
fn drop(&mut self) {
// Decrease the active lock.
unsafe {
let active = self.group.active.get();

Expand All @@ -180,9 +189,6 @@ impl Drop for GroupGuard<'_> {
}
}

// Release the lock.
self.group.owning.store(MTX_UNOWNED, Ordering::Release);

todo!("wakeup waiting thread");
self.release();
}
}
4 changes: 2 additions & 2 deletions kernel/src/malloc/stage2.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::config::PAGE_SIZE;
use crate::context::{current_thread, current_uma, CpuLocal};
use crate::uma::{UmaFlags, UmaZone};
use crate::uma::{Alloc, UmaFlags, UmaZone};
use alloc::string::ToString;
use alloc::sync::Arc;
use alloc::vec::Vec;
Expand Down Expand Up @@ -106,7 +106,7 @@ impl VmHeap {

// Allocate a memory from UMA zone.
let zone = &self.zones[align][size >> Self::KMEM_ZSHIFT];
let mem = zone.alloc();
let mem = zone.alloc(Alloc::Wait | Alloc::Zero);

// Update stats.
let stats = self.stats.lock();
Expand Down
15 changes: 15 additions & 0 deletions kernel/src/uma/boxed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use core::ops::Deref;

/// Encapsulates an object allocated from a UMA zone.
pub struct UmaBox<T: ?Sized>(*mut T);

impl<T: ?Sized> Deref for UmaBox<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}

unsafe impl<T: Send + ?Sized> Send for UmaBox<T> {}
unsafe impl<T: Sync + ?Sized> Sync for UmaBox<T> {}
9 changes: 7 additions & 2 deletions kernel/src/uma/bucket.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
/// Implementation of `uma_bucket` structure.
pub struct UmaBucket {
#[repr(C)]
pub struct UmaBucket<I: ?Sized> {
len: usize, // ub_cnt
items: I, // ub_bucket
}

impl UmaBucket {
impl<I: ?Sized> UmaBucket<I> {
pub fn len(&self) -> usize {
self.len
}
}

/// Each item in the [`UmaBucket::items`].
pub struct BucketItem {}
62 changes: 57 additions & 5 deletions kernel/src/uma/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
pub use self::boxed::*;
pub use self::zone::*;

use self::bucket::{BucketItem, UmaBucket};
use crate::config::PAGE_SIZE;
use alloc::format;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::alloc::Layout;
use core::num::NonZero;
use core::sync::atomic::AtomicBool;
use macros::bitflag;

mod boxed;
mod bucket;
mod keg;
mod slab;
Expand All @@ -15,6 +21,8 @@ mod zone;
/// Implementation of UMA system.
pub struct Uma {
bucket_enable: Arc<AtomicBool>,
bucket_keys: Arc<Vec<usize>>, // bucket_size
bucket_zones: Arc<Vec<UmaZone>>, // bucket_zones
}

impl Uma {
Expand All @@ -25,7 +33,6 @@ impl Uma {
const MAX_WASTE: NonZero<usize> = NonZero::new(PAGE_SIZE.get() / 10).unwrap();
const BUCKET_MAX: usize = 128;
const BUCKET_SHIFT: usize = 4;
const BUCKET_ZONES: usize = ((Self::BUCKET_MAX >> Self::BUCKET_SHIFT) + 1);

/// `bucket_zones`.
const BUCKET_SIZES: [usize; 4] = [16, 32, 64, 128];
Expand All @@ -38,18 +45,41 @@ impl Uma {
/// |PS4 11.00|0x13CA70|
pub fn new() -> Arc<Self> {
let bucket_enable = Arc::new(AtomicBool::new(true)); // TODO: Use a proper value.
let mut bucket_keys = [0; Self::BUCKET_ZONES];
let mut bucket_keys = Vec::new();
let mut bucket_zones = Vec::with_capacity(Self::BUCKET_SIZES.len());
let mut ki = 0;

// Create bucket zones.
for (si, size) in Self::BUCKET_SIZES.into_iter().enumerate() {
let items = Layout::array::<BucketItem>(size).unwrap();
let layout = Layout::new::<UmaBucket<()>>()
.extend(items)
.unwrap()
.0
.pad_to_align();

bucket_zones.push(UmaZone::new(
bucket_enable.clone(),
Arc::default(),
Arc::default(),
format!("{size} Bucket"),
None,
layout.size().try_into().unwrap(),
Some(layout.align() - 1),
UmaFlags::Bucket | UmaFlags::Internal,
));

while ki <= size {
bucket_keys[ki >> Self::BUCKET_SHIFT] = si;
bucket_keys.push(si);
ki += 1 << Self::BUCKET_SHIFT;
}
}

Arc::new(Self { bucket_enable })
Arc::new(Self {
bucket_enable,
bucket_keys: Arc::new(bucket_keys),
bucket_zones: Arc::new(bucket_zones),
})
}

/// See `uma_zcreate` on the Orbis for a reference.
Expand All @@ -67,7 +97,16 @@ impl Uma {
) -> UmaZone {
// The Orbis will allocate a new zone from masterzone_z. We choose to remove this since it
// does not idomatic to Rust, which mean our uma_zone itself can live on the stack.
UmaZone::new(self.bucket_enable.clone(), name, None, size, align, flags)
UmaZone::new(
self.bucket_enable.clone(),
self.bucket_keys.clone(),
self.bucket_zones.clone(),
name,
None,
size,
align,
flags,
)
}
}

Expand Down Expand Up @@ -96,8 +135,21 @@ pub enum UmaFlags {
CacheSpread = 0x1000,
/// `UMA_ZONE_VTOSLAB`.
VToSlab = 0x2000,
/// `UMA_ZFLAG_BUCKET`.
Bucket = 0x2000000,
/// `UMA_ZFLAG_INTERNAL`.
Internal = 0x20000000,
/// `UMA_ZFLAG_CACHEONLY`.
CacheOnly = 0x80000000,
}

/// Implementation of `malloc` flags.
#[bitflag(u32)]
pub enum Alloc {
/// `M_WAITOK`.
Wait = 0x2,
/// `M_ZERO`.
Zero = 0x100,
/// `M_NOVM`.
NoVm = 0x200,
}
81 changes: 54 additions & 27 deletions kernel/src/uma/zone.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use super::bucket::UmaBucket;
use super::bucket::{BucketItem, UmaBucket};
use super::keg::UmaKeg;
use super::{Uma, UmaFlags};
use super::{Alloc, Uma, UmaBox, UmaFlags};
use crate::context::{current_thread, CpuLocal};
use crate::lock::{Gutex, GutexGroup, GutexWrite};
use alloc::collections::VecDeque;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::cmp::min;
use core::num::NonZero;
Expand All @@ -16,15 +17,17 @@ use core::sync::atomic::{AtomicBool, Ordering};
/// Implementation of `uma_zone` structure.
pub struct UmaZone {
bucket_enable: Arc<AtomicBool>,
bucket_keys: Arc<Vec<usize>>,
bucket_zones: Arc<Vec<UmaZone>>,
ty: ZoneType,
size: NonZero<usize>, // uz_size
caches: CpuLocal<RefCell<UmaCache>>, // uz_cpu
full_buckets: Gutex<VecDeque<UmaBucket>>, // uz_full_bucket
free_buckets: Gutex<VecDeque<UmaBucket>>, // uz_free_bucket
alloc_count: Gutex<u64>, // uz_allocs
free_count: Gutex<u64>, // uz_frees
count: Gutex<usize>, // uz_count
flags: UmaFlags, // uz_flags
size: NonZero<usize>, // uz_size
caches: CpuLocal<RefCell<UmaCache>>, // uz_cpu
full_buckets: Gutex<VecDeque<UmaBox<UmaBucket<[BucketItem]>>>>, // uz_full_bucket
free_buckets: Gutex<VecDeque<UmaBox<UmaBucket<[BucketItem]>>>>, // uz_free_bucket
alloc_count: Gutex<u64>, // uz_allocs
free_count: Gutex<u64>, // uz_frees
count: Gutex<usize>, // uz_count
flags: UmaFlags, // uz_flags
}

impl UmaZone {
Expand All @@ -36,8 +39,11 @@ impl UmaZone {
/// | Version | Offset |
/// |---------|--------|
/// |PS4 11.00|0x13D490|
#[allow(clippy::too_many_arguments)] // TODO: Find a better way.
pub(super) fn new(
bucket_enable: Arc<AtomicBool>,
bucket_keys: Arc<Vec<usize>>,
bucket_zones: Arc<Vec<UmaZone>>,
name: impl Into<String>,
keg: Option<UmaKeg>,
size: NonZero<usize>,
Expand Down Expand Up @@ -100,6 +106,8 @@ impl UmaZone {

Self {
bucket_enable,
bucket_keys,
bucket_zones,
ty,
size: keg.size(),
caches: CpuLocal::new(|_| RefCell::default()),
Expand All @@ -122,13 +130,14 @@ impl UmaZone {
/// | Version | Offset |
/// |---------|--------|
/// |PS4 11.00|0x13E750|
pub fn alloc(&self) -> *mut u8 {
// Our implementation imply M_WAITOK and M_ZERO. Beware that we can't call into global
// allocator here otherwise it will cause a recursive call, which will end up panic.
let td = current_thread();
pub fn alloc(&self, flags: Alloc) -> *mut u8 {
if flags.has(Alloc::Wait) {
// TODO: The Orbis also modify td_pflags on a certain condition.
let td = current_thread();

if !td.can_sleep() {
panic!("heap allocation in a non-sleeping context is not supported");
if !td.can_sleep() {
panic!("attempt to do waitable heap allocation in a non-sleeping context");
}
}

loop {
Expand Down Expand Up @@ -184,6 +193,10 @@ impl UmaZone {
| ZoneType::Mbuf
| ZoneType::MbufCluster
) {
if flags.has(Alloc::Wait) {
todo!()
}

todo!()
}

Expand All @@ -200,8 +213,8 @@ impl UmaZone {
*count += 1;
}

if self.alloc_bucket(frees) {
return self.alloc_item();
if self.alloc_bucket(frees, count, flags) {
return self.alloc_item(flags);
}
}
}
Expand Down Expand Up @@ -229,16 +242,30 @@ impl UmaZone {
/// | Version | Offset |
/// |---------|--------|
/// |PS4 11.00|0x13EBA0|
fn alloc_bucket(&self, frees: GutexWrite<VecDeque<UmaBucket>>) -> bool {
fn alloc_bucket(
&self,
frees: GutexWrite<VecDeque<UmaBox<UmaBucket<[BucketItem]>>>>,
count: GutexWrite<usize>,
flags: Alloc,
) -> bool {
match frees.front() {
Some(_) => todo!(),
None => {
if self.bucket_enable.load(Ordering::Relaxed) {
// Get allocation flags.
let mut flags = flags & !Alloc::Zero;

if self.flags.has(UmaFlags::CacheOnly) {
todo!()
} else {
todo!()
flags |= Alloc::NoVm;
}

// Alloc a bucket.
let i = (*count + 15) >> Uma::BUCKET_SHIFT;
let k = self.bucket_keys[i];

self.bucket_zones[k].alloc_item(flags);

todo!()
}
}
}
Expand All @@ -252,7 +279,7 @@ impl UmaZone {
/// | Version | Offset |
/// |---------|--------|
/// |PS4 11.00|0x13DD50|
fn alloc_item(&self) -> *mut u8 {
fn alloc_item(&self, _: Alloc) -> *mut u8 {
todo!()
}
}
Expand All @@ -276,8 +303,8 @@ enum ZoneType {
/// Implementation of `uma_cache` structure.
#[derive(Default)]
struct UmaCache {
alloc: Option<UmaBucket>, // uc_allocbucket
free: Option<UmaBucket>, // uc_freebucket
allocs: u64, // uc_allocs
frees: u64, // uc_frees
alloc: Option<UmaBox<UmaBucket<[BucketItem]>>>, // uc_allocbucket
free: Option<UmaBox<UmaBucket<[BucketItem]>>>, // uc_freebucket
allocs: u64, // uc_allocs
frees: u64, // uc_frees
}
Loading