feat: SMP stage 2, lock-free fast paths (GetTime, GetProcessId, DebugPutchar, no-op Yield), storm checks yield equivalence

This commit is contained in:
2026-10-27 01:07:57 +01:00
parent 2e665fd66e
commit 19aeabd86d
4 changed files with 154 additions and 3 deletions

View File

@@ -241,7 +241,7 @@ fn next_runnable() -> types::id::ThreadId {
return tid;
}
crate::mmu::activate(crate::mmu::page_table::table_at(crate::smp::kernel_root()));
crate::smp::unlock();
release();
unsafe { core::arch::asm!("wfi") };
crate::smp::service_ipi();
crate::smp::lock();
@@ -271,6 +271,54 @@ fn dispatch_external() {
}
}
/// Releases the kernel lock, first publishing what the lock-free fast
/// paths may rely on.
fn release() {
let kern = kern();
for core in 0..crate::smp::num_harts() {
let id = types::id::CoreId::from_raw(core as u8);
crate::smp::publish_waiting(core, kern.highest_waiting(id).map(|p| p.value()));
}
let running = kern.current_thread();
crate::smp::publish_running(
running
.and_then(|t| kern.thread_process(t))
.map(|p| p.raw()),
running
.and_then(|t| kern.thread_priority(t))
.map(|p| p.value()),
);
crate::smp::unlock();
}
/// Syscalls answered without the kernel lock, as the kernel would answer
/// them: they read nothing the lock guards (the time, the UART, the
/// running thread's fixed process id) or, for a Yield that has nobody to
/// yield to, change nothing. Any pending IPI or reschedule request sends
/// the call down the slow path, so no request is ever skipped.
fn fast_syscall(frame: &mut TrapFrame) -> bool {
use types::syscall_abi::{SYS_DEBUG_PUTCHAR, SYS_GET_PROCESS_ID, SYS_GET_TIME, SYS_YIELD};
if crate::smp::has_pending_request() {
return false;
}
let result = match frame.regs[17] {
SYS_GET_TIME => kernel::SyscallResult::Value(crate::hw::timer::read_time() as usize),
SYS_GET_PROCESS_ID => match crate::smp::running_pid() {
Some(pid) => kernel::SyscallResult::Value(pid),
None => return false,
},
SYS_DEBUG_PUTCHAR => {
crate::hw::uart::Uart::new().put_char(frame.regs[10] as u8);
kernel::SyscallResult::Ok
}
SYS_YIELD if crate::smp::yield_is_noop() => kernel::SyscallResult::Ok,
_ => return false,
};
frame.sepc += 4;
write_result(frame, &result);
true
}
/// Last step before leaving the kernel to userspace: act on a reschedule
/// request, make sure this hart has a thread (its own may have been taken
/// away by another hart), and translate through that thread's address
@@ -311,7 +359,7 @@ pub fn enter_first() -> ! {
None => switch_to_next(&mut frame, None),
}
finish(&mut frame);
crate::smp::unlock();
release();
// SAFETY: `frame` holds a user context; sscratch is set from it on
// the way out, so later traps land at the top of this frame.
unsafe { trap_return(&mut frame) }
@@ -326,6 +374,9 @@ extern "C" fn trap_handler_rust(frame: &mut TrapFrame) {
// Checked before taking the lock: this hart may already hold it.
kernel_fault(frame, scause);
}
if scause == CAUSE_ECALL_U && fast_syscall(frame) {
return;
}
crate::smp::service_ipi();
crate::smp::lock();
if scause & CAUSE_INTERRUPT_BIT != 0 {
@@ -334,7 +385,7 @@ extern "C" fn trap_handler_rust(frame: &mut TrapFrame) {
handle_exception(frame, scause);
}
finish(frame);
crate::smp::unlock();
release();
}
fn handle_interrupt(frame: &mut TrapFrame, cause: usize) {