doc: Update context switching for arch porting

Updates the context switching documentation for arch porting
to reflect the current state.

Signed-off-by: Peter Mitsis <peter.mitsis@intel.com>
This commit is contained in:
Peter Mitsis 2025-04-08 14:50:27 -07:00 committed by Benjamin Cabé
commit 9416ef2684

View file

@ -176,15 +176,16 @@ Thread Context Switching
************************ ************************
Multi-threading is the basic purpose to have a kernel at all. Zephyr supports Multi-threading is the basic purpose to have a kernel at all. Zephyr supports
two types of threads: preemptible and cooperative. two types of threads: preemptible and cooperative. The rules for determining
the next thread to schedule are handled by the kernel. However, it is up to
the architecture port to implement the method of the context switch itself.
Two crucial concepts when writing an architecture port are the following: Zephyr provides two mutually exclusive interfaces for context switching. The
preferred interface to use is :code:`arch_switch` which is selected when
* Cooperative threads run at a higher priority than preemptible ones, and :kconfig:option:`CONFIG_USE_SWITCH` is enabled. The alternative interface is
always preempt them. :code:`arch_swap`--selected when :kconfig:option:`CONFIG_USE_SWITCH`
is disabled. When porting to a new architecture, only one of these needs to
* After handling an interrupt, if a cooperative thread was interrupted, the implemented; however, for SMP platforms it must be :code:`arch_switch`.
kernel always goes back to running that thread, since it is not preemptible.
A context switch can happen in several circumstances: A context switch can happen in several circumstances:
@ -203,12 +204,6 @@ A context switch can happen in several circumstances:
threads. For example, referencing invalid memory, threads. For example, referencing invalid memory,
Therefore, the context switching must thus be able to handle all these cases. Therefore, the context switching must thus be able to handle all these cases.
Zephyr provides two mutually exclusive methods for context switching. The first
is the traditional method that uses :code:`arch_swap()`. The second (and
recommended) approach uses :code:`arch_switch()`.
The kernel keeps the next thread to run in a "cache", and thus the context
switching code only has to fetch from that cache to select which thread to run.
There are two types of context switches: :dfn:`cooperative` and :dfn:`preemptive`. There are two types of context switches: :dfn:`cooperative` and :dfn:`preemptive`.
@ -231,19 +226,17 @@ There are two types of context switches: :dfn:`cooperative` and :dfn:`preemptive
running thread. running thread.
A cooperative context switch is always done by having a thread call the A cooperative context switch is always done by having a thread call the
:code:`z_swap()` kernel internal symbol (or one of its variants). When internal kernel routine :code:`z_swap` (or one of its variants). This in turn
:code:`z_swap` is called, the kernel logic knows that a context switch has to will call either :code:`arch_switch` or :code:`arch_swap` as appropriate.
happen: :code:`z_swap` does not check to see if a context switch must happen. When these are called, no checks are done to determine if the context switch is
Rather, :code:`z_swap` decides what thread to context switch in. to happen--the context switch must happen.
:code:`z_swap` is called by the kernel logic when an object being operated on
is unavailable, and some thread yielding/sleeping primitives.
.. note:: .. note::
On x86 and Nios2, :code:`z_swap` is generic enough and the architecture On x86 and Nios2, :code:`arch_swap` is generic enough and the architecture
flexible enough that :code:`z_swap` can be called when exiting an interrupt flexible enough that it can be called when exiting an interrupt to provoke
to provoke the context switch. This should not be taken as a rule, since the context switch. This should not be taken as a rule, since
neither the ARM Cortex-M or ARCv2 port do this. neither the ARM Cortex-M nor ARCv2 port do this.
Since :code:`z_swap` is cooperative, the caller-saved registers from the ABI are Since :code:`z_swap` is cooperative, the caller-saved registers from the ABI are
already on the stack. There is no need to save them in the k_thread structure. already on the stack. There is no need to save them in the k_thread structure.
@ -255,34 +248,25 @@ an ISR, in the kernel interrupt exit stub:
* :code:`z_arm_exc_exit` and :code:`z_arm_int_exit` on ARM. * :code:`z_arm_exc_exit` and :code:`z_arm_int_exit` on ARM.
* :code:`_firq_exit` and :code:`_rirq_exit` on ARCv2. * :code:`_firq_exit` and :code:`_rirq_exit` on ARCv2.
In this case, the context switch must only be invoked when the interrupted The decision logic to invoke the context switch is simple and is only performed
thread was preemptible, not when it was a cooperative one, and only when the when exiting a non-nested interrupt:
current interrupt is not nested.
The kernel also has the concept of "locking the scheduler". This is a concept When :kconfig:option:`CONFIG_USE_SWITCH` is enabled ...
similar to locking the interrupts, but lighter-weight since interrupts can
still occur. If a thread has locked the scheduler, is it temporarily
non-preemptible.
So, the decision logic to invoke the context switch when exiting an interrupt * The interrupt exit code shall call :c:func:`z_get_next_switch_handle`, and
is simple: return to the thread context identified by the returned switch handle
* If the interrupted thread is not preemptible, do not invoke it. When :kconfig:option:`CONFIG_USE_SWITCH` is not enabled ...
* Else, fetch the cached thread from the ready queue, and:
* The interrupt exit code shall fetch the cached thread from the ready queue, and:
* If the cached thread is not the current thread, invoke the context switch. * If the cached thread is not the current thread, invoke the context switch.
* Else, do not invoke it. * Otherwise do not invoke it.
This is simple, but crucial: if this is not implemented correctly, the kernel This is simple, but crucial: if this is not implemented correctly, the kernel
will not function as intended and will experience bizarre crashes, mostly due will not function as intended and will experience bizarre crashes, mostly due
to stack corruption. to stack corruption.
.. note::
If running a coop-only system, i.e. if :kconfig:option:`CONFIG_NUM_PREEMPT_PRIORITIES`
is 0, no preemptive context switch ever happens. The interrupt code can be
optimized to not take any scheduling decision when this is the case.
Thread Creation and Termination Thread Creation and Termination
******************************* *******************************