--- 2.3.38/kernel/timer.c Tue Dec 14 15:48:51 1999 +++ /tmp/timer.c Wed Jan 12 00:47:21 2000 @@ -138,6 +138,15 @@ /* can happen if you add a timer with expires == jiffies, * or you set a timer to go off in the past */ + if ((signed long) idx < -50) + /* Nobody should set a timer so insanely in the past or + waiting so many timer interrupts between reading + jiffies and calling the timer code. The timer code + is completly robust against this condition but + a printk may let us know about bugs in the + caller we might not notice otherwise. */ + printk(KERN_WARNING + "timer inserted in the past, idx = %ld\n", idx); insert_timer(timer, tv1.vec, tv1.index); } else if (idx <= 0xffffffffUL) { int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; @@ -222,10 +231,31 @@ tv->index = (tv->index + 1) & TVN_MASK; } +/* defer current timers to the next pass */ +static void cascade_current_timers(void) +{ + struct timer_list * timer; + int index = tv1.index; + + timer = tv1.vec[index]; + tv1.index = (tv1.index + 1) & TVR_MASK; + + while (timer) + { + struct timer_list *tmp = timer; + timer = timer->next; + insert_timer(tmp, tv1.vec, tv1.index); + } + tv1.vec[index] = NULL; +} + static inline void run_timer_list(void) { + long passes; + spin_lock_irq(&timerlist_lock); - while ((long)(jiffies - timer_jiffies) >= 0) { + passes = jiffies - timer_jiffies; + while (passes-- >= 0) { struct timer_list *timer; if (!tv1.index) { int n = 1; @@ -233,17 +263,21 @@ cascade_timers(tvecs[n]); } while (tvecs[n]->index == 1 && ++n < NOOF_TVECS); } - while ((timer = tv1.vec[tv1.index])) { + timer = tv1.vec[tv1.index]; + tv1.vec[tv1.index] = 0; + while (timer) { void (*fn)(unsigned long) = timer->function; unsigned long data = timer->data; - detach_timer(timer); - timer->next = timer->prev = NULL; + struct timer_list * tmp = timer; + timer = timer->next; + detach_timer(tmp); + tmp->next = tmp->prev = NULL; spin_unlock_irq(&timerlist_lock); fn(data); spin_lock_irq(&timerlist_lock); } ++timer_jiffies; - tv1.index = (tv1.index + 1) & TVR_MASK; + cascade_current_timers(); } spin_unlock_irq(&timerlist_lock); }