Currently thread abort doesn't work if a thread is currently scheduled on a different CPU, because we have no way of delivering an interrupt to the other CPU to force the issue. This patch adds a simple framework for an architecture to provide such an IPI, implements it for x86_64, and uses it to implement a spin loop in abort for the case where a thread is currently scheduled elsewhere. On SMP architectures (xtensa) where no such IPI is implemented, we fall back to waiting on an arbitrary interrupt to occur. This "works" for typical code (and all current tests), but of course it cannot be guaranteed on such an architecture that k_thread_abort() will return in finite time (e.g. the other thread on the other CPU might have taken a spinlock and entered an infinite loop, so it will never receive an interrupt to terminate itself)! On non-SMP architectures this patch changes no code paths at all. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
297 lines
7.5 KiB
C
297 lines
7.5 KiB
C
/*
|
|
* Copyright (c) 2016-2017 Wind River Systems, Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifndef ZEPHYR_KERNEL_INCLUDE_KSCHED_H_
|
|
#define ZEPHYR_KERNEL_INCLUDE_KSCHED_H_
|
|
|
|
#include <kernel_structs.h>
|
|
#include <timeout_q.h>
|
|
#include <tracing.h>
|
|
#include <stdbool.h>
|
|
|
|
#ifdef CONFIG_MULTITHREADING
|
|
#define Z_VALID_PRIO(prio, entry_point) \
|
|
(((prio) == K_IDLE_PRIO && z_is_idle_thread(entry_point)) || \
|
|
(z_is_prio_higher_or_equal((prio), \
|
|
K_LOWEST_APPLICATION_THREAD_PRIO) && \
|
|
z_is_prio_lower_or_equal((prio), \
|
|
K_HIGHEST_APPLICATION_THREAD_PRIO)))
|
|
|
|
#define Z_ASSERT_VALID_PRIO(prio, entry_point) do { \
|
|
__ASSERT(Z_VALID_PRIO((prio), (entry_point)), \
|
|
"invalid priority (%d); allowed range: %d to %d", \
|
|
(prio), \
|
|
K_LOWEST_APPLICATION_THREAD_PRIO, \
|
|
K_HIGHEST_APPLICATION_THREAD_PRIO); \
|
|
} while (false)
|
|
#else
|
|
#define Z_VALID_PRIO(prio, entry_point) ((prio) == -1)
|
|
#define Z_ASSERT_VALID_PRIO(prio, entry_point) __ASSERT((prio) == -1, "")
|
|
#endif
|
|
|
|
void z_sched_init(void);
|
|
void z_add_thread_to_ready_q(struct k_thread *thread);
|
|
void z_move_thread_to_end_of_prio_q(struct k_thread *thread);
|
|
void z_remove_thread_from_ready_q(struct k_thread *thread);
|
|
int z_is_thread_time_slicing(struct k_thread *thread);
|
|
void z_unpend_thread_no_timeout(struct k_thread *thread);
|
|
int z_pend_curr(struct k_spinlock *lock, k_spinlock_key_t key,
|
|
_wait_q_t *wait_q, s32_t timeout);
|
|
int z_pend_curr_irqlock(u32_t key, _wait_q_t *wait_q, s32_t timeout);
|
|
void z_pend_thread(struct k_thread *thread, _wait_q_t *wait_q, s32_t timeout);
|
|
void z_reschedule(struct k_spinlock *lock, k_spinlock_key_t key);
|
|
void z_reschedule_irqlock(u32_t key);
|
|
struct k_thread *z_unpend_first_thread(_wait_q_t *wait_q);
|
|
void z_unpend_thread(struct k_thread *thread);
|
|
int z_unpend_all(_wait_q_t *wait_q);
|
|
void z_thread_priority_set(struct k_thread *thread, int prio);
|
|
void *z_get_next_switch_handle(void *interrupted);
|
|
struct k_thread *z_find_first_thread_to_unpend(_wait_q_t *wait_q,
|
|
struct k_thread *from);
|
|
void idle(void *a, void *b, void *c);
|
|
void z_time_slice(int ticks);
|
|
void z_sched_abort(struct k_thread *thread);
|
|
void z_sched_ipi(void);
|
|
|
|
static inline void z_pend_curr_unlocked(_wait_q_t *wait_q, s32_t timeout)
|
|
{
|
|
(void) z_pend_curr_irqlock(z_arch_irq_lock(), wait_q, timeout);
|
|
}
|
|
|
|
static inline void z_reschedule_unlocked(void)
|
|
{
|
|
(void) z_reschedule_irqlock(z_arch_irq_lock());
|
|
}
|
|
|
|
/* find which one is the next thread to run */
|
|
/* must be called with interrupts locked */
|
|
#ifdef CONFIG_SMP
|
|
extern struct k_thread *z_get_next_ready_thread(void);
|
|
#else
|
|
static ALWAYS_INLINE struct k_thread *z_get_next_ready_thread(void)
|
|
{
|
|
return _kernel.ready_q.cache;
|
|
}
|
|
#endif
|
|
|
|
static inline bool z_is_idle_thread(void *entry_point)
|
|
{
|
|
return entry_point == idle;
|
|
}
|
|
|
|
static inline bool z_is_thread_pending(struct k_thread *thread)
|
|
{
|
|
return !!(thread->base.thread_state & _THREAD_PENDING);
|
|
}
|
|
|
|
static inline int z_is_thread_prevented_from_running(struct k_thread *thread)
|
|
{
|
|
u8_t state = thread->base.thread_state;
|
|
|
|
return state & (_THREAD_PENDING | _THREAD_PRESTART | _THREAD_DEAD |
|
|
_THREAD_DUMMY | _THREAD_SUSPENDED);
|
|
|
|
}
|
|
|
|
static inline bool z_is_thread_timeout_active(struct k_thread *thread)
|
|
{
|
|
return !z_is_inactive_timeout(&thread->base.timeout);
|
|
}
|
|
|
|
static inline bool z_is_thread_ready(struct k_thread *thread)
|
|
{
|
|
return !((z_is_thread_prevented_from_running(thread)) != 0 ||
|
|
z_is_thread_timeout_active(thread));
|
|
}
|
|
|
|
static inline bool z_has_thread_started(struct k_thread *thread)
|
|
{
|
|
return (thread->base.thread_state & _THREAD_PRESTART) == 0;
|
|
}
|
|
|
|
static inline bool z_is_thread_state_set(struct k_thread *thread, u32_t state)
|
|
{
|
|
return !!(thread->base.thread_state & state);
|
|
}
|
|
|
|
static inline bool z_is_thread_queued(struct k_thread *thread)
|
|
{
|
|
return z_is_thread_state_set(thread, _THREAD_QUEUED);
|
|
}
|
|
|
|
static inline void z_mark_thread_as_suspended(struct k_thread *thread)
|
|
{
|
|
thread->base.thread_state |= _THREAD_SUSPENDED;
|
|
}
|
|
|
|
static inline void z_mark_thread_as_not_suspended(struct k_thread *thread)
|
|
{
|
|
thread->base.thread_state &= ~_THREAD_SUSPENDED;
|
|
}
|
|
|
|
static inline void z_mark_thread_as_started(struct k_thread *thread)
|
|
{
|
|
thread->base.thread_state &= ~_THREAD_PRESTART;
|
|
}
|
|
|
|
static inline void z_mark_thread_as_pending(struct k_thread *thread)
|
|
{
|
|
thread->base.thread_state |= _THREAD_PENDING;
|
|
}
|
|
|
|
static inline void z_mark_thread_as_not_pending(struct k_thread *thread)
|
|
{
|
|
thread->base.thread_state &= ~_THREAD_PENDING;
|
|
}
|
|
|
|
static inline void z_set_thread_states(struct k_thread *thread, u32_t states)
|
|
{
|
|
thread->base.thread_state |= states;
|
|
}
|
|
|
|
static inline void z_reset_thread_states(struct k_thread *thread,
|
|
u32_t states)
|
|
{
|
|
thread->base.thread_state &= ~states;
|
|
}
|
|
|
|
static inline void z_mark_thread_as_queued(struct k_thread *thread)
|
|
{
|
|
z_set_thread_states(thread, _THREAD_QUEUED);
|
|
}
|
|
|
|
static inline void z_mark_thread_as_not_queued(struct k_thread *thread)
|
|
{
|
|
z_reset_thread_states(thread, _THREAD_QUEUED);
|
|
}
|
|
|
|
static inline bool z_is_under_prio_ceiling(int prio)
|
|
{
|
|
return prio >= CONFIG_PRIORITY_CEILING;
|
|
}
|
|
|
|
static inline int z_get_new_prio_with_ceiling(int prio)
|
|
{
|
|
return z_is_under_prio_ceiling(prio) ? prio : CONFIG_PRIORITY_CEILING;
|
|
}
|
|
|
|
static inline bool z_is_prio1_higher_than_or_equal_to_prio2(int prio1, int prio2)
|
|
{
|
|
return prio1 <= prio2;
|
|
}
|
|
|
|
static inline bool z_is_prio_higher_or_equal(int prio1, int prio2)
|
|
{
|
|
return z_is_prio1_higher_than_or_equal_to_prio2(prio1, prio2);
|
|
}
|
|
|
|
static inline bool z_is_prio1_lower_than_or_equal_to_prio2(int prio1, int prio2)
|
|
{
|
|
return prio1 >= prio2;
|
|
}
|
|
|
|
static inline bool z_is_prio1_higher_than_prio2(int prio1, int prio2)
|
|
{
|
|
return prio1 < prio2;
|
|
}
|
|
|
|
static inline bool z_is_prio_higher(int prio, int test_prio)
|
|
{
|
|
return z_is_prio1_higher_than_prio2(prio, test_prio);
|
|
}
|
|
|
|
static inline bool z_is_prio_lower_or_equal(int prio1, int prio2)
|
|
{
|
|
return z_is_prio1_lower_than_or_equal_to_prio2(prio1, prio2);
|
|
}
|
|
|
|
bool z_is_t1_higher_prio_than_t2(struct k_thread *t1, struct k_thread *t2);
|
|
|
|
static inline bool _is_valid_prio(int prio, void *entry_point)
|
|
{
|
|
if (prio == K_IDLE_PRIO && z_is_idle_thread(entry_point)) {
|
|
return true;
|
|
}
|
|
|
|
if (!z_is_prio_higher_or_equal(prio,
|
|
K_LOWEST_APPLICATION_THREAD_PRIO)) {
|
|
return false;
|
|
}
|
|
|
|
if (!z_is_prio_lower_or_equal(prio,
|
|
K_HIGHEST_APPLICATION_THREAD_PRIO)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static ALWAYS_INLINE void z_ready_thread(struct k_thread *thread)
|
|
{
|
|
if (z_is_thread_ready(thread)) {
|
|
z_add_thread_to_ready_q(thread);
|
|
}
|
|
|
|
sys_trace_thread_ready(thread);
|
|
}
|
|
|
|
static inline void _ready_one_thread(_wait_q_t *wq)
|
|
{
|
|
struct k_thread *th = z_unpend_first_thread(wq);
|
|
|
|
if (th != NULL) {
|
|
z_ready_thread(th);
|
|
}
|
|
}
|
|
|
|
static inline void z_sched_lock(void)
|
|
{
|
|
#ifdef CONFIG_PREEMPT_ENABLED
|
|
__ASSERT(!z_is_in_isr(), "");
|
|
__ASSERT(_current->base.sched_locked != 1, "");
|
|
|
|
--_current->base.sched_locked;
|
|
|
|
compiler_barrier();
|
|
|
|
K_DEBUG("scheduler locked (%p:%d)\n",
|
|
_current, _current->base.sched_locked);
|
|
#endif
|
|
}
|
|
|
|
static ALWAYS_INLINE void z_sched_unlock_no_reschedule(void)
|
|
{
|
|
#ifdef CONFIG_PREEMPT_ENABLED
|
|
__ASSERT(!z_is_in_isr(), "");
|
|
__ASSERT(_current->base.sched_locked != 0, "");
|
|
|
|
compiler_barrier();
|
|
|
|
++_current->base.sched_locked;
|
|
#endif
|
|
}
|
|
|
|
static ALWAYS_INLINE bool z_is_thread_timeout_expired(struct k_thread *thread)
|
|
{
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
return thread->base.timeout.dticks == _EXPIRED;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static inline struct k_thread *z_unpend1_no_timeout(_wait_q_t *wait_q)
|
|
{
|
|
struct k_thread *thread = z_find_first_thread_to_unpend(wait_q, NULL);
|
|
|
|
if (thread != NULL) {
|
|
z_unpend_thread_no_timeout(thread);
|
|
}
|
|
|
|
return thread;
|
|
}
|
|
|
|
#endif /* ZEPHYR_KERNEL_INCLUDE_KSCHED_H_ */
|