History

lockdep: Introduce wait-type checks

Extend lockdep to validate lock wait-type context. The current wait-types are: LD_WAIT_FREE, /* wait free, rcu etc.. */ LD_WAIT_SPIN, /* spin loops, raw_spinlock_t etc.. */ LD_WAIT_CONFIG, /* CONFIG_PREEMPT_LOCK, spinlock_t etc.. */ LD_WAIT_SLEEP, /* sleeping locks, mutex_t etc.. */ Where lockdep validates that the current lock (the one being acquired) fits in the current wait-context (as generated by the held stack). This ensures that there is no attempt to acquire mutexes while holding spinlocks, to acquire spinlocks while holding raw_spinlocks and so on. In other words, its a more fancy might_sleep(). Obviously RCU made the entire ordeal more complex than a simple single value test because RCU can be acquired in (pretty much) any context and while it presents a context to nested locks it is not the same as it got acquired in. Therefore its necessary to split the wait_type into two values, one representing the acquire (outer) and one representing the nested context (inner). For most 'normal' locks these two are the same. [ To make static initialization easier we have the rule that: .outer == INV means .outer == .inner; because INV == 0. ] It further means that its required to find the minimal .inner of the held stack to compare against the outer of the new lock; because while 'normal' RCU presents a CONFIG type to nested locks, if it is taken while already holding a SPIN type it obviously doesn't relax the rules. Below is an example output generated by the trivial test code: raw_spin_lock(&foo); spin_lock(&bar); spin_unlock(&bar); raw_spin_unlock(&foo); [ BUG: Invalid wait context ] ----------------------------- swapper/0/1 is trying to lock: ffffc90000013f20 (&bar){....}-{3:3}, at: kernel_init+0xdb/0x187 other info that might help us debug this: 1 lock held by swapper/0/1: #0: ffffc90000013ee0 (&foo){+.+.}-{2:2}, at: kernel_init+0xd1/0x187 The way to read it is to look at the new -{n,m} part in the lock description; -{3:3} for the attempted lock, and try and match that up to the held locks, which in this case is the one: -{2,2}. This tells that the acquiring lock requires a more relaxed environment than presented by the lock stack. Currently only the normal locks and RCU are converted, the rest of the lockdep users defaults to .inner = INV which is ignored. More conversions can be done when desired. The check for spinlock_t nesting is not enabled by default. It's a separate config option for now as there are known problems which are currently addressed. The config option allows to identify these problems and to verify that the solutions found are indeed solving them. The config switch will be removed and the checks will permanently enabled once the vast majority of issues has been addressed. [ bigeasy: Move LD_WAIT_FREE,… out of CONFIG_LOCKDEP to avoid compile failure with CONFIG_DEBUG_SPINLOCK + !CONFIG_LOCKDEP] [ tglx: Add the config option ]

Peter Zijlstra committed 6 years ago

Commit de8f5e41 parent a5c623415 files · +307 −47Browse files

Showing the changes to include/linux (8 of 15 files). Show all

Unified · Split

8 files
  1. include/linux/irqflags.h +7 −1
  2. include/linux/lockdep.h +60 −11
  3. include/linux/mutex.h +5 −2
  4. include/linux/rwlock_types.h +5 −1
  5. include/linux/rwsem.h +5 −1
  6. include/linux/sched.h +1 −0
  7. include/linux/spinlock.h +25 −10
  8. include/linux/spinlock_types.h +20 −4
include/linux/irqflags.h+7−1
@@ -37,7 +37,12 @@ · 36 unmodified lines
37# define trace_softirqs_enabled(p) ((p)->softirqs_enabled)37# define trace_softirqs_enabled(p) ((p)->softirqs_enabled)
38# define trace_hardirq_enter() \38# define trace_hardirq_enter() \
39do { \39do { \
40 current->hardirq_context++; \40 if (!current->hardirq_context++) \
41 current->hardirq_threaded = 0; \
42} while (0)
43# define trace_hardirq_threaded() \
44do { \
45 current->hardirq_threaded = 1; \
41} while (0)46} while (0)
42# define trace_hardirq_exit() \47# define trace_hardirq_exit() \
43do { \48do { \
@@ -59,6 +64,7 @@ do { \ · 15 unmodified lines
59# define trace_hardirqs_enabled(p) 064# define trace_hardirqs_enabled(p) 0
60# define trace_softirqs_enabled(p) 065# define trace_softirqs_enabled(p) 0
61# define trace_hardirq_enter() do { } while (0)66# define trace_hardirq_enter() do { } while (0)
67# define trace_hardirq_threaded() do { } while (0)
62# define trace_hardirq_exit() do { } while (0)68# define trace_hardirq_exit() do { } while (0)
63# define lockdep_softirq_enter() do { } while (0)69# define lockdep_softirq_enter() do { } while (0)
64# define lockdep_softirq_exit() do { } while (0)70# define lockdep_softirq_exit() do { } while (0)
include/linux/lockdep.h+60−11
@@ -21,6 +21,22 @@ extern int lock_stat; · 20 unmodified lines
21 21
22#include <linux/types.h>22#include <linux/types.h>
23 23
24enum lockdep_wait_type {
25 LD_WAIT_INV = 0, /* not checked, catch all */
26
27 LD_WAIT_FREE, /* wait free, rcu etc.. */
28 LD_WAIT_SPIN, /* spin loops, raw_spinlock_t etc.. */
29
30#ifdef CONFIG_PROVE_RAW_LOCK_NESTING
31 LD_WAIT_CONFIG, /* CONFIG_PREEMPT_LOCK, spinlock_t etc.. */
32#else
33 LD_WAIT_CONFIG = LD_WAIT_SPIN,
34#endif
35 LD_WAIT_SLEEP, /* sleeping locks, mutex_t etc.. */
36
37 LD_WAIT_MAX, /* must be last */
38};
39
24#ifdef CONFIG_LOCKDEP40#ifdef CONFIG_LOCKDEP
25 41
26#include <linux/linkage.h>42#include <linux/linkage.h>
@@ -111,6 +127,9 @@ struct lock_class { · 84 unmodified lines
111 int name_version;127 int name_version;
112 const char *name;128 const char *name;
113 129
130 short wait_type_inner;
131 short wait_type_outer;
132
114#ifdef CONFIG_LOCK_STAT133#ifdef CONFIG_LOCK_STAT
115 unsigned long contention_point[LOCKSTAT_POINTS];134 unsigned long contention_point[LOCKSTAT_POINTS];
116 unsigned long contending_point[LOCKSTAT_POINTS];135 unsigned long contending_point[LOCKSTAT_POINTS];
@@ -158,6 +177,8 @@ struct lockdep_map { · 41 unmodified lines
158 struct lock_class_key *key;177 struct lock_class_key *key;
159 struct lock_class *class_cache[NR_LOCKDEP_CACHING_CLASSES];178 struct lock_class *class_cache[NR_LOCKDEP_CACHING_CLASSES];
160 const char *name;179 const char *name;
180 short wait_type_outer; /* can be taken in this context */
181 short wait_type_inner; /* presents this context */
161#ifdef CONFIG_LOCK_STAT182#ifdef CONFIG_LOCK_STAT
162 int cpu;183 int cpu;
163 unsigned long ip;184 unsigned long ip;
@@ -299,8 +320,21 @@ extern void lockdep_unregister_key(struct lock_class_key *key); · 135 unmodified lines
299 * to lockdep:320 * to lockdep:
300 */321 */
301 322
302extern void lockdep_init_map(struct lockdep_map *lock, const char *name,323extern void lockdep_init_map_waits(struct lockdep_map *lock, const char *name,
303 struct lock_class_key *key, int subclass);324 struct lock_class_key *key, int subclass, short inner, short outer);
325
326static inline void
327lockdep_init_map_wait(struct lockdep_map *lock, const char *name,
328 struct lock_class_key *key, int subclass, short inner)
329{
330 lockdep_init_map_waits(lock, name, key, subclass, inner, LD_WAIT_INV);
331}
332
333static inline void lockdep_init_map(struct lockdep_map *lock, const char *name,
334 struct lock_class_key *key, int subclass)
335{
336 lockdep_init_map_wait(lock, name, key, subclass, LD_WAIT_INV);
337}
304 338
305/*339/*
306 * Reinitialize a lock key - for cases where there is special locking or340 * Reinitialize a lock key - for cases where there is special locking or
@@ -308,18 +342,29 @@ extern void lockdep_init_map(struct lockdep_map *lock, const char *name, · 1 unmodified line
308 * of dependencies wrong: they are either too broad (they need a class-split)342 * of dependencies wrong: they are either too broad (they need a class-split)
309 * or they are too narrow (they suffer from a false class-split):343 * or they are too narrow (they suffer from a false class-split):
310 */344 */
311#define lockdep_set_class(lock, key) \345#define lockdep_set_class(lock, key) \
312 lockdep_init_map(&(lock)->dep_map, #key, key, 0)346 lockdep_init_map_waits(&(lock)->dep_map, #key, key, 0, \
313#define lockdep_set_class_and_name(lock, key, name) \347 (lock)->dep_map.wait_type_inner, \
314 lockdep_init_map(&(lock)->dep_map, name, key, 0)348 (lock)->dep_map.wait_type_outer)
315#define lockdep_set_class_and_subclass(lock, key, sub) \349
316 lockdep_init_map(&(lock)->dep_map, #key, key, sub)350#define lockdep_set_class_and_name(lock, key, name) \
317#define lockdep_set_subclass(lock, sub) \351 lockdep_init_map_waits(&(lock)->dep_map, name, key, 0, \
318 lockdep_init_map(&(lock)->dep_map, #lock, \352 (lock)->dep_map.wait_type_inner, \
319 (lock)->dep_map.key, sub)353 (lock)->dep_map.wait_type_outer)
354
355#define lockdep_set_class_and_subclass(lock, key, sub) \
356 lockdep_init_map_waits(&(lock)->dep_map, #key, key, sub,\
357 (lock)->dep_map.wait_type_inner, \
358 (lock)->dep_map.wait_type_outer)
359
360#define lockdep_set_subclass(lock, sub) \
361 lockdep_init_map_waits(&(lock)->dep_map, #lock, (lock)->dep_map.key, sub,\
362 (lock)->dep_map.wait_type_inner, \
363 (lock)->dep_map.wait_type_outer)
320 364
321#define lockdep_set_novalidate_class(lock) \365#define lockdep_set_novalidate_class(lock) \
322 lockdep_set_class_and_name(lock, &__lockdep_no_validate__, #lock)366 lockdep_set_class_and_name(lock, &__lockdep_no_validate__, #lock)
367
323/*368/*
324 * Compare locking classes369 * Compare locking classes
325 */370 */
@@ -432,6 +477,10 @@ static inline void lockdep_set_selftest_task(struct task_struct *task) · 106 unmodified lines
432# define lock_set_class(l, n, k, s, i) do { } while (0)477# define lock_set_class(l, n, k, s, i) do { } while (0)
433# define lock_set_subclass(l, s, i) do { } while (0)478# define lock_set_subclass(l, s, i) do { } while (0)
434# define lockdep_init() do { } while (0)479# define lockdep_init() do { } while (0)
480# define lockdep_init_map_waits(lock, name, key, sub, inner, outer) \
481 do { (void)(name); (void)(key); } while (0)
482# define lockdep_init_map_wait(lock, name, key, sub, inner) \
483 do { (void)(name); (void)(key); } while (0)
435# define lockdep_init_map(lock, name, key, sub) \484# define lockdep_init_map(lock, name, key, sub) \
436 do { (void)(name); (void)(key); } while (0)485 do { (void)(name); (void)(key); } while (0)
437# define lockdep_set_class(lock, key) do { (void)(key); } while (0)486# define lockdep_set_class(lock, key) do { (void)(key); } while (0)
include/linux/mutex.h+5−2
@@ -109,8 +109,11 @@ do { \ · 108 unmodified lines
109} while (0)109} while (0)
110 110
111#ifdef CONFIG_DEBUG_LOCK_ALLOC111#ifdef CONFIG_DEBUG_LOCK_ALLOC
112# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \112# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \
113 , .dep_map = { .name = #lockname }113 , .dep_map = { \
114 .name = #lockname, \
115 .wait_type_inner = LD_WAIT_SLEEP, \
116 }
114#else117#else
115# define __DEP_MAP_MUTEX_INITIALIZER(lockname)118# define __DEP_MAP_MUTEX_INITIALIZER(lockname)
116#endif119#endif
include/linux/rwlock_types.h+5−1
@@ -22,7 +22,11 @@ typedef struct { · 21 unmodified lines
22#define RWLOCK_MAGIC 0xdeaf1eed22#define RWLOCK_MAGIC 0xdeaf1eed
23 23
24#ifdef CONFIG_DEBUG_LOCK_ALLOC24#ifdef CONFIG_DEBUG_LOCK_ALLOC
25# define RW_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname }25# define RW_DEP_MAP_INIT(lockname) \
26 .dep_map = { \
27 .name = #lockname, \
28 .wait_type_inner = LD_WAIT_CONFIG, \
29 }
26#else30#else
27# define RW_DEP_MAP_INIT(lockname)31# define RW_DEP_MAP_INIT(lockname)
28#endif32#endif
include/linux/rwsem.h+5−1
@@ -65,7 +65,11 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem) · 64 unmodified lines
65/* Common initializer macros and functions */65/* Common initializer macros and functions */
66 66
67#ifdef CONFIG_DEBUG_LOCK_ALLOC67#ifdef CONFIG_DEBUG_LOCK_ALLOC
68# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname }68# define __RWSEM_DEP_MAP_INIT(lockname) \
69 , .dep_map = { \
70 .name = #lockname, \
71 .wait_type_inner = LD_WAIT_SLEEP, \
72 }
69#else73#else
70# define __RWSEM_DEP_MAP_INIT(lockname)74# define __RWSEM_DEP_MAP_INIT(lockname)
71#endif75#endif
include/linux/sched.h+1−0
@@ -970,6 +970,7 @@ struct task_struct { · 969 unmodified lines
970 970
971#ifdef CONFIG_TRACE_IRQFLAGS971#ifdef CONFIG_TRACE_IRQFLAGS
972 unsigned int irq_events;972 unsigned int irq_events;
973 unsigned int hardirq_threaded;
973 unsigned long hardirq_enable_ip;974 unsigned long hardirq_enable_ip;
974 unsigned long hardirq_disable_ip;975 unsigned long hardirq_disable_ip;
975 unsigned int hardirq_enable_event;976 unsigned int hardirq_enable_event;
include/linux/spinlock.h+25−10
@@ -93,12 +93,13 @@ · 92 unmodified lines
93 93
94#ifdef CONFIG_DEBUG_SPINLOCK94#ifdef CONFIG_DEBUG_SPINLOCK
95 extern void __raw_spin_lock_init(raw_spinlock_t *lock, const char *name,95 extern void __raw_spin_lock_init(raw_spinlock_t *lock, const char *name,
96 struct lock_class_key *key);96 struct lock_class_key *key, short inner);
97# define raw_spin_lock_init(lock) \97
98do { \98# define raw_spin_lock_init(lock) \
99 static struct lock_class_key __key; \99do { \
100 \100 static struct lock_class_key __key; \
101 __raw_spin_lock_init((lock), #lock, &__key); \101 \
102 __raw_spin_lock_init((lock), #lock, &__key, LD_WAIT_SPIN); \
102} while (0)103} while (0)
103 104
104#else105#else
@@ -327,12 +328,26 @@ static __always_inline raw_spinlock_t *spinlock_check(spinlock_t *lock) · 222 unmodified lines
327 return &lock->rlock;328 return &lock->rlock;
328}329}
329 330
330#define spin_lock_init(_lock) \331#ifdef CONFIG_DEBUG_SPINLOCK
331do { \332
332 spinlock_check(_lock); \333# define spin_lock_init(lock) \
333 raw_spin_lock_init(&(_lock)->rlock); \334do { \
335 static struct lock_class_key __key; \
336 \
337 __raw_spin_lock_init(spinlock_check(lock), \
338 #lock, &__key, LD_WAIT_CONFIG); \
339} while (0)
340
341#else
342
343# define spin_lock_init(_lock) \
344do { \
345 spinlock_check(_lock); \
346 *(_lock) = __SPIN_LOCK_UNLOCKED(_lock); \
334} while (0)347} while (0)
335 348
349#endif
350
336static __always_inline void spin_lock(spinlock_t *lock)351static __always_inline void spin_lock(spinlock_t *lock)
337{352{
338 raw_spin_lock(&lock->rlock);353 raw_spin_lock(&lock->rlock);
include/linux/spinlock_types.h+20−4
@@ -33,8 +33,18 @@ typedef struct raw_spinlock { · 32 unmodified lines
33#define SPINLOCK_OWNER_INIT ((void *)-1L)33#define SPINLOCK_OWNER_INIT ((void *)-1L)
34 34
35#ifdef CONFIG_DEBUG_LOCK_ALLOC35#ifdef CONFIG_DEBUG_LOCK_ALLOC
36# define SPIN_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname }36# define RAW_SPIN_DEP_MAP_INIT(lockname) \
37 .dep_map = { \
38 .name = #lockname, \
39 .wait_type_inner = LD_WAIT_SPIN, \
40 }
41# define SPIN_DEP_MAP_INIT(lockname) \
42 .dep_map = { \
43 .name = #lockname, \
44 .wait_type_inner = LD_WAIT_CONFIG, \
45 }
37#else46#else
47# define RAW_SPIN_DEP_MAP_INIT(lockname)
38# define SPIN_DEP_MAP_INIT(lockname)48# define SPIN_DEP_MAP_INIT(lockname)
39#endif49#endif
40 50
@@ -51,7 +61,7 @@ typedef struct raw_spinlock { · 10 unmodified lines
51 { \61 { \
52 .raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \62 .raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \
53 SPIN_DEBUG_INIT(lockname) \63 SPIN_DEBUG_INIT(lockname) \
54 SPIN_DEP_MAP_INIT(lockname) }64 RAW_SPIN_DEP_MAP_INIT(lockname) }
55 65
56#define __RAW_SPIN_LOCK_UNLOCKED(lockname) \66#define __RAW_SPIN_LOCK_UNLOCKED(lockname) \
57 (raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)67 (raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)
@@ -72,11 +82,17 @@ typedef struct spinlock { · 14 unmodified lines
72 };82 };
73} spinlock_t;83} spinlock_t;
74 84
85#define ___SPIN_LOCK_INITIALIZER(lockname) \
86 { \
87 .raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \
88 SPIN_DEBUG_INIT(lockname) \
89 SPIN_DEP_MAP_INIT(lockname) }
90
75#define __SPIN_LOCK_INITIALIZER(lockname) \91#define __SPIN_LOCK_INITIALIZER(lockname) \
76 { { .rlock = __RAW_SPIN_LOCK_INITIALIZER(lockname) } }92 { { .rlock = ___SPIN_LOCK_INITIALIZER(lockname) } }
77 93
78#define __SPIN_LOCK_UNLOCKED(lockname) \94#define __SPIN_LOCK_UNLOCKED(lockname) \
79 (spinlock_t ) __SPIN_LOCK_INITIALIZER(lockname)95 (spinlock_t) __SPIN_LOCK_INITIALIZER(lockname)
80 96
81#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)97#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
82 98