kernel: Add alternative _arch_switch context switch primitive

The existing __swap() mechanism is too high level for some
applications because of its scheduler-awareness.  This introduces a
new _arch_switch() mechanism, which is a simpler primitive that looks
like:

    void _arch_switch(void *handle, void **old_handle_out);

The new thread handle (typically just a stack pointer) is specified
explicitly instead of being picked up from the scheduler by
per-architecture code, and on return the "old" thread handle that got
switched out is returned through the pointer.

The new primitive (currently available only on xtensa) is selected
when CONFIG_USE_SWITCH is "y".  A new C _Swap() implementation based
on this primitive is then added which operates compatibly.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
Andy Ross 2017-12-09 08:37:20 -08:00 committed by Anas Nashif
commit 042d8ecca9
4 changed files with 69 additions and 9 deletions

View file

@ -54,6 +54,10 @@ typedef struct _kernel_arch _kernel_arch_t;
#endif /*! _ASMLANGUAGE && ! __ASSEMBLER__ */
#ifdef CONFIG_USE_SWITCH
void xtensa_switch(void *switch_to, void **switched_from);
#define _arch_switch xtensa_switch
#endif
/* stacks */
#define STACK_ROUND_UP(x) ROUND_UP(x, STACK_ALIGN_SIZE)

View file

@ -469,6 +469,18 @@ struct k_thread {
k_thread_stack_t *stack_obj;
#endif /* CONFIG_USERSPACE */
#if defined(CONFIG_USE_SWITCH)
/* When using __switch() a few previously arch-specific items
* become part of the core OS
*/
/* _Swap() return value */
int swap_retval;
/* Context handle returned via _arch_switch() */
void *switch_handle;
#endif
/* arch-specifics: must always be at the end */
struct _thread_arch arch;
};

View file

@ -549,6 +549,18 @@ config MAX_DOMAIN_PARTITIONS
help
Configure the maximum number of partitions per memory domain.
config USE_SWITCH
bool
prompt "Use new-style _arch_switch instead of __swap"
default n
help
The _arch_switch() API is a lower level context switching
primitive than the original __swap mechanism. It is required
for an SMP-aware scheduler, or if the architecture does not
provide __swap. In uniprocess situations where the
architecture provides both, _arch_switch incurs more somewhat
overhead and may be slower.
source "kernel/Kconfig.event_logger"
source "kernel/Kconfig.power_mgmt"

View file

@ -16,6 +16,7 @@
#include <kernel.h>
#include <kernel_structs.h>
#include <ksched.h>
#ifndef _ASMLANGUAGE
@ -51,30 +52,61 @@ extern void _setup_new_thread(struct k_thread *new_thread,
void *p1, void *p2, void *p3,
int prio, u32_t options);
/* context switching and scheduling-related routines */
extern unsigned int __swap(unsigned int key);
#ifdef CONFIG_TIMESLICING
extern void _update_time_slice_before_swap(void);
#else
#define _update_time_slice_before_swap() /**/
#endif
#ifdef CONFIG_STACK_SENTINEL
extern void _check_stack_sentinel(void);
#else
#define _check_stack_sentinel() /**/
#endif
/* context switching and scheduling-related routines */
#ifdef CONFIG_USE_SWITCH
/* New style context switching. _arch_switch() is a lower level
* primitive that doesn't know about the scheduler or return value.
* Needed for SMP, where the scheduler requires spinlocking that we
* don't want to have to do in per-architecture assembly.
*/
static inline unsigned int _Swap(unsigned int key)
{
struct k_thread *new_thread, *old_thread;
old_thread = _kernel.current;
_check_stack_sentinel();
_update_time_slice_before_swap();
new_thread = _get_next_ready_thread();
old_thread->swap_retval = -EAGAIN;
_kernel.current = new_thread;
_arch_switch(new_thread->switch_handle,
&old_thread->switch_handle);
irq_unlock(key);
return _kernel.current->swap_retval;
}
#else /* !CONFIG_USE_SWITCH */
extern unsigned int __swap(unsigned int key);
static inline unsigned int _Swap(unsigned int key)
{
#ifdef CONFIG_STACK_SENTINEL
_check_stack_sentinel();
#endif
#ifdef CONFIG_TIMESLICING
_update_time_slice_before_swap();
#endif
return __swap(key);
}
#endif
#ifdef CONFIG_USERSPACE
/**