Skip to contentSkip to conversation

lockdep: Introduce wait-type checks

RFC PATCH In review Peter Zijlstra 8 participants 50 messages Jan 9, 2014 – Feb 10, 2014

Subsystems (4)
lore
Share & tools
lore

Peter Zijlstra

author maintainer
#0 lore raw

RFC PATCH 1/1 lockdep: Introduce wait-type checks

Subject: lockdep: Introduce wait-type checks
From: Peter Zijlstra <[email protected]>
Date: Tue, 19 Nov 2013 21:45:48 +0100

This patch extends 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 we do not try and acquire mutices while holding spinlocks, do not attempt 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 we can acquire RCU 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 we needed 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 we need 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 example:

	raw_spin_lock(&foo);
	spin_lock(&bar);
	spin_unlock(&bar);
	raw_spin_unlock(&foo);

The way to read it is to look at the new -{n,m} part in the lock description; -{3:3} for our attempted lock, and try and match that up to the held locks, which in this case is the one: -{2,2}.

This tells us that the acquiring lock requires a more relaxed environment that 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 convertions can be done when desired.

[ ] =============================
[ ] [ BUG: Invalid wait context ]
[ ] 3.13.0-rc7-01825-g4443577b1c38-dirty #718 Not tainted
[ ] -----------------------------
[ ] swapper/0/1 is trying to lock:
[ ] (bar){......}-{3:3}, at: [<ffffffff81d0acea>] sched_init_smp+0x423/0x45e [ ]
[ ] stack backtrace:
[ ] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.13.0-rc7-01825-g4443577b1c38-dirty #718 [ ] Hardware name: Supermicro X8DTN/X8DTN, BIOS 4.6.3 01/08/2010

 [ ]  00000000000000a2 ffff880236847cf8 ffffffff8165d96a 0000000000000001
 [ ]  ffff880236847df0 ffffffff810df09f ffffffff81c3e698 ffffffff820b0290
 [ ]  ffff880200000000 ffffea0000000000 ffff880400000000 0000000000004140
 [ ] Call Trace:
 [ ]  [<ffffffff8165d96a>] dump_stack+0x4e/0x7a
 [ ]  [<ffffffff810df09f>] __lock_acquire+0x44f/0x2100
 [ ]  [<ffffffff810c0c8a>] ? task_rq_lock+0x5a/0xa0
 [ ]  [<ffffffff816657ad>] ? _raw_spin_unlock_irqrestore+0x6d/0x80
 [ ]  [<ffffffff810e1317>] lock_acquire+0x87/0x120
 [ ]  [<ffffffff81d0acea>] ? sched_init_smp+0x423/0x45e
 [ ]  [<ffffffff81664e5b>] _raw_spin_lock+0x3b/0x50
 [ ]  [<ffffffff81d0acea>] ? sched_init_smp+0x423/0x45e
 [ ]  [<ffffffff81d0acea>] sched_init_smp+0x423/0x45e
 [ ]  [<ffffffff81cedf03>] kernel_init_freeable+0x91/0x197
 [ ]  [<ffffffff8164f600>] ? rest_init+0xd0/0xd0
 [ ]  [<ffffffff8164f60e>] kernel_init+0xe/0x130
 [ ]  [<ffffffff8166d36c>] ret_from_fork+0x7c/0xb0
 [ ]  [<ffffffff8164f600>] ? rest_init+0xd0/0xd0
 [ ]
 [ ] other info that might help us debug this:
 [ ] 1 lock held by swapper/0/1:
 [ ]  #0:  (foo){+.+...}-{2:2}, at: [<ffffffff81d0acde>] sched_init_smp+0x417/0x45e
 [ ]
 [ ] stack backtrace:
 [ ] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.13.0-rc7-01825-g4443577b1c38-dirty #718
 [ ] Hardware name: Supermicro X8DTN/X8DTN, BIOS 4.6.3 01/08/2010
 [ ]  00000000000000a2 ffff880236847cf8 ffffffff8165d96a 0000000000000001
 [ ]  ffff880236847df0 ffffffff810df0cf ffffffff81c3e698 ffffffff820b0290
 [ ]  ffff880200000000 ffffea0000000000 ffff880400000000 0000000000004140
 [ ] Call Trace:
 [ ]  [<ffffffff8165d96a>] dump_stack+0x4e/0x7a
 [ ]  [<ffffffff810df0cf>] __lock_acquire+0x47f/0x2100
 [ ]  [<ffffffff810c0c8a>] ? task_rq_lock+0x5a/0xa0
 [ ]  [<ffffffff816657ad>] ? _raw_spin_unlock_irqrestore+0x6d/0x80
 [ ]  [<ffffffff810e1317>] lock_acquire+0x87/0x120
 [ ]  [<ffffffff81d0acea>] ? sched_init_smp+0x423/0x45e
 [ ]  [<ffffffff81664e5b>] _raw_spin_lock+0x3b/0x50
 [ ]  [<ffffffff81d0acea>] ? sched_init_smp+0x423/0x45e
 [ ]  [<ffffffff81d0acea>] sched_init_smp+0x423/0x45e
 [ ]  [<ffffffff81cedf03>] kernel_init_freeable+0x91/0x197
 [ ]  [<ffffffff8164f600>] ? rest_init+0xd0/0xd0
 [ ]  [<ffffffff8164f60e>] kernel_init+0xe/0x130
 [ ]  [<ffffffff8166d36c>] ret_from_fork+0x7c/0xb0
 [ ]  [<ffffffff8164f600>] ? rest_init+0xd0/0xd0
---
 include/linux/lockdep.h         |   27 +++++++++-
 include/linux/mutex.h           |    7 +-
 include/linux/rwlock_types.h    |    6 +-
 include/linux/rwsem.h           |    6 +-
 include/linux/spinlock.h        |   36 ++++++++++---
 include/linux/spinlock_types.h  |   24 +++++++--
 kernel/locking/lockdep.c        |  103 +++++++++++++++++++++++++++++++++++++---
 kernel/locking/mutex-debug.c    |    2 
 kernel/locking/rwsem-spinlock.c |    2 
 kernel/locking/rwsem-xadd.c     |    2 
 kernel/locking/spinlock_debug.c |    6 +-
 kernel/rcu/update.c             |   24 ++++++---
 12 files changed, 207 insertions(+), 38 deletions(-)
include/linux/lockdep.h +25 -2
@@ -104,6 +104,9 @@ struct lock_class {
 	const char			*name;
 	int				name_version;
 
+	short				wait_type_inner;
+	short				wait_type_outer;
+
 #ifdef CONFIG_LOCK_STAT
 	unsigned long			contention_point[LOCKSTAT_POINTS];
 	unsigned long			contending_point[LOCKSTAT_POINTS];
@@ -143,6 +146,17 @@ struct lock_class_stats lock_stats(struc
 void clear_lock_stats(struct lock_class *class);
 #endif
 
+enum lockdep_wait_type {
+	LD_WAIT_INV = 0,	/* not checked, catch all */
+
+	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.. */
+
+	LD_WAIT_MAX,		/* must be last */
+};
+
 /*
  * Map the lock object (the lock instance) to the lock-class object.
  * This is embedded into specific lock instances:
@@ -151,6 +165,8 @@ struct lockdep_map {
 	struct lock_class_key		*key;
 	struct lock_class		*class_cache[NR_LOCKDEP_CACHING_CLASSES];
 	const char			*name;
+	short				wait_type_outer; /* can be taken in this context */
+	short				wait_type_inner; /* presents this context */
 #ifdef CONFIG_LOCK_STAT
 	int				cpu;
 	unsigned long			ip;
@@ -276,8 +292,14 @@ extern void lockdep_on(void);
  * to lockdep:
  */
 
-extern void lockdep_init_map(struct lockdep_map *lock, const char *name,
-			     struct lock_class_key *key, int subclass);
+extern void lockdep_init_map_wait(struct lockdep_map *lock, const char *name,
+		struct lock_class_key *key, int subclass, short inner);
+
+static inline void lockdep_init_map(struct lockdep_map *lock, const char *name,
+			     struct lock_class_key *key, int subclass)
+{
+	lockdep_init_map_wait(lock, name, key, subclass, LD_WAIT_INV);
+}
 
 /*
  * To initialize a lockdep_map statically use this macro.
@@ -304,6 +326,7 @@ extern void lockdep_init_map(struct lock
 
 #define lockdep_set_novalidate_class(lock) \
 	lockdep_set_class(lock, &__lockdep_no_validate__)
+
 /*
  * Compare locking classes
  */
include/linux/mutex.h +5 -2
@@ -100,8 +100,11 @@ static inline void mutex_destroy(struct
 #endif
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \
-		, .dep_map = { .name = #lockname }
+# define __DEP_MAP_MUTEX_INITIALIZER(lockname)			\
+		, .dep_map = {					\
+			.name = #lockname,			\
+			.wait_type_inner = LD_WAIT_SLEEP,	\
+		}
 #else
 # define __DEP_MAP_MUTEX_INITIALIZER(lockname)
 #endif
include/linux/rwlock_types.h +5 -1
@@ -25,7 +25,11 @@ typedef struct {
 #define RWLOCK_MAGIC		0xdeaf1eed
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define RW_DEP_MAP_INIT(lockname)	.dep_map = { .name = #lockname }
+# define RW_DEP_MAP_INIT(lockname)					\
+	.dep_map = {							\
+		.name = #lockname,					\
+		.wait_type_inner = LD_WAIT_CONFIG,			\
+	}
 #else
 # define RW_DEP_MAP_INIT(lockname)
 #endif
include/linux/rwsem.h +5 -1
@@ -50,7 +50,11 @@ static inline int rwsem_is_locked(struct
 /* Common initializer macros and functions */
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname }
+# define __RWSEM_DEP_MAP_INIT(lockname)			\
+	, .dep_map = {					\
+		.name = #lockname,			\
+		.wait_type_inner = LD_WAIT_SLEEP,	\
+	}
 #else
 # define __RWSEM_DEP_MAP_INIT(lockname)
 #endif
include/linux/spinlock.h +26 -10
@@ -91,12 +91,13 @@
 
 #ifdef CONFIG_DEBUG_SPINLOCK
   extern void __raw_spin_lock_init(raw_spinlock_t *lock, const char *name,
-				   struct lock_class_key *key);
-# define raw_spin_lock_init(lock)				\
-do {								\
-	static struct lock_class_key __key;			\
-								\
-	__raw_spin_lock_init((lock), #lock, &__key);		\
+				   struct lock_class_key *key, short inner);
+
+# define raw_spin_lock_init(lock)					\
+do {									\
+	static struct lock_class_key __key;				\
+									\
+	__raw_spin_lock_init((lock), #lock, &__key, LD_WAIT_SPIN);	\
 } while (0)
 
 #else
@@ -292,12 +293,27 @@ static inline raw_spinlock_t *spinlock_c
 	return &lock->rlock;
 }
 
-#define spin_lock_init(_lock)				\
-do {							\
-	spinlock_check(_lock);				\
-	raw_spin_lock_init(&(_lock)->rlock);		\
+#ifdef CONFIG_DEBUG_SPINLOCK
+
+# define spin_lock_init(lock)					\
+do {								\
+	static struct lock_class_key __key;			\
+								\
+	__raw_spin_lock_init(spinlock_check(lock),		\
+			     #lock, &__key, LD_WAIT_CONFIG);	\
+} while (0)
+
+#else
+
+# define spin_lock_init(_lock)			\
+do {						\
+	spinlock_check(_lock);			\
+	*(lock) = __SPIN_LOCK_UNLOCKED(lock);	\
 } while (0)
 
+#endif
+
+
 static inline void spin_lock(spinlock_t *lock)
 {
 	raw_spin_lock(&lock->rlock);
include/linux/spinlock_types.h +20 -4
@@ -36,8 +36,18 @@ typedef struct raw_spinlock {
 #define SPINLOCK_OWNER_INIT	((void *)-1L)
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define SPIN_DEP_MAP_INIT(lockname)	.dep_map = { .name = #lockname }
+# define RAW_SPIN_DEP_MAP_INIT(lockname)		\
+	.dep_map = {					\
+		.name = #lockname,			\
+		.wait_type_inner = LD_WAIT_SPIN,	\
+	}
+# define SPIN_DEP_MAP_INIT(lockname)			\
+	.dep_map = {					\
+		.name = #lockname,			\
+		.wait_type_inner = LD_WAIT_CONFIG,	\
+	}
 #else
+# define RAW_SPIN_DEP_MAP_INIT(lockname)
 # define SPIN_DEP_MAP_INIT(lockname)
 #endif
 
@@ -54,7 +64,7 @@ typedef struct raw_spinlock {
 	{					\
 	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
 	SPIN_DEBUG_INIT(lockname)		\
-	SPIN_DEP_MAP_INIT(lockname) }
+	RAW_SPIN_DEP_MAP_INIT(lockname) }
 
 #define __RAW_SPIN_LOCK_UNLOCKED(lockname)	\
 	(raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)
@@ -75,11 +85,17 @@ typedef struct spinlock {
 	};
 } spinlock_t;
 
+#define ___SPIN_LOCK_INITIALIZER(lockname)	\
+	{					\
+	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
+	SPIN_DEBUG_INIT(lockname)		\
+	SPIN_DEP_MAP_INIT(lockname) }
+
 #define __SPIN_LOCK_INITIALIZER(lockname) \
-	{ { .rlock = __RAW_SPIN_LOCK_INITIALIZER(lockname) } }
+	{ { .rlock = ___SPIN_LOCK_INITIALIZER(lockname) } }
 
 #define __SPIN_LOCK_UNLOCKED(lockname) \
-	(spinlock_t ) __SPIN_LOCK_INITIALIZER(lockname)
+	(spinlock_t) __SPIN_LOCK_INITIALIZER(lockname)
 
 #define DEFINE_SPINLOCK(x)	spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
 
kernel/locking/lockdep.c +97 -6

159 lines not on this page · raw message

kernel/locking/mutex-debug.c +1 -1
@@ -93,7 +93,7 @@ void debug_mutex_init(struct mutex *lock
 	 * Make sure we are not reinitializing a held lock:
 	 */
 	debug_check_no_locks_freed((void *)lock, sizeof(*lock));
-	lockdep_init_map(&lock->dep_map, name, key, 0);
+	lockdep_init_map_wait(&lock->dep_map, name, key, 0, LD_WAIT_SLEEP);
 #endif
 	lock->magic = lock;
 }
kernel/locking/rwsem-spinlock.c +1 -1
@@ -44,7 +44,7 @@ void __init_rwsem(struct rw_semaphore *s
 	 * Make sure we are not reinitializing a held semaphore:
 	 */
 	debug_check_no_locks_freed((void *)sem, sizeof(*sem));
-	lockdep_init_map(&sem->dep_map, name, key, 0);
+	lockdep_init_map_wait(&sem->dep_map, name, key, 0, LD_WAIT_SLEEP);
 #endif
 	sem->activity = 0;
 	raw_spin_lock_init(&sem->wait_lock);
kernel/locking/rwsem-xadd.c +1 -1
@@ -22,7 +22,7 @@ void __init_rwsem(struct rw_semaphore *s
 	 * Make sure we are not reinitializing a held semaphore:
 	 */
 	debug_check_no_locks_freed((void *)sem, sizeof(*sem));
-	lockdep_init_map(&sem->dep_map, name, key, 0);
+	lockdep_init_map_wait(&sem->dep_map, name, key, 0, LD_WAIT_SLEEP);
 #endif
 	sem->count = RWSEM_UNLOCKED_VALUE;
 	raw_spin_lock_init(&sem->wait_lock);
kernel/locking/spinlock_debug.c +3 -3
@@ -14,14 +14,14 @@
 #include <linux/export.h>
 
 void __raw_spin_lock_init(raw_spinlock_t *lock, const char *name,
-			  struct lock_class_key *key)
+			  struct lock_class_key *key, short inner)
 {
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	/*
 	 * Make sure we are not reinitializing a held lock:
 	 */
 	debug_check_no_locks_freed((void *)lock, sizeof(*lock));
-	lockdep_init_map(&lock->dep_map, name, key, 0);
+	lockdep_init_map_wait(&lock->dep_map, name, key, 0, inner);
 #endif
 	lock->raw_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
 	lock->magic = SPINLOCK_MAGIC;
@@ -39,7 +39,7 @@ void __rwlock_init(rwlock_t *lock, const
 	 * Make sure we are not reinitializing a held lock:
 	 */
 	debug_check_no_locks_freed((void *)lock, sizeof(*lock));
-	lockdep_init_map(&lock->dep_map, name, key, 0);
+	lockdep_init_map_wait(&lock->dep_map, name, key, 0, LD_WAIT_CONFIG);
 #endif
 	lock->raw_lock = (arch_rwlock_t) __ARCH_RW_LOCK_UNLOCKED;
 	lock->magic = RWLOCK_MAGIC;
kernel/rcu/update.c +18 -6
@@ -114,18 +114,30 @@ EXPORT_SYMBOL_GPL(__rcu_read_unlock);
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 static struct lock_class_key rcu_lock_key;
-struct lockdep_map rcu_lock_map =
-	STATIC_LOCKDEP_MAP_INIT("rcu_read_lock", &rcu_lock_key);
+struct lockdep_map rcu_lock_map = {
+	.name = "rcu_read_lock",
+	.key = &rcu_lock_key,
+	.wait_type_outer = LD_WAIT_FREE,
+	.wait_type_inner = LD_WAIT_CONFIG, /* XXX PREEMPT_RCU ? */
+};
 EXPORT_SYMBOL_GPL(rcu_lock_map);
 
 static struct lock_class_key rcu_bh_lock_key;
-struct lockdep_map rcu_bh_lock_map =
-	STATIC_LOCKDEP_MAP_INIT("rcu_read_lock_bh", &rcu_bh_lock_key);
+struct lockdep_map rcu_bh_lock_map = {
+	.name = "rcu_read_lock_bh",
+	.key = &rcu_bh_lock_key,
+	.wait_type_outer = LD_WAIT_FREE,
+	.wait_type_inner = LD_WAIT_CONFIG, /* PREEMPT_LOCK also makes BH preemptible */
+};
 EXPORT_SYMBOL_GPL(rcu_bh_lock_map);
 
 static struct lock_class_key rcu_sched_lock_key;
-struct lockdep_map rcu_sched_lock_map =
-	STATIC_LOCKDEP_MAP_INIT("rcu_read_lock_sched", &rcu_sched_lock_key);
+struct lockdep_map rcu_sched_lock_map = {
+	.name = "rcu_read_lock_sched",
+	.key = &rcu_sched_lock_key,
+	.wait_type_outer = LD_WAIT_FREE,
+	.wait_type_inner = LD_WAIT_SPIN,
+};
 EXPORT_SYMBOL_GPL(rcu_sched_lock_map);
 
 static struct lock_class_key rcu_callback_key;
author maintainer
#1 lore raw
Quoted 7 lines · Peter Zijlstra
(quoted diff) +1 -2
@@ -276,8 +292,14 @@ extern void lockdep_on(void);
  * to lockdep:
  */
 
-extern void lockdep_init_map(struct lockdep_map *lock, const char *name,
-			     struct lock_class_key *key, int subclass);
+extern void lockdep_init_map_wait(struct lockdep_map *lock, const char *name,

Review extern void lockdep_on(void); · line 305

Quoted 10 lines
(quoted diff) +7 -0
+		struct lock_class_key *key, int subclass, short inner);
+
+static inline void lockdep_init_map(struct lockdep_map *lock, const char *name,
+			     struct lock_class_key *key, int subclass)
+{
+	lockdep_init_map_wait(lock, name, key, subclass, LD_WAIT_INV);
+}
 
 /*
  * To initialize a lockdep_map statically use this macro.

Realized that the above destroys existing wait types for lockdep_set_*(), so update those to preserve the wait_types.

At which point a trylock triggered a wait_type violation which is of course a validation mistake, so ignore trylocks.

include/linux/lockdep.h +15 -9
@@ -314,15 +314,21 @@ static inline void lockdep_init_map(stru
  * of dependencies wrong: they are either too broad (they need a class-split)
  * or they are too narrow (they suffer from a false class-split):
  */
-#define lockdep_set_class(lock, key) \
-		lockdep_init_map(&(lock)->dep_map, #key, key, 0)
-#define lockdep_set_class_and_name(lock, key, name) \
-		lockdep_init_map(&(lock)->dep_map, name, key, 0)
-#define lockdep_set_class_and_subclass(lock, key, sub) \
-		lockdep_init_map(&(lock)->dep_map, #key, key, sub)
-#define lockdep_set_subclass(lock, sub)	\
-		lockdep_init_map(&(lock)->dep_map, #lock, \
-				 (lock)->dep_map.key, sub)
+#define lockdep_set_class(lock, key)				\
+	lockdep_init_map_wait(&(lock)->dep_map, #key, key, 0,	\
+			      (lock)->dep_map.wait_type_inner)
+
+#define lockdep_set_class_and_name(lock, key, name)		\
+	lockdep_init_map_wait(&(lock)->dep_map, name, key, 0,	\
+			      (lock)->dep_map.wait_type_inner)
+
+#define lockdep_set_class_and_subclass(lock, key, sub)		\
+	lockdep_init_map_wait(&(lock)->dep_map, #key, key, sub,	\
+			      (lock)->dep_map.wait_type_inner)
+
+#define lockdep_set_subclass(lock, sub)						\
+	lockdep_init_map_wait(&(lock)->dep_map, #lock, (lock)->dep_map.key, sub,\
+			      (lock)->dep_map.wait_type_inner)
 
 #define lockdep_set_novalidate_class(lock) \
 	lockdep_set_class(lock, &__lockdep_no_validate__)
kernel/locking/lockdep.c +6 -4
@@ -3092,14 +3092,14 @@ print_lock_invalid_wait_context(struct t
  * Therefore we must look for the strictest environment in the lock stack and
  * compare that to the lock we're trying to acquire.
  */
-static int check_context(struct task_struct *curr, struct held_lock *next)
+static int check_wait_context(struct task_struct *curr, struct held_lock *next)
 {
 	short next_inner = hlock_class(next)->wait_type_inner;
 	short next_outer = hlock_class(next)->wait_type_outer;
 	short curr_inner = LD_WAIT_MAX;
 	int depth;
 
-	if (!curr->lockdep_depth || !next_inner)
+	if (!curr->lockdep_depth || !next_inner || next->trylock)
 		return 0;
 
 	if (!next_outer)
@@ -3111,8 +3111,10 @@ static int check_context(struct task_str
 
 		if (prev_inner) {
 			/*
-			 * we can have a bigger inner than a previous one
+			 * We can have a bigger inner than a previous one
 			 * when outer is smaller than inner, as with RCU.
+			 *
+			 * Also due to trylocks.
 			 */
 			curr_inner = min(curr_inner, prev_inner);
 		}
@@ -3226,7 +3228,7 @@ static int __lock_acquire(struct lockdep
 	hlock->holdtime_stamp = lockstat_clock();
 #endif
 
-	if (check_context(curr, hlock))
+	if (check_wait_context(curr, hlock))
 		return 0;
 
 	if (check == 2 && !mark_irqflags(curr, hlock))
#2 lore raw
In reply to Peter Zijlstra
Quoted 22 lines · Peter Zijlstra
kernel/locking/lockdep.c +21 -0
+static int check_context(struct task_struct *curr, struct held_lock *next)
+{
+	short next_inner = hlock_class(next)->wait_type_inner;
+	short next_outer = hlock_class(next)->wait_type_outer;
+	short curr_inner = LD_WAIT_MAX;
+	int depth;
+
+	if (!curr->lockdep_depth || !next_inner)
+		return 0;
+
+	if (!next_outer)
+		next_outer = next_inner;
+
+	for (depth = 0; depth < curr->lockdep_depth; depth++) {
+		struct held_lock *prev = curr->held_locks + depth;
+		short prev_inner = hlock_class(prev)->wait_type_inner;
+
+		if (prev_inner) {
+			/*
+			 * we can have a bigger inner than a previous one
+			 * when outer is smaller than inner, as with RCU.

kernel/locking/lockdep.c print_lock_nested_lock_not_held(struct t · line 3125

Quoted 10 lines
kernel/locking/lockdep.c +10 -0
+			 */
+			curr_inner = min(curr_inner, prev_inner);
+		}
+	}
+
+	if (next_outer > curr_inner)
+		return print_lock_invalid_wait_context(curr, next);
+
+	return 0;
+}

This is really minor, but it seems you can simplify it a little bit. We do not really need curr_inner, the main loop can do

	for (...) {
		...

		if (prev_inner && prev_inner < next_outer)
			return print_lock_invalid_wait_context(...);
	}

	return 0;

Off-topic question... I can't understand the "int check" argument of lock_acquire(). First of all, __lock_acquire() does

	if (!prove_locking)
		check = 1;

Doesn't this mean lock_acquire_*() do not depend on CONFIG_PROVE_LOCKING? IOW, can't we do

	--- x/include/linux/lockdep.h
	+++ x/include/linux/lockdep.h
	@@ -479,15 +479,9 @@ static inline void print_irqtrace_events
	  * on the per lock-class debug mode:
	  */
	 
	-#ifdef CONFIG_PROVE_LOCKING
	- #define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 2, n, i)
	- #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 2, n, i)
	- #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 2, n, i)
	-#else
	- #define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 1, n, i)
	- #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 1, n, i)
	- #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 1, n, i)
	-#endif
	+#define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 2, n, i)
	+#define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 2, n, i)
	+#define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 2, n, i)
	 
	 #define spin_acquire(l, s, t, i)		lock_acquire_exclusive(l, s, t, NULL, i)
	 #define spin_acquire_nest(l, s, t, n, i)	lock_acquire_exclusive(l, s, t, n, i)

But what I really can't understans is what "check == 0" means? It seems that in fact it can be 1 or 2? Or, iow, "check == 0" is actually equivalent to "check == 1" ?

Oleg.

author maintainer
#3 lore raw
Quoted 11 lines · Oleg Nesterov

This is really minor, but it seems you can simplify it a little bit. We do not really need curr_inner, the main loop can do

	for (...) {
		...

		if (prev_inner && prev_inner < next_outer)
			return print_lock_invalid_wait_context(...);
	}

	return 0;

Indeed, that might work.. My brain still finds it easier to understand the min() version though.

Quoted 19 lines

Off-topic question... I can't understand the "int check" argument of lock_acquire(). First of all, __lock_acquire() does

	if (!prove_locking)
		check = 1;

Doesn't this mean lock_acquire_*() do not depend on CONFIG_PROVE_LOCKING? IOW, can't we do

	--- x/include/linux/lockdep.h
	+++ x/include/linux/lockdep.h
	@@ -479,15 +479,9 @@ static inline void print_irqtrace_events
	  * on the per lock-class debug mode:
	  */
	 
	-#ifdef CONFIG_PROVE_LOCKING
	- #define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 2, n, i)
	- #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 2, n, i)
	- #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 2, n, i)
Quoted 8 lines
	-#else
	- #define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 1, n, i)
	- #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 1, n, i)
	- #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 1, n, i)
	-#endif
	+#define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 2, n, i)
	+#define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 2, n, i)
	+#define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 2, n, i)

I suppose we could; note however that the if (!prove_locking) logic was added later.

Quoted 3 lines

But what I really can't understans is what "check == 0" means? It seems that in fact it can be 1 or 2? Or, iow, "check == 0" is actually equivalent to "check == 1" ?

Hmm indeed, the comment in lockdep.h says 0 means no checks at all, but the code doesn't actually appear to work like that. I'm not sure it ever did or not, I'd have to go dig through history.

That said, given the current state it certainly looks like we can remove the check argument.

Ingo?

#4 lore raw

check && lockdep_no_validate (Was: lockdep: Introduce wait-type checks)

I changed the subject to avoid the confusion.

Quoted 16 lines · Peter Zijlstra
Quoted 4 lines · Oleg Nesterov

-#ifdef CONFIG_PROVE_LOCKING
- #define lock_acquire_exclusive(l, s, t, n, i) lock_acquire(l, s, t, 0, 2, n, i)
- #define lock_acquire_shared(l, s, t, n, i) lock_acquire(l, s, t, 1, 2, n, i)
- #define lock_acquire_shared_recursive(l, s, t, n, i) lock_acquire(l, s, t, 2, 2, n, i)

Quoted 8 lines

-#else
- #define lock_acquire_exclusive(l, s, t, n, i) lock_acquire(l, s, t, 0, 1, n, i)
- #define lock_acquire_shared(l, s, t, n, i) lock_acquire(l, s, t, 1, 1, n, i)
- #define lock_acquire_shared_recursive(l, s, t, n, i) lock_acquire(l, s, t, 2, 1, n, i)
-#endif
+#define lock_acquire_exclusive(l, s, t, n, i) lock_acquire(l, s, t, 0, 2, n, i)
+#define lock_acquire_shared(l, s, t, n, i) lock_acquire(l, s, t, 1, 2, n, i)
+#define lock_acquire_shared_recursive(l, s, t, n, i) lock_acquire(l, s, t, 2, 2, n, i)

Quoted 2 lines

I suppose we could; note however that the if (!prove_locking) logic was added later.

OK, thanks...

Quoted 10 lines
Quoted 3 lines

But what I really can't understans is what "check == 0" means? It seems that in fact it can be 1 or 2? Or, iow, "check == 0" is actually equivalent to "check == 1" ?

Hmm indeed, the comment in lockdep.h says 0 means no checks at all, but the code doesn't actually appear to work like that. I'm not sure it ever did or not, I'd have to go dig through history.

That said, given the current state it certainly looks like we can remove the check argument.

Or yes, we can probably simply remove it. Unlikely we will need lock_acquire(check => 0).

But this connects to lockdep_no_validate. Not sure I understand what this class should actually do, but consider this code:

	DEFINE_MUTEX(m1);
	DEFINE_MUTEX(m2);
	DEFINE_MUTEX(mx);

	void lockdep_should_complain(void)
	{
		lockdep_set_novalidate_class(&mx);

		// m1 -> mx -> m2
		mutex_lock(&m1);
		mutex_lock(&mx);
		mutex_lock(&m2);
		mutex_unlock(&m2);
		mutex_unlock(&mx);
		mutex_unlock(&m1);


		// m2 -> m1 ; should trigger the warning
		mutex_lock(&m2);
		mutex_lock(&m1);
		mutex_unlock(&m1);
		mutex_unlock(&m2);
	}

lockdep doesn't not detect the trivial possible deadlock.

The patch below seems to work but most probably it is not right, and I forgot everything (not too much) I knew about lockdep internals.

Oleg.

kernel/locking/lockdep.c +2 -1
@@ -1939,7 +1939,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * Only non-recursive-read entries get new dependencies
 		 * added:
 		 */
-		if (hlock->read != 2) {
+		if (hlock->read != 2 &&
+		    hlock->instance->key != &__lockdep_no_validate__) {
 			if (!check_prev_add(curr, hlock, next,
 						distance, trylock_loop))
 				return 0;
author maintainer
#5 lore raw

Re: check && lockdep_no_validate (Was: lockdep: Introduce wait-type checks)

Quoted 38 lines · Oleg Nesterov

But this connects to lockdep_no_validate. Not sure I understand what this class should actually do, but consider this code:

	DEFINE_MUTEX(m1);
	DEFINE_MUTEX(m2);
	DEFINE_MUTEX(mx);

	void lockdep_should_complain(void)
	{
		lockdep_set_novalidate_class(&mx);

		// m1 -> mx -> m2
		mutex_lock(&m1);
		mutex_lock(&mx);
		mutex_lock(&m2);
		mutex_unlock(&m2);
		mutex_unlock(&mx);
		mutex_unlock(&m1);


		// m2 -> m1 ; should trigger the warning
		mutex_lock(&m2);
		mutex_lock(&m1);
		mutex_unlock(&m1);
		mutex_unlock(&m2);
	}

lockdep doesn't not detect the trivial possible deadlock.

The patch below seems to work but most probably it is not right, and I forgot everything (not too much) I knew about lockdep internals.

Oleg.

kernel/locking/lockdep.c check_prevs_add(struct task_struct *curr, struct held_lock *next) · line 1946

Quoted 9 lines
kernel/locking/lockdep.c +2 -1
@@ -1939,7 +1939,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * Only non-recursive-read entries get new dependencies
 		 * added:
 		 */
-		if (hlock->read != 2) {
+		if (hlock->read != 2 &&
+		    hlock->instance->key != &__lockdep_no_validate__) {
 			if (!check_prev_add(curr, hlock, next,
 						distance, trylock_loop))
 				return 0;

Hmm, you are quite right indeed; although I would write it like:

  if (hlock->read != 2 && hlock->check == 2)

because the __lockdep_no_validate__ thing forces the ->check value to 1.

#6 lore raw

Re: check && lockdep_no_validate (Was: lockdep: Introduce wait-type checks)

Quoted 18 lines · Peter Zijlstra
Quoted 5 lines · Oleg Nesterov

kernel/locking/lockdep.c check_prevs_add(struct task_struct *curr, struct held_lock *next) · line 1946

Quoted 9 lines
kernel/locking/lockdep.c +2 -1
@@ -1939,7 +1939,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * Only non-recursive-read entries get new dependencies
 		 * added:
 		 */
-		if (hlock->read != 2) {
+		if (hlock->read != 2 &&
+		    hlock->instance->key != &__lockdep_no_validate__) {
 			if (!check_prev_add(curr, hlock, next,
 						distance, trylock_loop))
 				return 0;

Hmm, you are quite right indeed;

Thanks!

Quoted 5 lines

although I would write it like:

  if (hlock->read != 2 && hlock->check == 2)

because the __lockdep_no_validate__ thing forces the ->check value to 1.

Agreed, hlock->check == 2 looks better. But this connects to another patch I sent which removes hlock->check...

OK, I'll wait for review on that patch, then resend this one with ->check or __lockdep_no_validate__ depending on the result.

Oleg.

#7 lore raw

Re: check && lockdep_no_validate (Was: lockdep: Introduce wait-type checks)

Quoted 27 lines · Oleg Nesterov
Quoted 18 lines · Peter Zijlstra
Quoted 5 lines · Oleg Nesterov

kernel/locking/lockdep.c check_prevs_add(struct task_struct *curr, struct held_lock *next) · line 1946

Quoted 9 lines
kernel/locking/lockdep.c +2 -1
@@ -1939,7 +1939,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * Only non-recursive-read entries get new dependencies
 		 * added:
 		 */
-		if (hlock->read != 2) {
+		if (hlock->read != 2 &&
+		    hlock->instance->key != &__lockdep_no_validate__) {
 			if (!check_prev_add(curr, hlock, next,
 						distance, trylock_loop))
 				return 0;

Hmm, you are quite right indeed;

Thanks!

Quoted 4 lines

although I would write it like:

  if (hlock->read != 2 && hlock->check == 2)
Quoted 7 lines
Quoted 1 line

because the __lockdep_no_validate__ thing forces the ->check value to 1.

Agreed, hlock->check == 2 looks better. But this connects to another patch I sent which removes hlock->check...

OK, I'll wait for review on that patch, then resend this one with ->check or __lockdep_no_validate__ depending on the result.

And I still think that we should try to remove hlock->check, but please forget about this for the moment. I'll try to send some patches later in any case.

OK, lets suppose we do the change above using ->key or ->check, doesn't matter. This will fix the already discussed pattern:

		static DEFINE_MUTEX(m1);
		static DEFINE_MUTEX(m2);
		static DEFINE_MUTEX(mx);

		lockdep_set_novalidate_class(&mx);

		// m1 -> mx -> m2
		mutex_lock(&m1);
		mutex_lock(&mx);
		mutex_lock(&m2);
		mutex_unlock(&m2);
		mutex_unlock(&mx);
		mutex_unlock(&m1);

		// m2 -> m1 ; should trigger the warning
		mutex_lock(&m2);
		mutex_lock(&m1);
		mutex_unlock(&m1);
		mutex_unlock(&m2);

before this change lockdep can't detect the trivial deadlock.

But with or without this change the following code

		static DEFINE_MUTEX(m1);
		static DEFINE_MUTEX(mx);

		lockdep_set_novalidate_class(&mx);

		// m1 -> mx
		mutex_lock(&m1);
		mutex_lock(&mx);
		mutex_unlock(&mx);
		mutex_unlock(&m1);

		// mx -> m1 ; should trigger the warning ???
		mutex_lock(&mx);
		mutex_lock(&m1);
		mutex_unlock(&m1);
		mutex_unlock(&mx);

doesn't trigger the warning too. This is correct because lockdep_set_novalidate_class() means, well, no-validate. The question is: do we really want to avoid all validations?

Why lockdep_set_novalidate_class() was added? Unlees I missed something the problem is that (say) __driver_attach() can take the "same" lock twice, drivers/base/ lacks annotations.

Perhaps we should change the meaning of lockdep_set_novalidate_class? (perhaps with rename). What do you think about the patch below?

With this patch __lockdep_no_validate__ means "automatically nested", although I have to remind I can hardly understand the code I am trying to change ;)

Oleg.

kernel/locking/lockdep.c +12 -6
index 576ba75..844d25d 100644
@@ -2515,9 +2515,6 @@ mark_held_locks(struct task_struct *curr, enum mark_type mark)
 
 		BUG_ON(usage_bit >= LOCK_USAGE_STATES);
 
-		if (hlock_class(hlock)->key == __lockdep_no_validate__.subkeys)
-			continue;
-
 		if (!mark_lock(curr, hlock, usage_bit))
 			return 0;
 	}
@@ -3067,8 +3064,15 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
 		return 0;
 
-	if (lock->key == &__lockdep_no_validate__)
-		check = 1;
+	if (lock->key == &__lockdep_no_validate__) {
+		int i;
+
+		for (i = curr->lockdep_depth; --i >= 0; ) {
+			hlock = curr->held_locks + i;
+			if (hlock->instance->key == lock->key)
+				goto nested;
+		}
+	}
 
 	if (subclass < NR_LOCKDEP_CACHING_CLASSES)
 		class = lock->class_cache[subclass];
@@ -3106,6 +3110,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	if (depth) {
 		hlock = curr->held_locks + depth - 1;
 		if (hlock->class_idx == class_idx && nest_lock) {
+nested:
 			if (hlock->references)
 				hlock->references++;
 			else
@@ -3282,8 +3287,9 @@ static int match_held_lock(struct held_lock *hlock, struct lockdep_map *lock)
 		 * References, but not a lock we're actually ref-counting?
 		 * State got messed up, follow the sites that change ->references
 		 * and try to make sense of it.
+		 * FIXME: s/0/novalidate/ ?
 		 */
-		if (DEBUG_LOCKS_WARN_ON(!hlock->nest_lock))
+		if (DEBUG_LOCKS_WARN_ON(0 && !hlock->nest_lock))
 			return 0;
 
 		if (hlock->class_idx == class - lock_classes + 1)
author maintainer
#8 lore raw

Re: check && lockdep_no_validate (Was: lockdep: Introduce wait-type checks)

Quoted 19 lines · Oleg Nesterov

But with or without this change the following code

		static DEFINE_MUTEX(m1);
		static DEFINE_MUTEX(mx);

		lockdep_set_novalidate_class(&mx);

		// m1 -> mx
		mutex_lock(&m1);
		mutex_lock(&mx);
		mutex_unlock(&mx);
		mutex_unlock(&m1);

		// mx -> m1 ; should trigger the warning ???
		mutex_lock(&mx);
		mutex_lock(&m1);
		mutex_unlock(&m1);
		mutex_unlock(&mx);
Quoted 3 lines

doesn't trigger the warning too. This is correct because lockdep_set_novalidate_class() means, well, no-validate. The question is: do we really want to avoid all validations?

Good question.

Quoted 3 lines

Why lockdep_set_novalidate_class() was added? Unlees I missed something the problem is that (say) __driver_attach() can take the "same" lock twice, drivers/base/ lacks annotations.

Indeed, the driver model locking always slips my mind but yes its creative. Alan Stern seems to have a good grasp on it though.

Quoted 4 lines

Perhaps we should change the meaning of lockdep_set_novalidate_class? (perhaps with rename). What do you think about the patch below?

With this patch __lockdep_no_validate__ means "automatically nested",

Yes, I suppose that might work, it would allow some validation.

Quoted 2 lines

although I have to remind I can hardly understand the code I am trying to change ;)

You don't seem to be doing too badly ;-)

#9 lore raw

Re: check && lockdep_no_validate (Was: lockdep: Introduce wait-type checks)

Quoted 24 lines · Peter Zijlstra
Quoted 19 lines · Oleg Nesterov

But with or without this change the following code

		static DEFINE_MUTEX(m1);
		static DEFINE_MUTEX(mx);

		lockdep_set_novalidate_class(&mx);

		// m1 -> mx
		mutex_lock(&m1);
		mutex_lock(&mx);
		mutex_unlock(&mx);
		mutex_unlock(&m1);

		// mx -> m1 ; should trigger the warning ???
		mutex_lock(&mx);
		mutex_lock(&m1);
		mutex_unlock(&m1);
		mutex_unlock(&mx);
Quoted 3 lines

doesn't trigger the warning too. This is correct because lockdep_set_novalidate_class() means, well, no-validate. The question is: do we really want to avoid all validations?

Quoted 8 lines

Good question.

Quoted 3 lines

Why lockdep_set_novalidate_class() was added? Unlees I missed something the problem is that (say) __driver_attach() can take the "same" lock twice, drivers/base/ lacks annotations.

Indeed, the driver model locking always slips my mind but yes its creative. Alan Stern seems to have a good grasp on it though.

Mostly my "grasp" is just a firm belief that trying to manage locking throughout the entire driver tree is hopeless. Individual sub-portions of it are usually well behaved, but the thing as a whole is a mess.

Quoted 6 lines
Quoted 4 lines

Perhaps we should change the meaning of lockdep_set_novalidate_class? (perhaps with rename). What do you think about the patch below?

With this patch __lockdep_no_validate__ means "automatically nested",

Yes, I suppose that might work, it would allow some validation.

I haven't seen the patch, but I'm not so sure it will work. Suppose we have two devices, D1 and D2, and some other mutex, M. Then the locking pattern:

	lock(D1);
	lock(M);
	unlock(M);
	unlock(D1);

generally should not conflict with:

	lock(M);
	lock(D2);
	unlock(D2);
	unlock(M);

even though D1's and D2's locks belong to the same class. For example, M might be a mutex embedded in the private data associated with D1, and D2 might be a child of D1.

Alan Stern

#10 lore raw

Re: check && lockdep_no_validate (Was: lockdep: Introduce wait-type checks)

Quoted 21 lines · Alan Stern
Quoted 8 lines · Peter Zijlstra
Quoted 4 lines · Oleg Nesterov

Perhaps we should change the meaning of lockdep_set_novalidate_class? (perhaps with rename). What do you think about the patch below?

With this patch __lockdep_no_validate__ means "automatically nested",

Yes, I suppose that might work, it would allow some validation.

I haven't seen the patch, but I'm not so sure it will work. Suppose we have two devices, D1 and D2, and some other mutex, M. Then the locking pattern:

	lock(D1);
	lock(M);
	unlock(M);
	unlock(D1);
Quoted 6 lines

generally should not conflict with:

	lock(M);
	lock(D2);
	unlock(D2);
	unlock(M);

Yes, sure. This change assumes that the only problem in drivers/base is dev->parent->mutex / dev->mutex dependency. If the locking is even more "broken" (wrt lockdep), we can't replace lockdep_set_novalidate_class() with lockdep_set_auto_nested().

And, otoh, with this change lockdep can miss the real problems too, for example:

	func1(dev)
	{
		device_lock(dev->parent);
		mutex_lock(MUTEX);
		device_lock(dev);
		...
	}

	func2(dev)
	{
		device_lock(dev);
		mutex_lock(MUTEX);
		...
	}

lockdep will only notice dev -> MUTEX dependency.

I booted the kernel (under kvm) with this change and there is nothing in dmesg, but of course this is not the real testing.

So do you think that dev->mutex should not be validated at all ?

Just in case... Of course, if we actually add auto_nested we should not use a single class unless dev->mutex will be the only user.

Oleg.

#11 lore raw

Re: check && lockdep_no_validate (Was: lockdep: Introduce wait-type checks)

Quoted 29 lines · Oleg Nesterov
Quoted 21 lines · Alan Stern
Quoted 8 lines · Peter Zijlstra
Quoted 4 lines · Oleg Nesterov

Perhaps we should change the meaning of lockdep_set_novalidate_class? (perhaps with rename). What do you think about the patch below?

With this patch __lockdep_no_validate__ means "automatically nested",

Yes, I suppose that might work, it would allow some validation.

I haven't seen the patch, but I'm not so sure it will work. Suppose we have two devices, D1 and D2, and some other mutex, M. Then the locking pattern:

	lock(D1);
	lock(M);
	unlock(M);
	unlock(D1);
Quoted 6 lines

generally should not conflict with:

	lock(M);
	lock(D2);
	unlock(D2);
	unlock(M);
Quoted 4 lines

Yes, sure. This change assumes that the only problem in drivers/base is dev->parent->mutex / dev->mutex dependency. If the locking is even more "broken" (wrt lockdep), we can't replace lockdep_set_novalidate_class() with lockdep_set_auto_nested().

I suspect it is even more "broken". But I can't point to specific examples.

Quoted 18 lines

And, otoh, with this change lockdep can miss the real problems too, for example:

	func1(dev)
	{
		device_lock(dev->parent);
		mutex_lock(MUTEX);
		device_lock(dev);
		...
	}

	func2(dev)
	{
		device_lock(dev);
		mutex_lock(MUTEX);
		...
	}
Quoted 6 lines

lockdep will only notice dev -> MUTEX dependency.

I booted the kernel (under kvm) with this change and there is nothing in dmesg, but of course this is not the real testing.

So do you think that dev->mutex should not be validated at all ?

My guess is that if your change is deployed widely, there will be reports of violations. That's only a guess.

Still, you could go ahead and try it, just to see what happens.

Quoted 2 lines

Just in case... Of course, if we actually add auto_nested we should not use a single class unless dev->mutex will be the only user.

That's a good point. I don't know of any other classes using LOCKDEP_NO_VALIDATE, but there may be one or two.

Also, take a look at commit 356c05d58af0. It's a similar situation (not exactly the same).

Alan Stern

#12 lore raw

cover lockdep: (Was: check && lockdep_no_validate)

Quoted 8 lines · Alan Stern
Quoted 4 lines · Oleg Nesterov

Yes, sure. This change assumes that the only problem in drivers/base is dev->parent->mutex / dev->mutex dependency. If the locking is even more "broken" (wrt lockdep), we can't replace lockdep_set_novalidate_class() with lockdep_set_auto_nested().

Quoted 7 lines

I suspect it is even more "broken". But I can't point to specific examples.

...

My guess is that if your change is deployed widely, there will be reports of violations. That's only a guess.

OK, lets (try to) do this later. Let me send the changes which I hope should be fine in any case.

Quoted 1 line

Still, you could go ahead and try it, just to see what happens.

Yes, perhaps it makes sense at least to test this change and see what happens... We will see.

Quoted 2 lines

Also, take a look at commit 356c05d58af0. It's a similar situation (not exactly the same).

At first glance, can't __ATTR_IGNORE_LOCKDEP() use no_validate too ? (ignoring the fact checkpatch.pl won't be happy). This can simplify the code, it seems.

Oleg.

#13 lore raw

PATCH 1/5 lockdep: make held_lock->check and "int check" argument bool

The "int check" argument of lock_acquire() and held_lock->check are misleading. This is actually a boolean: 2 means "true", everything else is "false".

And there is no need to pass 1 or 0 to lock_acquire() depending on CONFIG_PROVE_LOCKING, __lock_acquire() checks prove_locking at the start and clears "check" if !CONFIG_PROVE_LOCKING.

Note: probably we can simply kill this member/arg. The only explicit user of check => 0 is rcu_lock_acquire(), perhaps we can change it to use lock_acquire(trylock =>, read => 2). __lockdep_no_validate means check => 0 implicitly, but we can change validate_chain() to check hlock->instance->key instead. Not to mention it would be nice to get rid of lockdep_set_novalidate_class().

---
 drivers/tty/tty_ldsem.c  |   15 ++++-----------
 include/linux/lockdep.h  |   25 +++++++++----------------
 include/linux/rcupdate.h |    2 +-
 kernel/locking/lockdep.c |   11 ++++-------
 4 files changed, 18 insertions(+), 35 deletions(-)
drivers/tty/tty_ldsem.c +4 -11
index 22fad8a..9e9b0a9 100644
@@ -39,17 +39,10 @@
 				lock_acquire(&(l)->dep_map, s, t, r, c, n, i)
 # define __rel(l, n, i)				\
 				lock_release(&(l)->dep_map, n, i)
-# ifdef CONFIG_PROVE_LOCKING
-#  define lockdep_acquire(l, s, t, i)		__acq(l, s, t, 0, 2, NULL, i)
-#  define lockdep_acquire_nest(l, s, t, n, i)	__acq(l, s, t, 0, 2, n, i)
-#  define lockdep_acquire_read(l, s, t, i)	__acq(l, s, t, 1, 2, NULL, i)
-#  define lockdep_release(l, n, i)		__rel(l, n, i)
-# else
-#  define lockdep_acquire(l, s, t, i)		__acq(l, s, t, 0, 1, NULL, i)
-#  define lockdep_acquire_nest(l, s, t, n, i)	__acq(l, s, t, 0, 1, n, i)
-#  define lockdep_acquire_read(l, s, t, i)	__acq(l, s, t, 1, 1, NULL, i)
-#  define lockdep_release(l, n, i)		__rel(l, n, i)
-# endif
+#define lockdep_acquire(l, s, t, i)		__acq(l, s, t, 0, 1, NULL, i)
+#define lockdep_acquire_nest(l, s, t, n, i)	__acq(l, s, t, 0, 1, n, i)
+#define lockdep_acquire_read(l, s, t, i)	__acq(l, s, t, 1, 1, NULL, i)
+#define lockdep_release(l, n, i)		__rel(l, n, i)
 #else
 # define lockdep_acquire(l, s, t, i)		do { } while (0)
 # define lockdep_acquire_nest(l, s, t, n, i)	do { } while (0)
include/linux/lockdep.h +9 -16
index 92b1bfc..1626047 100644
@@ -252,9 +252,9 @@ struct held_lock {
 	unsigned int trylock:1;						/* 16 bits */
 
 	unsigned int read:2;        /* see lock_acquire() comment */
-	unsigned int check:2;       /* see lock_acquire() comment */
+	unsigned int check:1;       /* see lock_acquire() comment */
 	unsigned int hardirqs_off:1;
-	unsigned int references:11;					/* 32 bits */
+	unsigned int references:12;					/* 32 bits */
 };
 
 /*
@@ -326,9 +326,8 @@ static inline int lockdep_match_key(struct lockdep_map *lock,
  *
  * Values for check:
  *
- *   0: disabled
- *   1: simple checks (freeing, held-at-exit-time, etc.)
- *   2: full validation
+ *   0: simple checks (freeing, held-at-exit-time, etc.)
+ *   1: full validation
  */
 extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 			 int trylock, int read, int check,
@@ -479,15 +478,9 @@ static inline void print_irqtrace_events(struct task_struct *curr)
  * on the per lock-class debug mode:
  */
 
-#ifdef CONFIG_PROVE_LOCKING
- #define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 2, n, i)
- #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 2, n, i)
- #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 2, n, i)
-#else
- #define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 1, n, i)
- #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 1, n, i)
- #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 1, n, i)
-#endif
+#define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 1, n, i)
+#define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 1, n, i)
+#define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 1, n, i)
 
 #define spin_acquire(l, s, t, i)		lock_acquire_exclusive(l, s, t, NULL, i)
 #define spin_acquire_nest(l, s, t, n, i)	lock_acquire_exclusive(l, s, t, n, i)
@@ -518,13 +511,13 @@ static inline void print_irqtrace_events(struct task_struct *curr)
 # define might_lock(lock) 						\
 do {									\
 	typecheck(struct lockdep_map *, &(lock)->dep_map);		\
-	lock_acquire(&(lock)->dep_map, 0, 0, 0, 2, NULL, _THIS_IP_);	\
+	lock_acquire(&(lock)->dep_map, 0, 0, 0, 1, NULL, _THIS_IP_);	\
 	lock_release(&(lock)->dep_map, 0, _THIS_IP_);			\
 } while (0)
 # define might_lock_read(lock) 						\
 do {									\
 	typecheck(struct lockdep_map *, &(lock)->dep_map);		\
-	lock_acquire(&(lock)->dep_map, 0, 0, 1, 2, NULL, _THIS_IP_);	\
+	lock_acquire(&(lock)->dep_map, 0, 0, 1, 1, NULL, _THIS_IP_);	\
 	lock_release(&(lock)->dep_map, 0, _THIS_IP_);			\
 } while (0)
 #else
include/linux/rcupdate.h +1 -1
index a2482cf..2eef290 100644
@@ -314,7 +314,7 @@ static inline bool rcu_lockdep_current_cpu_online(void)
 
 static inline void rcu_lock_acquire(struct lockdep_map *map)
 {
-	lock_acquire(map, 0, 0, 2, 1, NULL, _THIS_IP_);
+	lock_acquire(map, 0, 0, 2, 0, NULL, _THIS_IP_);
 }
 
 static inline void rcu_lock_release(struct lockdep_map *map)
kernel/locking/lockdep.c +4 -7
index 576ba75..c6a7d9d 100644
@@ -2096,7 +2096,7 @@ static int validate_chain(struct task_struct *curr, struct lockdep_map *lock,
 	 * (If lookup_chain_cache() returns with 1 it acquires
 	 * graph_lock for us)
 	 */
-	if (!hlock->trylock && (hlock->check == 2) &&
+	if (!hlock->trylock && hlock->check &&
 	    lookup_chain_cache(curr, hlock, chain_key)) {
 		/*
 		 * Check whether last held lock:
@@ -3053,9 +3053,6 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	int class_idx;
 	u64 chain_key;
 
-	if (!prove_locking)
-		check = 1;
-
 	if (unlikely(!debug_locks))
 		return 0;
 
@@ -3067,8 +3064,8 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
 		return 0;
 
-	if (lock->key == &__lockdep_no_validate__)
-		check = 1;
+	if (!prove_locking || lock->key == &__lockdep_no_validate__)
+		check = 0;
 
 	if (subclass < NR_LOCKDEP_CACHING_CLASSES)
 		class = lock->class_cache[subclass];
@@ -3136,7 +3133,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	hlock->holdtime_stamp = lockstat_clock();
 #endif
 
-	if (check == 2 && !mark_irqflags(curr, hlock))
+	if (check && !mark_irqflags(curr, hlock))
 		return 0;
 
 	/* mark it as used: */
Signature · 1 line
1.5.5.1
#14 lore raw

[tip:core/locking] lockdep: Make held_lock->check and "int check" argument bool

lockdep: Make held_lock->check and "int check" argument bool
Commit-ID:  fb9edbe98493fcd9df66de926ae9157cbe0e4dcd
Gitweb:     http://git.kernel.org/tip/fb9edbe98493fcd9df66de926ae9157cbe0e4dcd
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Mon, 20 Jan 2014 19:20:06 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 9 Feb 2014 21:18:54 +0100

lockdep: Make held_lock->check and "int check" argument bool

The "int check" argument of lock_acquire() and held_lock->check are misleading. This is actually a boolean: 2 means "true", everything else is "false".

And there is no need to pass 1 or 0 to lock_acquire() depending on CONFIG_PROVE_LOCKING, __lock_acquire() checks prove_locking at the start and clears "check" if !CONFIG_PROVE_LOCKING.

Note: probably we can simply kill this member/arg. The only explicit user of check => 0 is rcu_lock_acquire(), perhaps we can change it to use lock_acquire(trylock =>, read => 2). __lockdep_no_validate means check => 0 implicitly, but we can change validate_chain() to check hlock->instance->key instead. Not to mention it would be nice to get rid of lockdep_set_novalidate_class().

---
 drivers/tty/tty_ldsem.c  | 15 ++++-----------
 include/linux/lockdep.h  | 25 +++++++++----------------
 include/linux/rcupdate.h |  2 +-
 kernel/locking/lockdep.c | 11 ++++-------
 4 files changed, 18 insertions(+), 35 deletions(-)
drivers/tty/tty_ldsem.c +4 -11
index d8a55e8..0ffb0cb 100644
@@ -39,17 +39,10 @@
 				lock_acquire(&(l)->dep_map, s, t, r, c, n, i)
 # define __rel(l, n, i)				\
 				lock_release(&(l)->dep_map, n, i)
-# ifdef CONFIG_PROVE_LOCKING
-#  define lockdep_acquire(l, s, t, i)		__acq(l, s, t, 0, 2, NULL, i)
-#  define lockdep_acquire_nest(l, s, t, n, i)	__acq(l, s, t, 0, 2, n, i)
-#  define lockdep_acquire_read(l, s, t, i)	__acq(l, s, t, 1, 2, NULL, i)
-#  define lockdep_release(l, n, i)		__rel(l, n, i)
-# else
-#  define lockdep_acquire(l, s, t, i)		__acq(l, s, t, 0, 1, NULL, i)
-#  define lockdep_acquire_nest(l, s, t, n, i)	__acq(l, s, t, 0, 1, n, i)
-#  define lockdep_acquire_read(l, s, t, i)	__acq(l, s, t, 1, 1, NULL, i)
-#  define lockdep_release(l, n, i)		__rel(l, n, i)
-# endif
+#define lockdep_acquire(l, s, t, i)		__acq(l, s, t, 0, 1, NULL, i)
+#define lockdep_acquire_nest(l, s, t, n, i)	__acq(l, s, t, 0, 1, n, i)
+#define lockdep_acquire_read(l, s, t, i)	__acq(l, s, t, 1, 1, NULL, i)
+#define lockdep_release(l, n, i)		__rel(l, n, i)
 #else
 # define lockdep_acquire(l, s, t, i)		do { } while (0)
 # define lockdep_acquire_nest(l, s, t, n, i)	do { } while (0)
include/linux/lockdep.h +9 -16
index 92b1bfc..1626047 100644
@@ -252,9 +252,9 @@ struct held_lock {
 	unsigned int trylock:1;						/* 16 bits */
 
 	unsigned int read:2;        /* see lock_acquire() comment */
-	unsigned int check:2;       /* see lock_acquire() comment */
+	unsigned int check:1;       /* see lock_acquire() comment */
 	unsigned int hardirqs_off:1;
-	unsigned int references:11;					/* 32 bits */
+	unsigned int references:12;					/* 32 bits */
 };
 
 /*
@@ -326,9 +326,8 @@ static inline int lockdep_match_key(struct lockdep_map *lock,
  *
  * Values for check:
  *
- *   0: disabled
- *   1: simple checks (freeing, held-at-exit-time, etc.)
- *   2: full validation
+ *   0: simple checks (freeing, held-at-exit-time, etc.)
+ *   1: full validation
  */
 extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 			 int trylock, int read, int check,
@@ -479,15 +478,9 @@ static inline void print_irqtrace_events(struct task_struct *curr)
  * on the per lock-class debug mode:
  */
 
-#ifdef CONFIG_PROVE_LOCKING
- #define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 2, n, i)
- #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 2, n, i)
- #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 2, n, i)
-#else
- #define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 1, n, i)
- #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 1, n, i)
- #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 1, n, i)
-#endif
+#define lock_acquire_exclusive(l, s, t, n, i)		lock_acquire(l, s, t, 0, 1, n, i)
+#define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 1, n, i)
+#define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 1, n, i)
 
 #define spin_acquire(l, s, t, i)		lock_acquire_exclusive(l, s, t, NULL, i)
 #define spin_acquire_nest(l, s, t, n, i)	lock_acquire_exclusive(l, s, t, n, i)
@@ -518,13 +511,13 @@ static inline void print_irqtrace_events(struct task_struct *curr)
 # define might_lock(lock) 						\
 do {									\
 	typecheck(struct lockdep_map *, &(lock)->dep_map);		\
-	lock_acquire(&(lock)->dep_map, 0, 0, 0, 2, NULL, _THIS_IP_);	\
+	lock_acquire(&(lock)->dep_map, 0, 0, 0, 1, NULL, _THIS_IP_);	\
 	lock_release(&(lock)->dep_map, 0, _THIS_IP_);			\
 } while (0)
 # define might_lock_read(lock) 						\
 do {									\
 	typecheck(struct lockdep_map *, &(lock)->dep_map);		\
-	lock_acquire(&(lock)->dep_map, 0, 0, 1, 2, NULL, _THIS_IP_);	\
+	lock_acquire(&(lock)->dep_map, 0, 0, 1, 1, NULL, _THIS_IP_);	\
 	lock_release(&(lock)->dep_map, 0, _THIS_IP_);			\
 } while (0)
 #else
include/linux/rcupdate.h +1 -1
index 72bf3a0..adff3c9 100644
@@ -314,7 +314,7 @@ static inline bool rcu_lockdep_current_cpu_online(void)
 
 static inline void rcu_lock_acquire(struct lockdep_map *map)
 {
-	lock_acquire(map, 0, 0, 2, 1, NULL, _THIS_IP_);
+	lock_acquire(map, 0, 0, 2, 0, NULL, _THIS_IP_);
 }
 
 static inline void rcu_lock_release(struct lockdep_map *map)
kernel/locking/lockdep.c +4 -7
index eb8a547..8c85a0d 100644
@@ -2098,7 +2098,7 @@ static int validate_chain(struct task_struct *curr, struct lockdep_map *lock,
 	 * (If lookup_chain_cache() returns with 1 it acquires
 	 * graph_lock for us)
 	 */
-	if (!hlock->trylock && (hlock->check == 2) &&
+	if (!hlock->trylock && hlock->check &&
 	    lookup_chain_cache(curr, hlock, chain_key)) {
 		/*
 		 * Check whether last held lock:
@@ -3055,9 +3055,6 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	int class_idx;
 	u64 chain_key;
 
-	if (!prove_locking)
-		check = 1;
-
 	if (unlikely(!debug_locks))
 		return 0;
 
@@ -3069,8 +3066,8 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
 		return 0;
 
-	if (lock->key == &__lockdep_no_validate__)
-		check = 1;
+	if (!prove_locking || lock->key == &__lockdep_no_validate__)
+		check = 0;
 
 	if (subclass < NR_LOCKDEP_CACHING_CLASSES)
 		class = lock->class_cache[subclass];
@@ -3138,7 +3135,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	hlock->holdtime_stamp = lockstat_clock();
 #endif
 
-	if (check == 2 && !mark_irqflags(curr, hlock))
+	if (check && !mark_irqflags(curr, hlock))
 		return 0;
 
 	/* mark it as used: */
#15 lore raw

PATCH 2/5 lockdep: don't create the wrong dependency on hlock->check == 0

In reply to Oleg Nesterov

Test-case:

	DEFINE_MUTEX(m1);
	DEFINE_MUTEX(m2);
	DEFINE_MUTEX(mx);

	void lockdep_should_complain(void)
	{
		lockdep_set_novalidate_class(&mx);

		// m1 -> mx -> m2
		mutex_lock(&m1);
		mutex_lock(&mx);
		mutex_lock(&m2);
		mutex_unlock(&m2);
		mutex_unlock(&mx);
		mutex_unlock(&m1);

		// m2 -> m1 ; should trigger the warning
		mutex_lock(&m2);
		mutex_lock(&m1);
		mutex_unlock(&m1);
		mutex_unlock(&m2);
	}

this doesn't trigger any warning, lockdep can't detect the trivial deadlock.

This is because lock(&mx) correctly avoids m1 -> mx dependency, it skips validate_chain() due to mx->check == 0. But lock(&m2) wrongly adds mx -> m2 and thus m1 -> m2 is not created.

rcu_lock_acquire()->lock_acquire(check => 0) is fine due to read == 2, so currently only __lockdep_no_validate__ can trigger this problem.

---
 kernel/locking/lockdep.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
kernel/locking/lockdep.c +2 -2
index c6a7d9d..543e120 100644
@@ -1934,12 +1934,12 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 
 	for (;;) {
 		int distance = curr->lockdep_depth - depth + 1;
-		hlock = curr->held_locks + depth-1;
+		hlock = curr->held_locks + depth - 1;
 		/*
 		 * Only non-recursive-read entries get new dependencies
 		 * added:
 		 */
-		if (hlock->read != 2) {
+		if (hlock->read != 2 && hlock->check) {
 			if (!check_prev_add(curr, hlock, next,
 						distance, trylock_loop))
 				return 0;
Signature · 1 line
1.5.5.1
#16 lore raw

[tip:core/locking] lockdep: Don' t create the wrong dependency on hlock->check == 0

lockdep: Don't create the wrong dependency on hlock->check == 0
Commit-ID:  1b5ff816cab708ba44c7d7b56b613516269eb577
Gitweb:     http://git.kernel.org/tip/1b5ff816cab708ba44c7d7b56b613516269eb577
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Mon, 20 Jan 2014 19:20:10 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 9 Feb 2014 21:18:57 +0100

lockdep: Don't create the wrong dependency on hlock->check == 0

Test-case:

	DEFINE_MUTEX(m1);
	DEFINE_MUTEX(m2);
	DEFINE_MUTEX(mx);

	void lockdep_should_complain(void)
	{
		lockdep_set_novalidate_class(&mx);

		// m1 -> mx -> m2
		mutex_lock(&m1);
		mutex_lock(&mx);
		mutex_lock(&m2);
		mutex_unlock(&m2);
		mutex_unlock(&mx);
		mutex_unlock(&m1);

		// m2 -> m1 ; should trigger the warning
		mutex_lock(&m2);
		mutex_lock(&m1);
		mutex_unlock(&m1);
		mutex_unlock(&m2);
	}

this doesn't trigger any warning, lockdep can't detect the trivial deadlock.

This is because lock(&mx) correctly avoids m1 -> mx dependency, it skips validate_chain() due to mx->check == 0. But lock(&m2) wrongly adds mx -> m2 and thus m1 -> m2 is not created.

rcu_lock_acquire()->lock_acquire(check => 0) is fine due to read == 2, so currently only __lockdep_no_validate__ can trigger this problem.

---
 kernel/locking/lockdep.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/locking/lockdep.c +2 -2
index 8c85a0d..f7eba92 100644
@@ -1936,12 +1936,12 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 
 	for (;;) {
 		int distance = curr->lockdep_depth - depth + 1;
-		hlock = curr->held_locks + depth-1;
+		hlock = curr->held_locks + depth - 1;
 		/*
 		 * Only non-recursive-read entries get new dependencies
 		 * added:
 		 */
-		if (hlock->read != 2) {
+		if (hlock->read != 2 && hlock->check) {
 			if (!check_prev_add(curr, hlock, next,
 						distance, trylock_loop))
 				return 0;
#17 lore raw

PATCH 3/5 lockdep: change mark_held_locks() to check hlock->check instead of lockdep_no_validate

In reply to Oleg Nesterov

The __lockdep_no_validate check in mark_held_locks() adds the subtle and (afaics) unnecessary difference between no-validate and check==0. And this looks even more inconsistent because __lock_acquire() skips mark_irqflags()->mark_lock() if !check.

Change mark_held_locks() to check hlock->check instead.

---
 kernel/locking/lockdep.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/locking/lockdep.c +1 -1
index 543e120..28e41c0 100644
@@ -2515,7 +2515,7 @@ mark_held_locks(struct task_struct *curr, enum mark_type mark)
 
 		BUG_ON(usage_bit >= LOCK_USAGE_STATES);
 
-		if (hlock_class(hlock)->key == __lockdep_no_validate__.subkeys)
+		if (!hlock->check)
 			continue;
 
 		if (!mark_lock(curr, hlock, usage_bit))
Signature · 1 line
1.5.5.1
#18 lore raw

[tip:core/locking] lockdep: Change mark_held_locks() to check hlock->check instead of lockdep_no_validate

lockdep: Change mark_held_locks() to check hlock->check instead of lockdep_no_validate
Commit-ID:  34d0ed5ea7a72d5961552fb1758a94f0d3f8f3dc
Gitweb:     http://git.kernel.org/tip/34d0ed5ea7a72d5961552fb1758a94f0d3f8f3dc
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Mon, 20 Jan 2014 19:20:13 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 9 Feb 2014 21:18:59 +0100

lockdep: Change mark_held_locks() to check hlock->check instead of lockdep_no_validate

The __lockdep_no_validate check in mark_held_locks() adds the subtle and (afaics) unnecessary difference between no-validate and check==0. And this looks even more inconsistent because __lock_acquire() skips mark_irqflags()->mark_lock() if !check.

Change mark_held_locks() to check hlock->check instead.

---
 kernel/locking/lockdep.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/locking/lockdep.c +1 -1
index f7eba92..bf0c6b0 100644
@@ -2517,7 +2517,7 @@ mark_held_locks(struct task_struct *curr, enum mark_type mark)
 
 		BUG_ON(usage_bit >= LOCK_USAGE_STATES);
 
-		if (hlock_class(hlock)->key == __lockdep_no_validate__.subkeys)
+		if (!hlock->check)
 			continue;
 
 		if (!mark_lock(curr, hlock, usage_bit))
#19 lore raw

PATCH 4/5 lockdep: change lockdep_set_novalidate_class() to use _and_name

In reply to Oleg Nesterov

Cosmetic. This doesn't really matter because a) device->mutex is the only user of __lockdep_no_validate__ and b) this class should be never reported as the source of problem, but if something goes wrong "&dev->mutex" looks better than "&__lockdep_no_validate__" as the name of the lock.

---
 include/linux/lockdep.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
include/linux/lockdep.h +1 -1
index 1626047..060e513 100644
@@ -303,7 +303,7 @@ extern void lockdep_init_map(struct lockdep_map *lock, const char *name,
 				 (lock)->dep_map.key, sub)
 
 #define lockdep_set_novalidate_class(lock) \
-	lockdep_set_class(lock, &__lockdep_no_validate__)
+	lockdep_set_class_and_name(lock, &__lockdep_no_validate__, #lock)
 /*
  * Compare locking classes
  */
Signature · 1 line
1.5.5.1
#20 lore raw

[tip:core/locking] lockdep: Change lockdep_set_novalidate_class() to use _and_name

lockdep: Change lockdep_set_novalidate_class() to use _and_name
Commit-ID:  47be1c1a0e188232b5e5962917b21750053cd3f8
Gitweb:     http://git.kernel.org/tip/47be1c1a0e188232b5e5962917b21750053cd3f8
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Mon, 20 Jan 2014 19:20:16 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 9 Feb 2014 21:19:01 +0100

lockdep: Change lockdep_set_novalidate_class() to use _and_name

Cosmetic. This doesn't really matter because a) device->mutex is the only user of __lockdep_no_validate__ and b) this class should be never reported as the source of problem, but if something goes wrong "&dev->mutex" looks better than "&__lockdep_no_validate__" as the name of the lock.

---
 include/linux/lockdep.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/lockdep.h +1 -1
index 1626047..060e513 100644
@@ -303,7 +303,7 @@ extern void lockdep_init_map(struct lockdep_map *lock, const char *name,
 				 (lock)->dep_map.key, sub)
 
 #define lockdep_set_novalidate_class(lock) \
-	lockdep_set_class(lock, &__lockdep_no_validate__)
+	lockdep_set_class_and_name(lock, &__lockdep_no_validate__, #lock)
 /*
  * Compare locking classes
  */
#21 lore raw

PATCH 5/5 lockdep: pack subclass/trylock/read/check into a single argument

In reply to Oleg Nesterov

lock_acquire() takes 7 arguments, we can pack 4 "short" enums into the single "unsigned long acqf" to help the callers which mostly use the constant values of subclass/trylock/read/check,

		$ size vmlinux
		   text	   data	    bss	    dec	    hex	filename
	-	5262832	2970280	10125312	18358424	1182098	vmlinux
	+	5255131	2974376	10125312	18354819	1181283	vmlinux
---
 include/linux/lockdep.h  |   29 ++++++++++++++++++++++++++---
 kernel/locking/lockdep.c |   17 +++++++++++++----
 2 files changed, 39 insertions(+), 7 deletions(-)
include/linux/lockdep.h +26 -3
index 060e513..23a8af3 100644
@@ -329,9 +329,32 @@ static inline int lockdep_match_key(struct lockdep_map *lock,
  *   0: simple checks (freeing, held-at-exit-time, etc.)
  *   1: full validation
  */
-extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
-			 int trylock, int read, int check,
-			 struct lockdep_map *nest_lock, unsigned long ip);
+#define ACQF_SUBCLASS_SHIFT	0
+#define ACQF_SUBCLASS_BITS	3
+
+#define ACQF_TRYLOCK_SHIFT	(ACQF_SUBCLASS_SHIFT + ACQF_SUBCLASS_BITS)
+#define ACQF_TRYLOCK_BITS	1
+
+#define ACQF_READ_SHIFT		(ACQF_TRYLOCK_SHIFT + ACQF_TRYLOCK_BITS)
+#define ACQF_READ_BITS		2
+
+#define ACQF_CHECK_SHIFT	(ACQF_READ_SHIFT + ACQF_READ_BITS)
+#define ACQF_CHECK_BITS		1
+
+extern void do_lock_acquire(struct lockdep_map *lock, unsigned long acqf,
+			struct lockdep_map *nest_lock, unsigned long ip);
+
+static inline void lock_acquire(struct lockdep_map *lock,
+		unsigned int subclass, int trylock, int read, int check,
+		struct lockdep_map *nest_lock, unsigned long ip)
+{
+	unsigned long acqf =	(subclass	<< ACQF_SUBCLASS_SHIFT) |
+				(trylock	<< ACQF_TRYLOCK_SHIFT) |
+				(read		<< ACQF_READ_SHIFT) |
+				(check		<< ACQF_CHECK_SHIFT);
+
+	do_lock_acquire(lock, acqf, nest_lock, ip);
+}
 
 extern void lock_release(struct lockdep_map *lock, int nested,
 			 unsigned long ip);
kernel/locking/lockdep.c +13 -4
index 28e41c0..8826557 100644
@@ -3581,10 +3581,19 @@ EXPORT_SYMBOL_GPL(lock_set_class);
  * We are not always called with irqs disabled - do that here,
  * and also avoid lockdep recursion:
  */
-void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
-			  int trylock, int read, int check,
-			  struct lockdep_map *nest_lock, unsigned long ip)
+void do_lock_acquire(struct lockdep_map *lock, unsigned long acqf,
+			struct lockdep_map *nest_lock, unsigned long ip)
 {
+	#define ACQF(bits, what)	\
+		((bits >> ACQF_ ## what ## _SHIFT) &	\
+		 ((1 << ACQF_ ## what ## _BITS) - 1))
+
+	unsigned int subclass = ACQF(acqf, SUBCLASS);
+	unsigned int trylock = ACQF(acqf, TRYLOCK);
+	unsigned int read = ACQF(acqf, READ);
+	unsigned int check = ACQF(acqf, CHECK);
+	#undef ACQF
+
 	unsigned long flags;
 
 	if (unlikely(current->lockdep_recursion))
@@ -3600,7 +3609,7 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	current->lockdep_recursion = 0;
 	raw_local_irq_restore(flags);
 }
-EXPORT_SYMBOL_GPL(lock_acquire);
+EXPORT_SYMBOL_GPL(do_lock_acquire);
 
 void lock_release(struct lockdep_map *lock, int nested,
 			  unsigned long ip)
Signature · 1 line
1.5.5.1
author maintainer
#22 lore raw

Re: [PATCH 5/5] lockdep: pack subclass/trylock/read/check into a single argument

I tried the below but filed to see my vmlinux shrink, maybe I'm just not building the right kernel, or otherwise GCC is stupid.

---
 include/linux/lockdep.h  |   33 ++++++++++++++++++++++++++++++---
 kernel/locking/lockdep.c |   11 +++++++----
 2 files changed, 32 insertions(+), 7 deletions(-)
include/linux/lockdep.h +25 -3
@@ -329,9 +329,31 @@ static inline int lockdep_match_key(stru
  *   0: simple checks (freeing, held-at-exit-time, etc.)
  *   1: full validation
  */
-extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
-			 int trylock, int read, int check,
-			 struct lockdep_map *nest_lock, unsigned long ip);
+struct lockdep_acquire_flags {
+	unsigned long subclass : 3;
+	unsigned long trylock  : 1;
+	unsigned long read     : 2;
+	unsigned long check    : 1;
+};
+
+extern void do_lock_acquire(struct lockdep_map *lock,
+			    struct lockdep_acquire_flags acqf,
+			    struct lockdep_map *nest_lock,
+			    unsigned long ip);
+
+static inline void lock_acquire(struct lockdep_map *lock,
+		unsigned int subclass, int trylock, int read, int check,
+		struct lockdep_map *nest_lock, unsigned long ip)
+{
+	struct lockdep_acquire_flags acqf = {
+		.subclass = subclass,
+		.trylock = trylock,
+		.read = read,
+		.check = check,
+	};
+
+	do_lock_acquire(lock, acqf, nest_lock, ip);
+}
 
 extern void lock_release(struct lockdep_map *lock, int nested,
 			 unsigned long ip);
kernel/locking/lockdep.c +7 -4
@@ -3583,10 +3583,13 @@ EXPORT_SYMBOL_GPL(lock_set_class);
  * We are not always called with irqs disabled - do that here,
  * and also avoid lockdep recursion:
  */
-void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
-			  int trylock, int read, int check,
-			  struct lockdep_map *nest_lock, unsigned long ip)
+void do_lock_acquire(struct lockdep_map *lock, struct lockdep_acquire_flags acqf,
+			struct lockdep_map *nest_lock, unsigned long ip)
 {
+	unsigned int subclass = acqf.subclass;
+	unsigned int trylock = acqf.trylock;
+	unsigned int read = acqf.read;
+	unsigned int check = acqf.check;
 	unsigned long flags;
 
 	if (unlikely(current->lockdep_recursion))
@@ -3602,7 +3605,7 @@ void lock_acquire(struct lockdep_map *lo
 	current->lockdep_recursion = 0;
 	raw_local_irq_restore(flags);
 }
-EXPORT_SYMBOL_GPL(lock_acquire);
+EXPORT_SYMBOL_GPL(do_lock_acquire);
 
 void lock_release(struct lockdep_map *lock, int nested,
 			  unsigned long ip)
#23 lore raw

Re: [PATCH 5/5] lockdep: pack subclass/trylock/read/check into a single argument

Quoted 3 lines · Peter Zijlstra

I tried the below but filed to see my vmlinux shrink, maybe I'm just not building the right kernel, or otherwise GCC is stupid.

...

include/linux/lockdep.h static inline int lockdep_match_key(stru · line 337

Quoted 6 lines
include/linux/lockdep.h +6 -0
+struct lockdep_acquire_flags {
+	unsigned long subclass : 3;
+	unsigned long trylock  : 1;
+	unsigned long read     : 2;
+	unsigned long check    : 1;
+};

Yes, I tried this too. I do see the difference, but the result is worse:

- 5262832
+ 5256987

looks like, gcc is not smart enough...

Oleg.

#24 lore raw

Re: [PATCH 5/5] lockdep: pack subclass/trylock/read/check into a single argument

In reply to Peter Zijlstra

include/linux/lockdep.h static inline int lockdep_match_key(stru · line 337

Quoted 13 lines · Peter Zijlstra

I tried the below but filed to see my vmlinux shrink, maybe I'm just not building the right kernel, or otherwise GCC is stupid.

include/linux/lockdep.h +6 -3
-extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
-			 int trylock, int read, int check,
-			 struct lockdep_map *nest_lock, unsigned long ip);
+struct lockdep_acquire_flags {
+	unsigned long subclass : 3;
+	unsigned long trylock  : 1;
+	unsigned long read     : 2;
+	unsigned long check    : 1;
+};

As it's only 7 bits here, could we pack them into a char ?

Dave

#25 lore raw

Re: [PATCH 5/5] lockdep: pack subclass/trylock/read/check into a single argument

Quoted 6 lines · Dave Jones
Quoted 4 lines · Peter Zijlstra

I tried the below but filed to see my vmlinux shrink, maybe I'm just not building the right kernel, or otherwise GCC is stupid.

Quoted 11 lines

include/linux/lockdep.h static inline int lockdep_match_key(stru · line 337

Quoted 9 lines
include/linux/lockdep.h +6 -3
-extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
-			 int trylock, int read, int check,
-			 struct lockdep_map *nest_lock, unsigned long ip);
+struct lockdep_acquire_flags {
+	unsigned long subclass : 3;
+	unsigned long trylock  : 1;
+	unsigned long read     : 2;
+	unsigned long check    : 1;
+};

As it's only 7 bits here, could we pack them into a char ?

Do you mean __attribute__((packed)) ?

Suprizingly it helps a bit. Still "size vmlinux" is worse than with bitmasks.

But I agreed that the code looks simpler with bitfields, so perhaps this patch is better.

Oleg.

#26 lore raw

Re: [PATCH 5/5] lockdep: pack subclass/trylock/read/check into a single argument

Quoted 17 lines · On Tue, 21 Jan 2014 19:43:10 +0100 Oleg Nesterov <[email protected]> wrote:
Quoted 16 lines · Dave Jones
Quoted 13 lines · Peter Zijlstra

I tried the below but filed to see my vmlinux shrink, maybe I'm just not building the right kernel, or otherwise GCC is stupid.

include/linux/lockdep.h +6 -3
-extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
-			 int trylock, int read, int check,
-			 struct lockdep_map *nest_lock, unsigned long ip);
+struct lockdep_acquire_flags {
+	unsigned long subclass : 3;
+	unsigned long trylock  : 1;
+	unsigned long read     : 2;
+	unsigned long check    : 1;
+};
Quoted 4 lines
Quoted 1 line

As it's only 7 bits here, could we pack them into a char ?

Do you mean __attribute__((packed)) ?

I think Dave means:

	unsigned char subclass	: 3;
	unsigned char trylock	: 1;
	unsigned char read	: 2;
	unsigned char check	: 1;

-- Steve

#27 lore raw

Re: [PATCH 5/5] lockdep: pack subclass/trylock/read/check into a single argument

Quoted 4 lines · Steven Rostedt

On Tue, 21 Jan 2014 19:43:10 +0100
Oleg Nesterov <[email protected]> wrote:

Quoted 8 lines
Quoted 1 line

Do you mean __attribute__((packed)) ?

I think Dave means:

	unsigned char subclass	: 3;
	unsigned char trylock	: 1;
	unsigned char read	: 2;
	unsigned char check	: 1;

Ah, yes. But I don't think this will change the code generation.

Oleg.

#28 lore raw

uninline rcu_lock_acquire/etc ?

In reply to Oleg Nesterov
Quoted 3 lines · Oleg Nesterov

But I agreed that the code looks simpler with bitfields, so perhaps this patch is better.

Besides, I guess the major offender is rcu...

Paul, can't we do something like below? Saves 19.5 kilobytes,

	-       5255131 2974376 10125312        18354819        1181283 vmlinux
	+	5235227 2970344 10125312        18330883        117b503 vmlinux

probably we can also uninline rcu_lockdep_assert()...

Oleg.
---

include/linux/rcupdate.h +35 -32
index 2eef290..58f7a97 100644
@@ -310,18 +310,34 @@ static inline bool rcu_lockdep_current_cpu_online(void)
 }
 #endif /* #else #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */
 
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-
-static inline void rcu_lock_acquire(struct lockdep_map *map)
+static inline void __rcu_lock_acquire(struct lockdep_map *map, unsigned long ip)
 {
-	lock_acquire(map, 0, 0, 2, 0, NULL, _THIS_IP_);
+	lock_acquire(map, 0, 0, 2, 0, NULL, ip);
 }
 
-static inline void rcu_lock_release(struct lockdep_map *map)
+static inline void __rcu_lock_release(struct lockdep_map *map, unsigned long ip)
 {
 	lock_release(map, 1, _THIS_IP_);
 }
 
+#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_PROVE_RCU)
+extern void rcu_lock_acquire(void);
+extern void rcu_lock_release(void);
+extern void rcu_lock_acquire_bh(void);
+extern void rcu_lock_release_bh(void);
+extern void rcu_lock_acquire_sched(void);
+extern void rcu_lock_release_sched(void);
+#else
+#define rcu_lock_acquire()		do { } while (0)
+#define rcu_lock_release()		do { } while (0)
+#define rcu_lock_acquire_bh()		do { } while (0)
+#define rcu_lock_release_bh()		do { } while (0)
+#define rcu_lock_acquire_sched()	do { } while (0)
+#define rcu_lock_release_sched()	do { } while (0)
+#endif
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+
 extern struct lockdep_map rcu_lock_map;
 extern struct lockdep_map rcu_bh_lock_map;
 extern struct lockdep_map rcu_sched_lock_map;
@@ -419,9 +435,6 @@ static inline int rcu_read_lock_sched_held(void)
 
 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
-# define rcu_lock_acquire(a)		do { } while (0)
-# define rcu_lock_release(a)		do { } while (0)
-
 static inline int rcu_read_lock_held(void)
 {
 	return 1;
@@ -766,11 +779,9 @@ static inline void rcu_preempt_sleep_check(void)
  */
 static inline void rcu_read_lock(void)
 {
-	__rcu_read_lock();
 	__acquire(RCU);
-	rcu_lock_acquire(&rcu_lock_map);
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_lock() used illegally while idle");
+	__rcu_read_lock();
+	rcu_lock_acquire();
 }
 
 /*
@@ -790,11 +801,9 @@ static inline void rcu_read_lock(void)
  */
 static inline void rcu_read_unlock(void)
 {
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_unlock() used illegally while idle");
-	rcu_lock_release(&rcu_lock_map);
-	__release(RCU);
+	rcu_lock_release();
 	__rcu_read_unlock();
+	__release(RCU);
 }
 
 /**
@@ -816,11 +825,9 @@ static inline void rcu_read_unlock(void)
  */
 static inline void rcu_read_lock_bh(void)
 {
-	local_bh_disable();
 	__acquire(RCU_BH);
-	rcu_lock_acquire(&rcu_bh_lock_map);
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_lock_bh() used illegally while idle");
+	local_bh_disable();
+	rcu_lock_acquire_bh();
 }
 
 /*
@@ -830,11 +837,9 @@ static inline void rcu_read_lock_bh(void)
  */
 static inline void rcu_read_unlock_bh(void)
 {
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_unlock_bh() used illegally while idle");
-	rcu_lock_release(&rcu_bh_lock_map);
-	__release(RCU_BH);
+	rcu_lock_release_bh();
 	local_bh_enable();
+	__release(RCU_BH);
 }
 
 /**
@@ -852,9 +857,9 @@ static inline void rcu_read_unlock_bh(void)
  */
 static inline void rcu_read_lock_sched(void)
 {
-	preempt_disable();
 	__acquire(RCU_SCHED);
-	rcu_lock_acquire(&rcu_sched_lock_map);
+	preempt_disable();
+	rcu_lock_acquire_sched();
 	rcu_lockdep_assert(rcu_is_watching(),
 			   "rcu_read_lock_sched() used illegally while idle");
 }
@@ -862,8 +867,8 @@ static inline void rcu_read_lock_sched(void)
 /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */
 static inline notrace void rcu_read_lock_sched_notrace(void)
 {
-	preempt_disable_notrace();
 	__acquire(RCU_SCHED);
+	preempt_disable_notrace();
 }
 
 /*
@@ -873,18 +878,16 @@ static inline notrace void rcu_read_lock_sched_notrace(void)
  */
 static inline void rcu_read_unlock_sched(void)
 {
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_unlock_sched() used illegally while idle");
-	rcu_lock_release(&rcu_sched_lock_map);
-	__release(RCU_SCHED);
+	rcu_lock_release_sched();
 	preempt_enable();
+	__release(RCU_SCHED);
 }
 
 /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */
 static inline notrace void rcu_read_unlock_sched_notrace(void)
 {
-	__release(RCU_SCHED);
 	preempt_enable_notrace();
+	__release(RCU_SCHED);
 }
 
 /**
include/linux/srcu.h +2 -2
index 9b058ee..9b0f568 100644
@@ -219,7 +219,7 @@ static inline int srcu_read_lock(struct srcu_struct *sp) __acquires(sp)
 {
 	int retval = __srcu_read_lock(sp);
 
-	rcu_lock_acquire(&(sp)->dep_map);
+	__rcu_lock_acquire(&(sp)->dep_map, _THIS_IP_);
 	return retval;
 }
 
@@ -233,7 +233,7 @@ static inline int srcu_read_lock(struct srcu_struct *sp) __acquires(sp)
 static inline void srcu_read_unlock(struct srcu_struct *sp, int idx)
 	__releases(sp)
 {
-	rcu_lock_release(&(sp)->dep_map);
+	__rcu_lock_release(&(sp)->dep_map, _THIS_IP_);
 	__srcu_read_unlock(sp, idx);
 }
 
kernel/rcu/update.c +43 -0

47 lines not on this page · raw message

#29 lore raw

Re: uninline rcu_lock_acquire/etc ?

Quoted 5 lines · Oleg Nesterov
Quoted 3 lines · Oleg Nesterov

But I agreed that the code looks simpler with bitfields, so perhaps this patch is better.

Quoted 8 lines

Besides, I guess the major offender is rcu...

Paul, can't we do something like below? Saves 19.5 kilobytes,

	-       5255131 2974376 10125312        18354819        1181283 vmlinux
	+	5235227 2970344 10125312        18330883        117b503 vmlinux

probably we can also uninline rcu_lockdep_assert()...

Looks mostly plausible, some questions inline below.

Thanx, Paul

Quoted 58 lines

Oleg.
---

include/linux/rcupdate.h +21 -8
index 2eef290..58f7a97 100644
@@ -310,18 +310,34 @@ static inline bool rcu_lockdep_current_cpu_online(void)
 }
 #endif /* #else #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */
 
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-
-static inline void rcu_lock_acquire(struct lockdep_map *map)
+static inline void __rcu_lock_acquire(struct lockdep_map *map, unsigned long ip)
 {
-	lock_acquire(map, 0, 0, 2, 0, NULL, _THIS_IP_);
+	lock_acquire(map, 0, 0, 2, 0, NULL, ip);
 }
 
-static inline void rcu_lock_release(struct lockdep_map *map)
+static inline void __rcu_lock_release(struct lockdep_map *map, unsigned long ip)
 {
 	lock_release(map, 1, _THIS_IP_);
 }
 
+#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_PROVE_RCU)
+extern void rcu_lock_acquire(void);
+extern void rcu_lock_release(void);
+extern void rcu_lock_acquire_bh(void);
+extern void rcu_lock_release_bh(void);
+extern void rcu_lock_acquire_sched(void);
+extern void rcu_lock_release_sched(void);
+#else
+#define rcu_lock_acquire()		do { } while (0)
+#define rcu_lock_release()		do { } while (0)
+#define rcu_lock_acquire_bh()		do { } while (0)
+#define rcu_lock_release_bh()		do { } while (0)
+#define rcu_lock_acquire_sched()	do { } while (0)
+#define rcu_lock_release_sched()	do { } while (0)
+#endif
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+
 extern struct lockdep_map rcu_lock_map;
 extern struct lockdep_map rcu_bh_lock_map;
 extern struct lockdep_map rcu_sched_lock_map;
@@ -419,9 +435,6 @@ static inline int rcu_read_lock_sched_held(void)
 
 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
-# define rcu_lock_acquire(a)		do { } while (0)
-# define rcu_lock_release(a)		do { } while (0)
-
 static inline int rcu_read_lock_held(void)
 {
 	return 1;

include/linux/rcupdate.h static inline void rcu_preempt_sleep_check(void) · line 784

Quoted 10 lines
include/linux/rcupdate.h +2 -4
@@ -766,11 +779,9 @@ static inline void rcu_preempt_sleep_check(void)
  */
 static inline void rcu_read_lock(void)
 {
-	__rcu_read_lock();
 	__acquire(RCU);
-	rcu_lock_acquire(&rcu_lock_map);
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_lock() used illegally while idle");
+	__rcu_read_lock();
+	rcu_lock_acquire();

Not sure why __rcu_read_lock() needs to be in any particular order with respect to the sparse __acquire(RCU), but should work either way. Same question about the other reorderings of similar statements.

Quoted 46 lines
include/linux/rcupdate.h +6 -12
 }
 
 /*
@@ -790,11 +801,9 @@ static inline void rcu_read_lock(void)
  */
 static inline void rcu_read_unlock(void)
 {
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_unlock() used illegally while idle");
-	rcu_lock_release(&rcu_lock_map);
-	__release(RCU);
+	rcu_lock_release();
 	__rcu_read_unlock();
+	__release(RCU);
 }
 
 /**
@@ -816,11 +825,9 @@ static inline void rcu_read_unlock(void)
  */
 static inline void rcu_read_lock_bh(void)
 {
-	local_bh_disable();
 	__acquire(RCU_BH);
-	rcu_lock_acquire(&rcu_bh_lock_map);
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_lock_bh() used illegally while idle");
+	local_bh_disable();
+	rcu_lock_acquire_bh();
 }
 
 /*
@@ -830,11 +837,9 @@ static inline void rcu_read_lock_bh(void)
  */
 static inline void rcu_read_unlock_bh(void)
 {
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_unlock_bh() used illegally while idle");
-	rcu_lock_release(&rcu_bh_lock_map);
-	__release(RCU_BH);
+	rcu_lock_release_bh();
 	local_bh_enable();
+	__release(RCU_BH);
 }
 
 /**

include/linux/rcupdate.h static inline void rcu_read_unlock_bh(void) · line 864

Quoted 10 lines
include/linux/rcupdate.h +2 -2
@@ -852,9 +857,9 @@ static inline void rcu_read_unlock_bh(void)
  */
 static inline void rcu_read_lock_sched(void)
 {
-	preempt_disable();
 	__acquire(RCU_SCHED);
-	rcu_lock_acquire(&rcu_sched_lock_map);
+	preempt_disable();
+	rcu_lock_acquire_sched();
 	rcu_lockdep_assert(rcu_is_watching(),
 			   "rcu_read_lock_sched() used illegally while idle");

The above pair of lines (rcu_lockdep_assert()) should also be removed, correct?

include/linux/rcupdate.h static inline void rcu_read_lock_sched(void) · line 871

Quoted 8 lines
include/linux/rcupdate.h +1 -1
 }
@@ -862,8 +867,8 @@ static inline void rcu_read_lock_sched(void)
 /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */
 static inline notrace void rcu_read_lock_sched_notrace(void)
 {
-	preempt_disable_notrace();
 	__acquire(RCU_SCHED);
+	preempt_disable_notrace();

I cannot help repeating myself on this one... ;-)

Why the change in order?

Quoted 30 lines
include/linux/rcupdate.h +3 -5
 }
 
 /*
@@ -873,18 +878,16 @@ static inline notrace void rcu_read_lock_sched_notrace(void)
  */
 static inline void rcu_read_unlock_sched(void)
 {
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_unlock_sched() used illegally while idle");
-	rcu_lock_release(&rcu_sched_lock_map);
-	__release(RCU_SCHED);
+	rcu_lock_release_sched();
 	preempt_enable();
+	__release(RCU_SCHED);
 }
 
 /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */
 static inline notrace void rcu_read_unlock_sched_notrace(void)
 {
-	__release(RCU_SCHED);
 	preempt_enable_notrace();
+	__release(RCU_SCHED);
 }
 
 /**
include/linux/srcu.h +0 -0
index 9b058ee..9b0f568 100644

include/linux/srcu.h static inline int srcu_read_lock(struct srcu_struct *sp) __acquires(sp) · line 222

Quoted 5 lines
include/linux/srcu.h +1 -1
@@ -219,7 +219,7 @@ static inline int srcu_read_lock(struct srcu_struct *sp) __acquires(sp)
 {
 	int retval = __srcu_read_lock(sp);
 
-	rcu_lock_acquire(&(sp)->dep_map);
+	__rcu_lock_acquire(&(sp)->dep_map, _THIS_IP_);

Good, we do not way srcu_read_lock() complaining about offline or idle CPUs.

Quoted 65 lines
include/linux/srcu.h +1 -1
 	return retval;
 }
 
@@ -233,7 +233,7 @@ static inline int srcu_read_lock(struct srcu_struct *sp) __acquires(sp)
 static inline void srcu_read_unlock(struct srcu_struct *sp, int idx)
 	__releases(sp)
 {
-	rcu_lock_release(&(sp)->dep_map);
+	__rcu_lock_release(&(sp)->dep_map, _THIS_IP_);
 	__srcu_read_unlock(sp, idx);
 }
 
kernel/rcu/update.c +43 -0
index a3596c8..19ff915 100644
@@ -333,4 +333,47 @@ static int __init check_cpu_stall_init(void)
 }
 early_initcall(check_cpu_stall_init);
 
+#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_PROVE_RCU)
+
+static void ck_rcu_is_watching(const char *message)
+{
+	rcu_lockdep_assert(rcu_is_watching(), message);
+}
+
+void rcu_lock_acquire(void)
+{
+	__rcu_lock_acquire(&rcu_lock_map, _RET_IP_);
+	ck_rcu_is_watching("rcu_read_lock() used illegally while idle");
+}
+
+void rcu_lock_release(void)
+{
+	ck_rcu_is_watching("rcu_read_unlock() used illegally while idle");
+	__rcu_lock_release(&rcu_lock_map, _RET_IP_);
+}
+
+void rcu_lock_acquire_bh(void)
+{
+	__rcu_lock_acquire(&rcu_bh_lock_map, _RET_IP_);
+	ck_rcu_is_watching("rcu_read_lock_bh() used illegally while idle");
+}
+
+void rcu_lock_release_bh(void)
+{
+	ck_rcu_is_watching("rcu_read_unlock_bh() used illegally while idle");
+	__rcu_lock_release(&rcu_bh_lock_map, _RET_IP_);
+}
+void rcu_lock_acquire_sched(void)
+{
+	__rcu_lock_acquire(&rcu_sched_lock_map, _RET_IP_);
+	ck_rcu_is_watching("rcu_read_lock_sched() used illegally while idle");
+}
+
+void rcu_lock_release_sched(void)
+{
+	ck_rcu_is_watching("rcu_read_unlock_sched() used illegally while idle");
+	__rcu_lock_release(&rcu_sched_lock_map, _RET_IP_);
+}
+#endif
+
 #endif /* #ifdef CONFIG_RCU_STALL_COMMON */
#30 lore raw

Re: uninline rcu_lock_acquire/etc ?

Quoted 9 lines · Paul E. McKenney
Quoted 7 lines · Oleg Nesterov
Quoted 3 lines · Oleg Nesterov

But I agreed that the code looks simpler with bitfields, so perhaps this patch is better.

Besides, I guess the major offender is rcu...

Quoted 8 lines
Quoted 6 lines

Paul, can't we do something like below? Saves 19.5 kilobytes,

	-       5255131 2974376 10125312        18354819        1181283 vmlinux
	+	5235227 2970344 10125312        18330883        117b503 vmlinux

probably we can also uninline rcu_lockdep_assert()...

Looks mostly plausible, some questions inline below.

Thanks!

Quoted 5 lines
Quoted 5 lines

static inline void rcu_read_lock(void)
{

include/linux/rcupdate.h +0 -2
-	__rcu_read_lock();
 	__acquire(RCU);
-	rcu_lock_acquire(&rcu_lock_map);
Quoted 8 lines

include/linux/rcupdate.h static inline void rcu_preempt_sleep_check(void) · line 784

Quoted 4 lines
include/linux/rcupdate.h +2 -2
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_lock() used illegally while idle");
+	__rcu_read_lock();
+	rcu_lock_acquire();

Not sure why __rcu_read_lock() needs to be in any particular order with respect to the sparse __acquire(RCU), but should work either way. Same question about the other reorderings of similar statements.

I did this unconsciously and for no reason, will revert this accidental change.

Quoted 4 lines
Quoted 4 lines

static inline void rcu_read_lock_sched(void)
{

include/linux/rcupdate.h +0 -1
-	preempt_disable();
 	__acquire(RCU_SCHED);
Quoted 8 lines

include/linux/rcupdate.h static inline void rcu_read_unlock_bh(void) · line 864

Quoted 5 lines
include/linux/rcupdate.h +2 -1
-	rcu_lock_acquire(&rcu_sched_lock_map);
+	preempt_disable();
+	rcu_lock_acquire_sched();
 	rcu_lockdep_assert(rcu_is_watching(),
 			   "rcu_read_lock_sched() used illegally while idle");

The above pair of lines (rcu_lockdep_assert()) should also be removed, correct?

yes, sure, thanks,

Quoted 11 lines

include/linux/rcupdate.h static inline void rcu_read_lock_sched(void) · line 871

Quoted 7 lines
include/linux/rcupdate.h +1 -1
@@ -862,8 +867,8 @@ static inline void rcu_read_lock_sched(void)
 /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */
 static inline notrace void rcu_read_lock_sched_notrace(void)
 {
-	preempt_disable_notrace();
 	__acquire(RCU_SCHED);
+	preempt_disable_notrace();

I cannot help repeating myself on this one... ;-)

Why the change in order?

see above ;)

Quoted 17 lines
Quoted 17 lines
kernel/rcu/update.c +11 -0
@@ -333,4 +333,47 @@ static int __init check_cpu_stall_init(void)
 }
 early_initcall(check_cpu_stall_init);
 
+#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_PROVE_RCU)
+
+static void ck_rcu_is_watching(const char *message)
+{
+	rcu_lockdep_assert(rcu_is_watching(), message);
+}
+
+void rcu_lock_acquire(void)
+{
+	__rcu_lock_acquire(&rcu_lock_map, _RET_IP_);
+	ck_rcu_is_watching("rcu_read_lock() used illegally while idle");
Quoted 8 lines

kernel/rcu/update.c static int __init check_cpu_stall_init(void) · line 353

Quoted 8 lines
kernel/rcu/update.c +7 -0
+}
+
+void rcu_lock_release(void)
+{
+	ck_rcu_is_watching("rcu_read_unlock() used illegally while idle");
+	__rcu_lock_release(&rcu_lock_map, _RET_IP_);
+}
...

Also, this all should be exported. And I think cleanuped somehow.

Oleg.

#31 lore raw

Re: uninline rcu_lock_acquire/etc ?

Quoted 86 lines · Oleg Nesterov
Quoted 9 lines · Paul E. McKenney
Quoted 7 lines · Oleg Nesterov
Quoted 3 lines · Oleg Nesterov

But I agreed that the code looks simpler with bitfields, so perhaps this patch is better.

Besides, I guess the major offender is rcu...

Quoted 8 lines
Quoted 6 lines

Paul, can't we do something like below? Saves 19.5 kilobytes,

	-       5255131 2974376 10125312        18354819        1181283 vmlinux
	+	5235227 2970344 10125312        18330883        117b503 vmlinux

probably we can also uninline rcu_lockdep_assert()...

Looks mostly plausible, some questions inline below.

Thanks!

Quoted 5 lines
Quoted 5 lines

static inline void rcu_read_lock(void)
{

include/linux/rcupdate.h +0 -2
-	__rcu_read_lock();
 	__acquire(RCU);
-	rcu_lock_acquire(&rcu_lock_map);
Quoted 8 lines

include/linux/rcupdate.h static inline void rcu_preempt_sleep_check(void) · line 784

Quoted 4 lines
include/linux/rcupdate.h +2 -2
-	rcu_lockdep_assert(rcu_is_watching(),
-			   "rcu_read_lock() used illegally while idle");
+	__rcu_read_lock();
+	rcu_lock_acquire();

Not sure why __rcu_read_lock() needs to be in any particular order with respect to the sparse __acquire(RCU), but should work either way. Same question about the other reorderings of similar statements.

I did this unconsciously and for no reason, will revert this accidental change.

Quoted 4 lines
Quoted 4 lines

static inline void rcu_read_lock_sched(void)
{

include/linux/rcupdate.h +0 -1
-	preempt_disable();
 	__acquire(RCU_SCHED);
Quoted 8 lines

include/linux/rcupdate.h static inline void rcu_read_unlock_bh(void) · line 864

Quoted 5 lines
include/linux/rcupdate.h +2 -1
-	rcu_lock_acquire(&rcu_sched_lock_map);
+	preempt_disable();
+	rcu_lock_acquire_sched();
 	rcu_lockdep_assert(rcu_is_watching(),
 			   "rcu_read_lock_sched() used illegally while idle");

The above pair of lines (rcu_lockdep_assert()) should also be removed, correct?

yes, sure, thanks,

Quoted 11 lines

include/linux/rcupdate.h static inline void rcu_read_lock_sched(void) · line 871

Quoted 7 lines
include/linux/rcupdate.h +1 -1
@@ -862,8 +867,8 @@ static inline void rcu_read_lock_sched(void)
 /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */
 static inline notrace void rcu_read_lock_sched_notrace(void)
 {
-	preempt_disable_notrace();
 	__acquire(RCU_SCHED);
+	preempt_disable_notrace();

I cannot help repeating myself on this one... ;-)

Why the change in order?

see above ;)

Quoted 19 lines
Quoted 19 lines
kernel/rcu/update.c +13 -0
@@ -333,4 +333,47 @@ static int __init check_cpu_stall_init(void)
 }
 early_initcall(check_cpu_stall_init);
 
+#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_PROVE_RCU)
+
+static void ck_rcu_is_watching(const char *message)
+{
+	rcu_lockdep_assert(rcu_is_watching(), message);
+}
+
+void rcu_lock_acquire(void)
+{
+	__rcu_lock_acquire(&rcu_lock_map, _RET_IP_);
+	ck_rcu_is_watching("rcu_read_lock() used illegally while idle");
+}
+
Quoted 8 lines
Quoted 6 lines

kernel/rcu/update.c static int __init check_cpu_stall_init(void) · line 353

Quoted 6 lines
kernel/rcu/update.c +5 -0
+void rcu_lock_release(void)
+{
+	ck_rcu_is_watching("rcu_read_unlock() used illegally while idle");
+	__rcu_lock_release(&rcu_lock_map, _RET_IP_);
+}
...

Also, this all should be exported. And I think cleanuped somehow.

I would be happy to take a patch with the above issues fixed.

Thanx, Paul

#32 lore raw

Re: uninline rcu_lock_acquire/etc ?

Quoted 6 lines · Paul E. McKenney
Quoted 2 lines · Oleg Nesterov

Also, this all should be exported. And I think cleanuped somehow.

I would be happy to take a patch with the above issues fixed.

Yes, yes, thanks.

Sorry for noise, I forgot to say that of course I'll try to make v2, but I will be offline a couple of days.

Oleg.

#33 lore raw

Re: [PATCH 0/5] lockdep: (Was: check && lockdep_no_validate)

In reply to Oleg Nesterov
Quoted 25 lines · Oleg Nesterov
Quoted 8 lines · Alan Stern
Quoted 4 lines · Oleg Nesterov

Yes, sure. This change assumes that the only problem in drivers/base is dev->parent->mutex / dev->mutex dependency. If the locking is even more "broken" (wrt lockdep), we can't replace lockdep_set_novalidate_class() with lockdep_set_auto_nested().

Quoted 7 lines

I suspect it is even more "broken". But I can't point to specific examples.

...

My guess is that if your change is deployed widely, there will be reports of violations. That's only a guess.

OK, lets (try to) do this later. Let me send the changes which I hope should be fine in any case.

Quoted 1 line

Still, you could go ahead and try it, just to see what happens.

Yes, perhaps it makes sense at least to test this change and see what happens... We will see.

Quoted 6 lines
Quoted 2 lines

Also, take a look at commit 356c05d58af0. It's a similar situation (not exactly the same).

At first glance, can't __ATTR_IGNORE_LOCKDEP() use no_validate too ? (ignoring the fact checkpatch.pl won't be happy). This can simplify the code, it seems.

Well, the macro itself doesn't specify the lockdep class. That happens implicitly in sysfs_get_active(), in the call to rwsem_acquire_read(). However, it ought to be possible to change the code so that when ignore_lockdep(sd) returns nonzero, we end up using no_validate.

Alan Stern

#34 lore raw

Re: [PATCH 0/5] lockdep: (Was: check && lockdep_no_validate)

Quoted 8 lines · Alan Stern
Quoted 5 lines · Oleg Nesterov
Quoted 3 lines · Alan Stern

Also, take a look at commit 356c05d58af0. It's a similar situation (not exactly the same).

Quoted 8 lines
Quoted 3 lines

At first glance, can't __ATTR_IGNORE_LOCKDEP() use no_validate too ? (ignoring the fact checkpatch.pl won't be happy). This can simplify the code, it seems.

Well, the macro itself doesn't specify the lockdep class. That happens implicitly in sysfs_get_active(), in the call to rwsem_acquire_read(). However, it ought to be possible to change the code so that when ignore_lockdep(sd) returns nonzero, we end up using no_validate.

sysfs_dirent_init_lockdep() can check ->ignore_lockdep and do lockdep_set_novalidate_class(). This way sysfs_ignore_lockdep() can go away.

I guess we could even change __ATTR_IGNORE_LOCKDEP() to initialize ->key = __lockdep_no_validate__ and kill ->ignore_lockdep.

Oleg.

#35 lore raw

Re: [PATCH 0/5] lockdep: (Was: check && lockdep_no_validate)

Quoted 9 lines · Oleg Nesterov
Quoted 8 lines
Quoted 3 lines

At first glance, can't __ATTR_IGNORE_LOCKDEP() use no_validate too ? (ignoring the fact checkpatch.pl won't be happy). This can simplify the code, it seems.

Well, the macro itself doesn't specify the lockdep class. That happens implicitly in sysfs_get_active(), in the call to rwsem_acquire_read(). However, it ought to be possible to change the code so that when ignore_lockdep(sd) returns nonzero, we end up using no_validate.

Quoted 6 lines

sysfs_dirent_init_lockdep() can check ->ignore_lockdep and do lockdep_set_novalidate_class(). This way sysfs_ignore_lockdep() can go away.

I guess we could even change __ATTR_IGNORE_LOCKDEP() to initialize ->key = __lockdep_no_validate__ and kill ->ignore_lockdep.

It's clear that you have a more thorough understanding of how sysfs and lockdep work than I do. :-)

This suggestion sounds quite reasonable.

Alan Stern

#36 lore raw
In reply to Peter Zijlstra
Quoted 4 lines · Peter Zijlstra
Quoted 3 lines

But what I really can't understans is what "check == 0" means? It seems that in fact it can be 1 or 2? Or, iow, "check == 0" is actually equivalent to "check == 1" ?

Quoted 8 lines

Hmm indeed, the comment in lockdep.h says 0 means no checks at all, but the code doesn't actually appear to work like that. I'm not sure it ever did or not, I'd have to go dig through history.

That said, given the current state it certainly looks like we can remove the check argument.

Ingo?

Agreed.

Thanks,

Ingo

#37 lore raw

cover lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

Quoted 11 lines · Ingo Molnar
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines

But what I really can't understans is what "check == 0" means? It seems that in fact it can be 1 or 2? Or, iow, "check == 0" is actually equivalent to "check == 1" ?

Hmm indeed, the comment in lockdep.h says 0 means no checks at all, but the code doesn't actually appear to work like that. I'm not sure it ever did or not, I'd have to go dig through history.

Quoted 6 lines
Quoted 4 lines

That said, given the current state it certainly looks like we can remove the check argument.

Ingo?

Agreed.

OK, could you and Peter review the patch?

If it passes the review I'll send another one which changes the callers of lock_acquire(). And trace_lock_acquire() should be trivially updated too.

But could someone please explain me what should lockdep_no_validate actually do? 1704f47b5 "lockdep: Add novalidate class for dev->mutex conversion" doesn't tell which kind of warnings it tries to avoid, and it looks buggy (see another email from me).

Oleg.

#38 lore raw

PATCH 1/1 lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

The "int check" argument of lock_acquire() and held_lock->check are misleading and unneeded. This is only used as a boolean, 2 denotes "true", everything else is "false". And this boolean is always equal to prove_locking.

The only exception is __lockdep_no_validate__ which should make this condition "false" in validate_chain().

This patch kills this member and removes the argument from __lock_acquire(), the next patch will remove the now unused arg from lock_acquire() and update its callers.

---
 include/linux/lockdep.h  |    1 -
 kernel/locking/lockdep.c |   22 ++++++++--------------
 2 files changed, 8 insertions(+), 15 deletions(-)
include/linux/lockdep.h +0 -1
index 92b1bfc..13bd13d 100644
@@ -252,7 +252,6 @@ struct held_lock {
 	unsigned int trylock:1;						/* 16 bits */
 
 	unsigned int read:2;        /* see lock_acquire() comment */
-	unsigned int check:2;       /* see lock_acquire() comment */
 	unsigned int hardirqs_off:1;
 	unsigned int references:11;					/* 32 bits */
 };
kernel/locking/lockdep.c +8 -14
index 576ba75..32ba948 100644
@@ -2096,7 +2096,8 @@ static int validate_chain(struct task_struct *curr, struct lockdep_map *lock,
 	 * (If lookup_chain_cache() returns with 1 it acquires
 	 * graph_lock for us)
 	 */
-	if (!hlock->trylock && (hlock->check == 2) &&
+	if (!hlock->trylock && prove_locking &&
+	    hlock_class(hlock)->key != __lockdep_no_validate__.subkeys &&
 	    lookup_chain_cache(curr, hlock, chain_key)) {
 		/*
 		 * Check whether last held lock:
@@ -3041,7 +3042,7 @@ static int __lock_is_held(struct lockdep_map *lock);
  * We maintain the dependency maps and validate the locking attempt:
  */
 static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
-			  int trylock, int read, int check, int hardirqs_off,
+			  int trylock, int read, int hardirqs_off,
 			  struct lockdep_map *nest_lock, unsigned long ip,
 			  int references)
 {
@@ -3053,9 +3054,6 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	int class_idx;
 	u64 chain_key;
 
-	if (!prove_locking)
-		check = 1;
-
 	if (unlikely(!debug_locks))
 		return 0;
 
@@ -3067,9 +3065,6 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
 		return 0;
 
-	if (lock->key == &__lockdep_no_validate__)
-		check = 1;
-
 	if (subclass < NR_LOCKDEP_CACHING_CLASSES)
 		class = lock->class_cache[subclass];
 	/*
@@ -3128,7 +3123,6 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	hlock->nest_lock = nest_lock;
 	hlock->trylock = trylock;
 	hlock->read = read;
-	hlock->check = check;
 	hlock->hardirqs_off = !!hardirqs_off;
 	hlock->references = references;
 #ifdef CONFIG_LOCK_STAT
@@ -3136,7 +3130,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	hlock->holdtime_stamp = lockstat_clock();
 #endif
 
-	if (check == 2 && !mark_irqflags(curr, hlock))
+	if (prove_locking && !mark_irqflags(curr, hlock))
 		return 0;
 
 	/* mark it as used: */
@@ -3338,7 +3332,7 @@ found_it:
 		hlock = curr->held_locks + i;
 		if (!__lock_acquire(hlock->instance,
 			hlock_class(hlock)->subclass, hlock->trylock,
-				hlock->read, hlock->check, hlock->hardirqs_off,
+				hlock->read, hlock->hardirqs_off,
 				hlock->nest_lock, hlock->acquire_ip,
 				hlock->references))
 			return 0;
@@ -3422,7 +3416,7 @@ found_it:
 		hlock = curr->held_locks + i;
 		if (!__lock_acquire(hlock->instance,
 			hlock_class(hlock)->subclass, hlock->trylock,
-				hlock->read, hlock->check, hlock->hardirqs_off,
+				hlock->read, hlock->hardirqs_off,
 				hlock->nest_lock, hlock->acquire_ip,
 				hlock->references))
 			return 0;
@@ -3598,8 +3592,8 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 
 	current->lockdep_recursion = 1;
 	trace_lock_acquire(lock, subclass, trylock, read, check, nest_lock, ip);
-	__lock_acquire(lock, subclass, trylock, read, check,
-		       irqs_disabled_flags(flags), nest_lock, ip, 0);
+	__lock_acquire(lock, subclass, trylock, read,
+			irqs_disabled_flags(flags), nest_lock, ip, 0);
 	current->lockdep_recursion = 0;
 	raw_local_irq_restore(flags);
 }
Signature · 1 line
1.5.5.1
#39 lore raw

Re: [PATCH 1/1] lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

include/linux/lockdep.h struct held_lock { · line 256

Quoted 11 lines · Oleg Nesterov
include/linux/lockdep.h +0 -1
index 92b1bfc..13bd13d 100644
@@ -252,7 +252,6 @@ struct held_lock {
 	unsigned int trylock:1;						/* 16 bits */
 
 	unsigned int read:2;        /* see lock_acquire() comment */
-	unsigned int check:2;       /* see lock_acquire() comment */
 	unsigned int hardirqs_off:1;
 	unsigned int references:11;					/* 32 bits */

The 'bits' comments seem to be crap, even before your patch.

Dave

#40 lore raw

Re: [PATCH 1/1] lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

Quoted 10 lines · Dave Jones
Quoted 7 lines · Oleg Nesterov
include/linux/lockdep.h +0 -0
index 92b1bfc..13bd13d 100644
@@ -252,7 +252,6 @@ struct held_lock {
 	unsigned int trylock:1;						/* 16 bits */
 
Quoted 6 lines

include/linux/lockdep.h struct held_lock { · line 256

Quoted 4 lines
include/linux/lockdep.h +0 -1
 	unsigned int read:2;        /* see lock_acquire() comment */
-	unsigned int check:2;       /* see lock_acquire() comment */
 	unsigned int hardirqs_off:1;
 	unsigned int references:11;					/* 32 bits */

The 'bits' comments seem to be crap, even before your patch.

Hmm, I didn't even notice them.

I think the comments were correct, note that the previous field is uint:13.

But after (with) this patch the width of "references" should be updated.

Thanks,

Oleg.

#41 lore raw

Re: [PATCH 1/1] lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

In reply to Oleg Nesterov
Quoted 8 lines · Oleg Nesterov

The "int check" argument of lock_acquire() and held_lock->check are misleading and unneeded. This is only used as a boolean, 2 denotes "true", everything else is "false". And this boolean is always equal to prove_locking.

The only exception is __lockdep_no_validate__ which should make this condition "false" in validate_chain().

And I missed mark_irqflags(),

kernel/locking/lockdep.c static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, · line 3134

Quoted 7 lines
kernel/locking/lockdep.c +1 -1
@@ -3136,7 +3130,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	hlock->holdtime_stamp = lockstat_clock();
 #endif
 
-	if (check == 2 && !mark_irqflags(curr, hlock))
+	if (prove_locking && !mark_irqflags(curr, hlock))
 		return 0;

This change is not right, at least it is not equivalent.

And I just realized that rcu_lock_acquire() does lock_acquire(check => 1). Probably we can mark rcu_lock_map's as __lockdep_no_validate__.

Anything else I missed?

Oleg.

author maintainer
#42 lore raw

Re: [PATCH 1/1] lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

Quoted 16 lines · Oleg Nesterov
Quoted 8 lines · Oleg Nesterov

The "int check" argument of lock_acquire() and held_lock->check are misleading and unneeded. This is only used as a boolean, 2 denotes "true", everything else is "false". And this boolean is always equal to prove_locking.

The only exception is __lockdep_no_validate__ which should make this condition "false" in validate_chain().

And I missed mark_irqflags(),

Quoted 4 lines
kernel/locking/lockdep.c +0 -0
@@ -3136,7 +3130,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	hlock->holdtime_stamp = lockstat_clock();
 #endif
 
Quoted 8 lines

kernel/locking/lockdep.c static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, · line 3134

Quoted 3 lines
kernel/locking/lockdep.c +1 -1
-	if (check == 2 && !mark_irqflags(curr, hlock))
+	if (prove_locking && !mark_irqflags(curr, hlock))
 		return 0;

This change is not right, at least it is not equivalent.

And I just realized that rcu_lock_acquire() does lock_acquire(check => 1). Probably we can mark rcu_lock_map's as __lockdep_no_validate__.

Can't, RCU needs its own classes. Otherwise it cannot tell which version of the RCU read lock its holding at just that moment.

Quoted 1 line

Anything else I missed?

Nothing springs to mind, but then, I totally missed the RCU thing too.

At the very least we can reduce check to a single bit.

#43 lore raw

Re: [PATCH 1/1] lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Oleg Nesterov

And I just realized that rcu_lock_acquire() does lock_acquire(check => 1). Probably we can mark rcu_lock_map's as __lockdep_no_validate__.

Can't, RCU needs its own classes. Otherwise it cannot tell which version of the RCU read lock its holding at just that moment.

Ah, indeed. Thanks.

Can't it do lock_acquire(trylock => 1, read => 2) ? this still means mark_irqflags(), but perhaps this won't hurt too much.

Quoted 1 line

At the very least we can reduce check to a single bit.

Or this, yes.

Oleg.

#44 lore raw

Re: [PATCH 1/1] lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

In reply to Peter Zijlstra
Quoted 23 lines · Peter Zijlstra
Quoted 22 lines · Oleg Nesterov
Quoted 8 lines · Oleg Nesterov

The "int check" argument of lock_acquire() and held_lock->check are misleading and unneeded. This is only used as a boolean, 2 denotes "true", everything else is "false". And this boolean is always equal to prove_locking.

The only exception is __lockdep_no_validate__ which should make this condition "false" in validate_chain().

And I missed mark_irqflags(),

kernel/locking/lockdep.c static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, · line 3134

Quoted 7 lines
kernel/locking/lockdep.c +1 -1
@@ -3136,7 +3130,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	hlock->holdtime_stamp = lockstat_clock();
 #endif
 
-	if (check == 2 && !mark_irqflags(curr, hlock))
+	if (prove_locking && !mark_irqflags(curr, hlock))
 		return 0;

This change is not right, at least it is not equivalent.

Quoted 5 lines
Quoted 2 lines

And I just realized that rcu_lock_acquire() does lock_acquire(check => 1). Probably we can mark rcu_lock_map's as __lockdep_no_validate__.

Can't, RCU needs its own classes. Otherwise it cannot tell which version of the RCU read lock its holding at just that moment.

Just confirming this. RCU uses this to detected mismatches between the rcu_read_lock() group and the rcu_dereference() group.

Thanx, Paul

Quoted 6 lines
Quoted 1 line

Anything else I missed?

Nothing springs to mind, but then, I totally missed the RCU thing too.

At the very least we can reduce check to a single bit.

author maintainer
#45 lore raw

Re: [PATCH 0/1] lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

In reply to Oleg Nesterov
Quoted 4 lines · Oleg Nesterov

But could someone please explain me what should lockdep_no_validate actually do? 1704f47b5 "lockdep: Add novalidate class for dev->mutex conversion" doesn't tell which kind of warnings it tries to avoid, and it looks buggy (see another email from me).

It should disable lock-order checks for the locks that set that as class. So we can still do the simple checks like detect if we free memory with a held lock in and make sure we do not return to userspace with a held lock; but the class should not participate in the lock graph.

I'll have a look at your earlier email again; I did note the question but it slipped my mind :/

#46 lore raw

Re: [PATCH 0/1] lockdep: Kill held_lock->check and "int check" arg of __lock_acquire()

Quoted 7 lines · Peter Zijlstra
Quoted 4 lines · Oleg Nesterov

But could someone please explain me what should lockdep_no_validate actually do? 1704f47b5 "lockdep: Add novalidate class for dev->mutex conversion" doesn't tell which kind of warnings it tries to avoid, and it looks buggy (see another email from me).

Quoted 5 lines

It should disable lock-order checks for the locks that set that as class. So we can still do the simple checks like detect if we free memory with a held lock in and make sure we do not return to userspace with a held lock; but the class should not participate in the lock graph.

Yes, this is what I more or less understood, but I was confused anyway. Somehow I thought that this patch tries to suppress the warnings caused by rw_semaphore -> mutex conversion, and I was completely puzzled. But 3142788b79 "drivers/base: Convert dev->sem to mutex" actually did the semaphore -> mutex change!

Everything is clear now, thanks!

Oleg.

#47 lore raw
In reply to Peter Zijlstra
Quoted 5 lines · Peter Zijlstra

Subject: lockdep: Introduce wait-type checks
From: Peter Zijlstra <[email protected]>
Date: Tue, 19 Nov 2013 21:45:48 +0100

This patch extends lockdep to validate lock wait-type context.

ooh, a new toy.

*boom*

[    0.298629] =============================
[    0.298732] [ BUG: Invalid wait context ]
[    0.298834] 3.13.0-rc7+ #15 Not tainted
[    0.298935] -----------------------------
[    0.299038] swapper/0/1 is trying to lock:
[    0.299135]  (&n->list_lock){......}-{3:3}, at: [<ffffffff816dea54>] get_partial_node.isra.49+0x4d/0x228
[    0.299453] 
stack backtrace:
[    0.299608] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.13.0-rc7+ #15 
[    0.299983]  0000000000000001 ffff880243f37a00 ffffffff816dfe5b 0000000000000014
[    0.300302]  ffff880243f37a78 ffffffff8109f1f7 0000000000000000 ffff880243f37a78
[    0.300611]  0000000000000046 ffffffff81189ae3 ffffffff00000000 0000000000000046
[    0.300927] Call Trace:
[    0.301028]  [<ffffffff816dfe5b>] dump_stack+0x4e/0x7a
[    0.301128]  [<ffffffff8109f1f7>] __lock_acquire.isra.28+0x3d7/0xd80
[    0.301238]  [<ffffffff81189ae3>] ? deactivate_slab+0x3c3/0x740
[    0.301345]  [<ffffffff810a030d>] lock_acquire+0x8d/0x120
[    0.302971]  [<ffffffff816dea54>] ? get_partial_node.isra.49+0x4d/0x228
[    0.303077]  [<ffffffff816e9e3b>] _raw_spin_lock+0x3b/0x50
[    0.303183]  [<ffffffff816dea54>] ? get_partial_node.isra.49+0x4d/0x228
[    0.303290]  [<ffffffff816dea54>] get_partial_node.isra.49+0x4d/0x228
[    0.303397]  [<ffffffff810cd482>] ? __module_text_address+0x12/0x60
[    0.303502]  [<ffffffff810d35ff>] ? is_module_text_address+0x2f/0x50
[    0.303610]  [<ffffffff81074548>] ? __kernel_text_address+0x58/0x80
[    0.303717]  [<ffffffff816dedfc>] __slab_alloc+0x1cd/0x562
[    0.303821]  [<ffffffff812e86ff>] ? alloc_cpumask_var_node+0x1f/0x90
[    0.303929]  [<ffffffff8118ab6a>] kmem_cache_alloc_node_trace+0xda/0x290
[    0.304037]  [<ffffffff812e86ff>] ? alloc_cpumask_var_node+0x1f/0x90
[    0.304145]  [<ffffffff812e86ff>] alloc_cpumask_var_node+0x1f/0x90
[    0.304250]  [<ffffffff812e879e>] alloc_cpumask_var+0xe/0x10
[    0.304357]  [<ffffffff81030990>] __assign_irq_vector+0x40/0x340
[    0.304462]  [<ffffffff810324a1>] __create_irqs+0x151/0x210
[    0.304567]  [<ffffffff810325a2>] create_irq+0x22/0x30
[    0.304674]  [<ffffffff815ab25d>] dmar_set_interrupt+0x2d/0xd0
[    0.304784]  [<ffffffff81d6a532>] enable_drhd_fault_handling+0x24/0x66
[    0.304890]  [<ffffffff81d6bc7f>] irq_remap_enable_fault_handling+0x26/0x30
[    0.304999]  [<ffffffff81d2f3e0>] bsp_end_local_APIC_setup+0x18/0x1a
[    0.305106]  [<ffffffff81d2d44a>] native_smp_prepare_cpus+0x35c/0x3d3
[    0.305215]  [<ffffffff81d20f93>] kernel_init_freeable+0x124/0x26c
[    0.305321]  [<ffffffff816d6d4e>] ? kernel_init+0xe/0x130
[    0.305427]  [<ffffffff816d6d40>] ? rest_init+0xd0/0xd0
[    0.305529]  [<ffffffff816d6d4e>] kernel_init+0xe/0x130
[    0.305627]  [<ffffffff816f23ac>] ret_from_fork+0x7c/0xb0
[    0.305731]  [<ffffffff816d6d40>] ? rest_init+0xd0/0xd0
[    0.305836] 
other info that might help us debug this:
[    0.305993] 1 lock held by swapper/0/1:
[    0.306093]  #0:  (vector_lock){......}-{2:2}, at: [<ffffffff8103245c>] __create_irqs+0x10c/0x210
[    0.306444] 
stack backtrace:
[    0.306599] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.13.0-rc7+ #15 
[    0.306972]  0000000000000001 ffff880243f37a00 ffffffff816dfe5b 0000000000000014
[    0.307282]  ffff880243f37a78 ffffffff8109f220 0000000000000000 ffff880243f37a78
[    0.307594]  0000000000000046 ffffffff81189ae3 ffffffff00000000 0000000000000046
[    0.307903] Call Trace:
[    0.307999]  [<ffffffff816dfe5b>] dump_stack+0x4e/0x7a
[    0.308103]  [<ffffffff8109f220>] __lock_acquire.isra.28+0x400/0xd80
[    0.308209]  [<ffffffff81189ae3>] ? deactivate_slab+0x3c3/0x740
[    0.308316]  [<ffffffff810a030d>] lock_acquire+0x8d/0x120
[    0.308422]  [<ffffffff816dea54>] ? get_partial_node.isra.49+0x4d/0x228
[    0.308529]  [<ffffffff816e9e3b>] _raw_spin_lock+0x3b/0x50
[    0.308631]  [<ffffffff816dea54>] ? get_partial_node.isra.49+0x4d/0x228
[    0.308735]  [<ffffffff816dea54>] get_partial_node.isra.49+0x4d/0x228
[    0.308843]  [<ffffffff810cd482>] ? __module_text_address+0x12/0x60
[    0.308949]  [<ffffffff810d35ff>] ? is_module_text_address+0x2f/0x50
[    0.309055]  [<ffffffff81074548>] ? __kernel_text_address+0x58/0x80
[    0.309161]  [<ffffffff816dedfc>] __slab_alloc+0x1cd/0x562
[    0.309265]  [<ffffffff812e86ff>] ? alloc_cpumask_var_node+0x1f/0x90
[    0.309372]  [<ffffffff8118ab6a>] kmem_cache_alloc_node_trace+0xda/0x290
[    0.309479]  [<ffffffff812e86ff>] ? alloc_cpumask_var_node+0x1f/0x90
[    0.309586]  [<ffffffff812e86ff>] alloc_cpumask_var_node+0x1f/0x90
[    0.309692]  [<ffffffff812e879e>] alloc_cpumask_var+0xe/0x10
[    0.309799]  [<ffffffff81030990>] __assign_irq_vector+0x40/0x340
[    0.309906]  [<ffffffff810324a1>] __create_irqs+0x151/0x210
[    0.310005]  [<ffffffff810325a2>] create_irq+0x22/0x30
[    0.310109]  [<ffffffff815ab25d>] dmar_set_interrupt+0x2d/0xd0
[    0.310215]  [<ffffffff81d6a532>] enable_drhd_fault_handling+0x24/0x66
[    0.310321]  [<ffffffff81d6bc7f>] irq_remap_enable_fault_handling+0x26/0x30
[    0.310429]  [<ffffffff81d2f3e0>] bsp_end_local_APIC_setup+0x18/0x1a
[    0.310536]  [<ffffffff81d2d44a>] native_smp_prepare_cpus+0x35c/0x3d3
[    0.310644]  [<ffffffff81d20f93>] kernel_init_freeable+0x124/0x26c
[    0.310746]  [<ffffffff816d6d4e>] ? kernel_init+0xe/0x130
[    0.310852]  [<ffffffff816d6d40>] ? rest_init+0xd0/0xd0
[    0.310955]  [<ffffffff816d6d4e>] kernel_init+0xe/0x130
[    0.311058]  [<ffffffff816f23ac>] ret_from_fork+0x7c/0xb0
[    0.311164]  [<ffffffff816d6d40>] ? rest_init+0xd0/0xd0

nitpick: Why is the backtrace printed twice ?

Dave

author maintainer
#48 lore raw
Quoted 11 lines · Dave Jones
Quoted 5 lines · Peter Zijlstra

Subject: lockdep: Introduce wait-type checks
From: Peter Zijlstra <[email protected]>
Date: Tue, 19 Nov 2013 21:45:48 +0100

This patch extends lockdep to validate lock wait-type context.

ooh, a new toy.

*boom*

Oh cute, I'll have to stare at slub. ISTR there's a current slub-rt patch floating about.

I'd have to just blindly convert slub to raw_spinlocks (although that'll get rid of the warning) without trying to limit the lock hold time of raw_spinlocks.

Quoted 1 line

nitpick: Why is the backtrace printed twice ?

Because I'm an idiot, lemme fix that ;-)

author maintainer
#49 lore raw
In reply to Dave Jones
Quoted 52 lines · Dave Jones
Quoted 5 lines · Peter Zijlstra

Subject: lockdep: Introduce wait-type checks
From: Peter Zijlstra <[email protected]>
Date: Tue, 19 Nov 2013 21:45:48 +0100

This patch extends lockdep to validate lock wait-type context.

ooh, a new toy.

*boom*

[    0.298629] =============================
[    0.298732] [ BUG: Invalid wait context ]
[    0.298834] 3.13.0-rc7+ #15 Not tainted
[    0.298935] -----------------------------
[    0.299038] swapper/0/1 is trying to lock:
[    0.299135]  (&n->list_lock){......}-{3:3}, at: [<ffffffff816dea54>] get_partial_node.isra.49+0x4d/0x228
[    0.299453] 
stack backtrace:
[    0.299608] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.13.0-rc7+ #15 
[    0.299983]  0000000000000001 ffff880243f37a00 ffffffff816dfe5b 0000000000000014
[    0.300302]  ffff880243f37a78 ffffffff8109f1f7 0000000000000000 ffff880243f37a78
[    0.300611]  0000000000000046 ffffffff81189ae3 ffffffff00000000 0000000000000046
[    0.300927] Call Trace:
[    0.301028]  [<ffffffff816dfe5b>] dump_stack+0x4e/0x7a
[    0.301128]  [<ffffffff8109f1f7>] __lock_acquire.isra.28+0x3d7/0xd80
[    0.301238]  [<ffffffff81189ae3>] ? deactivate_slab+0x3c3/0x740
[    0.301345]  [<ffffffff810a030d>] lock_acquire+0x8d/0x120
[    0.302971]  [<ffffffff816dea54>] ? get_partial_node.isra.49+0x4d/0x228
[    0.303077]  [<ffffffff816e9e3b>] _raw_spin_lock+0x3b/0x50
[    0.303183]  [<ffffffff816dea54>] ? get_partial_node.isra.49+0x4d/0x228
[    0.303290]  [<ffffffff816dea54>] get_partial_node.isra.49+0x4d/0x228
[    0.303397]  [<ffffffff810cd482>] ? __module_text_address+0x12/0x60
[    0.303502]  [<ffffffff810d35ff>] ? is_module_text_address+0x2f/0x50
[    0.303610]  [<ffffffff81074548>] ? __kernel_text_address+0x58/0x80
[    0.303717]  [<ffffffff816dedfc>] __slab_alloc+0x1cd/0x562
[    0.303821]  [<ffffffff812e86ff>] ? alloc_cpumask_var_node+0x1f/0x90
[    0.303929]  [<ffffffff8118ab6a>] kmem_cache_alloc_node_trace+0xda/0x290
[    0.304037]  [<ffffffff812e86ff>] ? alloc_cpumask_var_node+0x1f/0x90
[    0.304145]  [<ffffffff812e86ff>] alloc_cpumask_var_node+0x1f/0x90
[    0.304250]  [<ffffffff812e879e>] alloc_cpumask_var+0xe/0x10
[    0.304357]  [<ffffffff81030990>] __assign_irq_vector+0x40/0x340
[    0.304462]  [<ffffffff810324a1>] __create_irqs+0x151/0x210
[    0.304567]  [<ffffffff810325a2>] create_irq+0x22/0x30
[    0.304674]  [<ffffffff815ab25d>] dmar_set_interrupt+0x2d/0xd0
[    0.304784]  [<ffffffff81d6a532>] enable_drhd_fault_handling+0x24/0x66
[    0.304890]  [<ffffffff81d6bc7f>] irq_remap_enable_fault_handling+0x26/0x30
[    0.304999]  [<ffffffff81d2f3e0>] bsp_end_local_APIC_setup+0x18/0x1a
[    0.305106]  [<ffffffff81d2d44a>] native_smp_prepare_cpus+0x35c/0x3d3
[    0.305215]  [<ffffffff81d20f93>] kernel_init_freeable+0x124/0x26c
[    0.305321]  [<ffffffff816d6d4e>] ? kernel_init+0xe/0x130
[    0.305427]  [<ffffffff816d6d40>] ? rest_init+0xd0/0xd0
Quoted 8 lines
[    0.305529]  [<ffffffff816d6d4e>] kernel_init+0xe/0x130
[    0.305627]  [<ffffffff816f23ac>] ret_from_fork+0x7c/0xb0
[    0.305731]  [<ffffffff816d6d40>] ? rest_init+0xd0/0xd0
[    0.305836] 
other info that might help us debug this:
[    0.305993] 1 lock held by swapper/0/1:
[    0.306093]  #0:  (vector_lock){......}-{2:2}, at: [<ffffffff8103245c>] __create_irqs+0x10c/0x210
[    0.306444] 

Ok, so whatever way I turn this thing, we simply cannot allocate memory while holding a raw_spinlock, since all the allocator locks upto and including zone->lock are regular spinlocks and we very much want preemptible allocators, so changing that is not an option.

While -rt does appear to turn list_lock into a raw_spinlock that is at most a band-aid afaict because all those list iterations that are done while holding it aren't in any way bounded.

But even converting list_lock doesn't help, because SLUB (and any of the others) will eventually call into alloc_page and friends which will touch zone->lock, which is very much a spinlock, even on -rt.

So we must change this __create_irqs() site to not do this allocation while holding the lock, /me greps the -rt patches to see if anybody touches that.

Ah, no, -rt simply forbids CPUMASK_OFFSTACK and side-steps the issue here.

Bugger.

Thomas, any clue? __assign_irq_vector() is called rather deep down in the whole IRQ story and appears to be rather stupidly expensive, a sideways reading of it makes it appear to be O(nr_cpus^2) surely a complete fail even for !rt kernels.