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:
parent
0a9019f09a
commit
9416ef2684
1 changed files with 26 additions and 42 deletions
|
@ -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
|
||||||
*******************************
|
*******************************
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue