Re: [RFC PATCH 9/9] rv: Add srs per-task monitor

Juri Lelli
2025-04-10T08:53:40Z

Hi Gabriele,

On 04/04/25 10:45, Gabriele Monaco wrote:
> Add a per-task monitor for task switches as part of the sched model:
>
> * srs:
> Monitor to describe conditions for different types of task switch.
> This monitor enforces rules such as preempt after setting need
> for reschedule and suspend after setting a task to sleepable.
>
> This new monitor implies the previously introduced snroc (set non
> runnable on its own context), so replace that monitor with srs.
>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
> ---

...

> +Monitor srs
> +-----------
> +
> +The switch after resched or sleep (srs) monitor describes conditions for
> +different types of task switch. This is a complex model, below we are going to

Quite the ASCII art indeed. :-)

> +explain it step by step. Unfortunately splitting this into smaller monitor is
> +not trivial due to some shared events such as ``switch_in``::

Not splitting, but maybe providing several separate diagrams for the key
cases and transitions might help grasping the complete picture? Not
sure, just a thought.

In the below, set_{sleepable,runnable} corresponds to set_state_
{sleepable,runnable} in the code, does it? Not a big deal, but I was
confused at first.

Thanks,
Juri

> +
> + set_runnable
> + | wakeup +---+
> + | switch_vain | |
> + v | v wakeup
> + #================================================# set_runnable
> + switch_in H H <----------+
> + +------------------> H running H |
> + | H H -----+ |
> + | #================================================# | |
> + | | | | ^ ^ | |
> + | | switch_yield need_resched | | | |
> + | | | need_resched_lazy | | | |
> + | set_sleepable v | | | | |
> + | | +-------------+ | | | | |
> + | +--------+----> | preempted | ---+- switch_in | | |
> + | | | +-------------+ | | | |
> + | switch_preempt | | | | | |
> + | switch_yield | need_resched | +- switch_vain | |
> + | | | v | | | |
> + | | | +-------------+ | | | |
> + | need_resched -+--------+----> | resched_out | | | | |
> + | | | | +-------------+ | | | |
> + | | | | | | | need_resched | |
> + | | | | switch_in | | wakeup | |
> + | | | | v v | set_runnable | |
> + | | | | +--------------------------+ -------+ | |
> + | | | | | | | | |
> + | | +--------+----- | rescheduling | <------+ | |
> + | | | | | | |
> + | | | +--------------------------+ -----------+ | |
> + | | | | ^ wakeup | | |
> + | | | set_sleepable set_runnable | | |
> + | | | v | | | |
> + | | +------------------+----- +---------------------------+ | | |
> + | | | | | | | | |
> + +--+--+---+------------------+----> | resched_sleepable | ---+ | | |
> + | | | | | | | | | | |
> + | | | | +-------------+----> +---------------------------+ | | | |
> + | | | | | | | ^ | | | | |
> + | | | | | | switch_preempt | need_resched | | | |
> + | | | | | | | | set_sleepable | | | |
> + | | | | | | v +------+ | | | |
> + | | | | | | +---------------------------+ --+------+---+-----+--+
> + | | | | | | | preempted_sleepable | | | | | |
> + | | | | | | +---------------------------+ --+------+---+--+ | |
> + | | | | | | | ^ | | | | | |
> + | | | | | | switch_in switch_preempt | | | | | |
> + | | | | | | v | switch_vain | | | | |
> + | | | | | | +-------------------------+ | | | | | |
> + | | | | | +------> | | <--+ | | | | |
> + | | | | | | sleepable | | | | | |
> + | | | | +- need_resched------- | | ----------+---+--+--+ |
> + | | | | need_resched_lazy +-------------------------+ | | | |
> + | | | | | ^ | switch_block | | |
> + | | | | | | set_sleepable | | | |
> + | | | | switch_block | switch_vain +----------+ | | |
> + | | | | switch_suspend +------+ | | | |
> + | | | | v v | | |
> + | | | | switch_block +-----------------------------+ switch_block | |
> + | | | +-switch_suspend--------> | sleeping | <-----------+ | |
> + | | | +-----------------------------+ | |
> + | | | | wakeup | |
> + | | | v | |
> + | | +- need_resched ------------- +-------------+ wakeup | |
> + | | | waking | <------------------------------+ |
> + | +------------------------------- +-------------+ |
> + | |
> + | +-----------------------+ |
> + +----- switch_in -------- | resched_out_sleepable | <-- sched_need_resched --------------+
> + +-----------------------+
> +
> +Types of switches:
> +
> +* ``switch_in``:
> + a non running task is scheduled in, this leads to ``running`` if the task is
> + runnable and ``sleepable`` if the task was preempted before sleeping.
> +* ``switch_suspend``:
> + a task puts itself to sleep, this can happen only after explicitly setting
> + the task to ``sleepable``. After a task is suspended, it needs to be woken up
> + (``waking`` state) before being switched in again. The task can be set to
> + ``resched_sleepable`` via a ``need_resched`` but not preempted, in which case it
> + is equivalent to ``sleepable``.
> + Setting the task's state to ``sleepable`` can be reverted before switching if it
> + is woken up or set to runnable.
> +* ``switch_blocked``:
> + a special case of a ``switch_suspend`` where the task is waiting on a
> + sleeping RT lock (``PREEMPT_RT`` only), it is common to see wakeup and set
> + state events racing with each other and this leads the model to perceive this
> + type of switch when the task is not set to sleepable. This is a limitation of
> + the model in SMP system and workarounds may require to slow down the
> + scheduler.
> +* ``switch_yield``:
> + a task explicitly calls the scheduler, this looks like a preemption as the
> + task is still runnable but the ``need_resched`` flag is not set. It can
> + happen after a ``yield`` system call or from the idle task.
> +* ``switch_preempt``:
> + a task is ``preempted``, this can happen after the need for ``rescheduling``
> + has been set, also in its ``lazy`` flavour. ``need_resched`` can be set as a
> + flag to the task or in the per-core preemption count, either of them can
> + trigger a preemption.
> + The task was previously running and can be switched in directly, but it is
> + possible that a task is preempted after it sets itself as ``sleepable``
> + (``preempted_sleepable``), in this condition, once the task is switched back
> + in, it will not be ``running`` but continue its sleeping process in
> + ``sleepable``.
> +* ``switch_vain``:
> + a task goes through the scheduler but it is picked as the next task to run,
> + hence no real task switch occurs. Since we run the scheduler, this clears the
> + need to reschedule.
> +

...

> +enum events_srs {
> + sched_need_resched_srs = 0,
> + sched_need_resched_lazy_srs,
> + sched_set_state_runnable_srs,
> + sched_set_state_sleepable_srs,
> + sched_switch_blocking_srs,
> + sched_switch_in_srs,
> + sched_switch_preempt_srs,
> + sched_switch_suspend_srs,
> + sched_switch_vain_srs,
> + sched_switch_yield_srs,
> + sched_wakeup_srs,
> + event_max_srs
> +};
> +