kernel: apply P5-context-mod-sched, P8-percpu-sched, P8-percpu-wiring
Phase 0c, plan orders #3, #4, #7. P5-context-mod-sched: re-export SchedPolicy from context::mod (one-line change to the use statement). The type is defined in context::context by the previous P7-cache-affine-context commit; this just makes it available as crate::context::SchedPolicy. P8-percpu-sched: adds PerCpuSched struct to percpu.rs with SyncUnsafeCell- wrapped run_queues, balance/last_queue/last_balance_time cells, and take_lock/release_lock methods. Refactors PercpuBlock to embed PerCpuSched as 'sched' field instead of standalone 'balance'/'last_queue' fields. Adds get_percpu_block() helper. P8-percpu-wiring: rewrites src/context/switch.rs to consume PerCpuSched: - select_next_context reads from percpublock.sched.queues() instead of the global RunContextData.set - Initial placement chooses least-loaded CPU via PercpuSched.balance - Load balance trigger fires periodically and migrates contexts between per-CPU queues respecting sched_affinity - Adds pub const fn to access per-cpu sched state safely After this commit, the kernel builds with per-CPU run queues wired into the scheduler. cargo check still has 1 pre-existing unrelated error (src/acpi/fadt.rs:110 type mismatch) that predates the threading work. Combined with the P6-futex-sharding commit, this completes the foundation for Phase 1 (Futex Completeness) and Phase 2 (SMP Scheduling Quality).
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ use crate::{
|
||||
|
||||
use self::context::Kstack;
|
||||
pub use self::{
|
||||
context::{BorrowedHtBuf, Context, Status},
|
||||
context::{BorrowedHtBuf, Context, SchedPolicy, Status},
|
||||
switch::switch,
|
||||
};
|
||||
|
||||
|
||||
+747
-167
File diff suppressed because it is too large
Load Diff
+69
-6
@@ -1,9 +1,10 @@
|
||||
use alloc::{
|
||||
collections::VecDeque,
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
cell::{Cell, RefCell},
|
||||
cell::{Cell, RefCell, SyncUnsafeCell},
|
||||
sync::atomic::{AtomicBool, AtomicPtr, Ordering},
|
||||
};
|
||||
|
||||
@@ -12,7 +13,10 @@ use syscall::PtraceFlags;
|
||||
|
||||
use crate::{
|
||||
arch::device::ArchPercpuMisc,
|
||||
context::{empty_cr3, memory::AddrSpaceWrapper, switch::ContextSwitchPercpu},
|
||||
context::{
|
||||
empty_cr3, memory::AddrSpaceWrapper, switch::ContextSwitchPercpu, WeakContextRef,
|
||||
RUN_QUEUE_COUNT,
|
||||
},
|
||||
cpu_set::{LogicalCpuId, MAX_CPU_COUNT},
|
||||
cpu_stats::{CpuStats, CpuStatsData},
|
||||
ptrace::Session,
|
||||
@@ -20,6 +24,58 @@ use crate::{
|
||||
syscall::debug::SyscallDebugInfo,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct PerCpuSched {
|
||||
pub run_queues: SyncUnsafeCell<[VecDeque<WeakContextRef>; RUN_QUEUE_COUNT]>,
|
||||
pub run_queues_lock: AtomicBool,
|
||||
pub balance: Cell<[usize; RUN_QUEUE_COUNT]>,
|
||||
pub last_queue: Cell<usize>,
|
||||
pub last_balance_time: Cell<u128>,
|
||||
}
|
||||
|
||||
impl PerCpuSched {
|
||||
pub const fn new() -> Self {
|
||||
const EMPTY: VecDeque<WeakContextRef> = VecDeque::new();
|
||||
Self {
|
||||
run_queues: SyncUnsafeCell::new([EMPTY; RUN_QUEUE_COUNT]),
|
||||
run_queues_lock: AtomicBool::new(false),
|
||||
balance: Cell::new([0; RUN_QUEUE_COUNT]),
|
||||
last_queue: Cell::new(0),
|
||||
last_balance_time: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_lock(&self) {
|
||||
while self
|
||||
.run_queues_lock
|
||||
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
while self.run_queues_lock.load(Ordering::Relaxed) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release_lock(&self) {
|
||||
self.run_queues_lock.store(false, Ordering::Release);
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must hold `run_queues_lock` while accessing the returned reference.
|
||||
pub unsafe fn queues(&self) -> &[VecDeque<WeakContextRef>; RUN_QUEUE_COUNT] {
|
||||
unsafe { &*self.run_queues.get() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must hold `run_queues_lock` while accessing the returned reference.
|
||||
pub unsafe fn queues_mut(&self) -> &mut [VecDeque<WeakContextRef>; RUN_QUEUE_COUNT] {
|
||||
unsafe { &mut *self.run_queues.get() }
|
||||
}
|
||||
}
|
||||
|
||||
/// The percpu block, that stored all percpu variables.
|
||||
pub struct PercpuBlock {
|
||||
/// A unique immutable number that identifies the current CPU - used for scheduling
|
||||
@@ -31,8 +87,8 @@ pub struct PercpuBlock {
|
||||
pub current_addrsp: RefCell<Option<Arc<AddrSpaceWrapper>>>,
|
||||
pub new_addrsp_tmp: Cell<Option<Arc<AddrSpaceWrapper>>>,
|
||||
pub wants_tlb_shootdown: AtomicBool,
|
||||
pub balance: Cell<[usize; 40]>,
|
||||
pub last_queue: Cell<usize>,
|
||||
|
||||
pub sched: PerCpuSched,
|
||||
|
||||
// TODO: Put mailbox queues here, e.g. for TLB shootdown? Just be sure to 128-byte align it
|
||||
// first to avoid cache invalidation.
|
||||
@@ -57,6 +113,14 @@ pub unsafe fn init_tlb_shootdown(id: LogicalCpuId, block: *mut PercpuBlock) {
|
||||
ALL_PERCPU_BLOCKS[id.get() as usize].store(block, Ordering::Release)
|
||||
}
|
||||
|
||||
pub fn get_percpu_block(id: LogicalCpuId) -> Option<&'static PercpuBlock> {
|
||||
unsafe {
|
||||
ALL_PERCPU_BLOCKS[id.get() as usize]
|
||||
.load(Ordering::Acquire)
|
||||
.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_stats() -> Vec<(LogicalCpuId, CpuStatsData)> {
|
||||
let mut res = ALL_PERCPU_BLOCKS
|
||||
.iter()
|
||||
@@ -187,8 +251,7 @@ impl PercpuBlock {
|
||||
current_addrsp: RefCell::new(None),
|
||||
new_addrsp_tmp: Cell::new(None),
|
||||
wants_tlb_shootdown: AtomicBool::new(false),
|
||||
balance: Cell::new([0; 40]),
|
||||
last_queue: Cell::new(39),
|
||||
sched: PerCpuSched::new(),
|
||||
ptrace_flags: Cell::new(PtraceFlags::empty()),
|
||||
ptrace_session: RefCell::new(None),
|
||||
inside_syscall: Cell::new(false),
|
||||
|
||||
Reference in New Issue
Block a user