feat: SMP stage 2, lock-free fast paths (GetTime, GetProcessId, DebugPutchar, no-op Yield), storm checks yield equivalence
This commit is contained in:
@@ -97,6 +97,61 @@ pub fn unlock() {
|
||||
SERVING.fetch_add(1, Ordering::Release);
|
||||
}
|
||||
|
||||
// ---- what lock-free fast paths may know ----
|
||||
//
|
||||
// Published under the kernel lock just before it is released, read without
|
||||
// it. A fast path that reads them while another hart holds the lock simply
|
||||
// takes effect before that hart's operation.
|
||||
|
||||
/// Per core: highest waiting priority + 1, 0 if its queue is empty.
|
||||
static WAITING: [AtomicUsize; MAX_HARTS] = [const { AtomicUsize::new(0) }; MAX_HARTS];
|
||||
/// Per hart: the running thread's process id + 1 and priority + 1.
|
||||
static RUNNING_PID: [AtomicUsize; MAX_HARTS] = [const { AtomicUsize::new(0) }; MAX_HARTS];
|
||||
static RUNNING_PRIORITY: [AtomicUsize; MAX_HARTS] = [const { AtomicUsize::new(0) }; MAX_HARTS];
|
||||
|
||||
pub fn publish_waiting(core: usize, highest: Option<u8>) {
|
||||
if let Some(w) = WAITING.get(core) {
|
||||
w.store(highest.map_or(0, |p| p as usize + 1), Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn publish_running(pid: Option<u16>, priority: Option<u8>) {
|
||||
let me = hart_id();
|
||||
if let (Some(p), Some(q)) = (RUNNING_PID.get(me), RUNNING_PRIORITY.get(me)) {
|
||||
p.store(pid.map_or(0, |x| x as usize + 1), Ordering::Release);
|
||||
q.store(priority.map_or(0, |x| x as usize + 1), Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// The process the thread running on this hart belongs to.
|
||||
pub fn running_pid() -> Option<usize> {
|
||||
let v = RUNNING_PID.get(hart_id())?.load(Ordering::Acquire);
|
||||
v.checked_sub(1)
|
||||
}
|
||||
|
||||
/// Whether a Yield by the running thread would change nothing: no thread
|
||||
/// waits on this core at its priority or above.
|
||||
pub fn yield_is_noop() -> bool {
|
||||
let me = hart_id();
|
||||
let (Some(w), Some(p)) = (WAITING.get(me), RUNNING_PRIORITY.get(me)) else {
|
||||
return false;
|
||||
};
|
||||
let mine = p.load(Ordering::Acquire);
|
||||
mine != 0 && w.load(Ordering::Acquire) < mine
|
||||
}
|
||||
|
||||
/// Whether this hart has an IPI or a reschedule request it has not acted
|
||||
/// on; fast paths must then take the slow path.
|
||||
pub fn has_pending_request() -> bool {
|
||||
let sip: usize;
|
||||
// SAFETY: reading sip has no side effects.
|
||||
unsafe { core::arch::asm!("csrr {0}, sip", out(reg) sip) };
|
||||
sip & SIP_SSIP != 0
|
||||
|| RESCHEDULE
|
||||
.get(hart_id())
|
||||
.is_some_and(|r| r.load(Ordering::SeqCst))
|
||||
}
|
||||
|
||||
// ---- IPIs ----
|
||||
|
||||
/// The lock-free half of an IPI: acknowledges it and, if asked, leaves the
|
||||
|
||||
Reference in New Issue
Block a user