kernel: arch: Clarify output switch handle requirements in arch_switch

The original intent was that the output handle be written through the
pointer in the second argument, though not all architectures used that
scheme.  As it turns out, that write is becoming a synchronization
signal, so it's no longer optional.

Clarify the documentation in arch_switch() about this requirement, and
add an instruction to the x86_64 context switch to implement it as
original envisioned.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
Andy Ross 2020-01-16 09:49:12 -08:00 committed by Andrew Boie
commit 86430d8d46
2 changed files with 32 additions and 6 deletions

View file

@ -220,6 +220,13 @@ z_x86_switch:
movq $X86_KERNEL_CS, _thread_offset_to_cs(%rsi)
movq $X86_KERNEL_DS, _thread_offset_to_ss(%rsi)
#endif
/* Store the handle (i.e. our thread struct address) into the
* switch handle field, this is a synchronization signal that
* must occur after the last data from the old context is
* saved.
*/
movq %rsi, ___thread_t_switch_handle_OFFSET(%rsi)
movq %gs:__x86_tss64_t_ist1_OFFSET, %rsp
/* fall through to __resume */

View file

@ -89,12 +89,31 @@ void arch_new_thread(struct k_thread *thread, k_thread_stack_t *pStack,
* 3) Set the stack pointer to the value provided in switch_to
* 4) Pop off all thread state from the stack we switched to and return.
*
* Some arches may implement thread->switch handle as a pointer to the thread
* itself, and save context somewhere in thread->arch. In this case, on initial
* context switch from the dummy thread, thread->switch handle for the outgoing
* thread is NULL. Instead of dereferencing switched_from all the way to get
* the thread pointer, subtract ___thread_t_switch_handle_OFFSET to obtain the
* thread pointer instead.
* Some arches may implement thread->switch handle as a pointer to the
* thread itself, and save context somewhere in thread->arch. In this
* case, on initial context switch from the dummy thread,
* thread->switch handle for the outgoing thread is NULL. Instead of
* dereferencing switched_from all the way to get the thread pointer,
* subtract ___thread_t_switch_handle_OFFSET to obtain the thread
* pointer instead. That is, such a scheme would have behavior like
* (in C pseudocode):
*
* void arch_switch(void *switch_to, void **switched_from)
* {
* struct k_thread *new = switch_to;
* struct k_thread *old = CONTAINER_OF(switched_from, struct k_thread,
* switch_handle);
*
* // save old context...
* *switched_from = old;
* // restore new context...
* }
*
* Note that, regardless of the underlying handle representation, the
* incoming switched_from pointer MUST be written through with a
* non-NULL value after all relevant thread state has been saved. The
* kernel uses this as a synchronization signal to be able to wait for
* switch completion from another CPU.
*
* @param switch_to Incoming thread's switch handle
* @param switched_from Pointer to outgoing thread's switch handle storage