sched: formalize the passing of NULL to z_get_next_switch_handle()

This is an attempt at formally distinguishing and supporting the case
described in 40795 where an architecture doesn't preserve/restore the
complete thread state upon entering/exiting interrupt exception state.

This is mainly about promoting the current behavior from the accepted
workaround to a formal API specification. This workaround is currently
used on ARM64 but RISC-V requires it too.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
This commit is contained in:
Nicolas Pitre 2022-03-15 22:36:20 -04:00 committed by Anas Nashif
commit c9e3e0d956
2 changed files with 53 additions and 4 deletions

View file

@ -283,15 +283,33 @@ exactly two arguments. The first is an opaque (architecture defined)
handle to the context to which it should switch, and the second is a
pointer to such a handle into which it should store the handle
resulting from the thread that is being switched out.
The kernel then implements a portable :c:func:`z_swap` implementation on top
of this primitive which includes the relevant scheduler logic in a
location where the architecture doesn't need to understand it.
Similarly, on interrupt exit, switch-based architectures are expected
to call :c:func:`z_get_next_switch_handle` to retrieve the next thread to
run from the scheduler, passing in an "interrupted" handle reflecting
the same opaque type used by switch, which the kernel will then save
in the interrupted thread struct.
run from the scheduler. The argument to :c:func:`z_get_next_switch_handle`
is either the interrupted thread's "handle" reflecting the same opaque type
used by :c:func:`arch_switch`, or NULL if that thread cannot be released
to the scheduler just yet. The choice between a handle value or NULL
depends on the way CPU interrupt mode is implemented.
Architectures with a large CPU register file would typically preserve only
the caller-saved registers on the current thread's stack when interrupted
in order to minimize interrupt latency, and preserve the callee-saved
registers only when :c:func:`arch_switch` is called to minimize context
switching latency. Such architectures must use NULL as the argument to
:c:func:`z_get_next_switch_handle` to determine if there is a new thread
to schedule, and follow through with their own :c:func:`arch_switch` or
derrivative if so, or directly leave interrupt mode otherwise.
In the former case it is up to that switch code to store the handle
resulting from the thread that is being switched out in that thread's
"switch_handle" field after its context has fully been saved.
Architectures whose entry in interrupt mode already preserves the entire
thread state may pass that thread's handle directly to
:c:func:`z_get_next_switch_handle` and be done in one step.
Note that while SMP requires :kconfig:option:`CONFIG_USE_SWITCH`, the reverse is not
true. A uniprocessor architecture built with :kconfig:option:`CONFIG_SMP` set to No might

View file

@ -984,6 +984,37 @@ static inline void set_current(struct k_thread *new_thread)
_current_cpu->current = new_thread;
}
/**
* @brief Determine next thread to execute upon completion of an interrupt
*
* Thread preemption is performed by context switching after the completion
* of a non-recursed interrupt. This function determines which thread to
* switch to if any. This function accepts as @p interrupted either:
*
* - The handle for the interrupted thread in which case the thread's context
* must already be fully saved and ready to be picked up by a different CPU.
*
* - NULL if more work is required to fully save the thread's state after
* it is known that a new thread is to be scheduled. It is up to the caller
* to store the handle resulting from the thread that is being switched out
* in that thread's "switch_handle" field after its
* context has fully been saved, following the same requirements as with
* the @ref arch_switch() function.
*
* If a new thread needs to be scheduled then its handle is returned.
* Otherwise the same value provided as @p interrupted is returned back.
* Those handles are the same opaque types used by the @ref arch_switch()
* function.
*
* @warning
* The @ref _current value may have changed after this call and not refer
* to the interrupted thread anymore. It might be necessary to make a local
* copy before calling this function.
*
* @param interrupted Handle for the thread that was interrupted or NULL.
* @retval Handle for the next thread to execute, or @p interrupted when
* no new thread is to be scheduled.
*/
void *z_get_next_switch_handle(void *interrupted)
{
z_check_stack_sentinel();