zephyr/kernel/include/kswap.h
Andy Ross 9098a45c84 kernel: New timeslicing implementation
Instead of checking every time we hit the low-level context switch
path to see if the new thread has a "partner" with which it needs to
share time, just run the slice timer always and reset it from the
scheduler at the points where it has already decided a switch needs to
happen.  In TICKLESS_KERNEL situations, we pay the cost of extra timer
interrupts at ~10Hz or whatever, which is low (note also that this
kind of regular wakeup architecture is required on SMP anyway so the
scheduler can "notice" threads scheduled by other CPUs).  Advantages:

1. Much simpler logic.  Significantly smaller code.  No variance or
   dependence on tickless modes or timer driver (beyond setting a
   simple timeout).

2. No arch-specific assembly integration with _Swap() needed

3. Better performance on many workloads, as the accounting now happens
   at most once per timer interrupt (~5 Hz) and true rescheduling and
   not on every unrelated context switch and interrupt return.

4. It's SMP-safe.  The previous scheme kept the slice ticks as a
   global variable, which was an unnoticed bug.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2018-10-16 15:03:10 -04:00

106 lines
2.3 KiB
C

/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_KERNEL_INCLUDE_KSWAP_H_
#define ZEPHYR_KERNEL_INCLUDE_KSWAP_H_
#include <ksched.h>
#include <kernel_arch_func.h>
#ifdef CONFIG_STACK_SENTINEL
extern void _check_stack_sentinel(void);
#else
#define _check_stack_sentinel() /**/
#endif
/* In SMP, the irq_lock() is a spinlock which is implicitly released
* and reacquired on context switch to preserve the existing
* semantics. This means that whenever we are about to return to a
* thread (via either _Swap() or interrupt/exception return!) we need
* to restore the lock state to whatever the thread's counter
* expects.
*/
void _smp_reacquire_global_lock(struct k_thread *thread);
void _smp_release_global_lock(struct k_thread *thread);
/* 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 int _Swap(unsigned int key)
{
struct k_thread *new_thread, *old_thread;
int ret = 0;
#ifdef CONFIG_EXECUTION_BENCHMARKING
extern void read_timer_start_of_swap(void);
read_timer_start_of_swap();
#endif
old_thread = _current;
_check_stack_sentinel();
#ifdef CONFIG_TRACING
sys_trace_thread_switched_out();
#endif
new_thread = _get_next_ready_thread();
if (new_thread != old_thread) {
old_thread->swap_retval = -EAGAIN;
#ifdef CONFIG_SMP
_current_cpu->swap_ok = 0;
new_thread->base.cpu = _arch_curr_cpu()->id;
_smp_release_global_lock(new_thread);
#endif
_current = new_thread;
_arch_switch(new_thread->switch_handle,
&old_thread->switch_handle);
ret = _current->swap_retval;
}
#ifdef CONFIG_TRACING
sys_trace_thread_switched_in();
#endif
irq_unlock(key);
return ret;
}
#else /* !CONFIG_USE_SWITCH */
extern int __swap(unsigned int key);
static inline int _Swap(unsigned int key)
{
int ret;
_check_stack_sentinel();
#ifdef CONFIG_TRACING
sys_trace_thread_switched_out();
#endif
ret = __swap(key);
#ifdef CONFIG_TRACING
sys_trace_thread_switched_in();
#endif
return ret;
}
#endif
#endif /* ZEPHYR_KERNEL_INCLUDE_KSWAP_H_ */