diff --git a/Cargo.lock b/Cargo.lock
index 71aac01a55..44f84c4740 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -40,9 +40,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.11.0"
+version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "cc"
@@ -108,7 +108,7 @@ version = "0.5.12"
dependencies = [
"arrayvec",
"bitfield",
- "bitflags 2.11.0",
+ "bitflags 2.11.1",
"cc",
"fdt",
"hashbrown 0.14.5",
@@ -201,18 +201,18 @@ checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7"
[[package]]
name = "redox_syscall"
-version = "0.7.3"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16"
+checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags 2.11.1",
]
[[package]]
name = "rmm"
version = "0.1.0"
dependencies = [
- "bitflags 2.11.0",
+ "bitflags 2.11.1",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index ca6924893f..0fb581f896 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,7 +19,7 @@ fdt = { git = "https://github.com/repnop/fdt.git", rev = "2fb1409edd1877c714a0aa
hashbrown = { version = "0.14.3", default-features = false, features = ["ahash", "inline-more"] }
linked_list_allocator = "0.9.0"
redox-path = "0.2.0"
-redox_syscall = { version = "=0.7.3", default-features = false }
+redox_syscall = { version = "0.7.4", default-features = false }
rmm = { path = "rmm", default-features = false }
slab = { version = "0.4", default-features = false }
smallvec = { version = "1.15.1", default-features = false }
diff --git a/src/context/context.rs b/src/context/context.rs
index d3cdc06e86..065c8ad957 100644
--- a/src/context/context.rs
+++ b/src/context/context.rs
@@ -13,6 +13,7 @@ use crate::{
context::{
self, arch,
file::{FileDescriptor, LockedFileDescription},
+ run_contexts_mut,
},
cpu_set::{LogicalCpuId, LogicalCpuSet},
cpu_stats,
@@ -138,6 +139,10 @@ 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,
@@ -195,6 +200,8 @@ 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 23455cd766..5f2e2d3326 100644
--- a/src/context/mod.rs
+++ b/src/context/mod.rs
@@ -2,7 +2,10 @@
//!
//! 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, sync::Arc};
+use alloc::{
+ collections::{BTreeSet, VecDeque},
+ sync::Arc,
+};
use core::num::NonZeroUsize;
use crate::{
@@ -70,7 +73,6 @@ pub use self::arch::empty_cr3;
// Set of weak references to all contexts available for scheduling. The only strong references are
// the context file descriptors.
static CONTEXTS: Mutex> = Mutex::new(BTreeSet::new());
-
/// Try to get the global free contexts
pub fn free_contexts_try(
token: LockToken<'_, L1>,
@@ -83,7 +85,23 @@ pub fn free_contexts(token: LockToken<'_, L1>) -> MutexGuard<'_, L2, BTreeSet = 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> {
let percpu = PercpuBlock::current();
percpu.contexts.read(token)
@@ -95,6 +113,14 @@ pub fn contexts_mut(token: LockToken<'_, L0>) -> RwLockWriteGuard<'_, L1, BTreeS
percpu.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");
@@ -109,6 +135,7 @@ 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));
@@ -123,6 +150,30 @@ pub fn init(token: &mut CleanLockToken) {
}
}
+pub fn wakeup_context(context_lock: &Arc>, mut token: LockToken) {
+ let mut prio = None;
+ {
+ let mut context = context_lock.write(token.token());
+
+ context.wake = None;
+ context.unblock();
+
+ if !(context.status.is_runnable() && !context.running && !context.enqueued) {
+ return;
+ }
+
+ context.enqueued = true;
+
+ prio = Some(context.prio);
+ }
+
+ let mut run_queues = run_contexts_mut(token);
+
+ if let Some(priority) = prio {
+ run_queues.set[priority].push_back(ContextRef(Arc::clone(context_lock)));
+ }
+}
+
pub fn current() -> Arc {
PercpuBlock::current()
.switch_internals
@@ -185,7 +236,10 @@ pub fn spawn(
let context_lock = Arc::new(ContextLock::new(context));
let context_ref = ContextRef(Arc::clone(&context_lock));
- free_contexts(token.token().downgrade()).insert(context_ref);
+ let run_ref = ContextRef(Arc::clone(&context_lock));
+ run_contexts_mut(token.token()).set[20].push_back(run_ref);
+ contexts_mut(token.token()).insert(context_ref);
+ context_lock.write(token.token()).enqueued = true;
Ok(context_lock)
}
diff --git a/src/context/switch.rs b/src/context/switch.rs
index 4460bcf827..86ccf6a727 100644
--- a/src/context/switch.rs
+++ b/src/context/switch.rs
@@ -1,26 +1,26 @@
//! This module provides a context-switching mechanism that utilizes a simple round-robin scheduler.
//! The scheduler iterates over available contexts, selecting the next context to run, while
//! handling process states and synchronization.
-use core::{
- cell::{Cell, RefCell},
- hint, mem,
- ops::Bound,
- sync::atomic::Ordering,
-};
-
-use alloc::sync::Arc;
-use syscall::PtraceFlags;
-
+use crate::sync::ArcRwLockWriteGuard;
+use crate::sync::L4;
use crate::{
context::{
- arch, contexts, contexts_mut, free_contexts_try, ArcContextLockWriteGuard, Context,
- ContextLock,
+ self, arch, contexts, contexts_mut, free_contexts_try, run_contexts_mut,
+ ArcContextLockWriteGuard, Context, ContextLock,
},
cpu_set::LogicalCpuId,
cpu_stats,
percpu::PercpuBlock,
sync::CleanLockToken,
};
+use alloc::{sync::Arc, vec::Vec};
+use core::{
+ cell::{Cell, RefCell},
+ hint, mem,
+ ops::Bound,
+ sync::atomic::Ordering,
+};
+use syscall::PtraceFlags;
use super::ContextRef;
@@ -29,6 +29,13 @@ 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).
///
@@ -127,11 +134,8 @@ pub enum SwitchResult {
AllContextsIdle,
}
-/// 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.
+/// This function performs the context switch, using select_next_context to
+/// actually select the next context to switch to.
///
/// # Warning
/// This is not memory-unsafe to call. But do NOT call this while holding locks!
@@ -175,43 +179,44 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
return SwitchResult::Switched;
}
- let mut switch_context_opt = None;
+ // Alarm (previously in update_runnable)
+ // TODO: Optimise this somehow
+ let mut wakeups = Vec::new();
{
- let contexts = contexts(token.token());
+ let current_context = context::current();
- // 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)
- // ... but not the current context (note the `Bound::Excluded`),
- // which is already locked.
- {
- {
- // 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(next_context_guard);
- break;
+ let mut contexts_guard = contexts(token.token());
+ let (context, mut token) = contexts_guard.token_split();
+ for context_ref in context.iter().filter_map(|r| r.upgrade()) {
+ if Arc::ptr_eq(&context_ref, ¤t_context) {
+ continue;
+ }
+ let guard = context_ref.read(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, token.token());
+ }
+
+ let cpu_id = crate::cpu_id();
+
+ let mut switch_context_opt =
+ match select_next_context(token, percpu, cpu_id, switch_time, &mut prev_context_guard) {
+ Ok(opt) => opt,
+ Err(early_ret) => return early_ret,
+ };
if switch_context_opt.is_none() {
let mut this_contexts = contexts_mut(token.token());
@@ -334,6 +339,130 @@ 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,
+ prev_context_guard: &mut ArcRwLockWriteGuard,
+) -> Result