From 042d8ecca9cab72446f801f5859b737e5c36297d Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Sat, 9 Dec 2017 08:37:20 -0800 Subject: [PATCH] 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 --- arch/xtensa/include/kernel_arch_data.h | 4 +++ include/kernel.h | 12 +++++++ kernel/Kconfig | 12 +++++++ kernel/include/kernel_internal.h | 50 +++++++++++++++++++++----- 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/arch/xtensa/include/kernel_arch_data.h b/arch/xtensa/include/kernel_arch_data.h index 048e6fca4af..6575de205a0 100644 --- a/arch/xtensa/include/kernel_arch_data.h +++ b/arch/xtensa/include/kernel_arch_data.h @@ -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) diff --git a/include/kernel.h b/include/kernel.h index b4f185ce777..9716a09d55f 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -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; }; diff --git a/kernel/Kconfig b/kernel/Kconfig index c0508432864..c4ddef7d2c3 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -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" diff --git a/kernel/include/kernel_internal.h b/kernel/include/kernel_internal.h index 8dd12a655f6..6f679d62f1d 100644 --- a/kernel/include/kernel_internal.h +++ b/kernel/include/kernel_internal.h @@ -16,6 +16,7 @@ #include #include +#include #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 /**