Implement a workaround for a concurency issue on SMP systems:

A thread that just missed a semaphore and put itself into the sems notify
queue could be enqueued into the run queue by release_sem_etc() of another
CPU before the CPU running the thread had a chance to reschedule it. Therefore
there is a timeframe where a thread can be running on one CPU and already be
in the run queue again. In this case no other CPU may schedule this thread
because then it would overwrite the threads' CPU pointer which kills the rest
of the scheduler logic, smp_get_current_cpu() and everything that depends on
that (like the kernel debugger). The more CPUs you have the easier this could
happen, up to the point where it was always triggered during boot on my quad
core system. The system would freeze and you could not enter the kernel
debugger, because two CPUs thought they were the same and disabled each other
through SMP communication. This makes booting my system stable and might fix
the occasional hang on boot for other SMP systems with only 2 CPUs/cores.
I've put a ToDo comment that details this above the workaround. Maybe we
should fix this in another way. Reviews, comments and suggestions welcome ;-)

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23721 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2008-01-24 15:14:10 +00:00
parent 06b5aa85b5
commit bc1dc61522
+45 -23
View File
@@ -183,37 +183,59 @@ scheduler_reschedule(void)
nextThread = nextThread->queue_next;
}
} else {
// select next thread from the run queue
while (nextThread && nextThread->priority > B_IDLE_PRIORITY) {
while (nextThread) {
// select next thread from the run queue
while (nextThread && nextThread->priority > B_IDLE_PRIORITY) {
#if 0
if (oldThread == nextThread && nextThread->was_yielded) {
// ignore threads that called thread_yield() once
nextThread->was_yielded = false;
prevThread = nextThread;
nextThread = nextThread->queue_next;
}
if (oldThread == nextThread && nextThread->was_yielded) {
// ignore threads that called thread_yield() once
nextThread->was_yielded = false;
prevThread = nextThread;
nextThread = nextThread->queue_next;
}
#endif
// always extract real time threads
if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY)
break;
// always extract real time threads
if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY)
break;
// never skip last non-idle normal thread
if (nextThread->queue_next && nextThread->queue_next->priority == B_IDLE_PRIORITY)
break;
// never skip last non-idle normal thread
if (nextThread->queue_next && nextThread->queue_next->priority == B_IDLE_PRIORITY)
break;
// skip normal threads sometimes (roughly 20%)
if (_rand() > 0x1a00)
break;
// skip normal threads sometimes (roughly 20%)
if (_rand() > 0x1a00)
break;
// skip until next lower priority
int32 priority = nextThread->priority;
do {
// skip until next lower priority
int32 priority = nextThread->priority;
do {
prevThread = nextThread;
nextThread = nextThread->queue_next;
} while (nextThread->queue_next != NULL
&& priority == nextThread->queue_next->priority
&& nextThread->queue_next->priority > B_IDLE_PRIORITY);
}
if (nextThread->cpu
&& nextThread->cpu->cpu_num != oldThread->cpu->cpu_num) {
// ToDo: This thread is still running on another CPU. The
// thread just missed a semaphore, put itself into the notify
// queue but was not yet rescheduled. During this time frame
// release_sem_etc() was called for said semaphore and put the
// thread into the run queue to notify it.
// Therefore it is now _still_ running on one CPU and _already_
// part of the run queue again. We have to skip this thread
// here because otherwise we would overwrite the thread->cpu
// pointer with the current CPU which would make both CPUs
// "think" they are the same one and kill off the scheduler
// logic in here as well as all calls to smp_get_current_cpu().
prevThread = nextThread;
nextThread = nextThread->queue_next;
} while (nextThread->queue_next != NULL
&& priority == nextThread->queue_next->priority
&& nextThread->queue_next->priority > B_IDLE_PRIORITY);
continue;
}
break;
}
}