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
|
||||
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:
|
||||
|
||||
* Cooperative threads run at a higher priority than preemptible ones, and
|
||||
always preempt them.
|
||||
|
||||
* After handling an interrupt, if a cooperative thread was interrupted, the
|
||||
kernel always goes back to running that thread, since it is not preemptible.
|
||||
Zephyr provides two mutually exclusive interfaces for context switching. The
|
||||
preferred interface to use is :code:`arch_switch` which is selected when
|
||||
:kconfig:option:`CONFIG_USE_SWITCH` is enabled. The alternative interface is
|
||||
:code:`arch_swap`--selected when :kconfig:option:`CONFIG_USE_SWITCH`
|
||||
is disabled. When porting to a new architecture, only one of these needs to
|
||||
implemented; however, for SMP platforms it must be :code:`arch_switch`.
|
||||
|
||||
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,
|
||||
|
||||
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`.
|
||||
|
||||
|
@ -231,19 +226,17 @@ There are two types of context switches: :dfn:`cooperative` and :dfn:`preemptive
|
|||
running thread.
|
||||
|
||||
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
|
||||
:code:`z_swap` is called, the kernel logic knows that a context switch has to
|
||||
happen: :code:`z_swap` does not check to see if a context switch must happen.
|
||||
Rather, :code:`z_swap` decides what thread to context switch in.
|
||||
:code:`z_swap` is called by the kernel logic when an object being operated on
|
||||
is unavailable, and some thread yielding/sleeping primitives.
|
||||
internal kernel routine :code:`z_swap` (or one of its variants). This in turn
|
||||
will call either :code:`arch_switch` or :code:`arch_swap` as appropriate.
|
||||
When these are called, no checks are done to determine if the context switch is
|
||||
to happen--the context switch must happen.
|
||||
|
||||
.. note::
|
||||
|
||||
On x86 and Nios2, :code:`z_swap` is generic enough and the architecture
|
||||
flexible enough that :code:`z_swap` can be called when exiting an interrupt
|
||||
to provoke the context switch. This should not be taken as a rule, since
|
||||
neither the ARM Cortex-M or ARCv2 port do this.
|
||||
On x86 and Nios2, :code:`arch_swap` is generic enough and the architecture
|
||||
flexible enough that it can be called when exiting an interrupt to provoke
|
||||
the context switch. This should not be taken as a rule, since
|
||||
neither the ARM Cortex-M nor ARCv2 port do this.
|
||||
|
||||
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.
|
||||
|
@ -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:`_firq_exit` and :code:`_rirq_exit` on ARCv2.
|
||||
|
||||
In this case, the context switch must only be invoked when the interrupted
|
||||
thread was preemptible, not when it was a cooperative one, and only when the
|
||||
current interrupt is not nested.
|
||||
The decision logic to invoke the context switch is simple and is only performed
|
||||
when exiting a non-nested interrupt:
|
||||
|
||||
The kernel also has the concept of "locking the scheduler". This is a concept
|
||||
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.
|
||||
When :kconfig:option:`CONFIG_USE_SWITCH` is enabled ...
|
||||
|
||||
So, the decision logic to invoke the context switch when exiting an interrupt
|
||||
is simple:
|
||||
* The interrupt exit code shall call :c:func:`z_get_next_switch_handle`, and
|
||||
return to the thread context identified by the returned switch handle
|
||||
|
||||
* If the interrupted thread is not preemptible, do not invoke it.
|
||||
* Else, fetch the cached thread from the ready queue, and:
|
||||
When :kconfig:option:`CONFIG_USE_SWITCH` is not enabled ...
|
||||
|
||||
* 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.
|
||||
* Else, do not invoke it.
|
||||
* Otherwise do not invoke it.
|
||||
|
||||
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
|
||||
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
|
||||
*******************************
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue