From: Steven Rostedtrostedt@goodmis.org CPU0 expires a posix-timer and runs the callback function. The signal is queued. After releasing the posix-timer lock and before returning to hrtimer_run_queue CPU0 gets interrupted. CPU1 delivers the queued signal and rearms the timer. CPU0 comes back to hrtimer_run_queue and sets the timer state to expired. The next modification of the timer can result in an oops, because the state information is wrong. Keep track of state = RUNNING and check if the state has been in the return path of hrtimer_run_queue. In case the state has been changed, ignore a restart request and do not touch the state variable. Signed-off-by: Steven Rostedt Signed-off-by: Thomas Gleixner Signed-off-by: Andrew Morton --- include/linux/hrtimer.h | 1 + kernel/hrtimer.c | 5 +++++ 2 files changed, 6 insertions(+) diff -puN include/linux/hrtimer.h~hrtimers-fix-posix-timer-requeue-race include/linux/hrtimer.h --- devel/include/linux/hrtimer.h~hrtimers-fix-posix-timer-requeue-race 2006-01-19 20:11:24.000000000 -0800 +++ devel-akpm/include/linux/hrtimer.h 2006-01-19 20:11:24.000000000 -0800 @@ -40,6 +40,7 @@ enum hrtimer_restart { enum hrtimer_state { HRTIMER_INACTIVE, /* Timer is inactive */ HRTIMER_EXPIRED, /* Timer is expired */ + HRTIMER_RUNNING, /* Timer is running the callback function */ HRTIMER_PENDING, /* Timer is pending */ }; diff -puN kernel/hrtimer.c~hrtimers-fix-posix-timer-requeue-race kernel/hrtimer.c --- devel/kernel/hrtimer.c~hrtimers-fix-posix-timer-requeue-race 2006-01-19 20:11:24.000000000 -0800 +++ devel-akpm/kernel/hrtimer.c 2006-01-19 20:11:24.000000000 -0800 @@ -550,6 +550,7 @@ static inline void run_hrtimer_queue(str fn = timer->function; data = timer->data; set_curr_timer(base, timer); + timer->state = HRTIMER_RUNNING; __remove_hrtimer(timer, base); spin_unlock_irq(&base->lock); @@ -565,6 +566,10 @@ static inline void run_hrtimer_queue(str spin_lock_irq(&base->lock); + /* Another CPU has added back the timer */ + if (timer->state != HRTIMER_RUNNING) + continue; + if (restart == HRTIMER_RESTART) enqueue_hrtimer(timer, base); else _