mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
timer: implement lockdep deadlock detection
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>
This commit is contained in:
parent
673f820591
commit
6f2b9b9a9d
2 changed files with 141 additions and 20 deletions
|
@ -5,6 +5,7 @@
|
||||||
#include <linux/ktime.h>
|
#include <linux/ktime.h>
|
||||||
#include <linux/stddef.h>
|
#include <linux/stddef.h>
|
||||||
#include <linux/debugobjects.h>
|
#include <linux/debugobjects.h>
|
||||||
|
#include <linux/stringify.h>
|
||||||
|
|
||||||
struct tvec_base;
|
struct tvec_base;
|
||||||
|
|
||||||
|
@ -21,52 +22,126 @@ struct timer_list {
|
||||||
char start_comm[16];
|
char start_comm[16];
|
||||||
int start_pid;
|
int start_pid;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_LOCKDEP
|
||||||
|
struct lockdep_map lockdep_map;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct tvec_base boot_tvec_bases;
|
extern struct tvec_base boot_tvec_bases;
|
||||||
|
|
||||||
|
#ifdef CONFIG_LOCKDEP
|
||||||
|
/*
|
||||||
|
* NB: because we have to copy the lockdep_map, setting the lockdep_map key
|
||||||
|
* (second argument) here is required, otherwise it could be initialised to
|
||||||
|
* the copy of the lockdep_map later! We use the pointer to and the string
|
||||||
|
* "<file>:<line>" as the key resp. the name of the lockdep_map.
|
||||||
|
*/
|
||||||
|
#define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn) \
|
||||||
|
.lockdep_map = STATIC_LOCKDEP_MAP_INIT(_kn, &_kn),
|
||||||
|
#else
|
||||||
|
#define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define TIMER_INITIALIZER(_function, _expires, _data) { \
|
#define TIMER_INITIALIZER(_function, _expires, _data) { \
|
||||||
.entry = { .prev = TIMER_ENTRY_STATIC }, \
|
.entry = { .prev = TIMER_ENTRY_STATIC }, \
|
||||||
.function = (_function), \
|
.function = (_function), \
|
||||||
.expires = (_expires), \
|
.expires = (_expires), \
|
||||||
.data = (_data), \
|
.data = (_data), \
|
||||||
.base = &boot_tvec_bases, \
|
.base = &boot_tvec_bases, \
|
||||||
|
__TIMER_LOCKDEP_MAP_INITIALIZER( \
|
||||||
|
__FILE__ ":" __stringify(__LINE__)) \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEFINE_TIMER(_name, _function, _expires, _data) \
|
#define DEFINE_TIMER(_name, _function, _expires, _data) \
|
||||||
struct timer_list _name = \
|
struct timer_list _name = \
|
||||||
TIMER_INITIALIZER(_function, _expires, _data)
|
TIMER_INITIALIZER(_function, _expires, _data)
|
||||||
|
|
||||||
void init_timer(struct timer_list *timer);
|
void init_timer_key(struct timer_list *timer,
|
||||||
void init_timer_deferrable(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
|
#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);
|
extern void destroy_timer_on_stack(struct timer_list *timer);
|
||||||
#else
|
#else
|
||||||
static inline void destroy_timer_on_stack(struct timer_list *timer) { }
|
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
|
#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),
|
void (*function)(unsigned long),
|
||||||
unsigned long data)
|
unsigned long data)
|
||||||
{
|
{
|
||||||
timer->function = function;
|
timer->function = function;
|
||||||
timer->data = data;
|
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),
|
void (*function)(unsigned long),
|
||||||
unsigned long data)
|
unsigned long data)
|
||||||
{
|
{
|
||||||
timer->function = function;
|
timer->function = function;
|
||||||
timer->data = data;
|
timer->data = data;
|
||||||
init_timer_on_stack(timer);
|
init_timer_on_stack_key(timer, name, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -491,14 +491,18 @@ static inline void debug_timer_free(struct timer_list *timer)
|
||||||
debug_object_free(timer, &timer_debug_descr);
|
debug_object_free(timer, &timer_debug_descr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init_timer(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(struct timer_list *timer)
|
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);
|
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)
|
void destroy_timer_on_stack(struct timer_list *timer)
|
||||||
{
|
{
|
||||||
|
@ -512,7 +516,9 @@ static inline void debug_timer_activate(struct timer_list *timer) { }
|
||||||
static inline void debug_timer_deactivate(struct timer_list *timer) { }
|
static inline void debug_timer_deactivate(struct timer_list *timer) { }
|
||||||
#endif
|
#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->entry.next = NULL;
|
||||||
timer->base = __raw_get_cpu_var(tvec_bases);
|
timer->base = __raw_get_cpu_var(tvec_bases);
|
||||||
|
@ -521,6 +527,7 @@ static void __init_timer(struct timer_list *timer)
|
||||||
timer->start_pid = -1;
|
timer->start_pid = -1;
|
||||||
memset(timer->start_comm, 0, TASK_COMM_LEN);
|
memset(timer->start_comm, 0, TASK_COMM_LEN);
|
||||||
#endif
|
#endif
|
||||||
|
lockdep_init_map(&timer->lockdep_map, name, key, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -530,19 +537,23 @@ static void __init_timer(struct timer_list *timer)
|
||||||
* init_timer() must be done to a timer prior calling *any* of the
|
* init_timer() must be done to a timer prior calling *any* of the
|
||||||
* other timer functions.
|
* 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);
|
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);
|
timer_set_deferrable(timer);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(init_timer_deferrable);
|
EXPORT_SYMBOL(init_timer_deferrable_key);
|
||||||
|
|
||||||
static inline void detach_timer(struct timer_list *timer,
|
static inline void detach_timer(struct timer_list *timer,
|
||||||
int clear_pending)
|
int clear_pending)
|
||||||
|
@ -789,6 +800,15 @@ EXPORT_SYMBOL(try_to_del_timer_sync);
|
||||||
*/
|
*/
|
||||||
int del_timer_sync(struct timer_list *timer)
|
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 (;;) {
|
for (;;) {
|
||||||
int ret = try_to_del_timer_sync(timer);
|
int ret = try_to_del_timer_sync(timer);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
|
@ -861,10 +881,36 @@ static inline void __run_timers(struct tvec_base *base)
|
||||||
|
|
||||||
set_running_timer(base, timer);
|
set_running_timer(base, timer);
|
||||||
detach_timer(timer, 1);
|
detach_timer(timer, 1);
|
||||||
|
|
||||||
spin_unlock_irq(&base->lock);
|
spin_unlock_irq(&base->lock);
|
||||||
{
|
{
|
||||||
int preempt_count = preempt_count();
|
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);
|
fn(data);
|
||||||
|
|
||||||
|
lock_map_release(&lockdep_map);
|
||||||
|
|
||||||
if (preempt_count != preempt_count()) {
|
if (preempt_count != preempt_count()) {
|
||||||
printk(KERN_ERR "huh, entered %p "
|
printk(KERN_ERR "huh, entered %p "
|
||||||
"with preempt_count %08x, exited"
|
"with preempt_count %08x, exited"
|
||||||
|
|
Loading…
Reference in a new issue