diff --git a/src/context/context.rs b/src/context/context.rs
index 9e663274d9..0be56c7508 100644
--- a/src/context/context.rs
+++ b/src/context/context.rs
@@ -13,7 +13,6 @@ use crate::{
context::{
self, arch,
file::{FileDescriptor, LockedFileDescription},
- run_contexts_mut,
},
cpu_set::{LogicalCpuId, LogicalCpuSet},
cpu_stats,
@@ -138,10 +137,6 @@ pub struct Context {
pub userspace: bool,
pub being_sigkilled: bool,
pub fmap_ret: Option,
- /// Priority
- pub prio: usize,
- /// Enqueued
- pub enqueued: bool,
// TODO: id can reappear after wraparound?
pub owner_proc_id: Option,
@@ -199,8 +194,6 @@ impl Context {
files: Arc::new(RwLock::new(FdTbl::new())),
userspace: false,
fmap_ret: None,
- prio: 20,
- enqueued: false,
being_sigkilled: false,
owner_proc_id,
diff --git a/src/context/mod.rs b/src/context/mod.rs
index dd389bdf4b..62a0a575b5 100644
--- a/src/context/mod.rs
+++ b/src/context/mod.rs
@@ -2,11 +2,7 @@
//!
//! For resources on contexts, please consult [wikipedia](https://en.wikipedia.org/wiki/Context_switch) and [osdev](https://wiki.osdev.org/Context_Switching)
-use alloc::{
- collections::{BTreeSet, VecDeque},
- string::String,
- sync::Arc,
-};
+use alloc::{collections::BTreeSet, sync::Arc};
use core::num::NonZeroUsize;
use crate::{
@@ -75,22 +71,6 @@ pub use self::arch::empty_cr3;
// the context file descriptors.
static CONTEXTS: RwLock> = RwLock::new(BTreeSet::new());
-// Actual context store for the scheduler
-static RUN_CONTEXTS: RwLock = RwLock::new(RunContextData::new());
-
-pub struct RunContextData {
- set: [VecDeque; 40],
-}
-
-impl RunContextData {
- pub const fn new() -> Self {
- const EMPTY_VEC: VecDeque = VecDeque::new();
- Self {
- set: [EMPTY_VEC; 40],
- }
- }
-}
-
/// Get the global schemes list, const
pub fn contexts(token: LockToken<'_, L0>) -> RwLockReadGuard<'_, L1, BTreeSet> {
CONTEXTS.read(token)
@@ -101,14 +81,6 @@ pub fn contexts_mut(token: LockToken<'_, L0>) -> RwLockWriteGuard<'_, L1, BTreeS
CONTEXTS.write(token)
}
-pub fn run_contexts(token: LockToken<'_, L0>) -> RwLockReadGuard<'_, L1, RunContextData> {
- RUN_CONTEXTS.read(token)
-}
-
-pub fn run_contexts_mut(token: LockToken<'_, L0>) -> RwLockWriteGuard<'_, L1, RunContextData> {
- RUN_CONTEXTS.write(token)
-}
-
pub fn init(token: &mut CleanLockToken) {
let owner = None; // kmain not owned by any fd
let mut context = Context::new(owner).expect("failed to create kmain context");
@@ -123,7 +95,6 @@ pub fn init(token: &mut CleanLockToken) {
context.status = Status::Runnable;
context.running = true;
context.cpu_id = Some(crate::cpu_id());
- context.enqueued = false;
let context_lock = Arc::new(ContextLock::new(context));
@@ -139,52 +110,6 @@ pub fn init(token: &mut CleanLockToken) {
}
}
-pub fn wakeup_context(context_lock: &Arc>) {
- let mut global_token = unsafe { CleanLockToken::new() };
- let mut local_token = unsafe { CleanLockToken::new() };
-
- let mut run_queues = run_contexts_mut(global_token.token());
- let mut context = context_lock.write(local_token.token());
-
- context.wake = None;
-
- /*
- if context.status.is_soft_blocked() {
- context.status = Status::Runnable;
- context.status_reason = "";
-
- if !context.enqueued {
- let prio = context.prio;
- run_queues.set[prio].push_back(ContextRef(Arc::clone(context_lock)));
- context.enqueued = true;
- }
- }
- */
-
- context.unblock();
-
- if context.status.is_runnable() && !context.running && !context.enqueued {
- let prio = context.prio;
- run_queues.set[prio].push_back(ContextRef(Arc::clone(context_lock)));
- context.enqueued = true;
- }
-}
-
-pub fn set_priority(
- context_lock: &Arc>,
- new_prio: usize,
- local_token: &mut CleanLockToken,
-) -> Result<(), String> {
- if new_prio >= 40 {
- return Err("Priority out of bounds".into());
- }
-
- let mut guard = context_lock.write(local_token.token());
- guard.prio = new_prio;
-
- Ok(())
-}
-
pub fn current() -> Arc {
PercpuBlock::current()
.switch_internals
@@ -249,10 +174,6 @@ pub fn spawn(
contexts_mut(token.token()).insert(context_ref);
- let run_ref = ContextRef(Arc::clone(&context_lock));
- run_contexts_mut(token.token()).set[20].push_back(run_ref);
- context_lock.write(token.token()).enqueued = true;
-
Ok(context_lock)
}
diff --git a/src/context/switch.rs b/src/context/switch.rs
index 6a614520c8..c3815bf200 100644
--- a/src/context/switch.rs
+++ b/src/context/switch.rs
@@ -8,16 +8,13 @@ use core::{
sync::atomic::Ordering,
};
-use alloc::{sync::Arc, vec::Vec};
+use alloc::sync::Arc;
use syscall::PtraceFlags;
use crate::{
- context::{
- self, arch, contexts, run_contexts_mut, ArcContextLockWriteGuard, Context, ContextLock,
- Status,
- },
+ context::{arch, contexts, ArcContextLockWriteGuard, Context, ContextLock},
cpu_set::LogicalCpuId,
- cpu_stats, log,
+ cpu_stats,
percpu::PercpuBlock,
sync::CleanLockToken,
};
@@ -29,13 +26,6 @@ enum UpdateResult {
Skip,
}
-// A simple geometric series where value[i] ~= value[i - 1] * 1.25
-const sched_prio_to_weight: [usize; 40] = [
- 88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904,
- 3906, 3121, 2501, 1991, 1586, 1277, 1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87,
- 70, 56, 45, 36, 29, 23, 18, 15,
-];
-
/// Determines if a given context is eligible to be scheduled on a given CPU (in
/// principle, the current CPU).
///
@@ -64,6 +54,15 @@ unsafe fn update_runnable(
return UpdateResult::Skip;
}
+ // If context is soft-blocked and has a wake-up time, check if it should wake up.
+ if context.status.is_soft_blocked()
+ && let Some(wake) = context.wake
+ && switch_time >= wake
+ {
+ context.wake = None;
+ context.unblock_no_ipi();
+ }
+
// If the context is runnable, indicate it can be switched to.
if context.status.is_runnable() {
UpdateResult::CanSwitch
@@ -125,8 +124,11 @@ pub enum SwitchResult {
AllContextsIdle,
}
-/// This function performs the context switch, using select_next_context to
-/// actually select the next context to switch to.
+/// Selects and switches to the next context using a round-robin scheduler.
+///
+/// This function performs the context switch, checking each context in a loop for eligibility
+/// until it finds a context ready to run. If no other context is runnable, it returns to the
+/// idle context.
///
/// # Warning
/// This is not memory-unsafe to call. But do NOT call this while holding locks!
@@ -155,42 +157,73 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
percpu.maybe_handle_tlb_shootdown();
}
- // Alarm (previously in update_runnable)
- // TODO: Optimise this somehow
- let mut wakeups = Vec::new();
- {
- let current_context = context::current();
- let contexts_guard = contexts(token.token());
- for context_ref in contexts_guard.iter().filter_map(|r| r.upgrade()) {
- if Arc::ptr_eq(&context_ref, ¤t_context) {
- continue;
- }
- let mut local_token = unsafe { CleanLockToken::new() };
- let guard = context_ref.read(local_token.token());
- if guard.status.is_soft_blocked() {
- if let Some(wake) = guard.wake {
- if switch_time >= wake {
- wakeups.push(Arc::clone(&context_ref));
- continue;
- }
- }
- }
-
- if guard.status.is_runnable() && !guard.enqueued && !guard.running {
- wakeups.push(Arc::clone(&context_ref));
- }
- }
- }
-
- for context_lock in wakeups {
- context::wakeup_context(&context_lock);
- }
-
let cpu_id = crate::cpu_id();
- let switch_context_opt = match select_next_context(token, percpu, cpu_id, switch_time) {
- Ok(opt) => opt,
- Err(early_ret) => return early_ret,
+ let mut switch_context_opt = None;
+ {
+ let contexts = contexts(token.token());
+
+ // Lock the previous context.
+ let prev_context_lock = crate::context::current();
+ // We are careful not to lock this context twice
+ let prev_context_guard = unsafe { prev_context_lock.write_arc() };
+
+ if !prev_context_guard.is_preemptable() {
+ // Unset global lock
+ arch::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
+
+ // Pretend to have finished switching, so CPU is not idled
+ return SwitchResult::Switched;
+ }
+
+ let idle_context = percpu.switch_internals.idle_context();
+
+ // Stateful flag used to skip the idle process the first time it shows up.
+ // After that, this flag is set to `false` so the idle process can be
+ // picked up.
+ let mut skip_idle = true;
+
+ // Attempt to locate the next context to switch to.
+ for next_context_lock in contexts
+ // Include all contexts with IDs greater than the current...
+ .range((
+ Bound::Excluded(ContextRef(Arc::clone(&prev_context_lock))),
+ Bound::Unbounded,
+ ))
+ // ... and all contexts with IDs less than the current...
+ .chain(contexts.range((
+ Bound::Unbounded,
+ Bound::Excluded(ContextRef(Arc::clone(&prev_context_lock))),
+ )))
+ .filter_map(ContextRef::upgrade)
+ // ... and the idle context...
+ .chain(Some(Arc::clone(&idle_context)))
+ // ... but not the current context (note the `Bound::Excluded`),
+ // which is already locked.
+ {
+ if Arc::ptr_eq(&next_context_lock, &idle_context) && skip_idle {
+ // Skip idle process the first time it shows up, but allow it
+ // to be picked up again the next time.
+ skip_idle = false;
+ continue;
+ }
+
+ {
+ // Lock next context
+ // We are careful not to lock this context twice
+ let mut next_context_guard = unsafe { next_context_lock.write_arc() };
+
+ // Check if the context is runnable and can be switched to.
+ if let UpdateResult::CanSwitch =
+ unsafe { update_runnable(&mut next_context_guard, cpu_id, switch_time) }
+ {
+ // Store locks for previous and next context and break out from loop
+ // for the switch
+ switch_context_opt = Some((prev_context_guard, next_context_guard));
+ break;
+ }
+ }
+ }
};
// Update per-cpu times
@@ -264,8 +297,8 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
prev_context.syscall_debug_info = percpu
.syscall_debug_info
.replace(next_context.syscall_debug_info);
- prev_context.syscall_debug_info.on_switch_from();
- next_context.syscall_debug_info.on_switch_to();
+ prev_context.syscall_debug_info.on_switch_from(token);
+ next_context.syscall_debug_info.on_switch_to(token);
}
percpu
@@ -295,133 +328,6 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
}
}
-/// This is the scheduler function which currently utilises Deficit Weighted Round Robin Scheduler
-fn select_next_context(
- token: &mut CleanLockToken,
- percpu: &PercpuBlock,
- cpu_id: LogicalCpuId,
- switch_time: u128,
-) -> Result