Re: [PATCH sched_ext/for-7.1] sched_ext: Documentation: Add ops.dequeue() to task lifecycle

From: Kuba Piecuch

Date: Tue Apr 07 2026 - 05:54:34 EST


Hi Andrea,

On Mon Apr 6, 2026 at 11:47 AM UTC, Andrea Righi wrote:
> Document ops.dequeue() in the sched_ext task lifecycle now that its
> semantics are well-defined.
>
> Also update the pseudo-code to use task_is_runnable() consistently and
> clarify the case where ops.dispatch() does not refill the time slice.
>
> Fixes: ebf1ccff79c4 ("sched_ext: Fix ops.dequeue() semantics")
> Signed-off-by: Andrea Righi <arighi@xxxxxxxxxx>
> ---
> Documentation/scheduler/sched-ext.rst | 24 +++++++++++++++---------
> 1 file changed, 15 insertions(+), 9 deletions(-)
>
> diff --git a/Documentation/scheduler/sched-ext.rst b/Documentation/scheduler/sched-ext.rst
> index 404b4e4c33f7e..9f03650abfeba 100644
> --- a/Documentation/scheduler/sched-ext.rst
> +++ b/Documentation/scheduler/sched-ext.rst
> @@ -422,23 +422,29 @@ by a sched_ext scheduler:
>
> ops.runnable(); /* Task becomes ready to run */
>
> - while (task is runnable) {
> + while (task_is_runnable(task)) {
> if (task is not in a DSQ && task->scx.slice == 0) {
> ops.enqueue(); /* Task can be added to a DSQ */
>
> - /* Any usable CPU becomes available */
> + /* Task property change (i.e., affinity, nice, etc.)? */
> + if (sched_change(task)) {
> + ops.dequeue(); /* Exiting BPF scheduler custody */

Doesn't the task also go through quiescent -> runnable here? The full path
being dequeue -> quiescent -> (actual property change) -> runnable -> enqueue.

I guess we should be accurate here since quiescent and runnable are present
elsewhere in the pseudocode.

> + continue;
> + }
> + }
>
> - ops.dispatch(); /* Task is moved to a local DSQ */
> + /* Any usable CPU becomes available */
> +
> + ops.dispatch(); /* Task is moved to a local DSQ */

s/local/terminal/?

> + ops.dequeue(); /* Exiting BPF scheduler custody */
>
> - ops.dequeue(); /* Exiting BPF scheduler */
> - }
> ops.running(); /* Task starts running on its assigned CPU */
>
> - while task_is_runnable(p) {
> - while (task->scx.slice > 0 && task_is_runnable(p))
> - ops.tick(); /* Called every 1/HZ seconds */
> + while (task_is_runnable(task) && task->scx.slice > 0) {
> + ops.tick(); /* Called every 1/HZ seconds */
>
> - ops.dispatch(); /* task->scx.slice can be refilled */
> + if (task->scx.slice == 0)
> + ops.dispatch(); /* task->scx.slice can be refilled */
> }
>
> ops.stopping(); /* Task stops running (time slice expires or wait) */

Thanks,
Kuba