| /* |
| * Copyright (c) 2018 Intel Corporation |
| * Copyright The Zephyr Project Contributors |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief MetaIRQ scheduling helpers (CONFIG_NUM_METAIRQ_PRIORITIES) |
| * |
| * MetaIRQ threads are a special class of cooperative threads that run at higher |
| * priority than any other thread and must be returned to after preemption. |
| * This header encapsulates all of that bookkeeping so that sched.c itself |
| * stays focused on run-queue management. |
| */ |
| |
| #ifndef ZEPHYR_KERNEL_INCLUDE_METAIRQ_H_ |
| #define ZEPHYR_KERNEL_INCLUDE_METAIRQ_H_ |
| |
| #include <zephyr/kernel.h> |
| #include <kthread.h> |
| |
| BUILD_ASSERT(CONFIG_NUM_COOP_PRIORITIES >= CONFIG_NUM_METAIRQ_PRIORITIES, |
| "You need to provide at least as many CONFIG_NUM_COOP_PRIORITIES as " |
| "CONFIG_NUM_METAIRQ_PRIORITIES as Meta IRQs are just a special class of cooperative " |
| "threads."); |
| |
| /** |
| * @brief Update the per-CPU metairq preemption record. |
| * |
| * Must be called (with the scheduler spinlock held) whenever a new thread is |
| * selected to run. Records which cooperative thread a metairq preempted so |
| * that next_up() can return to it once the metairq finishes. |
| * |
| * @param thread The thread about to become _current. |
| */ |
| static inline void update_metairq_preempt(struct k_thread *thread) |
| { |
| #if (CONFIG_NUM_METAIRQ_PRIORITIES > 0) |
| if (thread_is_metairq(thread) && !thread_is_metairq(_current) && |
| !thread_is_preemptible(_current)) { |
| /* Record new preemption */ |
| _current_cpu->metairq_preempted = _current; |
| } else if (!thread_is_metairq(thread)) { |
| /* Returning from existing preemption */ |
| _current_cpu->metairq_preempted = NULL; |
| } |
| #else |
| ARG_UNUSED(thread); |
| #endif /* CONFIG_NUM_METAIRQ_PRIORITIES > 0 */ |
| } |
| |
| /** |
| * @brief Recover a preempted cooperative thread after a metairq completes. |
| * |
| * After the run-queue winner (@p thread) has been chosen, check whether a |
| * cooperative thread was preempted by a metairq and should be resumed instead. |
| * Returns the adjusted thread pointer (unchanged when metairq logic is |
| * disabled or no recovery is needed). |
| * |
| * @param thread Best candidate chosen from the run queue. |
| * @return Thread to run next. |
| */ |
| static inline struct k_thread *metairq_preempt_recover(struct k_thread *thread) |
| { |
| #if (CONFIG_NUM_METAIRQ_PRIORITIES > 0) |
| /* MetaIRQs must always attempt to return back to a cooperative thread |
| * they preempted and not whatever happens to be highest priority now. |
| * The cooperative thread was promised it wouldn't be preempted (by |
| * non-metairq threads)! |
| */ |
| struct k_thread *mirqp = _current_cpu->metairq_preempted; |
| |
| if (mirqp != NULL && (thread == NULL || !thread_is_metairq(thread))) { |
| if (z_is_thread_ready(mirqp)) { |
| return mirqp; |
| } |
| _current_cpu->metairq_preempted = NULL; |
| } |
| #else |
| ARG_UNUSED(thread); |
| #endif /* CONFIG_NUM_METAIRQ_PRIORITIES > 0 */ |
| return thread; |
| } |
| |
| /** |
| * @brief Return true if _current may be re-queued after being context-switched. |
| * |
| * When a metairq thread preempts a cooperative thread, the preempted thread |
| * must not be put back on the run queue until the metairq completes; |
| * otherwise a lower-priority thread might steal it. |
| * |
| * @return true if it is safe to re-queue _current, false otherwise. |
| */ |
| static inline bool metairq_current_requeue_allowed(void) |
| { |
| #if (CONFIG_NUM_METAIRQ_PRIORITIES > 0) |
| return _current != _current_cpu->metairq_preempted; |
| #else |
| return true; |
| #endif /* CONFIG_NUM_METAIRQ_PRIORITIES > 0 */ |
| } |
| |
| /** |
| * @brief Clear the metairq preemption record for a thread being halted. |
| * |
| * If @p thread is the cooperative thread recorded as having been preempted by |
| * a metairq, clear that record so that the stale pointer is not used after the |
| * thread is suspended or aborted. |
| * |
| * @param thread Thread that is being halted. |
| */ |
| static inline void z_metairq_preempted_clear(struct k_thread *thread) |
| { |
| #if (CONFIG_NUM_METAIRQ_PRIORITIES > 0) |
| unsigned int cpu_id = 0; |
| |
| #if defined(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1) |
| cpu_id = thread->base.cpu; |
| #endif |
| if (_kernel.cpus[cpu_id].metairq_preempted == thread) { |
| _kernel.cpus[cpu_id].metairq_preempted = NULL; |
| } |
| #else |
| ARG_UNUSED(thread); |
| #endif /* CONFIG_NUM_METAIRQ_PRIORITIES > 0 */ |
| } |
| |
| #endif /* ZEPHYR_KERNEL_INCLUDE_METAIRQ_H_ */ |