Subject: Add mod_timer_noact() Introduce mod_timer_noact() which for example is to replace the calls to del_timer()/add_timer() in __nf_ct_refresh_acct(). It works like mod_timer() but doesn't activate or modify the timeout of an inactive timer which is the behaviour we want in order to be able to use timers as a means of synchronization in nf_conntrack. A later patch will modify __nf_ct_refresh_acct() to use mod_timer_noact() which will then save one spin_lock_irqsave() / spin_lock_irqrestore() pair per conntrack timer update. This will also get rid of the race we currently have without adding more locking in nf_conntrack. Signed-off-by: Martin Josefsson --- include/linux/timer.h | 8 ++++++-- kernel/time/clocksource.c | 3 ++- kernel/timer.c | 40 +++++++++++++++++++++++++++++++++++----- 3 files changed, 43 insertions(+), 8 deletions(-) Index: linux-2.6.23-rc6.quilt/include/linux/timer.h =================================================================== --- linux-2.6.23-rc6.quilt.orig/include/linux/timer.h 2007-09-11 17:21:44.000000000 +0200 +++ linux-2.6.23-rc6.quilt/include/linux/timer.h 2007-09-11 17:21:49.000000000 +0200 @@ -24,6 +24,9 @@ struct timer_list { extern struct tvec_t_base_s boot_tvec_bases; +#define TIMER_ACT 1 +#define TIMER_NOACT 0 + #define TIMER_INITIALIZER(_function, _expires, _data) { \ .function = (_function), \ .expires = (_expires), \ @@ -64,8 +67,9 @@ static inline int timer_pending(const st extern void add_timer_on(struct timer_list *timer, int cpu); extern int del_timer(struct timer_list * timer); -extern int __mod_timer(struct timer_list *timer, unsigned long expires); +extern int __mod_timer(struct timer_list *timer, unsigned long expires, int activate); extern int mod_timer(struct timer_list *timer, unsigned long expires); +extern int mod_timer_noact(struct timer_list *timer, unsigned long expires); /* * The jiffies value which is added to now, when there is no timer @@ -143,7 +147,7 @@ extern void delayed_work_timer_fn(unsign static inline void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); - __mod_timer(timer, timer->expires); + __mod_timer(timer, timer->expires, TIMER_ACT); } #ifdef CONFIG_SMP Index: linux-2.6.23-rc6.quilt/kernel/timer.c =================================================================== --- linux-2.6.23-rc6.quilt.orig/kernel/timer.c 2007-09-11 17:21:44.000000000 +0200 +++ linux-2.6.23-rc6.quilt/kernel/timer.c 2007-09-11 17:21:49.000000000 +0200 @@ -390,7 +390,7 @@ static tvec_base_t *lock_timer_base(stru } } -int __mod_timer(struct timer_list *timer, unsigned long expires) +int __mod_timer(struct timer_list *timer, unsigned long expires, int activate) { tvec_base_t *base, *new_base; unsigned long flags; @@ -404,7 +404,8 @@ int __mod_timer(struct timer_list *timer if (timer_pending(timer)) { detach_timer(timer, 0); ret = 1; - } + } else if (activate == TIMER_NOACT) + goto out_unlock; new_base = __get_cpu_var(tvec_bases); @@ -428,8 +429,9 @@ int __mod_timer(struct timer_list *timer timer->expires = expires; internal_add_timer(base, timer); - spin_unlock_irqrestore(&base->lock, flags); +out_unlock: + spin_unlock_irqrestore(&base->lock, flags); return ret; } @@ -489,11 +491,39 @@ int mod_timer(struct timer_list *timer, if (timer->expires == expires && timer_pending(timer)) return 1; - return __mod_timer(timer, expires); + return __mod_timer(timer, expires, TIMER_ACT); } EXPORT_SYMBOL(mod_timer); +/*** + * mod_timer_noact - modify a timer's timeout + * @timer: the timer to be modified + * + * mod_timer_noact works like mod_timer except that it doesn't activate an + * inactive timer, instead it returns without updating timer->expires. + * + * The function returns whether it has modified a pending timer or not. + * (ie. mod_timer_noact() of an inactive timer returns 0, mod_timer_noact() of + * an active timer returns 1.) + */ +int mod_timer_noact(struct timer_list *timer, unsigned long expires) +{ + BUG_ON(!timer->function); + + /* + * This is a common optimization triggered by the + * networking code - if the timer is re-modified + * to be the same thing then just return: + */ + if (timer->expires == expires && timer_pending(timer)) + return 1; + + return __mod_timer(timer, expires, TIMER_NOACT); +} + +EXPORT_SYMBOL(mod_timer_noact); + /** * del_timer - deactive a timer. * @timer: the timer to be deactivated @@ -1067,7 +1097,7 @@ fastcall signed long __sched schedule_ti expire = timeout + jiffies; setup_timer(&timer, process_timeout, (unsigned long)current); - __mod_timer(&timer, expire); + __mod_timer(&timer, expire, TIMER_ACT); schedule(); del_singleshot_timer_sync(&timer); Index: linux-2.6.23-rc6.quilt/kernel/time/clocksource.c =================================================================== --- linux-2.6.23-rc6.quilt.orig/kernel/time/clocksource.c 2007-09-11 17:22:13.000000000 +0200 +++ linux-2.6.23-rc6.quilt/kernel/time/clocksource.c 2007-09-11 17:22:49.000000000 +0200 @@ -143,7 +143,8 @@ static void clocksource_watchdog(unsigne if (!list_empty(&watchdog_list)) { __mod_timer(&watchdog_timer, - watchdog_timer.expires + WATCHDOG_INTERVAL); + watchdog_timer.expires + WATCHDOG_INTERVAL, + TIMER_ACT); } spin_unlock(&watchdog_lock); }