From ecdfd360d1ebce68a1ed00cf6c163a9d229e2c00 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 16 Jan 2026 20:10:32 +0000 Subject: [PATCH] sched: remove `force_resched()` This function is used to force the scheduler to reschedule another task, avoiding the fast-path exit. Given the sheer number of task state change points in the code, the fast-path exit code has become too brittle. Instead, check that the current task's state is still `Running` before taking the fast-path short-circuit. The cost of one spinlock uncontended lock-unlock cycle is worth the cost of avoiding many subtle scheduling logic bugs. --- src/sched/mod.rs | 21 ++++++--------------- src/sched/uspc_ret.rs | 18 +++--------------- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/sched/mod.rs b/src/sched/mod.rs index a88580d..2dccefc 100644 --- a/src/sched/mod.rs +++ b/src/sched/mod.rs @@ -76,12 +76,6 @@ fn schedule() { SCHED_STATE.borrow_mut().do_schedule(); } -/// Set the force resched task for this CPU. This ensures that the next time -/// schedule() is called a full run of the schduling algorithm will occur. -fn force_resched() { - SCHED_STATE.borrow_mut().force_resched = true; -} - pub fn spawn_kernel_work(fut: impl Future + 'static + Send) { current_task().ctx.put_kernel_work(Box::pin(fut)); } @@ -282,15 +276,12 @@ impl SchedState { needs_resched = true; } - if !needs_resched { - // Fast Path: Only return if we have a valid task, it has budget, - // AND it's not the idle task. - // - // Ensure that, in a debug build, we are only taking the fast-path - // on a *running* task. - if let Some(current) = self.run_q.current_mut() { - debug_assert_eq!(*current.state.lock_save_irq(), TaskState::Running); - } + if !needs_resched + && let Some(current) = self.run_q.current() + && matches!(*current.state.lock_save_irq(), TaskState::Running) + { + // Fast Path: Only return if we have a valid task (Running state), + // it has budget, AND it's not the idle task. return; } diff --git a/src/sched/uspc_ret.rs b/src/sched/uspc_ret.rs index 6866d28..1a7424b 100644 --- a/src/sched/uspc_ret.rs +++ b/src/sched/uspc_ret.rs @@ -1,4 +1,4 @@ -use super::{current::current_task, force_resched, schedule, waker::create_waker}; +use super::{current::current_task, schedule, waker::create_waker}; use crate::{ arch::{Arch, ArchImpl}, process::{ @@ -128,7 +128,6 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) { // task. // Task is currently running or is runnable and will now sleep. TaskState::Running | TaskState::Runnable => { - force_resched(); *task_state = TaskState::Sleeping; } // If we were woken between the future returning @@ -142,9 +141,7 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) { // If the task finished concurrently while we were // polling its signal work, let the scheduler // pick another task; no further work to do here. - TaskState::Finished => { - force_resched(); - } + TaskState::Finished => {} // We should never get here for any other state. s => { unreachable!( @@ -178,9 +175,6 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) { // find another task to execute, removing this task // from the runqueue, reaping it's resouces. if task.state.lock_save_irq().is_finished() { - // Ensure we don't take the fast-path sched exit - // for a finished task. - force_resched(); state = State::PickNewTask; continue; } @@ -207,7 +201,6 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) { match *task_state { // Task is runnable or running, put it to sleep. TaskState::Running | TaskState::Runnable => { - force_resched(); *task_state = TaskState::Sleeping } // If we were woken between the future returning @@ -221,9 +214,7 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) { // Task finished concurrently while we were trying // to put it to sleep; just reschedule and let // teardown handle it. - TaskState::Finished => { - force_resched(); - } + TaskState::Finished => {} // We should never get here for any other state. s => { unreachable!( @@ -255,8 +246,6 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) { ptrace.set_waker(create_waker(task.descriptor())); *task.state.lock_save_irq() = TaskState::Stopped; - force_resched(); - state = State::PickNewTask; continue 'dispatch; } @@ -299,7 +288,6 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) { } } - force_resched(); state = State::PickNewTask; continue 'dispatch; }