diff options
Diffstat (limited to 'kernel/locking')
| -rw-r--r-- | kernel/locking/lockdep.c | 92 | ||||
| -rw-r--r-- | kernel/locking/locktorture.c | 188 | ||||
| -rw-r--r-- | kernel/locking/rwbase_rt.c | 9 | ||||
| -rw-r--r-- | kernel/locking/rwsem.c | 8 | ||||
| -rw-r--r-- | kernel/locking/test-ww_mutex.c | 2 | 
5 files changed, 255 insertions, 44 deletions
| diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 50d4863974e7..4dfd2f3e09b2 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -1881,6 +1881,8 @@ print_circular_lock_scenario(struct held_lock *src,  	struct lock_class *source = hlock_class(src);  	struct lock_class *target = hlock_class(tgt);  	struct lock_class *parent = prt->class; +	int src_read = src->read; +	int tgt_read = tgt->read;  	/*  	 * A direct locking problem where unsafe_class lock is taken @@ -1908,7 +1910,10 @@ print_circular_lock_scenario(struct held_lock *src,  	printk(" Possible unsafe locking scenario:\n\n");  	printk("       CPU0                    CPU1\n");  	printk("       ----                    ----\n"); -	printk("  lock("); +	if (tgt_read != 0) +		printk("  rlock("); +	else +		printk("  lock(");  	__print_lock_name(target);  	printk(KERN_CONT ");\n");  	printk("                               lock("); @@ -1917,7 +1922,12 @@ print_circular_lock_scenario(struct held_lock *src,  	printk("                               lock(");  	__print_lock_name(target);  	printk(KERN_CONT ");\n"); -	printk("  lock("); +	if (src_read != 0) +		printk("  rlock("); +	else if (src->sync) +		printk("  sync("); +	else +		printk("  lock(");  	__print_lock_name(source);  	printk(KERN_CONT ");\n");  	printk("\n *** DEADLOCK ***\n\n"); @@ -2253,6 +2263,9 @@ static inline bool usage_match(struct lock_list *entry, void *mask)  static inline bool usage_skip(struct lock_list *entry, void *mask)  { +	if (entry->class->lock_type == LD_LOCK_NORMAL) +		return false; +  	/*  	 * Skip local_lock() for irq inversion detection.  	 * @@ -2279,14 +2292,16 @@ static inline bool usage_skip(struct lock_list *entry, void *mask)  	 * As a result, we will skip local_lock(), when we search for irq  	 * inversion bugs.  	 */ -	if (entry->class->lock_type == LD_LOCK_PERCPU) { -		if (DEBUG_LOCKS_WARN_ON(entry->class->wait_type_inner < LD_WAIT_CONFIG)) -			return false; +	if (entry->class->lock_type == LD_LOCK_PERCPU && +	    DEBUG_LOCKS_WARN_ON(entry->class->wait_type_inner < LD_WAIT_CONFIG)) +		return false; -		return true; -	} +	/* +	 * Skip WAIT_OVERRIDE for irq inversion detection -- it's not actually +	 * a lock and only used to override the wait_type. +	 */ -	return false; +	return true;  }  /* @@ -4531,7 +4546,13 @@ mark_usage(struct task_struct *curr, struct held_lock *hlock, int check)  					return 0;  		}  	} -	if (!hlock->hardirqs_off) { + +	/* +	 * For lock_sync(), don't mark the ENABLED usage, since lock_sync() +	 * creates no critical section and no extra dependency can be introduced +	 * by interrupts +	 */ +	if (!hlock->hardirqs_off && !hlock->sync) {  		if (hlock->read) {  			if (!mark_lock(curr, hlock,  					LOCK_ENABLED_HARDIRQ_READ)) @@ -4752,7 +4773,8 @@ static int check_wait_context(struct task_struct *curr, struct held_lock *next)  	for (; depth < curr->lockdep_depth; depth++) {  		struct held_lock *prev = curr->held_locks + depth; -		u8 prev_inner = hlock_class(prev)->wait_type_inner; +		struct lock_class *class = hlock_class(prev); +		u8 prev_inner = class->wait_type_inner;  		if (prev_inner) {  			/* @@ -4762,6 +4784,14 @@ static int check_wait_context(struct task_struct *curr, struct held_lock *next)  			 * Also due to trylocks.  			 */  			curr_inner = min(curr_inner, prev_inner); + +			/* +			 * Allow override for annotations -- this is typically +			 * only valid/needed for code that only exists when +			 * CONFIG_PREEMPT_RT=n. +			 */ +			if (unlikely(class->lock_type == LD_LOCK_WAIT_OVERRIDE)) +				curr_inner = prev_inner;  		}  	} @@ -4910,7 +4940,7 @@ static int __lock_is_held(const struct lockdep_map *lock, int read);  static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,  			  int trylock, int read, int check, int hardirqs_off,  			  struct lockdep_map *nest_lock, unsigned long ip, -			  int references, int pin_count) +			  int references, int pin_count, int sync)  {  	struct task_struct *curr = current;  	struct lock_class *class = NULL; @@ -4961,7 +4991,8 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,  	class_idx = class - lock_classes; -	if (depth) { /* we're holding locks */ +	if (depth && !sync) { +		/* we're holding locks and the new held lock is not a sync */  		hlock = curr->held_locks + depth - 1;  		if (hlock->class_idx == class_idx && nest_lock) {  			if (!references) @@ -4995,6 +5026,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,  	hlock->trylock = trylock;  	hlock->read = read;  	hlock->check = check; +	hlock->sync = !!sync;  	hlock->hardirqs_off = !!hardirqs_off;  	hlock->references = references;  #ifdef CONFIG_LOCK_STAT @@ -5056,6 +5088,10 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,  	if (!validate_chain(curr, hlock, chain_head, chain_key))  		return 0; +	/* For lock_sync(), we are done here since no actual critical section */ +	if (hlock->sync) +		return 1; +  	curr->curr_chain_key = chain_key;  	curr->lockdep_depth++;  	check_chain_key(curr); @@ -5197,7 +5233,7 @@ static int reacquire_held_locks(struct task_struct *curr, unsigned int depth,  				    hlock->read, hlock->check,  				    hlock->hardirqs_off,  				    hlock->nest_lock, hlock->acquire_ip, -				    hlock->references, hlock->pin_count)) { +				    hlock->references, hlock->pin_count, 0)) {  		case 0:  			return 1;  		case 1: @@ -5667,7 +5703,7 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,  	lockdep_recursion_inc();  	__lock_acquire(lock, subclass, trylock, read, check, -		       irqs_disabled_flags(flags), nest_lock, ip, 0, 0); +		       irqs_disabled_flags(flags), nest_lock, ip, 0, 0, 0);  	lockdep_recursion_finish();  	raw_local_irq_restore(flags);  } @@ -5693,6 +5729,34 @@ void lock_release(struct lockdep_map *lock, unsigned long ip)  }  EXPORT_SYMBOL_GPL(lock_release); +/* + * lock_sync() - A special annotation for synchronize_{s,}rcu()-like API. + * + * No actual critical section is created by the APIs annotated with this: these + * APIs are used to wait for one or multiple critical sections (on other CPUs + * or threads), and it means that calling these APIs inside these critical + * sections is potential deadlock. + */ +void lock_sync(struct lockdep_map *lock, unsigned subclass, int read, +	       int check, struct lockdep_map *nest_lock, unsigned long ip) +{ +	unsigned long flags; + +	if (unlikely(!lockdep_enabled())) +		return; + +	raw_local_irq_save(flags); +	check_flags(flags); + +	lockdep_recursion_inc(); +	__lock_acquire(lock, subclass, 0, read, check, +		       irqs_disabled_flags(flags), nest_lock, ip, 0, 0, 1); +	check_chain_key(current); +	lockdep_recursion_finish(); +	raw_local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(lock_sync); +  noinstr int lock_is_held_type(const struct lockdep_map *lock, int read)  {  	unsigned long flags; diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c index f04b1978899d..153ddc4c47ef 100644 --- a/kernel/locking/locktorture.c +++ b/kernel/locking/locktorture.c @@ -51,8 +51,11 @@ torture_param(int, rt_boost, 2,  torture_param(int, rt_boost_factor, 50, "A factor determining how often rt-boost happens.");  torture_param(int, verbose, 1,  	     "Enable verbose debugging printk()s"); +torture_param(int, nested_locks, 0, "Number of nested locks (max = 8)"); +/* Going much higher trips "BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low!" errors */ +#define MAX_NESTED_LOCKS 8 -static char *torture_type = "spin_lock"; +static char *torture_type = IS_ENABLED(CONFIG_PREEMPT_RT) ? "raw_spin_lock" : "spin_lock";  module_param(torture_type, charp, 0444);  MODULE_PARM_DESC(torture_type,  		 "Type of lock to torture (spin_lock, spin_lock_irq, mutex_lock, ...)"); @@ -79,10 +82,12 @@ static void lock_torture_cleanup(void);  struct lock_torture_ops {  	void (*init)(void);  	void (*exit)(void); +	int (*nested_lock)(int tid, u32 lockset);  	int (*writelock)(int tid);  	void (*write_delay)(struct torture_random_state *trsp);  	void (*task_boost)(struct torture_random_state *trsp);  	void (*writeunlock)(int tid); +	void (*nested_unlock)(int tid, u32 lockset);  	int (*readlock)(int tid);  	void (*read_delay)(struct torture_random_state *trsp);  	void (*readunlock)(int tid); @@ -252,6 +257,59 @@ static struct lock_torture_ops spin_lock_irq_ops = {  	.name		= "spin_lock_irq"  }; +static DEFINE_RAW_SPINLOCK(torture_raw_spinlock); + +static int torture_raw_spin_lock_write_lock(int tid __maybe_unused) +__acquires(torture_raw_spinlock) +{ +	raw_spin_lock(&torture_raw_spinlock); +	return 0; +} + +static void torture_raw_spin_lock_write_unlock(int tid __maybe_unused) +__releases(torture_raw_spinlock) +{ +	raw_spin_unlock(&torture_raw_spinlock); +} + +static struct lock_torture_ops raw_spin_lock_ops = { +	.writelock	= torture_raw_spin_lock_write_lock, +	.write_delay	= torture_spin_lock_write_delay, +	.task_boost	= torture_rt_boost, +	.writeunlock	= torture_raw_spin_lock_write_unlock, +	.readlock	= NULL, +	.read_delay	= NULL, +	.readunlock	= NULL, +	.name		= "raw_spin_lock" +}; + +static int torture_raw_spin_lock_write_lock_irq(int tid __maybe_unused) +__acquires(torture_raw_spinlock) +{ +	unsigned long flags; + +	raw_spin_lock_irqsave(&torture_raw_spinlock, flags); +	cxt.cur_ops->flags = flags; +	return 0; +} + +static void torture_raw_spin_lock_write_unlock_irq(int tid __maybe_unused) +__releases(torture_raw_spinlock) +{ +	raw_spin_unlock_irqrestore(&torture_raw_spinlock, cxt.cur_ops->flags); +} + +static struct lock_torture_ops raw_spin_lock_irq_ops = { +	.writelock	= torture_raw_spin_lock_write_lock_irq, +	.write_delay	= torture_spin_lock_write_delay, +	.task_boost	= torture_rt_boost, +	.writeunlock	= torture_raw_spin_lock_write_unlock_irq, +	.readlock	= NULL, +	.read_delay	= NULL, +	.readunlock	= NULL, +	.name		= "raw_spin_lock_irq" +}; +  static DEFINE_RWLOCK(torture_rwlock);  static int torture_rwlock_write_lock(int tid __maybe_unused) @@ -365,6 +423,28 @@ static struct lock_torture_ops rw_lock_irq_ops = {  };  static DEFINE_MUTEX(torture_mutex); +static struct mutex torture_nested_mutexes[MAX_NESTED_LOCKS]; +static struct lock_class_key nested_mutex_keys[MAX_NESTED_LOCKS]; + +static void torture_mutex_init(void) +{ +	int i; + +	for (i = 0; i < MAX_NESTED_LOCKS; i++) +		__mutex_init(&torture_nested_mutexes[i], __func__, +			     &nested_mutex_keys[i]); +} + +static int torture_mutex_nested_lock(int tid __maybe_unused, +				     u32 lockset) +{ +	int i; + +	for (i = 0; i < nested_locks; i++) +		if (lockset & (1 << i)) +			mutex_lock(&torture_nested_mutexes[i]); +	return 0; +}  static int torture_mutex_lock(int tid __maybe_unused)  __acquires(torture_mutex) @@ -393,11 +473,24 @@ __releases(torture_mutex)  	mutex_unlock(&torture_mutex);  } +static void torture_mutex_nested_unlock(int tid __maybe_unused, +					u32 lockset) +{ +	int i; + +	for (i = nested_locks - 1; i >= 0; i--) +		if (lockset & (1 << i)) +			mutex_unlock(&torture_nested_mutexes[i]); +} +  static struct lock_torture_ops mutex_lock_ops = { +	.init		= torture_mutex_init, +	.nested_lock	= torture_mutex_nested_lock,  	.writelock	= torture_mutex_lock,  	.write_delay	= torture_mutex_delay,  	.task_boost     = torture_rt_boost,  	.writeunlock	= torture_mutex_unlock, +	.nested_unlock	= torture_mutex_nested_unlock,  	.readlock       = NULL,  	.read_delay     = NULL,  	.readunlock     = NULL, @@ -504,6 +597,28 @@ static struct lock_torture_ops ww_mutex_lock_ops = {  #ifdef CONFIG_RT_MUTEXES  static DEFINE_RT_MUTEX(torture_rtmutex); +static struct rt_mutex torture_nested_rtmutexes[MAX_NESTED_LOCKS]; +static struct lock_class_key nested_rtmutex_keys[MAX_NESTED_LOCKS]; + +static void torture_rtmutex_init(void) +{ +	int i; + +	for (i = 0; i < MAX_NESTED_LOCKS; i++) +		__rt_mutex_init(&torture_nested_rtmutexes[i], __func__, +				&nested_rtmutex_keys[i]); +} + +static int torture_rtmutex_nested_lock(int tid __maybe_unused, +				       u32 lockset) +{ +	int i; + +	for (i = 0; i < nested_locks; i++) +		if (lockset & (1 << i)) +			rt_mutex_lock(&torture_nested_rtmutexes[i]); +	return 0; +}  static int torture_rtmutex_lock(int tid __maybe_unused)  __acquires(torture_rtmutex) @@ -545,11 +660,24 @@ static void torture_rt_boost_rtmutex(struct torture_random_state *trsp)  	__torture_rt_boost(trsp);  } +static void torture_rtmutex_nested_unlock(int tid __maybe_unused, +					  u32 lockset) +{ +	int i; + +	for (i = nested_locks - 1; i >= 0; i--) +		if (lockset & (1 << i)) +			rt_mutex_unlock(&torture_nested_rtmutexes[i]); +} +  static struct lock_torture_ops rtmutex_lock_ops = { +	.init		= torture_rtmutex_init, +	.nested_lock	= torture_rtmutex_nested_lock,  	.writelock	= torture_rtmutex_lock,  	.write_delay	= torture_rtmutex_delay,  	.task_boost     = torture_rt_boost_rtmutex,  	.writeunlock	= torture_rtmutex_unlock, +	.nested_unlock	= torture_rtmutex_nested_unlock,  	.readlock       = NULL,  	.read_delay     = NULL,  	.readunlock     = NULL, @@ -684,6 +812,8 @@ static int lock_torture_writer(void *arg)  	struct lock_stress_stats *lwsp = arg;  	int tid = lwsp - cxt.lwsa;  	DEFINE_TORTURE_RANDOM(rand); +	u32 lockset_mask; +	bool skip_main_lock;  	VERBOSE_TOROUT_STRING("lock_torture_writer task started");  	set_user_nice(current, MAX_NICE); @@ -692,19 +822,40 @@ static int lock_torture_writer(void *arg)  		if ((torture_random(&rand) & 0xfffff) == 0)  			schedule_timeout_uninterruptible(1); -		cxt.cur_ops->task_boost(&rand); -		cxt.cur_ops->writelock(tid); -		if (WARN_ON_ONCE(lock_is_write_held)) -			lwsp->n_lock_fail++; -		lock_is_write_held = true; -		if (WARN_ON_ONCE(atomic_read(&lock_is_read_held))) -			lwsp->n_lock_fail++; /* rare, but... */ +		lockset_mask = torture_random(&rand); +		/* +		 * When using nested_locks, we want to occasionally +		 * skip the main lock so we can avoid always serializing +		 * the lock chains on that central lock. By skipping the +		 * main lock occasionally, we can create different +		 * contention patterns (allowing for multiple disjoint +		 * blocked trees) +		 */ +		skip_main_lock = (nested_locks && +				 !(torture_random(&rand) % 100)); -		lwsp->n_lock_acquired++; +		cxt.cur_ops->task_boost(&rand); +		if (cxt.cur_ops->nested_lock) +			cxt.cur_ops->nested_lock(tid, lockset_mask); + +		if (!skip_main_lock) { +			cxt.cur_ops->writelock(tid); +			if (WARN_ON_ONCE(lock_is_write_held)) +				lwsp->n_lock_fail++; +			lock_is_write_held = true; +			if (WARN_ON_ONCE(atomic_read(&lock_is_read_held))) +				lwsp->n_lock_fail++; /* rare, but... */ + +			lwsp->n_lock_acquired++; +		}  		cxt.cur_ops->write_delay(&rand); -		lock_is_write_held = false; -		WRITE_ONCE(last_lock_release, jiffies); -		cxt.cur_ops->writeunlock(tid); +		if (!skip_main_lock) { +			lock_is_write_held = false; +			WRITE_ONCE(last_lock_release, jiffies); +			cxt.cur_ops->writeunlock(tid); +		} +		if (cxt.cur_ops->nested_unlock) +			cxt.cur_ops->nested_unlock(tid, lockset_mask);  		stutter_wait("lock_torture_writer");  	} while (!torture_must_stop()); @@ -845,11 +996,11 @@ lock_torture_print_module_parms(struct lock_torture_ops *cur_ops,  				const char *tag)  {  	pr_alert("%s" TORTURE_FLAG -		 "--- %s%s: nwriters_stress=%d nreaders_stress=%d stat_interval=%d verbose=%d shuffle_interval=%d stutter=%d shutdown_secs=%d onoff_interval=%d onoff_holdoff=%d\n", +		 "--- %s%s: nwriters_stress=%d nreaders_stress=%d nested_locks=%d stat_interval=%d verbose=%d shuffle_interval=%d stutter=%d shutdown_secs=%d onoff_interval=%d onoff_holdoff=%d\n",  		 torture_type, tag, cxt.debug_lock ? " [debug]": "", -		 cxt.nrealwriters_stress, cxt.nrealreaders_stress, stat_interval, -		 verbose, shuffle_interval, stutter, shutdown_secs, -		 onoff_interval, onoff_holdoff); +		 cxt.nrealwriters_stress, cxt.nrealreaders_stress, +		 nested_locks, stat_interval, verbose, shuffle_interval, +		 stutter, shutdown_secs, onoff_interval, onoff_holdoff);  }  static void lock_torture_cleanup(void) @@ -919,6 +1070,7 @@ static int __init lock_torture_init(void)  	static struct lock_torture_ops *torture_ops[] = {  		&lock_busted_ops,  		&spin_lock_ops, &spin_lock_irq_ops, +		&raw_spin_lock_ops, &raw_spin_lock_irq_ops,  		&rw_lock_ops, &rw_lock_irq_ops,  		&mutex_lock_ops,  		&ww_mutex_lock_ops, @@ -1068,6 +1220,10 @@ static int __init lock_torture_init(void)  		}  	} +	/* cap nested_locks to MAX_NESTED_LOCKS */ +	if (nested_locks > MAX_NESTED_LOCKS) +		nested_locks = MAX_NESTED_LOCKS; +  	if (cxt.cur_ops->readlock) {  		reader_tasks = kcalloc(cxt.nrealreaders_stress,  				       sizeof(reader_tasks[0]), diff --git a/kernel/locking/rwbase_rt.c b/kernel/locking/rwbase_rt.c index c201aadb9301..25ec0239477c 100644 --- a/kernel/locking/rwbase_rt.c +++ b/kernel/locking/rwbase_rt.c @@ -72,15 +72,6 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb,  	int ret;  	raw_spin_lock_irq(&rtm->wait_lock); -	/* -	 * Allow readers, as long as the writer has not completely -	 * acquired the semaphore for write. -	 */ -	if (atomic_read(&rwb->readers) != WRITER_BIAS) { -		atomic_inc(&rwb->readers); -		raw_spin_unlock_irq(&rtm->wait_lock); -		return 0; -	}  	/*  	 * Call into the slow lock path with the rtmutex->wait_lock diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index acb5a50309a1..9eabd585ce7a 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -1240,7 +1240,7 @@ static struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem)  /*   * lock for reading   */ -static inline int __down_read_common(struct rw_semaphore *sem, int state) +static __always_inline int __down_read_common(struct rw_semaphore *sem, int state)  {  	int ret = 0;  	long count; @@ -1258,17 +1258,17 @@ out:  	return ret;  } -static inline void __down_read(struct rw_semaphore *sem) +static __always_inline void __down_read(struct rw_semaphore *sem)  {  	__down_read_common(sem, TASK_UNINTERRUPTIBLE);  } -static inline int __down_read_interruptible(struct rw_semaphore *sem) +static __always_inline int __down_read_interruptible(struct rw_semaphore *sem)  {  	return __down_read_common(sem, TASK_INTERRUPTIBLE);  } -static inline int __down_read_killable(struct rw_semaphore *sem) +static __always_inline int __down_read_killable(struct rw_semaphore *sem)  {  	return __down_read_common(sem, TASK_KILLABLE);  } diff --git a/kernel/locking/test-ww_mutex.c b/kernel/locking/test-ww_mutex.c index 29dc253d03af..93cca6e69860 100644 --- a/kernel/locking/test-ww_mutex.c +++ b/kernel/locking/test-ww_mutex.c @@ -659,7 +659,7 @@ static int __init test_ww_mutex_init(void)  	if (ret)  		return ret; -	ret = stress(4095, hweight32(STRESS_ALL)*ncpus, STRESS_ALL); +	ret = stress(2047, hweight32(STRESS_ALL)*ncpus, STRESS_ALL);  	if (ret)  		return ret; | 
