kernel/sched: Make z_ready_thread() safe vs. already-running threads

This is part of the scheduler API, and was always just a synchronized
wrapper around the internal ready_thread() function.  But where the
internal users seem to be careful not to call it on threads that are
not known to be already queued or running, the general users in the
IPC code seem to be less strict.

Add a simple test to detect the case where a thread is already
running.  Right now this just loops over the array of CPUs, so is O(N)
in the CPU count even though N is never more than four for us
currently.  But this is possible without modifying data structures.  A
more scalable way to do this if we ever need to run on very parallel
systems would be to use another state bit for RUNNING, or to keep a
backpointer in the thread struct to the CPU it's running on, etc...

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
Andy Ross 2021-02-19 15:24:24 -08:00 committed by Anas Nashif
commit 05c468f594

View file

@ -455,6 +455,25 @@ static void update_cache(int preempt_ok)
#endif
}
static bool thread_active_elsewhere(struct k_thread *thread)
{
/* True if the thread is currently running on another CPU.
* There are more scalable designs to answer this question in
* constant time, but this is fine for now.
*/
#ifdef CONFIG_SMP
int currcpu = _current_cpu->id;
for (int i = 0; i < CONFIG_MP_NUM_CPUS; i++) {
if ((i != currcpu) &&
(_kernel.cpus[i].current == thread)) {
return true;
}
}
#endif
return false;
}
static void ready_thread(struct k_thread *thread)
{
#ifdef CONFIG_KERNEL_COHERENCE
@ -477,7 +496,9 @@ static void ready_thread(struct k_thread *thread)
void z_ready_thread(struct k_thread *thread)
{
LOCKED(&sched_spinlock) {
ready_thread(thread);
if (!thread_active_elsewhere(thread)) {
ready_thread(thread);
}
}
}