Commit 0471db44 authored by Boqun Feng's avatar Boqun Feng
Browse files

locking/lockdep: Improve the deadlock scenario print for sync and read lock



Lock scenario print is always a weak spot of lockdep splats. Improvement
can be made if we rework the dependency search and the error printing.

However without touching the graph search, we can improve a little for
the circular deadlock case, since we have the to-be-added lock
dependency, and know whether these two locks are read/write/sync.

In order to know whether a held_lock is sync or not, a bit was
"stolen" from ->references, which reduce our limit for the same lock
class nesting from 2^12 to 2^11, and it should still be good enough.

Besides, since we now have bit in held_lock for sync, we don't need the
"hardirqoffs being 1" trick, and also we can avoid the __lock_release()
if we jump out of __lock_acquire() before the held_lock stored.

With these changes, a deadlock case evolved with read lock and sync gets
a better print-out from:

	[...]  Possible unsafe locking scenario:
	[...]
	[...]        CPU0                    CPU1
	[...]        ----                    ----
	[...]   lock(srcuA);
	[...]                                lock(srcuB);
	[...]                                lock(srcuA);
	[...]   lock(srcuB);

to

	[...]  Possible unsafe locking scenario:
	[...]
	[...]        CPU0                    CPU1
	[...]        ----                    ----
	[...]   rlock(srcuA);
	[...]                                lock(srcuB);
	[...]                                lock(srcuA);
	[...]   sync(srcuB);

Signed-off-by: default avatarBoqun Feng <boqun.feng@gmail.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarBoqun Feng <boqun.feng@gmail.com>
parent 60a1a64e
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -134,7 +134,8 @@ struct held_lock {
	unsigned int read:2;        /* see lock_acquire() comment */
	unsigned int check:1;       /* see lock_acquire() comment */
	unsigned int hardirqs_off:1;
	unsigned int references:12;					/* 32 bits */
	unsigned int sync:1;
	unsigned int references:11;					/* 32 bits */
	unsigned int pin_count;
};

+32 −16
Original line number Diff line number Diff line
@@ -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,6 +1910,9 @@ print_circular_lock_scenario(struct held_lock *src,
	printk(" Possible unsafe locking scenario:\n\n");
	printk("       CPU0                    CPU1\n");
	printk("       ----                    ----\n");
	if (tgt_read != 0)
		printk("  rlock(");
	else
		printk("  lock(");
	__print_lock_name(target);
	printk(KERN_CONT ");\n");
@@ -1917,6 +1922,11 @@ print_circular_lock_scenario(struct held_lock *src,
	printk("                               lock(");
	__print_lock_name(target);
	printk(KERN_CONT ");\n");
	if (src_read != 0)
		printk("  rlock(");
	else if (src->sync)
		printk("  sync(");
	else
		printk("  lock(");
	__print_lock_name(source);
	printk(KERN_CONT ");\n");
@@ -4531,7 +4541,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))
@@ -4910,7 +4926,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 +4977,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 +5012,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 +5074,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 +5219,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 +5689,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);
}
@@ -5700,11 +5722,6 @@ EXPORT_SYMBOL_GPL(lock_release);
 * 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.
 *
 * This annotation acts as an acquire+release annotation pair with hardirqoff
 * being 1. Since there's no critical section, no interrupt can create extra
 * dependencies "inside" the annotation, hardirqoff == 1 allows us to avoid
 * false positives.
 */
void lock_sync(struct lockdep_map *lock, unsigned subclass, int read,
	       int check, struct lockdep_map *nest_lock, unsigned long ip)
@@ -5718,9 +5735,8 @@ void lock_sync(struct lockdep_map *lock, unsigned subclass, int read,
	check_flags(flags);

	lockdep_recursion_inc();
	__lock_acquire(lock, subclass, 0, read, check, 1, nest_lock, ip, 0, 0);

	if (__lock_release(lock, ip))
	__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);