All of lore.kernel.org
 help / color / mirror / Atom feed
From: Peter Zijlstra <peterz@infradead.org>
To: Johannes Berg <johannes@sipsolutions.net>
Cc: Ingo Molnar <mingo@elte.hu>, Thomas Gleixner <tglx@linutronix.de>,
	Linux Kernel list <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v3] timer: implement lockdep deadlock detection
Date: Wed, 28 Jan 2009 09:20:15 +0100	[thread overview]
Message-ID: <1233130815.10992.39.camel@laptop> (raw)
In-Reply-To: <1233082650.4455.9.camel@johannes.local>

On Tue, 2009-01-27 at 19:57 +0100, Johannes Berg wrote:
> This modifies the timer code in a way to allow lockdep to detect
> deadlocks resulting from a lock being taken in the timer function
> as well as around the del_timer_sync() call.
> 
> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

Nice, thanks!

Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>

> ---
> Ingo, do you want an incremental patch instead?
> 
> v2: fix coding/comment style
> v3: accept freeing timer from timer function, fix init_timer_on_stack
>     in no-lockdep case
> 
>  include/linux/timer.h |  103 ++++++++++++++++++++++++++++++++++++++++++--------
>  kernel/timer.c        |   70 ++++++++++++++++++++++++++++-----
>  2 files changed, 146 insertions(+), 27 deletions(-)
> 
> --- wireless-testing.orig/include/linux/timer.h	2009-01-27 10:12:22.000000000 +0100
> +++ wireless-testing/include/linux/timer.h	2009-01-27 19:52:33.000000000 +0100
> @@ -21,52 +21,125 @@ struct timer_list {
>  	char start_comm[16];
>  	int start_pid;
>  #endif
> +#ifdef CONFIG_LOCKDEP
> +	struct lockdep_map lockdep_map;
> +#endif
>  };
>  
>  extern struct tvec_base boot_tvec_bases;
>  
> -#define TIMER_INITIALIZER(_function, _expires, _data) {		\
> -		.entry = { .prev = TIMER_ENTRY_STATIC },	\
> -		.function = (_function),			\
> -		.expires = (_expires),				\
> -		.data = (_data),				\
> -		.base = &boot_tvec_bases,			\
> +#ifdef CONFIG_LOCKDEP
> +/*
> + * NB: because we have to copy the lockdep_map, setting _key
> + * here is required, otherwise it could get initialised to the
> + * copy of the lockdep_map! We use the function pointer as the
> + * lockdep key here.
> + */
> +#define __TIMER_LOCKDEP_MAP_INITIALIZER(fn, name)		\
> +	.lockdep_map = STATIC_LOCKDEP_MAP_INIT(name, fn),
> +#else
> +#define __TIMER_LOCKDEP_MAP_INITIALIZER(fn, name)
> +#endif
> +
> +#define TIMER_INITIALIZER(_function, _expires, _data) {			\
> +		.entry = { .prev = TIMER_ENTRY_STATIC },		\
> +		.function = (_function),				\
> +		.expires = (_expires),					\
> +		.data = (_data),					\
> +		.base = &boot_tvec_bases,				\
> +		__TIMER_LOCKDEP_MAP_INITIALIZER((_function), #_function)\
>  	}
>  
>  #define DEFINE_TIMER(_name, _function, _expires, _data)		\
>  	struct timer_list _name =				\
>  		TIMER_INITIALIZER(_function, _expires, _data)
>  
> -void init_timer(struct timer_list *timer);
> -void init_timer_deferrable(struct timer_list *timer);
> +void init_timer_key(struct timer_list *timer,
> +		    const char *name,
> +		    struct lock_class_key *key);
> +void init_timer_deferrable_key(struct timer_list *timer,
> +			       const char *name,
> +			       struct lock_class_key *key);
> +
> +#ifdef CONFIG_LOCKDEP
> +#define init_timer(timer)						\
> +	do {								\
> +		static struct lock_class_key __key;			\
> +		init_timer_key((timer), #timer, &__key);		\
> +	} while (0)
> +
> +#define init_timer_deferrable(timer)					\
> +	do {								\
> +		static struct lock_class_key __key;			\
> +		init_timer_deferrable_key((timer), #timer, &__key);	\
> +	} while (0)
> +
> +#define init_timer_on_stack(timer)					\
> +	do {								\
> +		static struct lock_class_key __key;			\
> +		init_timer_on_stack_key((timer), #timer, &__key);	\
> +	} while (0)
> +
> +#define setup_timer(timer, fn, data)					\
> +	do {								\
> +		static struct lock_class_key __key;			\
> +		setup_timer_key((timer), #timer, &__key, (fn), (data));\
> +	} while (0)
> +
> +#define setup_timer_on_stack(timer, fn, data)				\
> +	do {								\
> +		static struct lock_class_key __key;			\
> +		setup_timer_on_stack_key((timer), #timer, &__key,	\
> +					 (fn), (data));			\
> +	} while (0)
> +#else
> +#define init_timer(timer)\
> +	init_timer_key((timer), NULL, NULL)
> +#define init_timer_deferrable(timer)\
> +	init_timer_deferrable_key((timer), NULL, NULL)
> +#define init_timer_on_stack(timer)\
> +	init_timer_on_stack_key((timer), NULL, NULL)
> +#define setup_timer(timer, fn, data)\
> +	setup_timer_key((timer), NULL, NULL, (fn), (data))
> +#define setup_timer_on_stack(timer, fn, data)\
> +	setup_timer_on_stack_key((timer), NULL, NULL, (fn), (data))
> +#endif
>  
>  #ifdef CONFIG_DEBUG_OBJECTS_TIMERS
> -extern void init_timer_on_stack(struct timer_list *timer);
> +extern void init_timer_on_stack_key(struct timer_list *timer,
> +				    const char *name,
> +				    struct lock_class_key *key);
>  extern void destroy_timer_on_stack(struct timer_list *timer);
>  #else
>  static inline void destroy_timer_on_stack(struct timer_list *timer) { }
> -static inline void init_timer_on_stack(struct timer_list *timer)
> +static inline void init_timer_on_stack_key(struct timer_list *timer,
> +					   const char *name,
> +					   struct lock_class_key *key)
>  {
> -	init_timer(timer);
> +	init_timer_key(timer, name, key);
>  }
>  #endif
>  
> -static inline void setup_timer(struct timer_list * timer,
> +static inline void setup_timer_key(struct timer_list * timer,
> +				const char *name,
> +				struct lock_class_key *key,
>  				void (*function)(unsigned long),
>  				unsigned long data)
>  {
>  	timer->function = function;
>  	timer->data = data;
> -	init_timer(timer);
> +	init_timer_key(timer, name, key);
>  }
>  
> -static inline void setup_timer_on_stack(struct timer_list *timer,
> +static inline void setup_timer_on_stack_key(struct timer_list *timer,
> +					const char *name,
> +					struct lock_class_key *key,
>  					void (*function)(unsigned long),
>  					unsigned long data)
>  {
>  	timer->function = function;
>  	timer->data = data;
> -	init_timer_on_stack(timer);
> +	init_timer_on_stack_key(timer, name, key);
>  }
>  
>  /**
> --- wireless-testing.orig/kernel/timer.c	2009-01-27 10:12:22.000000000 +0100
> +++ wireless-testing/kernel/timer.c	2009-01-27 19:50:36.000000000 +0100
> @@ -491,14 +491,18 @@ static inline void debug_timer_free(stru
>  	debug_object_free(timer, &timer_debug_descr);
>  }
>  
> -static void __init_timer(struct timer_list *timer);
> -
> -void init_timer_on_stack(struct timer_list *timer)
> +static void __init_timer(struct timer_list *timer,
> +			 const char *name,
> +			 struct lock_class_key *key);
> +
> +void init_timer_on_stack_key(struct timer_list *timer,
> +			     const char *name,
> +			     struct lock_class_key *key)
>  {
>  	debug_object_init_on_stack(timer, &timer_debug_descr);
> -	__init_timer(timer);
> +	__init_timer(timer, name, key);
>  }
> -EXPORT_SYMBOL_GPL(init_timer_on_stack);
> +EXPORT_SYMBOL_GPL(init_timer_on_stack_key);
>  
>  void destroy_timer_on_stack(struct timer_list *timer)
>  {
> @@ -512,7 +516,9 @@ static inline void debug_timer_activate(
>  static inline void debug_timer_deactivate(struct timer_list *timer) { }
>  #endif
>  
> -static void __init_timer(struct timer_list *timer)
> +static void __init_timer(struct timer_list *timer,
> +			 const char *name,
> +			 struct lock_class_key *key)
>  {
>  	timer->entry.next = NULL;
>  	timer->base = __raw_get_cpu_var(tvec_bases);
> @@ -521,6 +527,7 @@ static void __init_timer(struct timer_li
>  	timer->start_pid = -1;
>  	memset(timer->start_comm, 0, TASK_COMM_LEN);
>  #endif
> +	lockdep_init_map(&timer->lockdep_map, name, key, 0);
>  }
>  
>  /**
> @@ -530,19 +537,23 @@ static void __init_timer(struct timer_li
>   * init_timer() must be done to a timer prior calling *any* of the
>   * other timer functions.
>   */
> -void init_timer(struct timer_list *timer)
> +void init_timer_key(struct timer_list *timer,
> +		    const char *name,
> +		    struct lock_class_key *key)
>  {
>  	debug_timer_init(timer);
> -	__init_timer(timer);
> +	__init_timer(timer, name, key);
>  }
> -EXPORT_SYMBOL(init_timer);
> +EXPORT_SYMBOL(init_timer_key);
>  
> -void init_timer_deferrable(struct timer_list *timer)
> +void init_timer_deferrable_key(struct timer_list *timer,
> +			       const char *name,
> +			       struct lock_class_key *key)
>  {
> -	init_timer(timer);
> +	init_timer_key(timer, name, key);
>  	timer_set_deferrable(timer);
>  }
> -EXPORT_SYMBOL(init_timer_deferrable);
> +EXPORT_SYMBOL(init_timer_deferrable_key);
>  
>  static inline void detach_timer(struct timer_list *timer,
>  				int clear_pending)
> @@ -789,6 +800,15 @@ EXPORT_SYMBOL(try_to_del_timer_sync);
>   */
>  int del_timer_sync(struct timer_list *timer)
>  {
> +#ifdef CONFIG_LOCKDEP
> +	unsigned long flags;
> +
> +	local_irq_save(flags);
> +	lock_map_acquire(&timer->lockdep_map);
> +	lock_map_release(&timer->lockdep_map);
> +	local_irq_restore(flags);
> +#endif
> +
>  	for (;;) {
>  		int ret = try_to_del_timer_sync(timer);
>  		if (ret >= 0)
> @@ -861,10 +881,36 @@ static inline void __run_timers(struct t
>  
>  			set_running_timer(base, timer);
>  			detach_timer(timer, 1);
> +
>  			spin_unlock_irq(&base->lock);
>  			{
>  				int preempt_count = preempt_count();
> +
> +#ifdef CONFIG_LOCKDEP
> +				/*
> +				 * It is permissible to free the timer from
> +				 * inside the function that is called from
> +				 * it, this we need to take into account for
> +				 * lockdep too. To avoid bogus "held lock
> +				 * freed" warnings as well as problems when
> +				 * looking into timer->lockdep_map, make a
> +				 * copy and use that here.
> +				 */
> +				struct lockdep_map lockdep_map =
> +					timer->lockdep_map;
> +#endif
> +				/*
> +				 * Couple the lock chain with the lock chain at
> +				 * del_timer_sync() by acquiring the lock_map
> +				 * around the fn() call here and in
> +				 * del_timer_sync().
> +				 */
> +				lock_map_acquire(&lockdep_map);
> +
>  				fn(data);
> +
> +				lock_map_release(&lockdep_map);
> +
>  				if (preempt_count != preempt_count()) {
>  					printk(KERN_ERR "huh, entered %p "
>  					       "with preempt_count %08x, exited"
> 
> 


  reply	other threads:[~2009-01-28  8:20 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-01-26 22:59 [PATCH] timer: implement lockdep deadlock detection Johannes Berg
2009-01-26 23:03 ` Johannes Berg
2009-01-26 23:07 ` Ingo Molnar
2009-01-27  8:45   ` Johannes Berg
2009-01-27  8:46   ` [PATCH v2] " Johannes Berg
2009-01-27 13:27     ` Ingo Molnar
2009-01-27 13:41       ` Ingo Molnar
2009-01-27 18:06         ` Johannes Berg
2009-01-27 18:30           ` Peter Zijlstra
2009-01-27 18:33             ` Johannes Berg
2009-01-27 18:57             ` [PATCH v3] " Johannes Berg
2009-01-28  8:20               ` Peter Zijlstra [this message]
2009-01-28  9:54                 ` Johannes Berg
2009-01-28 10:13                   ` Peter Zijlstra
2009-01-28 10:54                     ` Johannes Berg
2009-01-28 18:06                       ` Johannes Berg
2009-01-29 12:38                         ` Ingo Molnar
2009-01-29 15:03                           ` [PATCH v4] " Johannes Berg
2009-01-29 13:38                         ` [PATCH v3] " Peter Zijlstra
2009-01-29 13:44                           ` Arnd Bergmann
2009-01-29 14:25                           ` Johannes Berg
2009-01-27 18:12         ` [PATCH v2] " Johannes Berg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1233130815.10992.39.camel@laptop \
    --to=peterz@infradead.org \
    --cc=johannes@sipsolutions.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@elte.hu \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.