diff options
Diffstat (limited to 'kernel/timer.c')
| -rw-r--r-- | kernel/timer.c | 37 | 
1 files changed, 34 insertions, 3 deletions
diff --git a/kernel/timer.c b/kernel/timer.c index 87f656cc2a5..fd6198692b5 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -404,6 +404,11 @@ static void timer_stats_account_timer(struct timer_list *timer) {}  static struct debug_obj_descr timer_debug_descr; +static void *timer_debug_hint(void *addr) +{ +	return ((struct timer_list *) addr)->function; +} +  /*   * fixup_init is called when:   * - an active object is initialized @@ -477,6 +482,7 @@ static int timer_fixup_free(void *addr, enum debug_obj_state state)  static struct debug_obj_descr timer_debug_descr = {  	.name		= "timer_list", +	.debug_hint	= timer_debug_hint,  	.fixup_init	= timer_fixup_init,  	.fixup_activate	= timer_fixup_activate,  	.fixup_free	= timer_fixup_free, @@ -959,20 +965,45 @@ EXPORT_SYMBOL(try_to_del_timer_sync);   *   * Synchronization rules: Callers must prevent restarting of the timer,   * otherwise this function is meaningless. It must not be called from - * hardirq contexts. The caller must not hold locks which would prevent + * interrupt contexts. The caller must not hold locks which would prevent   * completion of the timer's handler. The timer's handler must not call   * add_timer_on(). Upon exit the timer is not queued and the handler is   * not running on any CPU.   * + * Note: You must not hold locks that are held in interrupt context + *   while calling this function. Even if the lock has nothing to do + *   with the timer in question.  Here's why: + * + *    CPU0                             CPU1 + *    ----                             ---- + *                                   <SOFTIRQ> + *                                   call_timer_fn(); + *                                     base->running_timer = mytimer; + *  spin_lock_irq(somelock); + *                                     <IRQ> + *                                        spin_lock(somelock); + *  del_timer_sync(mytimer); + *   while (base->running_timer == mytimer); + * + * Now del_timer_sync() will never return and never release somelock. + * The interrupt on the other CPU is waiting to grab somelock but + * it has interrupted the softirq that CPU0 is waiting to finish. + *   * The function returns whether it has deactivated a pending timer or not.   */  int del_timer_sync(struct timer_list *timer)  {  #ifdef CONFIG_LOCKDEP -	local_bh_disable(); +	unsigned long flags; + +	/* +	 * If lockdep gives a backtrace here, please reference +	 * the synchronization rules above. +	 */ +	local_irq_save(flags);  	lock_map_acquire(&timer->lockdep_map);  	lock_map_release(&timer->lockdep_map); -	local_bh_enable(); +	local_irq_restore(flags);  #endif  	/*  	 * don't use it in hardirq context, because it  |