kernel: Remove B_TIMER_ACQUIRE_SCHEDULER_LOCK flag
The flag main purpose is to avoid race conditions between event handler and cancel_timer(). However, cancel_timer() is safe even without using gSchedulerLock. If the event is scheduled to happen on other CPU than the CPU that invokes cancel_timer() then cancel_timer() either disables the event before its handler starts executing or waits until the event handler is done. If the event is scheduled on the same CPU that calls cancel_timer() then, since cancel_timer() disables interrupts, the event is either executed before cancel_timer() or when the timer interrupt handler starts running the event is already disabled.
This commit is contained in:
parent
c8dd9f7780
commit
d8fcc8a825
|
@ -23,13 +23,8 @@ struct kernel_args;
|
||||||
#define B_TIMER_USE_TIMER_STRUCT_TIMES 0x4000
|
#define B_TIMER_USE_TIMER_STRUCT_TIMES 0x4000
|
||||||
// For add_timer(): Use the timer::schedule_time (absolute time) and
|
// For add_timer(): Use the timer::schedule_time (absolute time) and
|
||||||
// timer::period values instead of the period parameter.
|
// timer::period values instead of the period parameter.
|
||||||
#define B_TIMER_ACQUIRE_SCHEDULER_LOCK 0x8000
|
|
||||||
// The timer hook is invoked with the scheduler lock held. When invoking
|
|
||||||
// cancel_timer() with the scheduler lock held, too, this helps to avoid
|
|
||||||
// race conditions.
|
|
||||||
#define B_TIMER_FLAGS \
|
#define B_TIMER_FLAGS \
|
||||||
(B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK \
|
(B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_REAL_TIME_BASE)
|
||||||
| B_TIMER_REAL_TIME_BASE)
|
|
||||||
|
|
||||||
/* Timer info structure */
|
/* Timer info structure */
|
||||||
struct timer_info {
|
struct timer_info {
|
||||||
|
|
|
@ -191,6 +191,7 @@ UserTimer::Cancel()
|
||||||
/*static*/ int32
|
/*static*/ int32
|
||||||
UserTimer::HandleTimerHook(struct timer* timer)
|
UserTimer::HandleTimerHook(struct timer* timer)
|
||||||
{
|
{
|
||||||
|
InterruptsSpinLocker _(gSchedulerLock);
|
||||||
((UserTimer*)timer->user_data)->HandleTimer();
|
((UserTimer*)timer->user_data)->HandleTimer();
|
||||||
return B_HANDLED_INTERRUPT;
|
return B_HANDLED_INTERRUPT;
|
||||||
}
|
}
|
||||||
|
@ -352,9 +353,7 @@ SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now,
|
||||||
CheckPeriodicOverrun(now);
|
CheckPeriodicOverrun(now);
|
||||||
|
|
||||||
uint32 timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER
|
uint32 timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER
|
||||||
| B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK;
|
| B_TIMER_USE_TIMER_STRUCT_TIMES;
|
||||||
// We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions
|
|
||||||
// between setting/canceling the timer and the event handler.
|
|
||||||
|
|
||||||
fTimer.schedule_time = std::max(fNextTime, (bigtime_t)0);
|
fTimer.schedule_time = std::max(fNextTime, (bigtime_t)0);
|
||||||
fTimer.period = 0;
|
fTimer.period = 0;
|
||||||
|
@ -692,10 +691,7 @@ TeamTimeUserTimer::_Update(bool unscheduling)
|
||||||
// rounding errors.
|
// rounding errors.
|
||||||
|
|
||||||
add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time,
|
add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time,
|
||||||
B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES
|
B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES);
|
||||||
| B_TIMER_ACQUIRE_SCHEDULER_LOCK);
|
|
||||||
// We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions
|
|
||||||
// between setting/canceling the timer and the event handler.
|
|
||||||
// We use B_TIMER_USE_TIMER_STRUCT_TIMES, so period remains 0, which
|
// We use B_TIMER_USE_TIMER_STRUCT_TIMES, so period remains 0, which
|
||||||
// our base class expects.
|
// our base class expects.
|
||||||
|
|
||||||
|
@ -989,11 +985,7 @@ ThreadTimeUserTimer::Start()
|
||||||
fTimer.schedule_time = 0;
|
fTimer.schedule_time = 0;
|
||||||
fTimer.period = 0;
|
fTimer.period = 0;
|
||||||
|
|
||||||
uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER
|
uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES;
|
||||||
| B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK;
|
|
||||||
// We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions
|
|
||||||
// between setting/canceling the timer and the event handler.
|
|
||||||
|
|
||||||
add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags);
|
add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags);
|
||||||
|
|
||||||
fScheduled = true;
|
fScheduled = true;
|
||||||
|
|
|
@ -1507,7 +1507,7 @@ _scheduler_reschedule(void)
|
||||||
if (!thread_is_idle_thread(nextThread)) {
|
if (!thread_is_idle_thread(nextThread)) {
|
||||||
bigtime_t quantum = compute_quantum(oldThread);
|
bigtime_t quantum = compute_quantum(oldThread);
|
||||||
add_timer(quantumTimer, &reschedule_event, quantum,
|
add_timer(quantumTimer, &reschedule_event, quantum,
|
||||||
B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_SCHEDULER_LOCK);
|
B_ONE_SHOT_RELATIVE_TIMER);
|
||||||
|
|
||||||
update_cpu_performance(nextThread, thisCore);
|
update_cpu_performance(nextThread, thisCore);
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -1923,13 +1923,8 @@ thread_exit(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (team != kernelTeam) {
|
if (team != kernelTeam) {
|
||||||
// Cancel previously installed alarm timer, if any. Hold the scheduler
|
// Cancel previously installed alarm timer, if any.
|
||||||
// lock to make sure that when cancel_timer() returns, the alarm timer
|
|
||||||
// hook will not be invoked anymore (since
|
|
||||||
// B_TIMER_ACQUIRE_SCHEDULER_LOCK is used).
|
|
||||||
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
|
|
||||||
cancel_timer(&thread->alarm);
|
cancel_timer(&thread->alarm);
|
||||||
schedulerLocker.Unlock();
|
|
||||||
|
|
||||||
// Delete all user timers associated with the thread.
|
// Delete all user timers associated with the thread.
|
||||||
ThreadLocker threadLocker(thread);
|
ThreadLocker threadLocker(thread);
|
||||||
|
@ -2782,12 +2777,8 @@ thread_preboot_init_percpu(struct kernel_args *args, int32 cpuNum)
|
||||||
static status_t
|
static status_t
|
||||||
thread_block_timeout(timer* timer)
|
thread_block_timeout(timer* timer)
|
||||||
{
|
{
|
||||||
// The timer has been installed with B_TIMER_ACQUIRE_SCHEDULER_LOCK, so
|
|
||||||
// we're holding the scheduler lock already. This makes things comfortably
|
|
||||||
// easy.
|
|
||||||
|
|
||||||
Thread* thread = (Thread*)timer->user_data;
|
Thread* thread = (Thread*)timer->user_data;
|
||||||
thread_unblock_locked(thread, B_TIMED_OUT);
|
thread_unblock(thread, B_TIMED_OUT);
|
||||||
|
|
||||||
return B_HANDLED_INTERRUPT;
|
return B_HANDLED_INTERRUPT;
|
||||||
}
|
}
|
||||||
|
@ -2858,10 +2849,7 @@ thread_block_with_timeout_locked(uint32 timeoutFlags, bigtime_t timeout)
|
||||||
&& timeout != B_INFINITE_TIMEOUT;
|
&& timeout != B_INFINITE_TIMEOUT;
|
||||||
|
|
||||||
if (useTimer) {
|
if (useTimer) {
|
||||||
// Timer flags: absolute/relative + "acquire thread lock". The latter
|
// Timer flags: absolute/relative.
|
||||||
// avoids nasty race conditions and deadlock problems that could
|
|
||||||
// otherwise occur between our cancel_timer() and a concurrently
|
|
||||||
// executing thread_block_timeout().
|
|
||||||
uint32 timerFlags;
|
uint32 timerFlags;
|
||||||
if ((timeoutFlags & B_RELATIVE_TIMEOUT) != 0) {
|
if ((timeoutFlags & B_RELATIVE_TIMEOUT) != 0) {
|
||||||
timerFlags = B_ONE_SHOT_RELATIVE_TIMER;
|
timerFlags = B_ONE_SHOT_RELATIVE_TIMER;
|
||||||
|
@ -2870,7 +2858,6 @@ thread_block_with_timeout_locked(uint32 timeoutFlags, bigtime_t timeout)
|
||||||
if ((timeoutFlags & B_TIMEOUT_REAL_TIME_BASE) != 0)
|
if ((timeoutFlags & B_TIMEOUT_REAL_TIME_BASE) != 0)
|
||||||
timerFlags |= B_TIMER_REAL_TIME_BASE;
|
timerFlags |= B_TIMER_REAL_TIME_BASE;
|
||||||
}
|
}
|
||||||
timerFlags |= B_TIMER_ACQUIRE_SCHEDULER_LOCK;
|
|
||||||
|
|
||||||
// install the timer
|
// install the timer
|
||||||
thread->wait.unblock_timer.user_data = thread;
|
thread->wait.unblock_timer.user_data = thread;
|
||||||
|
|
|
@ -274,25 +274,8 @@ timer_interrupt()
|
||||||
// call the callback
|
// call the callback
|
||||||
// note: if the event is not periodic, it is ok
|
// note: if the event is not periodic, it is ok
|
||||||
// to delete the event structure inside the callback
|
// to delete the event structure inside the callback
|
||||||
if (event->hook) {
|
if (event->hook)
|
||||||
bool callHook = true;
|
rc = event->hook(event);
|
||||||
|
|
||||||
// we may need to acquire the scheduler lock
|
|
||||||
if ((mode & B_TIMER_ACQUIRE_SCHEDULER_LOCK) != 0) {
|
|
||||||
acquire_spinlock(&gSchedulerLock);
|
|
||||||
|
|
||||||
// If the event has been cancelled in the meantime, we don't
|
|
||||||
// call the hook anymore.
|
|
||||||
if (cpuData.current_event == NULL)
|
|
||||||
callHook = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callHook)
|
|
||||||
rc = event->hook(event);
|
|
||||||
|
|
||||||
if ((mode & B_TIMER_ACQUIRE_SCHEDULER_LOCK) != 0)
|
|
||||||
release_spinlock(&gSchedulerLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuData.current_event_in_progress = 0;
|
cpuData.current_event_in_progress = 0;
|
||||||
|
|
||||||
|
@ -461,8 +444,7 @@ cancel_timer(timer* event)
|
||||||
// lock to be held while calling the event hook, we'll have to wait
|
// lock to be held while calling the event hook, we'll have to wait
|
||||||
// for the hook to complete. When called from the timer hook we don't
|
// for the hook to complete. When called from the timer hook we don't
|
||||||
// wait either, of course.
|
// wait either, of course.
|
||||||
if ((event->flags & B_TIMER_ACQUIRE_SCHEDULER_LOCK) == 0
|
if (cpu != smp_get_current_cpu()) {
|
||||||
&& cpu != smp_get_current_cpu()) {
|
|
||||||
spinLocker.Unlock();
|
spinLocker.Unlock();
|
||||||
|
|
||||||
while (cpuData.current_event_in_progress == 1) {
|
while (cpuData.current_event_in_progress == 1) {
|
||||||
|
|
Loading…
Reference in New Issue