linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/15] lockdep: Implement crossrelease feature
@ 2016-12-09  5:11 Byungchul Park
  2016-12-09  5:11 ` [PATCH v4 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
                   ` (15 more replies)
  0 siblings, 16 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:11 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

I checked if crossrelease feature works well on my qemu-i386 machine.
There's no problem at all to work on mine. But I wonder if it's also
true even on other machines. Especially, on large system. Could you
let me know if it doesn't work on yours? Or Could you let me know if
crossrelease feature is useful? Please let me know if you need to
backport it to another version but it's not easy. Then I can provide
the backported version after working it.

I added output text of 'cat /proc/lockdep' on my machine applying
crossrelease feature, showing dependencies of lockdep. You can check
what kind of dependencies are added by crossrelease feature. Please
use '(complete)' or '(PG_locked)' as a keyword to find dependencies
added by this patch set.

And I still keep the base unchanged (v4.7). I will rebase it on the
latest once you have a consensus on it. Your opinions?

-----8<-----

Change from v3
	- reviced document

Change from v2
	- rebase on vanilla v4.7 tag
	- move lockdep data for page lock from struct page to page_ext
	- allocate plocks buffer via vmalloc instead of in struct task
	- enhanced comments and document
	- optimize performance
	- make reporting function crossrelease-aware

Change from v1
	- enhanced the document
	- removed save_stack_trace() optimizing patch
	- made this based on the seperated save_stack_trace patchset
	  https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1182242.html

Can we detect deadlocks below with original lockdep?

Example 1)

	PROCESS X	PROCESS Y
	--------------	--------------
	mutext_lock A
			lock_page B
	lock_page B
			mutext_lock A // DEADLOCK
	unlock_page B
			mutext_unlock A
	mutex_unlock A
			unlock_page B

where A and B are different lock classes.

No, we cannot.

Example 2)

	PROCESS X	PROCESS Y	PROCESS Z
	--------------	--------------	--------------
			mutex_lock A
	lock_page B
			lock_page B
					mutext_lock A // DEADLOCK
					mutext_unlock A
					unlock_page B
					(B was held by PROCESS X)
			unlock_page B
			mutex_unlock A

where A and B are different lock classes.

No, we cannot.

Example 3)

	PROCESS X	PROCESS Y
	--------------	--------------
			mutex_lock A
	mutex_lock A
	mutex_unlock A
			wait_for_complete B // DEADLOCK
	complete B
			mutex_unlock A

where A is a lock class and B is a completion variable.

No, we cannot.

Not only lock operations, but also any operations causing to wait or
spin for something can cause deadlock unless it's eventually *released*
by someone. The important point here is that the waiting or spinning
must be *released* by someone.

Using crossrelease feature, we can check dependency and detect deadlock
possibility not only for typical lock, but also for lock_page(),
wait_for_xxx() and so on, which might be released in any context.

See the last patch including the document for more information.

Byungchul Park (15):
  x86/dumpstack: Optimize save_stack_trace
  x86/dumpstack: Add save_stack_trace()_fast()
  lockdep: Refactor lookup_chain_cache()
  lockdep: Add a function building a chain between two classes
  lockdep: Make check_prev_add can use a separate stack_trace
  lockdep: Make save_trace can skip stack tracing of the current
  lockdep: Implement crossrelease feature
  lockdep: Make crossrelease use save_stack_trace_fast()
  lockdep: Make print_circular_bug() crosslock-aware
  lockdep: Apply crossrelease to completion operation
  pagemap.h: Remove trailing white space
  lockdep: Apply crossrelease to PG_locked lock
  lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked
  lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to
    page_ext
  lockdep: Crossrelease feature documentation

 Documentation/locking/crossrelease.txt | 1053 ++++++++++++++++++++++++++++++++
 arch/x86/include/asm/stacktrace.h      |    1 +
 arch/x86/kernel/dumpstack.c            |    4 +
 arch/x86/kernel/dumpstack_32.c         |    2 +
 arch/x86/kernel/stacktrace.c           |   32 +
 include/linux/completion.h             |  121 +++-
 include/linux/irqflags.h               |   12 +-
 include/linux/lockdep.h                |  122 ++++
 include/linux/mm_types.h               |    4 +
 include/linux/page-flags.h             |   43 +-
 include/linux/page_ext.h               |    5 +
 include/linux/pagemap.h                |  124 +++-
 include/linux/sched.h                  |    5 +
 include/linux/stacktrace.h             |    2 +
 kernel/exit.c                          |    9 +
 kernel/fork.c                          |   20 +
 kernel/locking/lockdep.c               |  804 +++++++++++++++++++++---
 kernel/sched/completion.c              |   54 +-
 lib/Kconfig.debug                      |   30 +
 mm/filemap.c                           |   76 ++-
 mm/page_ext.c                          |    4 +
 21 files changed, 2392 insertions(+), 135 deletions(-)
 create mode 100644 Documentation/locking/crossrelease.txt

-- 
1.9.1

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [PATCH v4 01/15] x86/dumpstack: Optimize save_stack_trace
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
@ 2016-12-09  5:11 ` Byungchul Park
  2016-12-09  5:11 ` [PATCH v4 02/15] x86/dumpstack: Add save_stack_trace()_fast() Byungchul Park
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:11 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Currently, x86 implementation of save_stack_trace() is walking all stack
region word by word regardless of what the trace->max_entries is.
However, it's unnecessary to walk after already fulfilling caller's
requirement, say, if trace->nr_entries >= trace->max_entries is true.

I measured its overhead and printed its difference of sched_clock() with
my QEMU x86 machine. The latency was improved over 70% when
trace->max_entries = 5.

Before this patch:

[    2.329573] save_stack_trace() takes 76820 ns
[    2.329863] save_stack_trace() takes 62131 ns
[    2.330000] save_stack_trace() takes 99476 ns
[    2.329846] save_stack_trace() takes 62419 ns
[    2.330000] save_stack_trace() takes 88918 ns
[    2.330253] save_stack_trace() takes 73669 ns
[    2.330520] save_stack_trace() takes 67876 ns
[    2.330671] save_stack_trace() takes 75963 ns
[    2.330983] save_stack_trace() takes 95079 ns
[    2.330451] save_stack_trace() takes 62352 ns

After this patch:

[    2.795000] save_stack_trace() takes 21147 ns
[    2.795397] save_stack_trace() takes 20230 ns
[    2.795397] save_stack_trace() takes 31274 ns
[    2.795739] save_stack_trace() takes 19706 ns
[    2.796484] save_stack_trace() takes 20266 ns
[    2.796484] save_stack_trace() takes 20902 ns
[    2.797000] save_stack_trace() takes 38110 ns
[    2.797510] save_stack_trace() takes 20224 ns
[    2.798181] save_stack_trace() takes 20172 ns
[    2.798837] save_stack_trace() takes 20824 ns

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 arch/x86/include/asm/stacktrace.h | 1 +
 arch/x86/kernel/dumpstack.c       | 4 ++++
 arch/x86/kernel/dumpstack_32.c    | 2 ++
 arch/x86/kernel/stacktrace.c      | 7 +++++++
 4 files changed, 14 insertions(+)

diff --git a/arch/x86/include/asm/stacktrace.h b/arch/x86/include/asm/stacktrace.h
index 0944218..f6d0694 100644
--- a/arch/x86/include/asm/stacktrace.h
+++ b/arch/x86/include/asm/stacktrace.h
@@ -41,6 +41,7 @@ struct stacktrace_ops {
 	/* On negative return stop dumping */
 	int (*stack)(void *data, char *name);
 	walk_stack_t	walk_stack;
+	int (*end_walk)(void *data);
 };
 
 void dump_trace(struct task_struct *tsk, struct pt_regs *regs,
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index ef8017c..274d42a 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -113,6 +113,8 @@ print_context_stack(struct task_struct *task,
 			print_ftrace_graph_addr(addr, data, ops, task, graph);
 		}
 		stack++;
+		if (ops->end_walk && ops->end_walk(data))
+			break;
 	}
 	return bp;
 }
@@ -138,6 +140,8 @@ print_context_stack_bp(struct task_struct *task,
 		frame = frame->next_frame;
 		ret_addr = &frame->return_address;
 		print_ftrace_graph_addr(addr, data, ops, task, graph);
+		if (ops->end_walk && ops->end_walk(data))
+			break;
 	}
 
 	return (unsigned long)frame;
diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c
index fef917e..762d1fd 100644
--- a/arch/x86/kernel/dumpstack_32.c
+++ b/arch/x86/kernel/dumpstack_32.c
@@ -69,6 +69,8 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
 
 		bp = ops->walk_stack(task, stack, bp, ops, data,
 				     end_stack, &graph);
+		if (ops->end_walk && ops->end_walk(data))
+			break;
 
 		/* Stop if not on irq stack */
 		if (!end_stack)
diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c
index 9ee98ee..a44de4d 100644
--- a/arch/x86/kernel/stacktrace.c
+++ b/arch/x86/kernel/stacktrace.c
@@ -47,10 +47,17 @@ save_stack_address_nosched(void *data, unsigned long addr, int reliable)
 	return __save_stack_address(data, addr, reliable, true);
 }
 
+static int save_stack_end(void *data)
+{
+	struct stack_trace *trace = data;
+	return trace->nr_entries >= trace->max_entries;
+}
+
 static const struct stacktrace_ops save_stack_ops = {
 	.stack		= save_stack_stack,
 	.address	= save_stack_address,
 	.walk_stack	= print_context_stack,
+	.end_walk	= save_stack_end,
 };
 
 static const struct stacktrace_ops save_stack_ops_nosched = {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 02/15] x86/dumpstack: Add save_stack_trace()_fast()
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
  2016-12-09  5:11 ` [PATCH v4 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
@ 2016-12-09  5:11 ` Byungchul Park
  2016-12-09  5:11 ` [PATCH v4 03/15] lockdep: Refactor lookup_chain_cache() Byungchul Park
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:11 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

In non-oops case, it's usually not necessary to check all words of stack
area to extract backtrace. Instead, we can achieve it by tracking frame
pointer. So made it possible to save stack trace lightly in normal case.

I measured its ovehead and printed its difference of sched_clock() with
my QEMU x86 machine. The latency was improved over 80% when
trace->max_entries = 5.

Before this patch:

[    2.795000] save_stack_trace() takes 21147 ns
[    2.795397] save_stack_trace() takes 20230 ns
[    2.795397] save_stack_trace() takes 31274 ns
[    2.795739] save_stack_trace() takes 19706 ns
[    2.796484] save_stack_trace() takes 20266 ns
[    2.796484] save_stack_trace() takes 20902 ns
[    2.797000] save_stack_trace() takes 38110 ns
[    2.797510] save_stack_trace() takes 20224 ns
[    2.798181] save_stack_trace() takes 20172 ns
[    2.798837] save_stack_trace() takes 20824 ns

After this patch:

[    3.133807] save_stack_trace() takes 3297 ns
[    3.133954] save_stack_trace() takes 3330 ns
[    3.134235] save_stack_trace() takes 3517 ns
[    3.134711] save_stack_trace() takes 3773 ns
[    3.135000] save_stack_trace() takes 3685 ns
[    3.135541] save_stack_trace() takes 4757 ns
[    3.135865] save_stack_trace() takes 3420 ns
[    3.136000] save_stack_trace() takes 3329 ns
[    3.137000] save_stack_trace() takes 4058 ns
[    3.137000] save_stack_trace() takes 3499 ns

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 arch/x86/kernel/stacktrace.c | 25 +++++++++++++++++++++++++
 include/linux/stacktrace.h   |  2 ++
 2 files changed, 27 insertions(+)

diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c
index a44de4d..d8da90f 100644
--- a/arch/x86/kernel/stacktrace.c
+++ b/arch/x86/kernel/stacktrace.c
@@ -53,6 +53,10 @@ static int save_stack_end(void *data)
 	return trace->nr_entries >= trace->max_entries;
 }
 
+/*
+ * This operation should be used in the oops case where
+ * stack might be broken.
+ */
 static const struct stacktrace_ops save_stack_ops = {
 	.stack		= save_stack_stack,
 	.address	= save_stack_address,
@@ -60,6 +64,13 @@ static const struct stacktrace_ops save_stack_ops = {
 	.end_walk	= save_stack_end,
 };
 
+static const struct stacktrace_ops save_stack_ops_fast = {
+	.stack		= save_stack_stack,
+	.address	= save_stack_address,
+	.walk_stack	= print_context_stack_bp,
+	.end_walk	= save_stack_end,
+};
+
 static const struct stacktrace_ops save_stack_ops_nosched = {
 	.stack		= save_stack_stack,
 	.address	= save_stack_address_nosched,
@@ -68,6 +79,7 @@ static const struct stacktrace_ops save_stack_ops_nosched = {
 
 /*
  * Save stack-backtrace addresses into a stack_trace buffer.
+ * It works even in oops.
  */
 void save_stack_trace(struct stack_trace *trace)
 {
@@ -77,6 +89,19 @@ void save_stack_trace(struct stack_trace *trace)
 }
 EXPORT_SYMBOL_GPL(save_stack_trace);
 
+/*
+ * Save stack-backtrace addresses into a stack_trace buffer.
+ * This is perfered in normal case where we expect the stack is
+ * reliable.
+ */
+void save_stack_trace_fast(struct stack_trace *trace)
+{
+	dump_trace(current, NULL, NULL, 0, &save_stack_ops_fast, trace);
+	if (trace->nr_entries < trace->max_entries)
+		trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
+EXPORT_SYMBOL_GPL(save_stack_trace_fast);
+
 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
 {
 	dump_trace(current, regs, NULL, 0, &save_stack_ops, trace);
diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h
index 0a34489..ddef1d0 100644
--- a/include/linux/stacktrace.h
+++ b/include/linux/stacktrace.h
@@ -14,6 +14,7 @@ struct stack_trace {
 };
 
 extern void save_stack_trace(struct stack_trace *trace);
+extern void save_stack_trace_fast(struct stack_trace *trace);
 extern void save_stack_trace_regs(struct pt_regs *regs,
 				  struct stack_trace *trace);
 extern void save_stack_trace_tsk(struct task_struct *tsk,
@@ -31,6 +32,7 @@ extern void save_stack_trace_user(struct stack_trace *trace);
 
 #else
 # define save_stack_trace(trace)			do { } while (0)
+# define save_stack_trace_fast(trace)			do { } while (0)
 # define save_stack_trace_tsk(tsk, trace)		do { } while (0)
 # define save_stack_trace_user(trace)			do { } while (0)
 # define print_stack_trace(trace, spaces)		do { } while (0)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 03/15] lockdep: Refactor lookup_chain_cache()
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
  2016-12-09  5:11 ` [PATCH v4 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
  2016-12-09  5:11 ` [PATCH v4 02/15] x86/dumpstack: Add save_stack_trace()_fast() Byungchul Park
@ 2016-12-09  5:11 ` Byungchul Park
  2016-12-09  5:12 ` [PATCH v4 04/15] lockdep: Add a function building a chain between two classes Byungchul Park
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:11 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Currently, lookup_chain_cache() provides both 'lookup' and 'add'
functionalities in a function. However, each one is useful. So this
patch makes lookup_chain_cache() only do lookup functionality and
makes add_chain_cahce() only do add functionality. And it's more
readable than these functionalities are mixed in a function.

Crossrelease feature also needs to use each one separately.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 129 +++++++++++++++++++++++++++++------------------
 1 file changed, 81 insertions(+), 48 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 81f1a71..5df56aa 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -2105,15 +2105,9 @@ static int check_no_collision(struct task_struct *curr,
 	return 1;
 }
 
-/*
- * Look up a dependency chain. If the key is not present yet then
- * add it and return 1 - in this case the new dependency chain is
- * validated. If the key is already hashed, return 0.
- * (On return with 1 graph_lock is held.)
- */
-static inline int lookup_chain_cache(struct task_struct *curr,
-				     struct held_lock *hlock,
-				     u64 chain_key)
+static inline int add_chain_cache(struct task_struct *curr,
+				  struct held_lock *hlock,
+				  u64 chain_key)
 {
 	struct lock_class *class = hlock_class(hlock);
 	struct hlist_head *hash_head = chainhashentry(chain_key);
@@ -2121,49 +2115,18 @@ static inline int lookup_chain_cache(struct task_struct *curr,
 	int i, j;
 
 	/*
+	 * Allocate a new chain entry from the static array, and add
+	 * it to the hash:
+	 */
+
+	/*
 	 * We might need to take the graph lock, ensure we've got IRQs
 	 * disabled to make this an IRQ-safe lock.. for recursion reasons
 	 * lockdep won't complain about its own locking errors.
 	 */
 	if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
 		return 0;
-	/*
-	 * We can walk it lock-free, because entries only get added
-	 * to the hash:
-	 */
-	hlist_for_each_entry_rcu(chain, hash_head, entry) {
-		if (chain->chain_key == chain_key) {
-cache_hit:
-			debug_atomic_inc(chain_lookup_hits);
-			if (!check_no_collision(curr, hlock, chain))
-				return 0;
 
-			if (very_verbose(class))
-				printk("\nhash chain already cached, key: "
-					"%016Lx tail class: [%p] %s\n",
-					(unsigned long long)chain_key,
-					class->key, class->name);
-			return 0;
-		}
-	}
-	if (very_verbose(class))
-		printk("\nnew hash chain, key: %016Lx tail class: [%p] %s\n",
-			(unsigned long long)chain_key, class->key, class->name);
-	/*
-	 * Allocate a new chain entry from the static array, and add
-	 * it to the hash:
-	 */
-	if (!graph_lock())
-		return 0;
-	/*
-	 * We have to walk the chain again locked - to avoid duplicates:
-	 */
-	hlist_for_each_entry(chain, hash_head, entry) {
-		if (chain->chain_key == chain_key) {
-			graph_unlock();
-			goto cache_hit;
-		}
-	}
 	if (unlikely(nr_lock_chains >= MAX_LOCKDEP_CHAINS)) {
 		if (!debug_locks_off_graph_unlock())
 			return 0;
@@ -2215,6 +2178,75 @@ cache_hit:
 	return 1;
 }
 
+/*
+ * Look up a dependency chain.
+ */
+static inline struct lock_chain *lookup_chain_cache(u64 chain_key)
+{
+	struct hlist_head *hash_head = chainhashentry(chain_key);
+	struct lock_chain *chain;
+
+	/*
+	 * We can walk it lock-free, because entries only get added
+	 * to the hash:
+	 */
+	hlist_for_each_entry_rcu(chain, hash_head, entry) {
+		if (chain->chain_key == chain_key) {
+			debug_atomic_inc(chain_lookup_hits);
+			return chain;
+		}
+	}
+	return NULL;
+}
+
+/*
+ * If the key is not present yet in dependency chain cache then
+ * add it and return 1 - in this case the new dependency chain is
+ * validated. If the key is already hashed, return 0.
+ * (On return with 1 graph_lock is held.)
+ */
+static inline int lookup_chain_cache_add(struct task_struct *curr,
+					 struct held_lock *hlock,
+					 u64 chain_key)
+{
+	struct lock_class *class = hlock_class(hlock);
+	struct lock_chain *chain = lookup_chain_cache(chain_key);
+
+	if (chain) {
+cache_hit:
+		if (!check_no_collision(curr, hlock, chain))
+			return 0;
+
+		if (very_verbose(class))
+			printk("\nhash chain already cached, key: "
+					"%016Lx tail class: [%p] %s\n",
+					(unsigned long long)chain_key,
+					class->key, class->name);
+		return 0;
+	}
+
+	if (very_verbose(class))
+		printk("\nnew hash chain, key: %016Lx tail class: [%p] %s\n",
+			(unsigned long long)chain_key, class->key, class->name);
+
+	if (!graph_lock())
+		return 0;
+
+	/*
+	 * We have to walk the chain again locked - to avoid duplicates:
+	 */
+	chain = lookup_chain_cache(chain_key);
+	if (chain) {
+		graph_unlock();
+		goto cache_hit;
+	}
+
+	if (!add_chain_cache(curr, hlock, chain_key))
+		return 0;
+
+	return 1;
+}
+
 static int validate_chain(struct task_struct *curr, struct lockdep_map *lock,
 		struct held_lock *hlock, int chain_head, u64 chain_key)
 {
@@ -2225,11 +2257,11 @@ static int validate_chain(struct task_struct *curr, struct lockdep_map *lock,
 	 *
 	 * We look up the chain_key and do the O(N^2) check and update of
 	 * the dependencies only if this is a new dependency chain.
-	 * (If lookup_chain_cache() returns with 1 it acquires
+	 * (If lookup_chain_cache_add() return with 1 it acquires
 	 * graph_lock for us)
 	 */
 	if (!hlock->trylock && hlock->check &&
-	    lookup_chain_cache(curr, hlock, chain_key)) {
+	    lookup_chain_cache_add(curr, hlock, chain_key)) {
 		/*
 		 * Check whether last held lock:
 		 *
@@ -2260,9 +2292,10 @@ static int validate_chain(struct task_struct *curr, struct lockdep_map *lock,
 		if (!chain_head && ret != 2)
 			if (!check_prevs_add(curr, hlock))
 				return 0;
+
 		graph_unlock();
 	} else
-		/* after lookup_chain_cache(): */
+		/* after lookup_chain_cache_add(): */
 		if (unlikely(!debug_locks))
 			return 0;
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 04/15] lockdep: Add a function building a chain between two classes
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (2 preceding siblings ...)
  2016-12-09  5:11 ` [PATCH v4 03/15] lockdep: Refactor lookup_chain_cache() Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2017-01-10 21:00   ` Peter Zijlstra
  2016-12-09  5:12 ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

add_chain_cache() should be used in the context where the hlock is
owned since it might be racy in another context. However crossrelease
feature needs to build a chain between two locks regardless of context.
So introduce a new function making it possible.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 5df56aa..111839f 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -2105,6 +2105,62 @@ static int check_no_collision(struct task_struct *curr,
 	return 1;
 }
 
+/*
+ * This is for building a chain between just two different classes,
+ * instead of adding a new hlock upon current, which is done by
+ * add_chain_cache().
+ *
+ * This can be called in any context with two classes, while
+ * add_chain_cache() must be done within the lock owener's context
+ * since it uses hlock which might be racy in another context.
+ */
+static inline int add_chain_cache_classes(unsigned int prev,
+					  unsigned int next,
+					  unsigned int irq_context,
+					  u64 chain_key)
+{
+	struct hlist_head *hash_head = chainhashentry(chain_key);
+	struct lock_chain *chain;
+
+	/*
+	 * Allocate a new chain entry from the static array, and add
+	 * it to the hash:
+	 */
+
+	/*
+	 * We might need to take the graph lock, ensure we've got IRQs
+	 * disabled to make this an IRQ-safe lock.. for recursion reasons
+	 * lockdep won't complain about its own locking errors.
+	 */
+	if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
+		return 0;
+
+	if (unlikely(nr_lock_chains >= MAX_LOCKDEP_CHAINS)) {
+		if (!debug_locks_off_graph_unlock())
+			return 0;
+
+		print_lockdep_off("BUG: MAX_LOCKDEP_CHAINS too low!");
+		dump_stack();
+		return 0;
+	}
+
+	chain = lock_chains + nr_lock_chains++;
+	chain->chain_key = chain_key;
+	chain->irq_context = irq_context;
+	chain->depth = 2;
+	if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) {
+		chain->base = nr_chain_hlocks;
+		nr_chain_hlocks += chain->depth;
+		chain_hlocks[chain->base] = prev - 1;
+		chain_hlocks[chain->base + 1] = next -1;
+	}
+	hlist_add_head_rcu(&chain->entry, hash_head);
+	debug_atomic_inc(chain_lookup_misses);
+	inc_chains();
+
+	return 1;
+}
+
 static inline int add_chain_cache(struct task_struct *curr,
 				  struct held_lock *hlock,
 				  u64 chain_key)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (3 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 04/15] lockdep: Add a function building a chain between two classes Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2017-01-12 16:16   ` Peter Zijlstra
  2016-12-09  5:12 ` [PATCH v4 06/15] lockdep: Make save_trace can skip stack tracing of the current Byungchul Park
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

check_prev_add() saves a stack trace of the current. But crossrelease
feature needs to use a separate stack trace of another context in
check_prev_add(). So make it use a separate stack trace instead of one
of the current.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 111839f..3eaa11c 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1793,7 +1793,8 @@ check_deadlock(struct task_struct *curr, struct held_lock *next,
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance, int *stack_saved)
+	       struct held_lock *next, int distance, int *stack_saved,
+	       struct stack_trace *own_trace)
 {
 	struct lock_list *entry;
 	int ret;
@@ -1854,7 +1855,7 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 		}
 	}
 
-	if (!*stack_saved) {
+	if (!own_trace && stack_saved && !*stack_saved) {
 		if (!save_trace(&trace))
 			return 0;
 		*stack_saved = 1;
@@ -1866,14 +1867,14 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, own_trace ?: &trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, own_trace ?: &trace);
 	if (!ret)
 		return 0;
 
@@ -1882,7 +1883,8 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 	 */
 	if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) {
 		/* We drop graph lock, so another thread can overwrite trace. */
-		*stack_saved = 0;
+		if (stack_saved)
+			*stack_saved = 0;
 		graph_unlock();
 		printk("\n new dependency: ");
 		print_lock_name(hlock_class(prev));
@@ -1931,8 +1933,8 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		 * added:
 		 */
 		if (hlock->read != 2 && hlock->check) {
-			if (!check_prev_add(curr, hlock, next,
-						distance, &stack_saved))
+			if (!check_prev_add(curr, hlock, next, distance,
+						&stack_saved, NULL))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 06/15] lockdep: Make save_trace can skip stack tracing of the current
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (4 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2017-01-12 16:37   ` Peter Zijlstra
  2016-12-09  5:12 ` [PATCH v4 07/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Currently, save_trace() always performs save_stack_trace() for the
current. However, crossrelease needs to use stack trace data of another
context instead of the current. So add a parameter for skipping stack
tracing of the current and make it use trace data, which is already
saved by crossrelease framework.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 3eaa11c..11580ec 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -387,15 +387,22 @@ static void print_lockdep_off(const char *bug_msg)
 #endif
 }
 
-static int save_trace(struct stack_trace *trace)
+static int save_trace(struct stack_trace *trace, int skip_tracing)
 {
-	trace->nr_entries = 0;
-	trace->max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
-	trace->entries = stack_trace + nr_stack_trace_entries;
+	unsigned int nr_avail = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
 
-	trace->skip = 3;
-
-	save_stack_trace(trace);
+	if (skip_tracing) {
+		trace->nr_entries = min(trace->nr_entries, nr_avail);
+		memcpy(stack_trace + nr_stack_trace_entries, trace->entries,
+				trace->nr_entries * sizeof(trace->entries[0]));
+		trace->entries = stack_trace + nr_stack_trace_entries;
+	} else {
+		trace->nr_entries = 0;
+		trace->max_entries = nr_avail;
+		trace->entries = stack_trace + nr_stack_trace_entries;
+		trace->skip = 3;
+		save_stack_trace(trace);
+	}
 
 	/*
 	 * Some daft arches put -1 at the end to indicate its a full trace.
@@ -1172,7 +1179,7 @@ static noinline int print_circular_bug(struct lock_list *this,
 	if (!debug_locks_off_graph_unlock() || debug_locks_silent)
 		return 0;
 
-	if (!save_trace(&this->trace))
+	if (!save_trace(&this->trace, 0))
 		return 0;
 
 	depth = get_lock_depth(target);
@@ -1518,13 +1525,13 @@ print_bad_irq_dependency(struct task_struct *curr,
 
 	printk("\nthe dependencies between %s-irq-safe lock", irqclass);
 	printk(" and the holding lock:\n");
-	if (!save_trace(&prev_root->trace))
+	if (!save_trace(&prev_root->trace, 0))
 		return 0;
 	print_shortest_lock_dependencies(backwards_entry, prev_root);
 
 	printk("\nthe dependencies between the lock to be acquired");
 	printk(" and %s-irq-unsafe lock:\n", irqclass);
-	if (!save_trace(&next_root->trace))
+	if (!save_trace(&next_root->trace, 0))
 		return 0;
 	print_shortest_lock_dependencies(forwards_entry, next_root);
 
@@ -1856,7 +1863,7 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
 	}
 
 	if (!own_trace && stack_saved && !*stack_saved) {
-		if (!save_trace(&trace))
+		if (!save_trace(&trace, 0))
 			return 0;
 		*stack_saved = 1;
 	}
@@ -2547,7 +2554,7 @@ print_irq_inversion_bug(struct task_struct *curr,
 	lockdep_print_held_locks(curr);
 
 	printk("\nthe shortest dependencies between 2nd lock and 1st lock:\n");
-	if (!save_trace(&root->trace))
+	if (!save_trace(&root->trace, 0))
 		return 0;
 	print_shortest_lock_dependencies(other, root);
 
@@ -3134,7 +3141,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this,
 
 	hlock_class(this)->usage_mask |= new_mask;
 
-	if (!save_trace(hlock_class(this)->usage_traces + new_bit))
+	if (!save_trace(hlock_class(this)->usage_traces + new_bit, 0))
 		return 0;
 
 	switch (new_bit) {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (5 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 06/15] lockdep: Make save_trace can skip stack tracing of the current Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2017-01-13  4:39   ` Lai Jiangshan
                     ` (2 more replies)
  2016-12-09  5:12 ` [PATCH v4 08/15] lockdep: Make crossrelease use save_stack_trace_fast() Byungchul Park
                   ` (8 subsequent siblings)
  15 siblings, 3 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Crossrelease feature calls a lock 'crosslock' if it is releasable
in any context. For crosslock, all locks having been held in the
release context of the crosslock, until eventually the crosslock
will be released, have dependency with the crosslock.

Using crossrelease feature, we can detect deadlock possibility even
for lock_page(), wait_for_complete() and so on.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 include/linux/irqflags.h |  12 +-
 include/linux/lockdep.h  | 122 +++++++++++
 include/linux/sched.h    |   5 +
 kernel/exit.c            |   9 +
 kernel/fork.c            |  20 ++
 kernel/locking/lockdep.c | 517 +++++++++++++++++++++++++++++++++++++++++++++--
 lib/Kconfig.debug        |  13 ++
 7 files changed, 682 insertions(+), 16 deletions(-)

diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h
index 5dd1272..b1854fa 100644
--- a/include/linux/irqflags.h
+++ b/include/linux/irqflags.h
@@ -23,9 +23,17 @@
 # define trace_softirq_context(p)	((p)->softirq_context)
 # define trace_hardirqs_enabled(p)	((p)->hardirqs_enabled)
 # define trace_softirqs_enabled(p)	((p)->softirqs_enabled)
-# define trace_hardirq_enter()	do { current->hardirq_context++; } while (0)
+# define trace_hardirq_enter()		\
+do {					\
+	current->hardirq_context++;	\
+	crossrelease_hardirq_start();	\
+} while (0)
 # define trace_hardirq_exit()	do { current->hardirq_context--; } while (0)
-# define lockdep_softirq_enter()	do { current->softirq_context++; } while (0)
+# define lockdep_softirq_enter()	\
+do {					\
+	current->softirq_context++;	\
+	crossrelease_softirq_start();	\
+} while (0)
 # define lockdep_softirq_exit()	do { current->softirq_context--; } while (0)
 # define INIT_TRACE_IRQFLAGS	.softirqs_enabled = 1,
 #else
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index eabe013..6b3708b 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -108,6 +108,12 @@ struct lock_class {
 	unsigned long			contention_point[LOCKSTAT_POINTS];
 	unsigned long			contending_point[LOCKSTAT_POINTS];
 #endif
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+	/*
+	 * Flag to indicate whether it's a crosslock or normal.
+	 */
+	int				cross;
+#endif
 };
 
 #ifdef CONFIG_LOCK_STAT
@@ -143,6 +149,9 @@ struct lock_class_stats lock_stats(struct lock_class *class);
 void clear_lock_stats(struct lock_class *class);
 #endif
 
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+struct cross_lock;
+#endif
 /*
  * Map the lock object (the lock instance) to the lock-class object.
  * This is embedded into specific lock instances:
@@ -155,6 +164,9 @@ struct lockdep_map {
 	int				cpu;
 	unsigned long			ip;
 #endif
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+	struct cross_lock		*xlock;
+#endif
 };
 
 static inline void lockdep_copy_map(struct lockdep_map *to,
@@ -258,7 +270,82 @@ struct held_lock {
 	unsigned int hardirqs_off:1;
 	unsigned int references:12;					/* 32 bits */
 	unsigned int pin_count;
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+	/*
+	 * This is used to find out the first plock among plocks having
+	 * been acquired since a crosslock was held. Crossrelease feature
+	 * uses chain cache between the crosslock and the first plock to
+	 * avoid building unnecessary dependencies, like how lockdep uses
+	 * a sort of chain cache for normal locks.
+	 */
+	unsigned int gen_id;
+#endif
+};
+
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+#define MAX_PLOCK_TRACE_ENTRIES		5
+
+/*
+ * This is for keeping locks waiting for commit to happen so that
+ * dependencies are actually built later at commit step.
+ *
+ * Every task_struct has an array of pend_lock. Each entiry will be
+ * added with a lock whenever lock_acquire() is called for normal lock.
+ */
+struct pend_lock {
+	/*
+	 * prev_gen_id is used to check whether any other hlock in the
+	 * current is already dealing with the xlock, with which commit
+	 * is performed. If so, this plock can be skipped.
+	 */
+	unsigned int		prev_gen_id;
+	/*
+	 * A kind of global timestamp increased and set when this plock
+	 * is inserted.
+	 */
+	unsigned int		gen_id;
+
+	int			hardirq_context;
+	int			softirq_context;
+
+	/*
+	 * Whenever irq happens, these are updated so that we can
+	 * distinguish each irq context uniquely.
+	 */
+	unsigned int		hardirq_id;
+	unsigned int		softirq_id;
+
+	/*
+	 * Seperate stack_trace data. This will be used at commit step.
+	 */
+	struct stack_trace	trace;
+	unsigned long		trace_entries[MAX_PLOCK_TRACE_ENTRIES];
+
+	/*
+	 * Seperate hlock instance. This will be used at commit step.
+	 */
+	struct held_lock	hlock;
+};
+
+/*
+ * One cross_lock per one lockdep_map.
+ *
+ * To initialize a lock as crosslock, lockdep_init_map_crosslock() should
+ * be used instead of lockdep_init_map(), where the pointer of cross_lock
+ * instance should be passed as a parameter.
+ */
+struct cross_lock {
+	unsigned int		gen_id;
+	struct list_head	xlock_entry;
+
+	/*
+	 * Seperate hlock instance. This will be used at commit step.
+	 */
+	struct held_lock	hlock;
+
+	int			ref; /* reference count */
 };
+#endif
 
 /*
  * Initialization, self-test and debugging-output methods:
@@ -281,6 +368,37 @@ extern void lockdep_on(void);
 extern void lockdep_init_map(struct lockdep_map *lock, const char *name,
 			     struct lock_class_key *key, int subclass);
 
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+extern void lockdep_init_map_crosslock(struct lockdep_map *lock,
+				       struct cross_lock *xlock,
+				       const char *name,
+				       struct lock_class_key *key,
+				       int subclass);
+extern void lock_commit_crosslock(struct lockdep_map *lock);
+
+/*
+ * What we essencially have to initialize is 'ref'.
+ * Other members will be initialized in add_xlock().
+ */
+#define STATIC_CROSS_LOCK_INIT() \
+	{ .ref = 0,}
+
+/*
+ * Note that _name and _xlock must not be NULL.
+ */
+#define STATIC_CROSS_LOCKDEP_MAP_INIT(_name, _key, _xlock) \
+	{ .name = (_name), .key = (void *)(_key), .xlock = (_xlock), }
+
+/*
+ * To initialize a lockdep_map statically use this macro.
+ * Note that _name must not be NULL.
+ */
+#define STATIC_LOCKDEP_MAP_INIT(_name, _key) \
+	{ .name = (_name), .key = (void *)(_key), .xlock = NULL, }
+
+extern void crossrelease_hardirq_start(void);
+extern void crossrelease_softirq_start(void);
+#else
 /*
  * To initialize a lockdep_map statically use this macro.
  * Note that _name must not be NULL.
@@ -288,6 +406,10 @@ extern void lockdep_init_map(struct lockdep_map *lock, const char *name,
 #define STATIC_LOCKDEP_MAP_INIT(_name, _key) \
 	{ .name = (_name), .key = (void *)(_key), }
 
+void crossrelease_hardirq_start(void) {}
+void crossrelease_softirq_start(void) {}
+#endif
+
 /*
  * Reinitialize a lock key - for cases where there is special locking or
  * special initialization of locks so that the validator gets the scope
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 253538f..592ee368 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1719,6 +1719,11 @@ struct task_struct {
 	struct held_lock held_locks[MAX_LOCK_DEPTH];
 	gfp_t lockdep_reclaim_gfp;
 #endif
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+#define MAX_PLOCKS_NR 1024UL
+	int plock_index;
+	struct pend_lock *plocks;
+#endif
 #ifdef CONFIG_UBSAN
 	unsigned int in_ubsan;
 #endif
diff --git a/kernel/exit.c b/kernel/exit.c
index 9e6e135..9c69995 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -54,6 +54,7 @@
 #include <linux/writeback.h>
 #include <linux/shm.h>
 #include <linux/kcov.h>
+#include <linux/vmalloc.h>
 
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -822,6 +823,14 @@ void do_exit(long code)
 	smp_mb();
 	raw_spin_unlock_wait(&tsk->pi_lock);
 
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+	if (tsk->plocks) {
+		void *tmp = tsk->plocks;
+		/* Disable crossrelease operation for current */
+		tsk->plocks = NULL;
+		vfree(tmp);
+	}
+#endif
 	/* causes final put_task_struct in finish_task_switch(). */
 	tsk->state = TASK_DEAD;
 	tsk->flags |= PF_NOFREEZE;	/* tell freezer to ignore us */
diff --git a/kernel/fork.c b/kernel/fork.c
index 4a7ec0c..91ab81b 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -323,6 +323,14 @@ void __init fork_init(void)
 	init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
 	init_task.signal->rlim[RLIMIT_SIGPENDING] =
 		init_task.signal->rlim[RLIMIT_NPROC];
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+	/*
+	 * TODO: We need to make init_task also use crossrelease feature.
+	 * For simplicity, now just disable the feature for init_task.
+	 */
+	init_task.plock_index = 0;
+	init_task.plocks = NULL;
+#endif
 }
 
 int __weak arch_dup_task_struct(struct task_struct *dst,
@@ -1443,6 +1451,10 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	p->lockdep_depth = 0; /* no locks held yet */
 	p->curr_chain_key = 0;
 	p->lockdep_recursion = 0;
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+	p->plock_index = 0;
+	p->plocks = vzalloc(sizeof(struct pend_lock) * MAX_PLOCKS_NR);
+#endif
 #endif
 
 #ifdef CONFIG_DEBUG_MUTEXES
@@ -1686,6 +1698,14 @@ bad_fork_cleanup_audit:
 bad_fork_cleanup_perf:
 	perf_event_free_task(p);
 bad_fork_cleanup_policy:
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+	if (p->plocks) {
+		void *tmp = p->plocks;
+		/* Diable crossrelease operation for current */
+		p->plocks = NULL;
+		vfree(tmp);
+	}
+#endif
 #ifdef CONFIG_NUMA
 	mpol_put(p->mempolicy);
 bad_fork_cleanup_threadgroup_lock:
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 11580ec..2c8b2c1 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -711,6 +711,20 @@ look_up_lock_class(struct lockdep_map *lock, unsigned int subclass)
 	return NULL;
 }
 
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+static int cross_class(struct lock_class *class);
+static void init_map_noncrosslock(struct lockdep_map *lock);
+static void init_class_crosslock(struct lock_class *class, int cross);
+static int lock_acquire_crosslock(struct held_lock *hlock);
+static int lock_release_crosslock(struct lockdep_map *lock);
+#else
+static inline int cross_class(struct lock_class *class) { return 0; }
+static inline void init_map_noncrosslock(struct lockdep_map *lock) {}
+static inline void init_class_crosslock(struct lock_class *class, int cross) {}
+static inline int lock_acquire_crosslock(struct held_lock *hlock) { return 0; }
+static inline int lock_release_crosslock(struct lockdep_map *lock) { return 0; }
+#endif
+
 /*
  * Register a lock's class in the hash-table, if the class is not present
  * yet. Otherwise we look it up. We cache the result in the lock object
@@ -779,6 +793,7 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
 	INIT_LIST_HEAD(&class->locks_before);
 	INIT_LIST_HEAD(&class->locks_after);
 	class->name_version = count_matching_names(class);
+	init_class_crosslock(class, !!lock->xlock);
 	/*
 	 * We use RCU's safe list-add method to make
 	 * parallel walking of the hash-list safe:
@@ -1771,6 +1786,9 @@ check_deadlock(struct task_struct *curr, struct held_lock *next,
 		if (nest)
 			return 2;
 
+		if (cross_class(hlock_class(prev)))
+			continue;
+
 		return print_deadlock_bug(curr, prev, next);
 	}
 	return 1;
@@ -1936,21 +1954,27 @@ check_prevs_add(struct task_struct *curr, struct held_lock *next)
 		int distance = curr->lockdep_depth - depth + 1;
 		hlock = curr->held_locks + depth - 1;
 		/*
-		 * Only non-recursive-read entries get new dependencies
-		 * added:
+		 * Only non-crosslock entries get new dependencies added.
+		 * Crosslock entries will be added by commit later:
 		 */
-		if (hlock->read != 2 && hlock->check) {
-			if (!check_prev_add(curr, hlock, next, distance,
-						&stack_saved, NULL))
-				return 0;
+		if (!cross_class(hlock_class(hlock))) {
 			/*
-			 * Stop after the first non-trylock entry,
-			 * as non-trylock entries have added their
-			 * own direct dependencies already, so this
-			 * lock is connected to them indirectly:
+			 * Only non-recursive-read entries get new dependencies
+			 * added:
 			 */
-			if (!hlock->trylock)
-				break;
+			if (hlock->read != 2 && hlock->check) {
+				if (!check_prev_add(curr, hlock, next, distance,
+							&stack_saved, NULL))
+					return 0;
+				/*
+				 * Stop after the first non-trylock entry,
+				 * as non-trylock entries have added their
+				 * own direct dependencies already, so this
+				 * lock is connected to them indirectly:
+				 */
+				if (!hlock->trylock)
+					break;
+			}
 		}
 		depth--;
 		/*
@@ -3184,7 +3208,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this,
 /*
  * Initialize a lock instance's lock-class mapping info:
  */
-void lockdep_init_map(struct lockdep_map *lock, const char *name,
+static void __lockdep_init_map(struct lockdep_map *lock, const char *name,
 		      struct lock_class_key *key, int subclass)
 {
 	int i;
@@ -3242,8 +3266,27 @@ void lockdep_init_map(struct lockdep_map *lock, const char *name,
 		raw_local_irq_restore(flags);
 	}
 }
+
+void lockdep_init_map(struct lockdep_map *lock, const char *name,
+		      struct lock_class_key *key, int subclass)
+{
+	init_map_noncrosslock(lock);
+	__lockdep_init_map(lock, name, key, subclass);
+}
 EXPORT_SYMBOL_GPL(lockdep_init_map);
 
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+static void init_map_crosslock(struct lockdep_map *lock, struct cross_lock *xlock);
+void lockdep_init_map_crosslock(struct lockdep_map *lock,
+		      struct cross_lock *xlock, const char *name,
+		      struct lock_class_key *key, int subclass)
+{
+	init_map_crosslock(lock, xlock);
+	__lockdep_init_map(lock, name, key, subclass);
+}
+EXPORT_SYMBOL_GPL(lockdep_init_map_crosslock);
+#endif
+
 struct lock_class_key __lockdep_no_validate__;
 EXPORT_SYMBOL_GPL(__lockdep_no_validate__);
 
@@ -3347,7 +3390,8 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 
 	class_idx = class - lock_classes + 1;
 
-	if (depth) {
+	/* TODO: nest_lock is not implemented for crosslock yet. */
+	if (depth && !cross_class(class)) {
 		hlock = curr->held_locks + depth - 1;
 		if (hlock->class_idx == class_idx && nest_lock) {
 			if (hlock->references)
@@ -3428,6 +3472,9 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	if (!validate_chain(curr, lock, hlock, chain_head, chain_key))
 		return 0;
 
+	if (lock_acquire_crosslock(hlock))
+		return 1;
+
 	curr->curr_chain_key = chain_key;
 	curr->lockdep_depth++;
 	check_chain_key(curr);
@@ -3596,6 +3643,9 @@ __lock_release(struct lockdep_map *lock, int nested, unsigned long ip)
 	if (unlikely(!debug_locks))
 		return 0;
 
+	if (lock_release_crosslock(lock))
+		return 1;
+
 	depth = curr->lockdep_depth;
 	/*
 	 * So we're all set to release this lock.. wait what lock? We don't
@@ -4538,3 +4588,442 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s)
 	dump_stack();
 }
 EXPORT_SYMBOL_GPL(lockdep_rcu_suspicious);
+
+#ifdef CONFIG_LOCKDEP_CROSSRELEASE
+
+static LIST_HEAD(xlocks_head);
+
+/*
+ * Whenever a crosslock is held, cross_gen_id will be increased.
+ */
+static atomic_t cross_gen_id; /* Can be wrapped */
+
+/* Implement a circular buffer - for internal use */
+#define cir_p(n, i)		((i) ? (i) - 1 : (n) - 1)
+#define cir_n(n, i)		((i) == (n) - 1 ? 0 : (i) + 1)
+#define p_idx_p(i)		cir_p(MAX_PLOCKS_NR, i)
+#define p_idx_n(i)		cir_n(MAX_PLOCKS_NR, i)
+#define p_idx(t)		((t)->plock_index)
+
+/* For easy access to plock */
+#define plock(t, i)		((t)->plocks + (i))
+#define plock_prev(t, p)	plock(t, p_idx_p((p) - (t)->plocks))
+#define plock_curr(t)		plock(t, p_idx(t))
+#define plock_incr(t)		({p_idx(t) = p_idx_n(p_idx(t));})
+
+/*
+ * Crossrelease needs to distinguish each hardirq context.
+ */
+static DEFINE_PER_CPU(unsigned int, hardirq_id);
+void crossrelease_hardirq_start(void)
+{
+	per_cpu(hardirq_id, smp_processor_id())++;
+}
+
+/*
+ * Crossrelease needs to distinguish each softirq context.
+ */
+static DEFINE_PER_CPU(unsigned int, softirq_id);
+void crossrelease_softirq_start(void)
+{
+	per_cpu(softirq_id, smp_processor_id())++;
+}
+
+static int cross_class(struct lock_class *class)
+{
+	if (!class)
+		return 0;
+
+	return class->cross;
+}
+
+/*
+ * This is needed to decide the relationship between wrapable variables.
+ */
+static inline int before(unsigned int a, unsigned int b)
+{
+	return (int)(a - b) < 0;
+}
+
+static inline struct lock_class *plock_class(struct pend_lock *plock)
+{
+	return hlock_class(&plock->hlock);
+}
+
+static inline struct lock_class *xlock_class(struct cross_lock *xlock)
+{
+	return hlock_class(&xlock->hlock);
+}
+
+/*
+ * To find the earlist crosslock among all crosslocks not released yet.
+ */
+static unsigned int gen_id_begin(void)
+{
+	struct cross_lock *xlock = list_entry_rcu(xlocks_head.next,
+			struct cross_lock, xlock_entry);
+
+	/* If empty */
+	if (&xlock->xlock_entry == &xlocks_head)
+		return (unsigned int)atomic_read(&cross_gen_id) + 1;
+
+	return READ_ONCE(xlock->gen_id);
+}
+
+/*
+ * To find the latest crosslock among all crosslocks already released.
+ */
+static inline unsigned int gen_id_done(void)
+{
+	return gen_id_begin() - 1;
+}
+
+/*
+ * Should we check a dependency with previous one?
+ */
+static inline int depend_before(struct held_lock *hlock)
+{
+	return hlock->read != 2 && hlock->check && !hlock->trylock;
+}
+
+/*
+ * Should we check a dependency with next one?
+ */
+static inline int depend_after(struct held_lock *hlock)
+{
+	return hlock->read != 2 && hlock->check;
+}
+
+/*
+ * Check if the plock is used at least once after initializaion.
+ * Remind pend_lock is implemented as a ring buffer.
+ */
+static inline int plock_used(struct pend_lock *plock)
+{
+	/*
+	 * plock->hlock.instance must be !NULL if it's used.
+	 */
+	return !!plock->hlock.instance;
+}
+
+/*
+ * Get a pend_lock from pend_lock ring buffer.
+ *
+ * No contention. Irq disable is only required.
+ */
+static struct pend_lock *alloc_plock(unsigned int gen_id_done)
+{
+	struct task_struct *curr = current;
+	struct pend_lock *plock = plock_curr(curr);
+
+	if (plock_used(plock) && before(gen_id_done, plock->gen_id)) {
+		printk_once("crossrelease: plock pool is full.\n");
+		return NULL;
+	}
+
+	plock_incr(curr);
+	return plock;
+}
+
+/*
+ * No contention. Irq disable is only required.
+ */
+static void add_plock(struct held_lock *hlock, unsigned int prev_gen_id,
+		unsigned int gen_id_done)
+{
+	struct task_struct *curr = current;
+	int cpu = smp_processor_id();
+	struct pend_lock *plock;
+	/*
+	 *	CONTEXT 1		CONTEXT 2
+	 *	---------		---------
+	 *	acquire A (cross)
+	 *	X = atomic_inc_return()
+	 *	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ serialize
+	 *				Y = atomic_read_acquire()
+	 *				acquire B
+	 *				acquire C
+	 *
+	 * For ordering between this and all following LOCKs.
+	 * This way we ensure the order A -> B -> C when CONTEXT 2
+	 * can see Y is equal to or greater than X.
+	 *
+	 * Pairs with atomic_inc_return() in add_xlock().
+	 */
+	unsigned int gen_id = (unsigned int)atomic_read_acquire(&cross_gen_id);
+
+	plock = alloc_plock(gen_id_done);
+
+	if (plock) {
+		/* Initialize pend_lock's members here */
+		memcpy(&plock->hlock, hlock, sizeof(struct held_lock));
+		plock->prev_gen_id = prev_gen_id;
+		plock->gen_id = gen_id;
+		plock->hardirq_context = curr->hardirq_context;
+		plock->softirq_context = curr->softirq_context;
+		plock->hardirq_id = per_cpu(hardirq_id, cpu);
+		plock->softirq_id = per_cpu(softirq_id, cpu);
+
+		plock->trace.nr_entries = 0;
+		plock->trace.max_entries = MAX_PLOCK_TRACE_ENTRIES;
+		plock->trace.entries = plock->trace_entries;
+		plock->trace.skip = 3;
+		save_stack_trace(&plock->trace);
+	}
+}
+
+/*
+ * No contention. Irq disable is only required.
+ */
+static int same_context_plock(struct pend_lock *plock)
+{
+	struct task_struct *curr = current;
+	int cpu = smp_processor_id();
+
+	/* In the case of hardirq context */
+	if (curr->hardirq_context) {
+		if (plock->hardirq_id != per_cpu(hardirq_id, cpu) ||
+		    plock->hardirq_context != curr->hardirq_context)
+			return 0;
+	/* In the case of softriq context */
+	} else if (curr->softirq_context) {
+		if (plock->softirq_id != per_cpu(softirq_id, cpu) ||
+		    plock->softirq_context != curr->softirq_context)
+			return 0;
+	/* In the case of process context */
+	} else {
+		if (plock->hardirq_context != 0 ||
+		    plock->softirq_context != 0)
+			return 0;
+	}
+	return 1;
+}
+
+/*
+ * Called from lock_acquire() in case of non-crosslock. This should be
+ * lockless if possible.
+ */
+static void check_add_plock(struct held_lock *hlock)
+{
+	struct held_lock *prev;
+	struct held_lock *start;
+	struct cross_lock *xlock;
+	struct lock_chain *chain;
+	unsigned int id;
+	unsigned int gen_id;
+	unsigned int gen_id_e;
+	u64 chain_key;
+
+	if (!current->plocks || !depend_before(hlock))
+		return;
+
+	gen_id = (unsigned int)atomic_read(&cross_gen_id);
+	gen_id_e = gen_id_done();
+	start = current->held_locks;
+
+	list_for_each_entry_rcu(xlock, &xlocks_head, xlock_entry) {
+		id = xlock_class(xlock) - lock_classes;
+		chain_key = iterate_chain_key((u64)0, id);
+		id = hlock_class(hlock) - lock_classes;
+		chain_key = iterate_chain_key(chain_key, id);
+		chain = lookup_chain_cache(chain_key);
+
+		if (!chain) {
+			for (prev = hlock - 1; prev >= start &&
+			     !depend_before(prev); prev--);
+
+			if (prev < start)
+				add_plock(hlock, gen_id_e, gen_id_e);
+			else if (prev->gen_id != gen_id)
+				add_plock(hlock, prev->gen_id, gen_id_e);
+
+			break;
+		}
+	}
+}
+
+/*
+ * This will be called when lock_acquire() is called for crosslock.
+ */
+static int add_xlock(struct held_lock *hlock)
+{
+	struct cross_lock *xlock;
+	unsigned int gen_id;
+
+	if (!depend_after(hlock))
+		return 1;
+
+	if (!graph_lock())
+		return 0;
+
+	xlock = hlock->instance->xlock;
+	if (!xlock)
+		goto unlock;
+
+	if (xlock->ref++)
+		goto unlock;
+
+	/*
+	 * We assign class_idx here redundantly even though following
+	 * memcpy will cover it, in order to ensure a rcu reader can
+	 * access the class_idx atomically without lock.
+	 *
+	 * Here we assume setting a word-sized variable is atomic.
+	 */
+	xlock->hlock.class_idx = hlock->class_idx;
+	gen_id = (unsigned int)atomic_inc_return(&cross_gen_id);
+	WRITE_ONCE(xlock->gen_id, gen_id);
+	memcpy(&xlock->hlock, hlock, sizeof(struct held_lock));
+	INIT_LIST_HEAD(&xlock->xlock_entry);
+	list_add_tail_rcu(&xlock->xlock_entry, &xlocks_head);
+unlock:
+	graph_unlock();
+	return 1;
+}
+
+/*
+ * return 0: Need to do normal acquire operation.
+ * return 1: Done. No more acquire ops is needed.
+ */
+static int lock_acquire_crosslock(struct held_lock *hlock)
+{
+	unsigned int gen_id = (unsigned int)atomic_read(&cross_gen_id);
+
+	hlock->gen_id = gen_id;
+
+	if (cross_class(hlock_class(hlock)))
+		return add_xlock(hlock);
+
+	check_add_plock(hlock);
+	return 0;
+}
+
+static int commit_plock(struct cross_lock *xlock, struct pend_lock *plock)
+{
+	unsigned int xid, pid;
+	u64 chain_key;
+
+	xid = xlock_class(xlock) - lock_classes;
+	chain_key = iterate_chain_key((u64)0, xid);
+	pid = plock_class(plock) - lock_classes;
+	chain_key = iterate_chain_key(chain_key, pid);
+
+	if (lookup_chain_cache(chain_key))
+		return 1;
+
+	if (!add_chain_cache_classes(xid, pid, plock->hlock.irq_context,
+				chain_key))
+		return 0;
+
+	if (!save_trace(&plock->trace, 1))
+		return 0;
+
+	if (!check_prev_add(current, &xlock->hlock, &plock->hlock, 1,
+			    NULL, &plock->trace))
+		return 0;
+
+	return 1;
+}
+
+static int commit_plocks(struct cross_lock *xlock)
+{
+	struct task_struct *curr = current;
+	struct pend_lock *plock_c = plock_curr(curr);
+	struct pend_lock *plock = plock_c;
+
+	do {
+		plock = plock_prev(curr, plock);
+
+		if (!plock_used(plock))
+			break;
+
+		if (before(plock->gen_id, xlock->gen_id))
+			break;
+
+		if (same_context_plock(plock) &&
+		    before(plock->prev_gen_id, xlock->gen_id) &&
+		    !commit_plock(xlock, plock))
+			return 0;
+	} while (plock_c != plock);
+
+	return 1;
+}
+
+/*
+ * Commit function.
+ */
+void lock_commit_crosslock(struct lockdep_map *lock)
+{
+	struct cross_lock *xlock;
+	unsigned long flags;
+
+	if (!current->plocks)
+		return;
+
+	if (unlikely(current->lockdep_recursion))
+		return;
+
+	raw_local_irq_save(flags);
+	check_flags(flags);
+	current->lockdep_recursion = 1;
+
+	if (unlikely(!debug_locks))
+		return;
+
+	if (!graph_lock())
+		return;
+
+	xlock = lock->xlock;
+	if (xlock && xlock->ref > 0 && !commit_plocks(xlock))
+		return;
+
+	graph_unlock();
+	current->lockdep_recursion = 0;
+	raw_local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(lock_commit_crosslock);
+
+/*
+ * return 0: Need to do normal release operation.
+ * return 1: Done. No more release ops is needed.
+ */
+static int lock_release_crosslock(struct lockdep_map *lock)
+{
+	struct cross_lock *xlock;
+
+	if (!graph_lock())
+		return 0;
+
+	xlock = lock->xlock;
+	if (xlock && !--xlock->ref)
+		list_del_rcu(&xlock->xlock_entry);
+
+	graph_unlock();
+	return !!xlock;
+}
+
+static void init_map_noncrosslock(struct lockdep_map *lock)
+{
+	lock->xlock = NULL;
+}
+
+static void init_map_crosslock(struct lockdep_map *lock, struct cross_lock *xlock)
+{
+	unsigned long flags;
+
+	BUG_ON(!lock || !xlock);
+
+	raw_local_irq_save(flags);
+	if (graph_lock()) {
+		memset(xlock, 0x0, sizeof(struct cross_lock));
+		lock->xlock = xlock;
+		graph_unlock();
+	}
+	raw_local_irq_restore(flags);
+}
+
+static void init_class_crosslock(struct lock_class *class, int cross)
+{
+	class->cross = cross;
+}
+#endif
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index b9cfdbf..ef9ca8d 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1027,6 +1027,19 @@ config DEBUG_LOCK_ALLOC
 	 spin_lock_init()/mutex_init()/etc., or whether there is any lock
 	 held during task exit.
 
+config LOCKDEP_CROSSRELEASE
+	bool "Lock debugging: allow other context to unlock a lock"
+	depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
+	select LOCKDEP
+	select TRACE_IRQFLAGS
+	default n
+	help
+	 This allows any context to unlock a lock held by another context.
+	 Normally a lock must be unlocked by the context holding the lock.
+	 However, relexing this constraint helps locks like (un)lock_page()
+	 or wait_for_complete() can use lock correctness detector using
+	 lockdep.
+
 config PROVE_LOCKING
 	bool "Lock debugging: prove locking correctness"
 	depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 08/15] lockdep: Make crossrelease use save_stack_trace_fast()
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (6 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 07/15] lockdep: Implement crossrelease feature Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2016-12-09  5:12 ` [PATCH v4 09/15] lockdep: Make print_circular_bug() crosslock-aware Byungchul Park
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Currently crossrelease feature uses save_stack_trace() to save
backtrace. However, it has much overhead. So this patch makes it
use save_stack_trace_norm() instead, which has smaller overhead.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2c8b2c1..fbd07ee 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -4768,7 +4768,7 @@ static void add_plock(struct held_lock *hlock, unsigned int prev_gen_id,
 		plock->trace.max_entries = MAX_PLOCK_TRACE_ENTRIES;
 		plock->trace.entries = plock->trace_entries;
 		plock->trace.skip = 3;
-		save_stack_trace(&plock->trace);
+		save_stack_trace_fast(&plock->trace);
 	}
 }
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 09/15] lockdep: Make print_circular_bug() crosslock-aware
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (7 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 08/15] lockdep: Make crossrelease use save_stack_trace_fast() Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2016-12-09  5:12 ` [PATCH v4 10/15] lockdep: Apply crossrelease to completion operation Byungchul Park
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Friends of print_circular_bug() reporting circular bug assumes that
target hlock is owned by the current. However, in crossrelease feature,
target hlock can be owned by any context.

In this case, the circular bug is caused by target hlock which cannot be
released since its dependent lock cannot be released. So the report
format needs to be changed to be aware of this.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 56 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 17 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index fbd07ee..cb1a600 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1126,22 +1126,41 @@ print_circular_lock_scenario(struct held_lock *src,
 		printk("\n\n");
 	}
 
-	printk(" Possible unsafe locking scenario:\n\n");
-	printk("       CPU0                    CPU1\n");
-	printk("       ----                    ----\n");
-	printk("  lock(");
-	__print_lock_name(target);
-	printk(");\n");
-	printk("                               lock(");
-	__print_lock_name(parent);
-	printk(");\n");
-	printk("                               lock(");
-	__print_lock_name(target);
-	printk(");\n");
-	printk("  lock(");
-	__print_lock_name(source);
-	printk(");\n");
-	printk("\n *** DEADLOCK ***\n\n");
+	if (cross_class(target)) {
+		printk(" Possible unsafe locking scenario by crosslock:\n\n");
+		printk("       CPU0                    CPU1\n");
+		printk("       ----                    ----\n");
+		printk("  lock(");
+		__print_lock_name(parent);
+		printk(");\n");
+		printk("  lock(");
+		__print_lock_name(target);
+		printk(");\n");
+		printk("                               lock(");
+		__print_lock_name(source);
+		printk(");\n");
+		printk("                               unlock(");
+		__print_lock_name(target);
+		printk(");\n");
+		printk("\n *** DEADLOCK ***\n\n");
+	} else {
+		printk(" Possible unsafe locking scenario:\n\n");
+		printk("       CPU0                    CPU1\n");
+		printk("       ----                    ----\n");
+		printk("  lock(");
+		__print_lock_name(target);
+		printk(");\n");
+		printk("                               lock(");
+		__print_lock_name(parent);
+		printk(");\n");
+		printk("                               lock(");
+		__print_lock_name(target);
+		printk(");\n");
+		printk("  lock(");
+		__print_lock_name(source);
+		printk(");\n");
+		printk("\n *** DEADLOCK ***\n\n");
+	}
 }
 
 /*
@@ -1166,7 +1185,10 @@ print_circular_bug_header(struct lock_list *entry, unsigned int depth,
 	printk("%s/%d is trying to acquire lock:\n",
 		curr->comm, task_pid_nr(curr));
 	print_lock(check_src);
-	printk("\nbut task is already holding lock:\n");
+	if (cross_class(hlock_class(check_tgt)))
+		printk("\nbut now in the release context of lock:\n");
+	else
+		printk("\nbut task is already holding lock:\n");
 	print_lock(check_tgt);
 	printk("\nwhich lock already depends on the new lock.\n\n");
 	printk("\nthe existing dependency chain (in reverse order) is:\n");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 10/15] lockdep: Apply crossrelease to completion operation
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (8 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 09/15] lockdep: Make print_circular_bug() crosslock-aware Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2016-12-09  5:12 ` [PATCH v4 11/15] pagemap.h: Remove trailing white space Byungchul Park
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

wait_for_completion() and its family can cause deadlock. Nevertheless,
it cannot use the lock correntness validator because complete() will be
called in different context from the context calling
wait_for_completion(), which violates lockdep's assumption without
crossrelease feature.

However, thanks to CONFIG_LOCKDEP_CROSSRELEASE, we can apply the lockdep
detector to wait_for_completion() and complete(). Applied it.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 include/linux/completion.h | 121 +++++++++++++++++++++++++++++++++++++++++----
 kernel/locking/lockdep.c   |  17 +++++++
 kernel/sched/completion.c  |  54 +++++++++++---------
 lib/Kconfig.debug          |   8 +++
 4 files changed, 167 insertions(+), 33 deletions(-)

diff --git a/include/linux/completion.h b/include/linux/completion.h
index 5d5aaae..67a27af 100644
--- a/include/linux/completion.h
+++ b/include/linux/completion.h
@@ -9,6 +9,9 @@
  */
 
 #include <linux/wait.h>
+#ifdef CONFIG_LOCKDEP_COMPLETE
+#include <linux/lockdep.h>
+#endif
 
 /*
  * struct completion - structure used to maintain state for a "completion"
@@ -25,10 +28,53 @@
 struct completion {
 	unsigned int done;
 	wait_queue_head_t wait;
+#ifdef CONFIG_LOCKDEP_COMPLETE
+	struct lockdep_map map;
+	struct cross_lock xlock;
+#endif
 };
 
+#ifdef CONFIG_LOCKDEP_COMPLETE
+static inline void complete_acquire(struct completion *x)
+{
+	lock_acquire_exclusive(&x->map, 0, 0, NULL, _RET_IP_);
+}
+
+static inline void complete_release(struct completion *x)
+{
+	lock_release(&x->map, 0, _RET_IP_);
+}
+
+static inline void complete_release_commit(struct completion *x)
+{
+	lock_commit_crosslock(&x->map);
+}
+
+#define init_completion(x)				\
+do {							\
+	static struct lock_class_key __key;		\
+	lockdep_init_map_crosslock(&(x)->map,		\
+			&(x)->xlock,			\
+			"(complete)" #x,		\
+			&__key, 0);			\
+	__init_completion(x);				\
+} while (0)
+#else
+#define init_completion(x) __init_completion(x)
+static inline void complete_acquire(struct completion *x, int try) {}
+static inline void complete_release(struct completion *x) {}
+static inline void complete_release_commit(struct completion *x) {}
+#endif
+
+#ifdef CONFIG_LOCKDEP_COMPLETE
+#define COMPLETION_INITIALIZER(work) \
+	{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait), \
+	STATIC_CROSS_LOCKDEP_MAP_INIT("(complete)" #work, &(work), \
+	&(work).xlock), STATIC_CROSS_LOCK_INIT()}
+#else
 #define COMPLETION_INITIALIZER(work) \
 	{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }
+#endif
 
 #define COMPLETION_INITIALIZER_ONSTACK(work) \
 	({ init_completion(&work); work; })
@@ -70,7 +116,7 @@ struct completion {
  * This inline function will initialize a dynamically created completion
  * structure.
  */
-static inline void init_completion(struct completion *x)
+static inline void __init_completion(struct completion *x)
 {
 	x->done = 0;
 	init_waitqueue_head(&x->wait);
@@ -88,18 +134,75 @@ static inline void reinit_completion(struct completion *x)
 	x->done = 0;
 }
 
-extern void wait_for_completion(struct completion *);
-extern void wait_for_completion_io(struct completion *);
-extern int wait_for_completion_interruptible(struct completion *x);
-extern int wait_for_completion_killable(struct completion *x);
-extern unsigned long wait_for_completion_timeout(struct completion *x,
+extern void __wait_for_completion(struct completion *);
+extern void __wait_for_completion_io(struct completion *);
+extern int __wait_for_completion_interruptible(struct completion *x);
+extern int __wait_for_completion_killable(struct completion *x);
+extern unsigned long __wait_for_completion_timeout(struct completion *x,
 						   unsigned long timeout);
-extern unsigned long wait_for_completion_io_timeout(struct completion *x,
+extern unsigned long __wait_for_completion_io_timeout(struct completion *x,
 						    unsigned long timeout);
-extern long wait_for_completion_interruptible_timeout(
+extern long __wait_for_completion_interruptible_timeout(
 	struct completion *x, unsigned long timeout);
-extern long wait_for_completion_killable_timeout(
+extern long __wait_for_completion_killable_timeout(
 	struct completion *x, unsigned long timeout);
+
+static inline void wait_for_completion(struct completion *x)
+{
+	complete_acquire(x);
+	__wait_for_completion(x);
+	complete_release(x);
+}
+
+static inline void wait_for_completion_io(struct completion *x)
+{
+	complete_acquire(x);
+	__wait_for_completion_io(x);
+	complete_release(x);
+}
+
+static inline int wait_for_completion_interruptible(struct completion *x)
+{
+	int ret;
+	complete_acquire(x);
+	ret = __wait_for_completion_interruptible(x);
+	complete_release(x);
+	return ret;
+}
+
+static inline int wait_for_completion_killable(struct completion *x)
+{
+	int ret;
+	complete_acquire(x);
+	ret = __wait_for_completion_killable(x);
+	complete_release(x);
+	return ret;
+}
+
+static inline unsigned long wait_for_completion_timeout(struct completion *x,
+		unsigned long timeout)
+{
+	return __wait_for_completion_timeout(x, timeout);
+}
+
+static inline unsigned long wait_for_completion_io_timeout(struct completion *x,
+		unsigned long timeout)
+{
+	return __wait_for_completion_io_timeout(x, timeout);
+}
+
+static inline long wait_for_completion_interruptible_timeout(
+	struct completion *x, unsigned long timeout)
+{
+	return __wait_for_completion_interruptible_timeout(x, timeout);
+}
+
+static inline long wait_for_completion_killable_timeout(
+	struct completion *x, unsigned long timeout)
+{
+	return __wait_for_completion_killable_timeout(x, timeout);
+}
+
 extern bool try_wait_for_completion(struct completion *x);
 extern bool completion_done(struct completion *x);
 
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index cb1a600..fea69ac 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -4794,6 +4794,12 @@ static void add_plock(struct held_lock *hlock, unsigned int prev_gen_id,
 	}
 }
 
+#ifdef CONFIG_LOCKDEP_COMPLETE
+static int xlock_might_onstack = 1;
+#else
+static int xlock_might_onstack = 0;
+#endif
+
 /*
  * No contention. Irq disable is only required.
  */
@@ -4839,6 +4845,14 @@ static void check_add_plock(struct held_lock *hlock)
 	if (!current->plocks || !depend_before(hlock))
 		return;
 
+	/*
+	 * If a xlock instance is on stack, it can be overwritten randomly
+	 * after escaping the stack frame, so we cannot refer to rcu list
+	 * without holding lock.
+	 */
+	if (xlock_might_onstack && !graph_lock())
+		return;
+
 	gen_id = (unsigned int)atomic_read(&cross_gen_id);
 	gen_id_e = gen_id_done();
 	start = current->held_locks;
@@ -4862,6 +4876,9 @@ static void check_add_plock(struct held_lock *hlock)
 			break;
 		}
 	}
+
+	if (xlock_might_onstack)
+		graph_unlock();
 }
 
 /*
diff --git a/kernel/sched/completion.c b/kernel/sched/completion.c
index 8d0f35d..a8b2167 100644
--- a/kernel/sched/completion.c
+++ b/kernel/sched/completion.c
@@ -31,6 +31,10 @@ void complete(struct completion *x)
 	unsigned long flags;
 
 	spin_lock_irqsave(&x->wait.lock, flags);
+	/*
+	 * Lock dependency should be built here.
+	 */
+	complete_release_commit(x);
 	x->done++;
 	__wake_up_locked(&x->wait, TASK_NORMAL, 1);
 	spin_unlock_irqrestore(&x->wait.lock, flags);
@@ -108,7 +112,7 @@ wait_for_common_io(struct completion *x, long timeout, int state)
 }
 
 /**
- * wait_for_completion: - waits for completion of a task
+ * __wait_for_completion: - waits for completion of a task
  * @x:  holds the state of this particular completion
  *
  * This waits to be signaled for completion of a specific task. It is NOT
@@ -117,14 +121,14 @@ wait_for_common_io(struct completion *x, long timeout, int state)
  * See also similar routines (i.e. wait_for_completion_timeout()) with timeout
  * and interrupt capability. Also see complete().
  */
-void __sched wait_for_completion(struct completion *x)
+void __sched __wait_for_completion(struct completion *x)
 {
 	wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion);
+EXPORT_SYMBOL(__wait_for_completion);
 
 /**
- * wait_for_completion_timeout: - waits for completion of a task (w/timeout)
+ * __wait_for_completion_timeout: - waits for completion of a task (w/timeout)
  * @x:  holds the state of this particular completion
  * @timeout:  timeout value in jiffies
  *
@@ -136,28 +140,28 @@ EXPORT_SYMBOL(wait_for_completion);
  * till timeout) if completed.
  */
 unsigned long __sched
-wait_for_completion_timeout(struct completion *x, unsigned long timeout)
+__wait_for_completion_timeout(struct completion *x, unsigned long timeout)
 {
 	return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion_timeout);
+EXPORT_SYMBOL(__wait_for_completion_timeout);
 
 /**
- * wait_for_completion_io: - waits for completion of a task
+ * __wait_for_completion_io: - waits for completion of a task
  * @x:  holds the state of this particular completion
  *
  * This waits to be signaled for completion of a specific task. It is NOT
  * interruptible and there is no timeout. The caller is accounted as waiting
  * for IO (which traditionally means blkio only).
  */
-void __sched wait_for_completion_io(struct completion *x)
+void __sched __wait_for_completion_io(struct completion *x)
 {
 	wait_for_common_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion_io);
+EXPORT_SYMBOL(__wait_for_completion_io);
 
 /**
- * wait_for_completion_io_timeout: - waits for completion of a task (w/timeout)
+ * __wait_for_completion_io_timeout: - waits for completion of a task (w/timeout)
  * @x:  holds the state of this particular completion
  * @timeout:  timeout value in jiffies
  *
@@ -170,14 +174,14 @@ EXPORT_SYMBOL(wait_for_completion_io);
  * till timeout) if completed.
  */
 unsigned long __sched
-wait_for_completion_io_timeout(struct completion *x, unsigned long timeout)
+__wait_for_completion_io_timeout(struct completion *x, unsigned long timeout)
 {
 	return wait_for_common_io(x, timeout, TASK_UNINTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion_io_timeout);
+EXPORT_SYMBOL(__wait_for_completion_io_timeout);
 
 /**
- * wait_for_completion_interruptible: - waits for completion of a task (w/intr)
+ * __wait_for_completion_interruptible: - waits for completion of a task (w/intr)
  * @x:  holds the state of this particular completion
  *
  * This waits for completion of a specific task to be signaled. It is
@@ -185,17 +189,18 @@ EXPORT_SYMBOL(wait_for_completion_io_timeout);
  *
  * Return: -ERESTARTSYS if interrupted, 0 if completed.
  */
-int __sched wait_for_completion_interruptible(struct completion *x)
+int __sched __wait_for_completion_interruptible(struct completion *x)
 {
 	long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE);
+
 	if (t == -ERESTARTSYS)
 		return t;
 	return 0;
 }
-EXPORT_SYMBOL(wait_for_completion_interruptible);
+EXPORT_SYMBOL(__wait_for_completion_interruptible);
 
 /**
- * wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr))
+ * __wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr))
  * @x:  holds the state of this particular completion
  * @timeout:  timeout value in jiffies
  *
@@ -206,15 +211,15 @@ EXPORT_SYMBOL(wait_for_completion_interruptible);
  * or number of jiffies left till timeout) if completed.
  */
 long __sched
-wait_for_completion_interruptible_timeout(struct completion *x,
+__wait_for_completion_interruptible_timeout(struct completion *x,
 					  unsigned long timeout)
 {
 	return wait_for_common(x, timeout, TASK_INTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion_interruptible_timeout);
+EXPORT_SYMBOL(__wait_for_completion_interruptible_timeout);
 
 /**
- * wait_for_completion_killable: - waits for completion of a task (killable)
+ * __wait_for_completion_killable: - waits for completion of a task (killable)
  * @x:  holds the state of this particular completion
  *
  * This waits to be signaled for completion of a specific task. It can be
@@ -222,17 +227,18 @@ EXPORT_SYMBOL(wait_for_completion_interruptible_timeout);
  *
  * Return: -ERESTARTSYS if interrupted, 0 if completed.
  */
-int __sched wait_for_completion_killable(struct completion *x)
+int __sched __wait_for_completion_killable(struct completion *x)
 {
 	long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE);
+
 	if (t == -ERESTARTSYS)
 		return t;
 	return 0;
 }
-EXPORT_SYMBOL(wait_for_completion_killable);
+EXPORT_SYMBOL(__wait_for_completion_killable);
 
 /**
- * wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable))
+ * __wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable))
  * @x:  holds the state of this particular completion
  * @timeout:  timeout value in jiffies
  *
@@ -244,12 +250,12 @@ EXPORT_SYMBOL(wait_for_completion_killable);
  * or number of jiffies left till timeout) if completed.
  */
 long __sched
-wait_for_completion_killable_timeout(struct completion *x,
+__wait_for_completion_killable_timeout(struct completion *x,
 				     unsigned long timeout)
 {
 	return wait_for_common(x, timeout, TASK_KILLABLE);
 }
-EXPORT_SYMBOL(wait_for_completion_killable_timeout);
+EXPORT_SYMBOL(__wait_for_completion_killable_timeout);
 
 /**
  *	try_wait_for_completion - try to decrement a completion without blocking
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ef9ca8d..3466e57 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1040,6 +1040,14 @@ config LOCKDEP_CROSSRELEASE
 	 or wait_for_complete() can use lock correctness detector using
 	 lockdep.
 
+config LOCKDEP_COMPLETE
+	bool "Lock debugging: allow completions to use deadlock detector"
+	select LOCKDEP_CROSSRELEASE
+	default n
+	help
+	 A deadlock caused by wait_for_completion() and complete() can be
+	 detected by lockdep using crossrelease feature.
+
 config PROVE_LOCKING
 	bool "Lock debugging: prove locking correctness"
 	depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 11/15] pagemap.h: Remove trailing white space
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (9 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 10/15] lockdep: Apply crossrelease to completion operation Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2016-12-09  5:12 ` [PATCH v4 12/15] lockdep: Apply crossrelease to PG_locked lock Byungchul Park
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Trailing white space is not accepted in kernel coding style. Remove
them.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 include/linux/pagemap.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 9735410..0cf6980 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -485,7 +485,7 @@ static inline void wake_up_page(struct page *page, int bit)
 	__wake_up_bit(page_waitqueue(page), &page->flags, bit);
 }
 
-/* 
+/*
  * Wait for a page to be unlocked.
  *
  * This must be called with the caller "holding" the page,
@@ -498,7 +498,7 @@ static inline void wait_on_page_locked(struct page *page)
 		wait_on_page_bit(compound_head(page), PG_locked);
 }
 
-/* 
+/*
  * Wait for a page to complete writeback
  */
 static inline void wait_on_page_writeback(struct page *page)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 12/15] lockdep: Apply crossrelease to PG_locked lock
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (10 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 11/15] pagemap.h: Remove trailing white space Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2016-12-09  5:12 ` [PATCH v4 13/15] lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked Byungchul Park
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

lock_page() and its family can cause deadlock. Nevertheless, it cannot
use the lock correctness validator becasue unlock_page() can be called
in different context from the context calling lock_page(), which
violates lockdep's assumption without crossrelease feature.

However, thanks to CONFIG_LOCKDEP_CROSSRELEASE, we can apply the lockdep
detector to lock_page(). Applied it.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 include/linux/mm_types.h |   9 +++++
 include/linux/pagemap.h  | 100 ++++++++++++++++++++++++++++++++++++++++++++---
 lib/Kconfig.debug        |   8 ++++
 mm/filemap.c             |   4 +-
 mm/page_alloc.c          |   3 ++
 5 files changed, 116 insertions(+), 8 deletions(-)

diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index ca3e517..87db0ac 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -16,6 +16,10 @@
 #include <asm/page.h>
 #include <asm/mmu.h>
 
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+#include <linux/lockdep.h>
+#endif
+
 #ifndef AT_VECTOR_SIZE_ARCH
 #define AT_VECTOR_SIZE_ARCH 0
 #endif
@@ -220,6 +224,11 @@ struct page {
 #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
 	int _last_cpupid;
 #endif
+
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+	struct lockdep_map map;
+	struct cross_lock xlock;
+#endif
 }
 /*
  * The struct page can be forced to be double word aligned so that atomic ops
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 0cf6980..dbe7adf 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -14,6 +14,9 @@
 #include <linux/bitops.h>
 #include <linux/hardirq.h> /* for in_interrupt() */
 #include <linux/hugetlb_inline.h>
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+#include <linux/lockdep.h>
+#endif
 
 /*
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
@@ -413,26 +416,90 @@ static inline pgoff_t linear_page_index(struct vm_area_struct *vma,
 	return pgoff;
 }
 
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+#define lock_page_init(p)					\
+do {								\
+	static struct lock_class_key __key;			\
+	lockdep_init_map_crosslock(&(p)->map, &(p)->xlock,	\
+			"(PG_locked)" #p, &__key, 0);		\
+} while (0)
+
+static inline void lock_page_acquire(struct page *page, int try)
+{
+	page = compound_head(page);
+	lock_acquire_exclusive(&page->map, 0, try, NULL, _RET_IP_);
+}
+
+static inline void lock_page_release(struct page *page)
+{
+	page = compound_head(page);
+	/*
+	 * lock_commit_crosslock() is necessary for crosslock
+	 * when the lock is released, before lock_release().
+	 */
+	lock_commit_crosslock(&page->map);
+	lock_release(&page->map, 0, _RET_IP_);
+}
+#else
+static inline void lock_page_init(struct page *page) {}
+static inline void lock_page_free(struct page *page) {}
+static inline void lock_page_acquire(struct page *page, int try) {}
+static inline void lock_page_release(struct page *page) {}
+#endif
+
 extern void __lock_page(struct page *page);
 extern int __lock_page_killable(struct page *page);
 extern int __lock_page_or_retry(struct page *page, struct mm_struct *mm,
 				unsigned int flags);
-extern void unlock_page(struct page *page);
+extern void do_raw_unlock_page(struct page *page);
 
-static inline int trylock_page(struct page *page)
+static inline void unlock_page(struct page *page)
+{
+	lock_page_release(page);
+	do_raw_unlock_page(page);
+}
+
+static inline int do_raw_trylock_page(struct page *page)
 {
 	page = compound_head(page);
 	return (likely(!test_and_set_bit_lock(PG_locked, &page->flags)));
 }
 
+static inline int trylock_page(struct page *page)
+{
+	if (do_raw_trylock_page(page)) {
+		lock_page_acquire(page, 1);
+		return 1;
+	}
+	return 0;
+}
+
 /*
  * lock_page may only be called if we have the page's inode pinned.
  */
 static inline void lock_page(struct page *page)
 {
 	might_sleep();
-	if (!trylock_page(page))
+
+	if (!do_raw_trylock_page(page))
 		__lock_page(page);
+	/*
+	 * Acquire() must be after actual lock operation of crosslock.
+	 * This way crosslock and other locks can be serialized like,
+	 *
+	 *	CONTEXT 1		CONTEXT 2
+	 *	LOCK crosslock
+	 *	ACQUIRE crosslock
+	 *	  atomic_inc_return
+	 *	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	 *				ACQUIRE lock1
+	 *				  atomic_read_acquire lock1
+	 *				LOCK lock1
+	 *				LOCK lock2
+	 *
+	 * so that 'crosslock -> lock1 -> lock2' can be seen globally.
+	 */
+	lock_page_acquire(page, 0);
 }
 
 /*
@@ -442,9 +509,20 @@ static inline void lock_page(struct page *page)
  */
 static inline int lock_page_killable(struct page *page)
 {
+	int ret;
+
 	might_sleep();
-	if (!trylock_page(page))
-		return __lock_page_killable(page);
+
+	if (!do_raw_trylock_page(page)) {
+		ret = __lock_page_killable(page);
+		if (ret)
+			return ret;
+	}
+	/*
+	 * Acquire() must be after actual lock operation of crosslock.
+	 * This way crosslock and other locks can be serialized.
+	 */
+	lock_page_acquire(page, 0);
 	return 0;
 }
 
@@ -459,7 +537,17 @@ static inline int lock_page_or_retry(struct page *page, struct mm_struct *mm,
 				     unsigned int flags)
 {
 	might_sleep();
-	return trylock_page(page) || __lock_page_or_retry(page, mm, flags);
+
+	if (do_raw_trylock_page(page) || __lock_page_or_retry(page, mm, flags)) {
+		/*
+		 * Acquire() must be after actual lock operation of crosslock.
+		 * This way crosslock and other locks can be serialized.
+		 */
+		lock_page_acquire(page, 0);
+		return 1;
+	}
+
+	return 0;
 }
 
 /*
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 3466e57..1926435 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1048,6 +1048,14 @@ config LOCKDEP_COMPLETE
 	 A deadlock caused by wait_for_completion() and complete() can be
 	 detected by lockdep using crossrelease feature.
 
+config LOCKDEP_PAGELOCK
+	bool "Lock debugging: allow PG_locked lock to use deadlock detector"
+	select LOCKDEP_CROSSRELEASE
+	default n
+	help
+	 PG_locked lock is a kind of crosslock. Using crossrelease feature,
+	 PG_locked lock can participate in deadlock detector.
+
 config PROVE_LOCKING
 	bool "Lock debugging: prove locking correctness"
 	depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
diff --git a/mm/filemap.c b/mm/filemap.c
index 20f3b1f..e1f60fd 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -827,7 +827,7 @@ EXPORT_SYMBOL_GPL(add_page_wait_queue);
  * The mb is necessary to enforce ordering between the clear_bit and the read
  * of the waitqueue (to avoid SMP races with a parallel wait_on_page_locked()).
  */
-void unlock_page(struct page *page)
+void do_raw_unlock_page(struct page *page)
 {
 	page = compound_head(page);
 	VM_BUG_ON_PAGE(!PageLocked(page), page);
@@ -835,7 +835,7 @@ void unlock_page(struct page *page)
 	smp_mb__after_atomic();
 	wake_up_page(page, PG_locked);
 }
-EXPORT_SYMBOL(unlock_page);
+EXPORT_SYMBOL(do_raw_unlock_page);
 
 /**
  * end_page_writeback - end writeback against a page
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 8b3e134..0adc46c 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5225,6 +5225,9 @@ not_early:
 		} else {
 			__init_single_pfn(pfn, zone, nid);
 		}
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+		lock_page_init(pfn_to_page(pfn));
+#endif
 	}
 }
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 13/15] lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (11 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 12/15] lockdep: Apply crossrelease to PG_locked lock Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2016-12-09  5:12 ` [PATCH v4 14/15] lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to page_ext Byungchul Park
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Usually PG_locked bit is updated by lock_page() or unlock_page().
However, it can be also updated through __SetPageLocked() or
__ClearPageLockded(). They have to be considered, to get paired between
acquire and release.

Furthermore, e.g. __SetPageLocked() in add_to_page_cache_lru() is called
frequently. We might miss many chances to check deadlock if we ignore it.
Consider __Set(__Clear)PageLockded as well.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 include/linux/page-flags.h | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index e5a3244..e28f232 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -249,7 +249,6 @@ static inline int TestClearPage##uname(struct page *page) { return 0; }
 #define TESTSCFLAG_FALSE(uname)						\
 	TESTSETFLAG_FALSE(uname) TESTCLEARFLAG_FALSE(uname)
 
-__PAGEFLAG(Locked, locked, PF_NO_TAIL)
 PAGEFLAG(Error, error, PF_NO_COMPOUND) TESTCLEARFLAG(Error, error, PF_NO_COMPOUND)
 PAGEFLAG(Referenced, referenced, PF_HEAD)
 	TESTCLEARFLAG(Referenced, referenced, PF_HEAD)
@@ -351,6 +350,35 @@ TESTCLEARFLAG(Young, young, PF_ANY)
 PAGEFLAG(Idle, idle, PF_ANY)
 #endif
 
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+#include <linux/lockdep.h>
+
+TESTPAGEFLAG(Locked, locked, PF_NO_TAIL)
+
+static __always_inline void __SetPageLocked(struct page *page)
+{
+	__set_bit(PG_locked, &PF_NO_TAIL(page, 1)->flags);
+
+	page = compound_head(page);
+	lock_acquire_exclusive(&page->map, 0, 1, NULL, _RET_IP_);
+}
+
+static __always_inline void __ClearPageLocked(struct page *page)
+{
+	__clear_bit(PG_locked, &PF_NO_TAIL(page, 1)->flags);
+
+	page = compound_head(page);
+	/*
+	 * lock_commit_crosslock() is necessary for crosslock
+	 * when the lock is released, before lock_release().
+	 */
+	lock_commit_crosslock(&page->map);
+	lock_release(&page->map, 0, _RET_IP_);
+}
+#else
+__PAGEFLAG(Locked, locked, PF_NO_TAIL)
+#endif
+
 /*
  * On an anonymous page mapped into a user virtual memory area,
  * page->mapping points to its anon_vma, not to a struct address_space;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 14/15] lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to page_ext
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (12 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 13/15] lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2016-12-09  5:12 ` [PATCH v4 15/15] lockdep: Crossrelease feature documentation Byungchul Park
  2016-12-09  5:21 ` [FYI] Output of 'cat /proc/lockdep' after applying crossrelease Byungchul Park
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

CONFIG_LOCKDEP_PAGELOCK is keeping data, with which lockdep can check
and detect deadlock by page lock, e.g. lockdep_map and cross_lock in
struct page. But move it to page_ext since it's a debug feature so it's
preferred to keep it in struct page_ext than struct page.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 include/linux/mm_types.h   |  5 ----
 include/linux/page-flags.h | 19 ++++++++++--
 include/linux/page_ext.h   |  5 ++++
 include/linux/pagemap.h    | 28 +++++++++++++++---
 lib/Kconfig.debug          |  1 +
 mm/filemap.c               | 72 ++++++++++++++++++++++++++++++++++++++++++++++
 mm/page_alloc.c            |  3 --
 mm/page_ext.c              |  4 +++
 8 files changed, 122 insertions(+), 15 deletions(-)

diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 87db0ac..6558e12 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -224,11 +224,6 @@ struct page {
 #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
 	int _last_cpupid;
 #endif
-
-#ifdef CONFIG_LOCKDEP_PAGELOCK
-	struct lockdep_map map;
-	struct cross_lock xlock;
-#endif
 }
 /*
  * The struct page can be forced to be double word aligned so that atomic ops
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index e28f232..9f677ff 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -352,28 +352,41 @@ PAGEFLAG(Idle, idle, PF_ANY)
 
 #ifdef CONFIG_LOCKDEP_PAGELOCK
 #include <linux/lockdep.h>
+#include <linux/page_ext.h>
 
 TESTPAGEFLAG(Locked, locked, PF_NO_TAIL)
 
 static __always_inline void __SetPageLocked(struct page *page)
 {
+	struct page_ext *e;
+
 	__set_bit(PG_locked, &PF_NO_TAIL(page, 1)->flags);
 
 	page = compound_head(page);
-	lock_acquire_exclusive(&page->map, 0, 1, NULL, _RET_IP_);
+	e = lookup_page_ext(page);
+	if (unlikely(!e))
+		return;
+
+	lock_acquire_exclusive(&e->map, 0, 1, NULL, _RET_IP_);
 }
 
 static __always_inline void __ClearPageLocked(struct page *page)
 {
+	struct page_ext *e;
+
 	__clear_bit(PG_locked, &PF_NO_TAIL(page, 1)->flags);
 
 	page = compound_head(page);
+	e = lookup_page_ext(page);
+	if (unlikely(!e))
+		return;
+
 	/*
 	 * lock_commit_crosslock() is necessary for crosslock
 	 * when the lock is released, before lock_release().
 	 */
-	lock_commit_crosslock(&page->map);
-	lock_release(&page->map, 0, _RET_IP_);
+	lock_commit_crosslock(&e->map);
+	lock_release(&e->map, 0, _RET_IP_);
 }
 #else
 __PAGEFLAG(Locked, locked, PF_NO_TAIL)
diff --git a/include/linux/page_ext.h b/include/linux/page_ext.h
index e1fe7cf..f84e9be 100644
--- a/include/linux/page_ext.h
+++ b/include/linux/page_ext.h
@@ -48,6 +48,11 @@ struct page_ext {
 	int last_migrate_reason;
 	unsigned long trace_entries[8];
 #endif
+
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+	struct lockdep_map map;
+	struct cross_lock xlock;
+#endif
 };
 
 extern void pgdat_page_ext_init(struct pglist_data *pgdat);
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index dbe7adf..79174ad 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -16,6 +16,7 @@
 #include <linux/hugetlb_inline.h>
 #ifdef CONFIG_LOCKDEP_PAGELOCK
 #include <linux/lockdep.h>
+#include <linux/page_ext.h>
 #endif
 
 /*
@@ -417,28 +418,47 @@ static inline pgoff_t linear_page_index(struct vm_area_struct *vma,
 }
 
 #ifdef CONFIG_LOCKDEP_PAGELOCK
+extern struct page_ext_operations lockdep_pagelock_ops;
+
 #define lock_page_init(p)					\
 do {								\
 	static struct lock_class_key __key;			\
-	lockdep_init_map_crosslock(&(p)->map, &(p)->xlock,	\
+	struct page_ext *e = lookup_page_ext(p);		\
+								\
+	if (unlikely(!e))					\
+		break;						\
+								\
+	lockdep_init_map_crosslock(&(e)->map, &(e)->xlock,	\
 			"(PG_locked)" #p, &__key, 0);		\
 } while (0)
 
 static inline void lock_page_acquire(struct page *page, int try)
 {
+	struct page_ext *e;
+
 	page = compound_head(page);
-	lock_acquire_exclusive(&page->map, 0, try, NULL, _RET_IP_);
+	e = lookup_page_ext(page);
+	if (unlikely(!e))
+		return;
+
+	lock_acquire_exclusive(&e->map, 0, try, NULL, _RET_IP_);
 }
 
 static inline void lock_page_release(struct page *page)
 {
+	struct page_ext *e;
+
 	page = compound_head(page);
+	e = lookup_page_ext(page);
+	if (unlikely(!e))
+		return;
+
 	/*
 	 * lock_commit_crosslock() is necessary for crosslock
 	 * when the lock is released, before lock_release().
 	 */
-	lock_commit_crosslock(&page->map);
-	lock_release(&page->map, 0, _RET_IP_);
+	lock_commit_crosslock(&e->map);
+	lock_release(&e->map, 0, _RET_IP_);
 }
 #else
 static inline void lock_page_init(struct page *page) {}
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 1926435..9c6dc15 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1051,6 +1051,7 @@ config LOCKDEP_COMPLETE
 config LOCKDEP_PAGELOCK
 	bool "Lock debugging: allow PG_locked lock to use deadlock detector"
 	select LOCKDEP_CROSSRELEASE
+	select PAGE_EXTENSION
 	default n
 	help
 	 PG_locked lock is a kind of crosslock. Using crossrelease feature,
diff --git a/mm/filemap.c b/mm/filemap.c
index e1f60fd..a6a52a4 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -35,6 +35,9 @@
 #include <linux/memcontrol.h>
 #include <linux/cleancache.h>
 #include <linux/rmap.h>
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+#include <linux/page_ext.h>
+#endif
 #include "internal.h"
 
 #define CREATE_TRACE_POINTS
@@ -955,6 +958,75 @@ int __lock_page_or_retry(struct page *page, struct mm_struct *mm,
 	}
 }
 
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+static bool need_lockdep_pagelock(void) { return true; }
+
+static void init_pages_in_zone(pg_data_t *pgdat, struct zone *zone)
+{
+	struct page *page;
+	struct page_ext *page_ext;
+	unsigned long pfn = zone->zone_start_pfn;
+	unsigned long end_pfn = pfn + zone->spanned_pages;
+	unsigned long count = 0;
+
+	for (; pfn < end_pfn; pfn++) {
+		if (!pfn_valid(pfn)) {
+			pfn = ALIGN(pfn + 1, MAX_ORDER_NR_PAGES);
+			continue;
+		}
+
+		if (!pfn_valid_within(pfn))
+			continue;
+
+		page = pfn_to_page(pfn);
+
+		if (page_zone(page) != zone)
+			continue;
+
+		if (PageReserved(page))
+			continue;
+
+		page_ext = lookup_page_ext(page);
+		if (unlikely(!page_ext))
+			continue;
+
+		lock_page_init(page);
+		count++;
+	}
+
+	pr_info("Node %d, zone %8s: lockdep pagelock found early allocated %lu pages\n",
+		pgdat->node_id, zone->name, count);
+}
+
+static void init_zones_in_node(pg_data_t *pgdat)
+{
+	struct zone *zone;
+	struct zone *node_zones = pgdat->node_zones;
+	unsigned long flags;
+
+	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
+		if (!populated_zone(zone))
+			continue;
+
+		spin_lock_irqsave(&zone->lock, flags);
+		init_pages_in_zone(pgdat, zone);
+		spin_unlock_irqrestore(&zone->lock, flags);
+	}
+}
+
+static void init_lockdep_pagelock(void)
+{
+	pg_data_t *pgdat;
+	for_each_online_pgdat(pgdat)
+		init_zones_in_node(pgdat);
+}
+
+struct page_ext_operations lockdep_pagelock_ops = {
+	.need = need_lockdep_pagelock,
+	.init = init_lockdep_pagelock,
+};
+#endif
+
 /**
  * page_cache_next_hole - find the next hole (not-present entry)
  * @mapping: mapping
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 0adc46c..8b3e134 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5225,9 +5225,6 @@ not_early:
 		} else {
 			__init_single_pfn(pfn, zone, nid);
 		}
-#ifdef CONFIG_LOCKDEP_PAGELOCK
-		lock_page_init(pfn_to_page(pfn));
-#endif
 	}
 }
 
diff --git a/mm/page_ext.c b/mm/page_ext.c
index 44a4c02..54f7027 100644
--- a/mm/page_ext.c
+++ b/mm/page_ext.c
@@ -7,6 +7,7 @@
 #include <linux/kmemleak.h>
 #include <linux/page_owner.h>
 #include <linux/page_idle.h>
+#include <linux/pagemap.h>
 
 /*
  * struct page extension
@@ -63,6 +64,9 @@ static struct page_ext_operations *page_ext_ops[] = {
 #if defined(CONFIG_IDLE_PAGE_TRACKING) && !defined(CONFIG_64BIT)
 	&page_idle_ops,
 #endif
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+	&lockdep_pagelock_ops,
+#endif
 };
 
 static unsigned long total_usage;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (13 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 14/15] lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to page_ext Byungchul Park
@ 2016-12-09  5:12 ` Byungchul Park
  2017-01-10 20:08   ` Peter Zijlstra
  2017-01-18  6:42   ` Boqun Feng
  2016-12-09  5:21 ` [FYI] Output of 'cat /proc/lockdep' after applying crossrelease Byungchul Park
  15 siblings, 2 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:12 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

This document describes the concept of crossrelease feature, which
generalizes what causes a deadlock and how can detect a deadlock.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 Documentation/locking/crossrelease.txt | 1053 ++++++++++++++++++++++++++++++++
 1 file changed, 1053 insertions(+)
 create mode 100644 Documentation/locking/crossrelease.txt

diff --git a/Documentation/locking/crossrelease.txt b/Documentation/locking/crossrelease.txt
new file mode 100644
index 0000000..7170b2f
--- /dev/null
+++ b/Documentation/locking/crossrelease.txt
@@ -0,0 +1,1053 @@
+Crossrelease
+============
+
+Started by Byungchul Park <byungchul.park@lge.com>
+
+Contents:
+
+ (*) Background.
+
+     - What causes deadlock.
+     - What lockdep detects.
+     - How lockdep works.
+
+ (*) Limitation.
+
+     - Limit to typical locks.
+     - Pros from the limitation.
+     - Cons from the limitation.
+
+ (*) Generalization.
+
+     - Relax the limitation.
+
+ (*) Crossrelease.
+
+     - Introduce crossrelease.
+     - Pick true dependencies.
+     - Introduce commit.
+
+ (*) Implementation.
+
+     - Data structures.
+     - How crossrelease works.
+
+ (*) Optimizations.
+
+     - Avoid duplication.
+     - Lockless for hot paths.
+
+
+==========
+Background
+==========
+
+What causes deadlock
+--------------------
+
+A deadlock occurs when a context is waiting for an event to happen,
+which is impossible because another (or the) context who can trigger the
+event is also waiting for another (or the) event to happen, which is
+also impossible due to the same reason. Single or more contexts
+paricipate in such a deadlock.
+
+For example,
+
+   A context going to trigger event D is waiting for event A to happen.
+   A context going to trigger event A is waiting for event B to happen.
+   A context going to trigger event B is waiting for event C to happen.
+   A context going to trigger event C is waiting for event D to happen.
+
+A deadlock occurs when these four wait operations run at the same time,
+because event D cannot be triggered if event A does not happen, which in
+turn cannot be triggered if event B does not happen, which in turn
+cannot be triggered if event C does not happen, which in turn cannot be
+triggered if event D does not happen. After all, no event can be
+triggered since any of them never meets its precondition to wake up.
+
+In terms of dependency, a wait for an event creates a dependency if the
+context is going to wake up another waiter by triggering an proper event.
+In other words, a dependency exists if,
+
+   COND 1. There are two waiters waiting for each event at the same time.
+   COND 2. Only way to wake up each waiter is to trigger its events.
+   COND 3. Whether one can be woken up depends on whether the other can.
+
+Each wait in the example creates its dependency like,
+
+   Event D depends on event A.
+   Event A depends on event B.
+   Event B depends on event C.
+   Event C depends on event D.
+
+   NOTE: Precisely speaking, a dependency is one between whether a
+   waiter for an event can be woken up and whether another waiter for
+   another event can be woken up. However from now on, we will describe
+   a dependency as if it's one between an event and another event for
+   simplicity, so e.g. 'event D depends on event A'.
+
+And they form circular dependencies like,
+
+    -> D -> A -> B -> C -
+   /                     \
+   \                     /
+    ---------------------
+
+   where A, B,..., D are different events, and '->' represents 'depends
+   on'.
+
+Such circular dependencies lead to a deadlock since no waiter can meet
+its precondition to wake up if they run simultaneously, as described.
+
+CONCLUSION
+
+Circular dependencies cause a deadlock.
+
+
+What lockdep detects
+--------------------
+
+Lockdep tries to detect a deadlock by checking dependencies created by
+lock operations e.i. acquire and release. Waiting for a lock to be
+released corresponds to waiting for an event to happen, and releasing a
+lock corresponds to triggering an event. See 'What causes deadlock'
+section.
+
+A deadlock actually occurs when all wait operations creating circular
+dependencies run at the same time. Even though they don't, a potential
+deadlock exists if the problematic dependencies exist. Thus it's
+meaningful to detect not only an actual deadlock but also its potential
+possibility. Lockdep does the both.
+
+Whether or not a deadlock actually occurs depends on several factors.
+For example, what order contexts are switched in is a factor. Assuming
+circular dependencies exist, a deadlock would occur when contexts are
+switched so that all wait operations creating the problematic
+dependencies run simultaneously.
+
+To detect a potential possibility which means a deadlock has not
+happened yet but might happen in future, lockdep considers all possible
+combinations of dependencies so that its potential possibility can be
+detected in advance. To do this, lockdep is trying to,
+
+1. Use a global dependency graph.
+
+   Lockdep combines all dependencies into one global graph and uses them,
+   regardless of which context generates them or what order contexts are
+   switched in. Aggregated dependencies are only considered so they are
+   prone to be circular if a problem exists.
+
+2. Check dependencies between classes instead of instances.
+
+   What actually causes a deadlock are instances of lock. However,
+   lockdep checks dependencies between classes instead of instances.
+   This way lockdep can detect a deadlock which has not happened but
+   might happen in future by others but the same classes.
+
+3. Assume all acquisitions lead to waiting.
+
+   Although locks might be acquired without waiting which is essential
+   to create dependencies, lockdep assumes all acquisitions lead to
+   waiting and generates dependencies, since it might be true some time
+   or another. Potential possibilities can be checked in this way.
+
+Lockdep detects both an actual deadlock and its possibility. But the
+latter is more valuable than the former. When a deadlock occurs actually,
+we can identify what happens in the system by some means or other even
+without lockdep. However, there's no way to detect possiblity without
+lockdep unless the whole code is parsed in head. It's terrible.
+
+CONCLUSION
+
+Lockdep detects and reports,
+
+   1. A deadlock possibility.
+   2. A deadlock which actually occured.
+
+
+How lockdep works
+-----------------
+
+Lockdep does,
+
+   1. Detect a new dependency created.
+   2. Keep the dependency in a global data structure, graph.
+   3. Check if circular dependencies exist.
+   4. Report a deadlock or its possibility if so.
+
+A graph built by lockdep looks like, e.g.
+
+   A -> B -        -> F -> G
+           \      /
+            -> E -        -> L
+           /      \      /
+   C -> D -        -> H -
+                         \
+                          -> I -> K
+                         /
+                      J -
+
+   where A, B,..., L are different lock classes.
+
+Lockdep will add a dependency into graph when a new dependency is
+detected. For example, it will add a dependency 'K -> J' when a new
+dependency between lock K and lock J is detected. Then the graph will be,
+
+   A -> B -        -> F -> G
+           \      /
+            -> E -        -> L
+           /      \      /
+   C -> D -        -> H -
+                         \
+                          -> I -> K -
+                         /           \
+                   -> J -             \
+                  /                   /
+                  \                  /
+                   ------------------
+
+   where A, B,..., L are different lock classes.
+
+Now, circular dependencies are detected like,
+
+           -> I -> K -
+          /           \
+    -> J -             \
+   /                   /
+   \                  /
+    ------------------
+
+   where J, I and K are different lock classes.
+
+As decribed in 'What causes deadlock', this is the condition under which
+a deadlock might occur. Lockdep detects a deadlock or its possibility by
+checking if circular dependencies were created after adding each new
+dependency into the global graph. This is the way how lockdep works.
+
+CONCLUSION
+
+Lockdep detects a deadlock or its possibility by checking if circular
+dependencies were created after adding each new dependency.
+
+
+==========
+Limitation
+==========
+
+Limit to typical locks
+----------------------
+
+Limiting lockdep to checking dependencies only on typical locks e.g.
+spin locks and mutexes, which should be released within the acquire
+context, the implementation of detecting and adding dependencies becomes
+simple but its capacity for detection becomes limited. Let's check what
+its pros and cons are, in next section.
+
+CONCLUSION
+
+Limiting lockdep to working on typical locks e.g. spin locks and mutexes,
+the implmentation becomes simple but limits its capacity.
+
+
+Pros from the limitation
+------------------------
+
+Given the limitation, when acquiring a lock, locks in the held_locks of
+the context cannot be released if the context fails to acquire it and
+has to wait for it. It also makes waiters for the locks in the
+held_locks stuck. It's the exact case to create a dependency 'A -> B',
+where lock A is each lock in held_locks and lock B is the lock to
+acquire. See 'What casues deadlock' section.
+
+For example,
+
+   CONTEXT X
+   ---------
+   acquire A
+
+   acquire B /* Add a dependency 'A -> B' */
+
+   acquire C /* Add a dependency 'B -> C' */
+
+   release C
+
+   release B
+
+   release A
+
+   where A, B and C are different lock classes.
+
+When acquiring lock A, the held_locks of CONTEXT X is empty thus no
+dependency is added. When acquiring lock B, lockdep detects and adds
+a new dependency 'A -> B' between lock A in held_locks and lock B. When
+acquiring lock C, lockdep also adds another dependency 'B -> C' for the
+same reason. They can be simply added whenever acquiring each lock.
+
+And most data required by lockdep exists in a local structure e.i.
+'task_struct -> held_locks'. Forcing to access those data within the
+context, lockdep can avoid racy problems without explicit locks while
+handling the local data.
+
+Lastly, lockdep only needs to keep locks currently being held, to build
+the dependency graph. However relaxing the limitation, it might need to
+keep even locks already released, because the decision of whether they
+created dependencies might be long-deferred. See 'Crossrelease' section.
+
+To sum up, we can expect several advantages from the limitation.
+
+1. Lockdep can easily identify a dependency when acquiring a lock.
+2. Requiring only local locks makes many races avoidable.
+3. Lockdep only needs to keep locks currently being held.
+
+CONCLUSION
+
+Given the limitation, the implementation becomes simple and efficient.
+
+
+Cons from the limitation
+------------------------
+
+Given the limitation, lockdep is applicable only to typical locks. For
+example, page locks for page access or completions for synchronization
+cannot play with lockdep under the limitation.
+
+Can we detect deadlocks below, under the limitation?
+
+Example 1:
+
+   CONTEXT X		   CONTEXT Y
+   ---------		   ---------
+   mutext_lock A
+			   lock_page B
+   lock_page B
+			   mutext_lock A /* DEADLOCK */
+   unlock_page B
+			   mutext_unlock A
+   mutex_unlock A
+			   unlock_page B
+
+   where A is a lock class and B is a page lock.
+
+No, we cannot.
+
+Example 2:
+
+   CONTEXT X	   CONTEXT Y	   CONTEXT Z
+   ---------	   ---------	   ----------
+		   mutex_lock A
+   lock_page B
+		   lock_page B
+				   mutext_lock A /* DEADLOCK */
+				   mutext_unlock A
+				   unlock_page B held by X
+		   unlock_page B
+		   mutex_unlock A
+
+   where A is a lock class and B is a page lock.
+
+No, we cannot.
+
+Example 3:
+
+   CONTEXT X		   CONTEXT Y
+   ---------		   ---------
+			   mutex_lock A
+   mutex_lock A
+   mutex_unlock A
+			   wait_for_complete B /* DEADLOCK */
+   complete B
+			   mutex_unlock A
+
+   where A is a lock class and B is a completion variable.
+
+No, we cannot.
+
+CONCLUSION
+
+Given the limitation, lockdep cannot detect a deadlock or its
+possibility caused by page locks or completions.
+
+
+==============
+Generalization
+==============
+
+Relax the limitation
+--------------------
+
+Under the limitation, things to create dependencies are limited to
+typical locks. However, e.g. page locks and completions which are not
+typical locks also create dependencies and cause a deadlock. Therefore
+it would be better for lockdep to detect a deadlock or its possibility
+even for them.
+
+Detecting and adding dependencies into graph is very important for
+lockdep to work because adding a dependency means adding a chance to
+check if it causes a deadlock. The more lockdep adds dependencies, the
+more it thoroughly works. Therefore Lockdep has to do its best to add as
+many true dependencies as possible into the graph.
+
+Relaxing the limitation, lockdep can add more dependencies since
+additional things e.g. page locks or completions create additional
+dependencies. However even so, it needs to be noted that the relaxation
+does not affect the behavior of adding dependencies for typical locks.
+
+For example, considering only typical locks, lockdep builds a graph like,
+
+   A -> B -        -> F -> G
+           \      /
+            -> E -        -> L
+           /      \      /
+   C -> D -        -> H -
+                         \
+                          -> I -> K
+                         /
+                      J -
+
+   where A, B,..., L are different lock classes.
+
+On the other hand, under the relaxation, additional dependencies might
+be created and added. Assuming additional 'MX -> H', 'L -> NX' and
+'OX -> J' dependencies are added thanks to the relaxation, the graph
+will be, giving additional chances to check circular dependencies,
+
+   A -> B -        -> F -> G
+           \      /
+            -> E -        -> L -> NX
+           /      \      /
+   C -> D -        -> H -
+                  /      \
+              MX -        -> I -> K
+                         /
+                   -> J -
+                  /
+              OX -
+
+   where A, B,..., L, MX, NX and OX are different lock classes, and
+   a suffix 'X' is added on non-typical locks e.g. page locks and
+   completions.
+
+However, it might suffer performance degradation since relaxing the
+limitation with which design and implementation of lockdep could be
+efficient might introduce inefficiency inevitably. Each option, strong
+detection or efficient detection, has its pros and cons, thus the right
+of choice between two options should be given to users.
+
+Choosing efficient detection, lockdep only deals with locks satisfying,
+
+   A lock should be released within the context holding the lock.
+
+Choosing strong detection, lockdep deals with any locks satisfying,
+
+   A lock can be released in any context.
+
+The latter, of course, doesn't allow illegal contexts to release a lock.
+For example, acquiring a lock in irq-safe context before releasing the
+lock in irq-unsafe context is not allowed, which after all ends in
+circular dependencies, meaning a deadlock. Otherwise, any contexts are
+allowed to release it.
+
+CONCLUSION
+
+Relaxing the limitation, lockdep can add additional dependencies and
+get additional chances to check if they cause deadlocks.
+
+
+============
+Crossrelease
+============
+
+Introduce crossrelease
+----------------------
+
+To allow lockdep to handle additional dependencies by what might be
+released in any context, namely 'crosslock', a new feature 'crossrelease'
+is introduced. Thanks to the feature, now lockdep can identify such
+dependencies. Crossrelease feature has to do,
+
+   1. Identify dependencies by crosslocks.
+   2. Add the dependencies into graph.
+
+That's all. Once a meaningful dependency is added into graph, then
+lockdep would work with the graph as it did. So the most important thing
+crossrelease feature has to do is to correctly identify and add true
+dependencies into the global graph.
+
+A dependency e.g. 'A -> B' can be identified only in the A's release
+context because a decision required to identify the dependency can be
+made only in the release context. That is to decide whether A can be
+released so that a waiter for A can be woken up. It cannot be made in
+other contexts than the A's release context. See 'What causes deadlock'
+section to remind what a dependency is.
+
+It's no matter for typical locks because each acquire context is same as
+its release context, thus lockdep can decide whether a lock can be
+released, in the acquire context. However for crosslocks, lockdep cannot
+make the decision in the acquire context but has to wait until the
+release context is identified.
+
+Therefore lockdep has to queue all acquisitions which might create
+dependencies until the decision can be made, so that they can be used
+when it proves they are the right ones. We call the step 'commit'. See
+'Introduce commit' section.
+
+Of course, some actual deadlocks caused by crosslocks cannot be detected
+just when it happens, because the deadlocks cannot be identified until
+the crosslocks is actually released. However, deadlock possibilities can
+be detected in this way. It's worth possibility detection of deadlock.
+See 'What lockdep does' section.
+
+CONCLUSION
+
+With crossrelease feature, lockdep can work with what might be released
+in any context, namely crosslock.
+
+
+Pick true dependencies
+----------------------
+
+Remind what a dependency is. A dependency exists if,
+
+   COND 1. There are two waiters waiting for each event at the same time.
+   COND 2. Only way to wake up each waiter is to trigger its events.
+   COND 3. Whether one can be woken up depends on whether the other can.
+
+For example,
+
+   TASK X
+   ------
+   acquire A
+
+   acquire B /* A dependency 'A -> B' exists */
+
+   acquire C /* A dependency 'B -> C' exists */
+
+   release C
+
+   release B
+
+   release A
+
+   where A, B and C are different lock classes.
+
+A depedency 'A -> B' exists since,
+
+   1. A waiter for A and a waiter for B might exist when acquiring B.
+   2. Only way to wake up each of them is to release what it waits for.
+   3. Whether the waiter for A can be woken up depends on whether the
+      other can. IOW, TASK X cannot release A if it cannot acquire B.
+
+Other dependencies 'B -> C' and 'A -> C' also exist for the same reason.
+But the second is ignored since it's covered by 'A -> B' and 'B -> C'.
+
+For another example,
+
+   TASK X			   TASK Y
+   ------			   ------
+				   acquire AX
+   acquire D
+   /* A dependency 'AX -> D' exists */
+				   acquire B
+   release D
+				   acquire C
+				   /* A dependency 'B -> C' exists */
+   acquire E
+   /* A dependency 'AX -> E' exists */
+				   acquire D
+				   /* A dependency 'C -> D' exists */
+   release E
+				   release D
+   release AX held by Y
+				   release C
+
+				   release B
+
+   where AX, B, C,..., E are different lock classes, and a suffix 'X' is
+   added on crosslocks.
+
+Even in this case involving crosslocks, the same rules can be applied. A
+depedency 'AX -> D' exists since,
+
+   1. A waiter for AX and a waiter for D might exist when acquiring D.
+   2. Only way to wake up each of them is to release what it waits for.
+   3. Whether the waiter for AX can be woken up depends on whether the
+      other can. IOW, TASK X cannot release AX if it cannot acquire D.
+
+The same rules can be applied to other dependencies, too.
+
+Let's take a look at more complicated example.
+
+   TASK X			   TASK Y
+   ------			   ------
+   acquire B
+
+   release B
+
+   acquire C
+
+   release C
+   (1)
+   fork Y
+				   acquire AX
+   acquire D
+   /* A dependency 'AX -> D' exists */
+				   acquire F
+   release D
+				   acquire G
+				   /* A dependency 'F -> G' exists */
+   acquire E
+   /* A dependency 'AX -> E' exists */
+				   acquire H
+				   /* A dependency 'G -> H' exists */
+   release E
+				   release H
+   release AX held by Y
+				   release G
+
+				   release F
+
+   where AX, B, C,..., H are different lock classes, and a suffix 'X' is
+   added on crosslocks.
+
+Does a dependency 'AX -> B' exist? Nope.
+
+Two waiters, one is for AX and the other is for B, are essential
+elements to create the dependency 'AX -> B'. However in this example,
+these two waiters cannot exist at the same time. Thus the dependency
+'AX -> B' cannot be created.
+
+In fact, AX depends on all acquisitions after (1) in TASK X e.i. D and E,
+but excluding all acquisitions before (1) in the context e.i. A and C.
+Thus only 'AX -> D' and 'AX -> E' are true dependencies by AX.
+
+It would be ideal if the full set of true ones can be added. But parsing
+the whole code is necessary to do it, which is impossible. Relying on
+what actually happens at runtime, we can anyway add only true ones even
+though they might be a subset of the full set. This way we can avoid
+adding false ones.
+
+It's similar to how lockdep works for typical locks. Ideally there might
+be more true dependencies than ones being in the gloabl dependency graph,
+however, lockdep has no choice but to rely on what actually happens
+since otherwise it's almost impossible.
+
+CONCLUSION
+
+Relying on what actually happens, adding false dependencies can be
+avoided.
+
+
+Introduce commit
+----------------
+
+Crossrelease feature names it 'commit' to identify and add dependencies
+into graph in batches. Lockdep is already doing what commit is supposed
+to do, when acquiring a lock for typical locks. However, that way must
+be changed for crosslocks so that it identifies a crosslock's release
+context first, then does commit.
+
+There are four types of dependencies.
+
+1. TT type: 'Typical lock A -> Typical lock B' dependency
+
+   Just when acquiring B, lockdep can see it's in the A's release
+   context. So the dependency between A and B can be identified
+   immediately. Commit is unnecessary.
+
+2. TC type: 'Typical lock A -> Crosslock BX' dependency
+
+   Just when acquiring BX, lockdep can see it's in the A's release
+   context. So the dependency between A and BX can be identified
+   immediately. Commit is unnecessary, too.
+
+3. CT type: 'Crosslock AX -> Typical lock B' dependency
+
+   When acquiring B, lockdep cannot identify the dependency because
+   there's no way to know whether it's in the AX's release context. It
+   has to wait until the decision can be made. Commit is necessary.
+
+4. CC type: 'Crosslock AX -> Crosslock BX' dependency
+
+   If there is a typical lock acting as a bridge so that 'AX -> a lock'
+   and 'the lock -> BX' can be added, then this dependency can be
+   detected. But direct ways are not implemented yet. It's a future work.
+
+Lockdep works even without commit for typical locks. However, commit
+step is necessary once crosslocks are involved, until all crosslocks in
+progress are released. Introducing commit, lockdep performs three steps
+i.e. acquire, commit and release. What lockdep does in each step is,
+
+1. Acquire
+
+   1) For typical lock
+
+      Lockdep does what it originally did and queues the lock so that
+      lockdep can check CT type dependencies using it at commit step.
+
+   2) For crosslock
+
+      The crosslock is added to a global linked list so that lockdep can
+      check CT type dependencies using it at commit step.
+
+2. Commit
+
+   1) For typical lock
+
+      N/A.
+
+   2) For crosslock
+
+      Lockdep checks and adds CT Type dependencies using data saved at
+      acquire step.
+
+3. Release
+
+   1) For typical lock
+
+      No change.
+
+   2) For crosslock
+
+      Lockdep just remove the crosslock from the global linked list, to
+      which it was added at acquire step.
+
+CONCLUSION
+
+Crossrelease feature introduces commit step to handle dependencies by
+crosslocks in batches, which lockdep cannot handle in its original way.
+
+
+==============
+Implementation
+==============
+
+Data structures
+---------------
+
+Crossrelease feature introduces two main data structures.
+
+1. pend_lock
+
+   This is an array embedded in task_struct, for keeping locks queued so
+   that real dependencies can be added using them at commit step. Since
+   it's local data, it can be accessed locklessly in the owner context.
+   The array is filled at acquire step and consumed at commit step. And
+   it's managed in circular manner.
+
+2. cross_lock
+
+   This is a global linked list, for keeping all crosslocks in progress.
+   The list grows at acquire step and is shrunk at release step.
+
+CONCLUSION
+
+Crossrelease feature introduces two main data structures.
+
+1. A pend_lock array for queueing typical locks in circular manner.
+2. A cross_lock linked list for managing crosslocks in progress.
+
+
+How crossrelease works
+----------------------
+
+Let's take a look at how crossrelease feature works step by step,
+starting from how lockdep works without crossrelease feaure.
+
+For example, the below is how lockdep works for typical locks.
+
+   A's RELEASE CONTEXT (= A's ACQUIRE CONTEXT)
+   -------------------------------------------
+   acquire A
+
+   acquire B /* Add 'A -> B' */
+
+   acquire C /* Add 'B -> C' */
+
+   release C
+
+   release B
+
+   release A
+
+   where A, B and C are different lock classes.
+
+After adding 'A -> B', the dependency graph will be,
+
+   A -> B
+
+   where A and B are different lock classes.
+
+And after adding 'B -> C', the graph will be,
+
+   A -> B -> C
+
+   where A, B and C are different lock classes.
+
+What if we use commit step to add dependencies even for typical locks?
+Commit step is not necessary for them, however it anyway would work well,
+because this is a more general way.
+
+   A's RELEASE CONTEXT (= A's ACQUIRE CONTEXT)
+   -------------------------------------------
+   acquire A
+   /*
+    * 1. Mark A as started
+    * 2. Queue A
+    *
+    * In pend_lock: A
+    * In graph: Empty
+    */
+
+   acquire B
+   /*
+    * 1. Mark B as started
+    * 2. Queue B
+    *
+    * In pend_lock: A, B
+    * In graph: Empty
+    */
+
+   acquire C
+   /*
+    * 1. Mark C as started
+    * 2. Queue C
+    *
+    * In pend_lock: A, B, C
+    * In graph: Empty
+    */
+
+   release C
+   /*
+    * 1. Commit C (= Add 'C -> ?')
+    *   a. What queued since C was marked: Nothing
+    *   b. Add nothing
+    *
+    * In pend_lock: A, B, C
+    * In graph: Empty
+    */
+
+   release B
+   /*
+    * 1. Commit B (= Add 'B -> ?')
+    *   a. What queued since B was marked: C
+    *   b. Add 'B -> C'
+    *
+    * In pend_lock: A, B, C
+    * In graph: 'B -> C'
+    */
+
+   release A
+   /*
+    * 1. Commit A (= Add 'A -> ?')
+    *   a. What queued since A was marked: B, C
+    *   b. Add 'A -> B'
+    *   c. Add 'A -> C'
+    *
+    * In pend_lock: A, B, C
+    * In graph: 'B -> C', 'A -> B', 'A -> C'
+    */
+
+   where A, B and C are different lock classes.
+
+After doing commit A, B and C, the dependency graph becomes like,
+
+   A -> B -> C
+
+   where A, B and C are different lock classes.
+
+   NOTE: A dependency 'A -> C' is optimized out.
+
+We can see the former graph built without commit step is same as the
+latter graph built using commit steps. Of course the former way leads to
+earlier finish for building the graph, which means we can detect a
+deadlock or its possibility sooner. So the former way would be prefered
+if possible. But we cannot avoid using the latter way for crosslocks.
+
+Let's look at how commit works for crosslocks.
+
+   AX's RELEASE CONTEXT		   AX's ACQUIRE CONTEXT
+   --------------------		   --------------------
+				   acquire AX
+				   /*
+				    * 1. Mark AX as started
+				    *
+				    * (No queuing for crosslocks)
+				    *
+				    * In pend_lock: Empty
+				    * In graph: Empty
+				    */
+
+   (serialized by some means e.g. barrier)
+
+   acquire D
+   /*
+    * (No marking for typical locks)
+    *
+    * 1. Queue D
+    *
+    * In pend_lock: D
+    * In graph: Empty
+    */
+				   acquire B
+				   /*
+				    * (No marking for typical locks)
+				    *
+				    * 1. Queue B
+				    *
+				    * In pend_lock: B
+				    * In graph: Empty
+				    */
+   release D
+   /*
+    * (No commit for typical locks)
+    *
+    * In pend_lock: D
+    * In graph: Empty
+    */
+				   acquire C
+				   /*
+				    * (No marking for typical locks)
+				    *
+				    * 1. Add 'B -> C' of TT type
+				    * 2. Queue C
+				    *
+				    * In pend_lock: B, C
+				    * In graph: 'B -> C'
+				    */
+   acquire E
+   /*
+    * (No marking for typical locks)
+    *
+    * 1. Queue E
+    *
+    * In pend_lock: D, E
+    * In graph: 'B -> C'
+    */
+				   acquire D
+				   /*
+				    * (No marking for typical locks)
+				    *
+				    * 1. Add 'C -> D' of TT type
+				    * 2. Queue D
+				    *
+				    * In pend_lock: B, C, D
+				    * In graph: 'B -> C', 'C -> D'
+				    */
+   release E
+   /*
+    * (No commit for typical locks)
+    *
+    * In pend_lock: D, E
+    * In graph: 'B -> C', 'C -> D'
+    */
+				   release D
+				   /*
+				    * (No commit for typical locks)
+				    *
+				    * In pend_lock: B, C, D
+				    * In graph: 'B -> C', 'C -> D'
+				    */
+   release AX
+   /*
+    * 1. Commit AX (= Add 'AX -> ?')
+    *   a. What queued since AX was marked: D, E
+    *   b. Add 'AX -> D' of CT type
+    *   c. Add 'AX -> E' of CT type
+    *
+    * In pend_lock: D, E
+    * In graph: 'B -> C', 'C -> D',
+    *           'AX -> D', 'AX -> E'
+    */
+				   release C
+				   /*
+				    * (No commit for typical locks)
+				    *
+				    * In pend_lock: B, C, D
+				    * In graph: 'B -> C', 'C -> D',
+				    *           'AX -> D', 'AX -> E'
+				    */
+
+				   release B
+				   /*
+				    * (No commit for typical locks)
+				    *
+				    * In pend_lock: B, C, D
+				    * In graph: 'B -> C', 'C -> D',
+				    *           'AX -> D', 'AX -> E'
+				    */
+
+   where AX, B, C,..., E are different lock classes, and a suffix 'X' is
+   added on crosslocks.
+
+When acquiring crosslock AX, crossrelease feature marks AX as started,
+which means all acquisitions from now are candidates which might create
+dependencies with AX. True dependencies will be determined when
+identifying the AX's release context.
+
+When acquiring typical lock B, lockdep queues B so that it can be used
+at commit step later since any crosslocks in progress might depends on B.
+The same thing is done on lock C, D and E. And then two dependencies
+'AX -> D' and 'AX -> E' are added at commit step, when identifying the
+AX's release context.
+
+The final graph is, with crossrelease feature using commit,
+
+   B -> C -
+           \
+            -> D
+           /
+       AX -
+           \
+            -> E
+
+   where AX, B, C,..., E are different lock classes, and a suffix 'X' is
+   added on crosslocks.
+
+However, without crossrelease feature, the final graph would be,
+
+   B -> C -> D
+
+   where B and C are different lock classes.
+
+The former graph has two more dependencies 'AX -> D' and 'AX -> E'
+giving additional chances to check if they cause deadlocks. This way
+lockdep can detect a deadlock or its possibility caused by crosslocks.
+Again, crossrelease feature does not affect the behavior of adding
+dependencies for typical locks.
+
+CONCLUSION
+
+Crossrelease works well for crosslock, thanks to commit step.
+
+
+=============
+Optimizations
+=============
+
+Avoid duplication
+-----------------
+
+Crossrelease feature uses a cache like what lockdep is already using for
+dependency chains, but this time it's for caching a dependency of CT
+type, crossing between two different context. Once that dependency is
+cached, same dependencies will never be added again. Queueing
+unnecessary locks is also prevented based on the cache.
+
+CONCLUSION
+
+Crossrelease does not add any duplicate dependencies.
+
+
+Lockless for hot paths
+----------------------
+
+To keep all typical locks for later use, crossrelease feature adopts a
+local array embedded in task_struct, which makes accesses to arrays
+lockless by forcing the accesses to happen only within the owner context.
+It's like how lockdep accesses held_locks. Lockless implmentation is
+important since typical locks are very frequently acquired and released.
+
+CONCLUSION
+
+Crossrelease is designed to use no lock for hot paths.
+
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [FYI] Output of 'cat /proc/lockdep' after applying crossrelease
  2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (14 preceding siblings ...)
  2016-12-09  5:12 ` [PATCH v4 15/15] lockdep: Crossrelease feature documentation Byungchul Park
@ 2016-12-09  5:21 ` Byungchul Park
  15 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2016-12-09  5:21 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin


all lock classes:
c1c8b858 FD:   38 BD:    1 +.+...: cgroup_mutex
 -> [c1c8b7b0] cgroup_idr_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1c8b770] cgroup_file_kn_lock
 -> [c1c8b7f0] css_set_lock
 -> [c1c8bbb8] freezer_mutex

c1cd86c8 FD:    1 BD:  104 -.-...: input_pool.lock

c1cd85c8 FD:    1 BD:  103 ..-...: nonblocking_pool.lock

c1c80634 FD:    2 BD:   16 ++++..: resource_lock
 -> [c1c805f0] bootmem_resource_lock

c1c7f3f0 FD:    1 BD:   21 +.+...: pgd_lock

c1c7e1d8 FD:   12 BD:    1 +.+...: acpi_ioapic_lock
 -> [c1c7e6d0] ioapic_lock
 -> [c1c7e698] ioapic_mutex

c1c7e6d0 FD:    2 BD:   71 -.-...: ioapic_lock
 -> [c1c7bdb0] i8259A_lock

c1cf63d0 FD:    1 BD:    1 ......: map_entries_lock

c1c7d7b0 FD:    1 BD:    1 ......: x86_mce_decoder_chain.lock

c1c89db8 FD:   44 BD:    2 +.+...: clocksource_mutex
 -> [c1c89d10] watchdog_lock

c1c89d10 FD:    9 BD:    4 +.-...: watchdog_lock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1c7ff18 FD:  172 BD:    2 +.+.+.: cpu_add_remove_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25bc6b4] &swhash->hlist_mutex
 -> [c1c7fec0] cpu_hotplug.lock
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c1e08e10] (complete)&work.complete
 -> [c1e08e08] &x->wait#6
 -> [c1e0a8bc] &rq->lock
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1cde990] req_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c1e06400] subsys mutex#18
 -> [c1e0640c] subsys mutex#19
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1e0a16c] &wq->mutex
 -> [c1c82a10] kthread_create_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1c827b8] wq_pool_mutex
 -> [c259f24c] &(&base->lock)->rlock
 -> [c25c4058] &(&n->list_lock)->rlock
 -> [c2607b50] &(&k->k_lock)->rlock

c1c90058 FD:   42 BD:    8 +.+...: jump_label_mutex

c25bc904 FD:    1 BD:  164 ..-...: &(&zone->lock)->rlock

c1c934b8 FD:    2 BD:   77 +.+.+.: pcpu_alloc_mutex
 -> [c1c934f0] pcpu_lock

c1c934f0 FD:    1 BD:   80 ..-...: pcpu_lock

c25c4058 FD:    1 BD:    8 -.-...: &(&n->list_lock)->rlock

c1c7fec0 FD:   41 BD:    3 ++++++: cpu_hotplug.lock
 -> [c1c7fea8] cpu_hotplug.lock#2

c1c7fea8 FD:   40 BD:   26 +.+.+.: cpu_hotplug.lock#2
 -> [c1c7fe54] cpu_hotplug.wq.lock
 -> [c1e098e4] &p->pi_lock
 -> [c1c830d8] smpboot_threads_lock
 -> [c25bc6b4] &swhash->hlist_mutex
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c82a10] kthread_create_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1e0a8bc] &rq->lock
 -> [c1e0a184] &pool->attach_mutex
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c259f1d0] rcu_node_0
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1c8cf98] relay_channels_mutex
 -> [c1c7c378] smp_alt
 -> [c1c878d8] sparse_irq_lock
 -> [c1e09d9d] (complete)&st->done
 -> [c1e09d95] &x->wait#2

c1c93558 FD:   47 BD:   14 +.+.+.: slab_mutex
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)

c1e0aa3c FD:    1 BD:    2 +.+...: &dl_b->dl_runtime_lock

c1e0a8bc FD:    4 BD:  361 -.-.-.: &rq->lock
 -> [c1e0a9e8] &rt_b->rt_runtime_lock
 -> [c1e0aa54] &cp->lock

c1c6d034 FD:    5 BD:    1 ......: init_task.pi_lock
 -> [c1e0a8bc] &rq->lock

c259f13f FD:    1 BD:    1 ......: rcu_read_lock

c259f1d0 FD:    1 BD:   82 ..-...: rcu_node_0

c1c8d458 FD:   24 BD:    1 +.+.+.: trace_types_lock
 -> [c1c99ff0] pin_fs_lock
 -> [c1cc3234] &sb->s_type->i_mutex_key#6

c1c7f970 FD:    1 BD:    1 ......: panic_notifier_list.lock

c1c82a50 FD:    1 BD:    1 ......: die_chain.lock

c1c8d6d8 FD:   25 BD:    2 +.+.+.: trace_event_sem
 -> [c1c99ff0] pin_fs_lock
 -> [c1cc3234] &sb->s_type->i_mutex_key#6
 -> [c25c4058] &(&n->list_lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock

c1c8e238 FD:    1 BD:    1 ......: trigger_cmd_mutex

c1c7bdb0 FD:    1 BD:   72 -.-...: i8259A_lock

c259f0c4 FD:    4 BD:   70 -.-...: &irq_desc_lock_class
 -> [c1c7bdb0] i8259A_lock
 -> [c1c7e5b0] vector_lock
 -> [c1c7e6d0] ioapic_lock

c1c87bf8 FD:   10 BD:    3 +.+.+.: irq_domain_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1c7e5b0] vector_lock
 -> [c259f0c4] &irq_desc_lock_class
 -> [c1c7bdb0] i8259A_lock
 -> [c1c87b98] revmap_trees_mutex

c1c7c830 FD:    1 BD:   29 ......: rtc_lock

c1c898b0 FD:    2 BD:   64 -.-...: timekeeper_lock
 -> [c259ffc4] tk_core

c259ffc4 FD:    1 BD:   65 ----..: tk_core

c1c87830 FD:    1 BD:    1 ......: read_lock

c1c8fd98 FD:   55 BD:    1 +.+.+.: pmus_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25db814] &x->wait#3
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1c8f9ec] subsys mutex#23
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a8bc] &rq->lock

c25bc6b4 FD:    1 BD:   27 +.+...: &swhash->hlist_mutex

c1cd0eb0 FD:    1 BD:    4 ......: tty_ldiscs_lock

c1c87750 FD:    6 BD:   11 ......: (console_sem).lock
 -> [c1e098e4] &p->pi_lock

c1c8772c FD:   67 BD:   11 +.+.+.: console_lock
 -> [c1c80634] resource_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1cd1750] kbd_event_lock
 -> [c1cccd30] vga_lock
 -> [c1c87710] logbuf_lock
 -> [c25d74d4] &port_lock_key
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25d5768] subsys mutex#12
 -> [c1b956b4] drivers/tty/vt/vt.c:231
 -> [c1cd1250] vt_event_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1b95555] drivers/tty/vt/keyboard.c:252

c259f24c FD:    1 BD:  170 -.-.-.: &(&base->lock)->rlock

c1cd1750 FD:   10 BD:   23 -.-...: kbd_event_lock
 -> [c1cd1710] led_lock
 -> [c1e0a1a5] &pool->lock/1

c1cd1710 FD:    1 BD:   24 ..-...: led_lock

c1cccd30 FD:    1 BD:   12 ......: vga_lock

c1c87710 FD:    1 BD:   13 ......: logbuf_lock

c25d74d4 FD:    7 BD:   17 -.-...: &port_lock_key
 -> [c25d60c4] &tty->write_wait

c1c972d0 FD:    1 BD:   14 +.+...: vmap_area_lock

c1c97344 FD:    1 BD:    7 +.+...: init_mm.page_table_lock

c1c8a350 FD:    2 BD:    1 ......: clockevents_lock
 -> [c1c8a3d0] tick_broadcast_lock

c1c8a3d0 FD:    1 BD:    2 -.....: tick_broadcast_lock

c1c632e8 FD:    2 BD:   64 -.-...: jiffies_lock
 -> [c1c632c4] jiffies_lock#2

c1c632c4 FD:    1 BD:   65 ---...: jiffies_lock#2

c7eaf850 FD:    1 BD:   78 -.-...: hrtimer_bases.lock

c1c828f8 FD:    1 BD:    9 +.+...: text_mutex

c25d57d4 FD:    1 BD:    8 ......: semaphore->lock

c25d5ee4 FD:    1 BD:    5 ......: &(*(&acpi_gbl_reference_count_lock))->rlock

c1ccd33c FD:    3 BD:    2 +.+.+.: acpi_ioremap_lock
 -> [c1c972d0] vmap_area_lock
 -> [c1c97344] init_mm.page_table_lock

c1ccb9d0 FD:    1 BD:   13 ......: percpu_counters_lock

c25d2d9c FD:    1 BD:   81 ......: &(&idp->lock)->rlock

c1ccb5f0 FD:    6 BD:   80 ......: simple_ida_lock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1cc9300] (blk_queue_ida).idr.lock
 -> [c1ce3420] (host_index_ida).idr.lock
 -> [c1cf0de0] (input_ida).idr.lock
 -> [c1cf1980] (rtc_ida).idr.lock

c1c9de78 FD:   31 BD:   69 +.+.+.: kernfs_mutex
 -> [c1ccb5f0] simple_ida_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1c63550] inode_hash_lock
 -> [c1c9e04c] &sb->s_type->i_lock_key#17
 -> [c25cf500] &isec->lock
 -> [c25bc904] &(&zone->lock)->rlock

c1c99db4 FD:    1 BD:    1 ++++..: file_systems_lock

c1c99f80 FD:    1 BD:   53 ......: (mnt_id_ida).idr.lock

c1c99ef0 FD:    2 BD:   52 +.+...: mnt_id_lock
 -> [c1c99f80] (mnt_id_ida).idr.lock

c1c99970 FD:    3 BD:   34 +.+...: sb_lock
 -> [c1c99940] (unnamed_dev_ida).idr.lock
 -> [c1c998f0] unnamed_dev_lock

c1c6caa5 FD:   29 BD:    1 +.+...: &type->s_umount_key/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c6cacc] &sb->s_type->i_lock_key
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c99940 FD:    1 BD:   36 ......: (unnamed_dev_ida).idr.lock

c1c998f0 FD:    2 BD:   35 +.+...: unnamed_dev_lock
 -> [c1c99940] (unnamed_dev_ida).idr.lock

c1c90a18 FD:    1 BD:   27 +.+...: shrinker_rwsem

c1c6cacc FD:   17 BD:    8 +.+...: &sb->s_type->i_lock_key
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c25c41a4 FD:    1 BD:  120 +.+...: &(&s->s_inode_list_lock)->rlock

c25cf500 FD:    2 BD:  119 +.+...: &isec->lock
 -> [c25cf4f0] &(&sbsec->isec_lock)->rlock

c25cf4f0 FD:    1 BD:  120 +.+...: &(&sbsec->isec_lock)->rlock

c25c4628 FD:   16 BD:  172 +.+...: &(&dentry->d_lockref.lock)->rlock
 -> [c25c4600] &wq
 -> [c25c45f8] &wq#2
 -> [c25bcc48] &(&lru->node[i].lock)->rlock
 -> [c25c4629] &(&dentry->d_lockref.lock)->rlock/1
 -> [c25c6f7c] &wq#3
 -> [c1c9dc50] sysctl_lock
 -> [c25c462a] &(&dentry->d_lockref.lock)->rlock/2

c25cf4f8 FD:    1 BD:   28 +.+...: &sbsec->lock

c1cc7894 FD:    1 BD:    1 .+.+..: policy_rwlock

c1cc46d0 FD:    1 BD:   12 ......: notif_lock

c25ca4c0 FD:    1 BD:   12 ......: &(&avc_cache.slots_lock[i])->rlock

c1c635a8 FD:   19 BD:   53 +.+...: mount_lock
 -> [c1c63584] mount_lock#2

c1c63584 FD:   18 BD:   54 +.+...: mount_lock#2
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25c575c] &new_ns->poll

c1c9dbe0 FD:    1 BD:   26 ......: (proc_inum_ida).idr.lock

c1c9db90 FD:    2 BD:   25 ......: proc_inum_lock
 -> [c1c9dbe0] (proc_inum_ida).idr.lock

c1c9c0d4 FD:   18 BD:   73 +.+...: init_fs.lock
 -> [c1c9c0ec] init_fs.seq
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c9c0ec FD:    1 BD:   74 +.+...: init_fs.seq

c1c9c2e5 FD:   27 BD:    1 +.+...: &type->s_umount_key#2/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c9c30c] &sb->s_type->i_lock_key#2
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c9c30c FD:   17 BD:  103 +.+...: &sb->s_type->i_lock_key#2
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c9dc14 FD:    1 BD:   13 ++++..: proc_subdir_lock

c1cff098 FD:  215 BD:    1 +.+.+.: net_mutex
 -> [c1c9dbe0] (proc_inum_ida).idr.lock
 -> [c1c9db90] proc_inum_lock
 -> [c1cff938] rtnl_mutex
 -> [c1c9dc50] sysctl_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1cfdbac] &sb->s_type->i_lock_key#7
 -> [c25e1900] slock-AF_NETLINK
 -> [c25e1a60] sk_lock-AF_NETLINK
 -> [c1d01e14] nl_table_lock
 -> [c1d01e50] nl_table_wait.lock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c25e29b0] &(&net->rules_mod_lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c7ff18] cpu_add_remove_lock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1ccb9d0] percpu_counters_lock
 -> [c1cd8790] random_write_wait.lock
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c1d038d4] raw_v4_hashinfo.lock
 -> [c1cfdda0] (net_generic_ids).idr.lock
 -> [c1d0bbf0] cache_list_lock
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25e3ad0] &xt[i].mutex
 -> [c1d020d8] nf_hook_mutex
 -> [c1d07ab4] raw_v6_hashinfo.lock
 -> [c25efc40] &(&ip6addrlbl_table.lock)->rlock
 -> [c1c90058] jump_label_mutex

c1c9dc50 FD:    1 BD:  174 +.+...: sysctl_lock

c1c9c185 FD:   26 BD:    1 +.+...: &type->s_umount_key#3/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1c9c1ac] &sb->s_type->i_lock_key#3
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c9c1ac FD:   17 BD:    2 +.+...: &sb->s_type->i_lock_key#3
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c8b7b0 FD:    1 BD:    2 +.....: cgroup_idr_lock

c1c8b770 FD:    1 BD:    2 ......: cgroup_file_kn_lock

c1c8b7f0 FD:    1 BD:    2 ......: css_set_lock

c1c8c678 FD:    2 BD:    1 +.+...: cpuset_mutex
 -> [c1c8c610] callback_lock

c1c8c610 FD:    1 BD:    2 ......: callback_lock

c1c8bbb8 FD:    1 BD:    2 +.+...: freezer_mutex

c1c63490 FD:    1 BD:    1 +.+...: kmap_lock

c1c971f0 FD:    2 BD:    1 +.+...: purge_lock
 -> [c1c972d0] vmap_area_lock

c1c7f570 FD:    2 BD:    1 +.+...: cpa_lock
 -> [c1c7f3f0] pgd_lock

c1c90630 FD:    1 BD:    1 +.+...: managed_page_count_lock

c25b38d4 FD:  130 BD:    4 .+.+.+: &cgroup_threadgroup_rwsem
 -> [c1c972d0] vmap_area_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c1e098e4] &p->pi_lock
 -> [c1c99cd0] init_files.file_lock
 -> [c1c9c0d4] init_fs.lock
 -> [c1c6d010] init_task.alloc_lock
 -> [c1c99f80] (mnt_id_ida).idr.lock
 -> [c1c99ef0] mnt_id_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1c99970] sb_lock
 -> [c1c9db25] &type->s_umount_key#4/1
 -> [c1c635a8] mount_lock
 -> [c1c63250] pidmap_lock
 -> [c1c631d4] tasklist_lock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25c5748] &(&newf->file_lock)->rlock
 -> [c1c97344] init_mm.page_table_lock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c1c7f3f0] pgd_lock
 -> [c1dfe42c] &mm->context.lock
 -> [c1e09914] &mm->mmap_sem
 -> [c25c57bc] &(&fs->lock)->rlock

c1e098e4 FD:    5 BD:  337 -.-.-.: &p->pi_lock
 -> [c1e0a8bc] &rq->lock

c1c99cd0 FD:    1 BD:    5 +.+...: init_files.file_lock

c1c6d010 FD:    1 BD:    5 +.+...: init_task.alloc_lock

c1c9db25 FD:   55 BD:    5 +.+...: &type->s_umount_key#4/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c9db4c] &sb->s_type->i_lock_key#4
 -> [c25cf500] &isec->lock
 -> [c1c9db54] &sb->s_type->i_mutex_key
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c9db4c FD:   17 BD:   10 +.+...: &sb->s_type->i_lock_key#4
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c9db54 FD:   49 BD:    7 ++++.+: &sb->s_type->i_mutex_key
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c9db4c] &sb->s_type->i_lock_key#4
 -> [c25cf500] &isec->lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c1c9dc50] sysctl_lock

c1c63250 FD:    1 BD:   79 ......: pidmap_lock

c1c631d4 FD:   23 BD:    5 .+.+..: tasklist_lock
 -> [c1c6edd4] init_sighand.siglock
 -> [c1e098a4] &(&sighand->siglock)->rlock

c1c6edd4 FD:    1 BD:    6 ......: init_sighand.siglock

c1da38a0 FD:    1 BD:    1 +.+...: (complete)kthreadd_done

c1da38b4 FD:    6 BD:    1 ......: (kthreadd_done).wait.lock
 -> [c1e098e4] &p->pi_lock

c1e098f4 FD:   40 BD:   72 +.+...: &(&p->alloc_lock)->rlock
 -> [c1e098ec] &p->mems_allowed_seq
 -> [c1c9c0d4] init_fs.lock
 -> [c25c57bc] &(&fs->lock)->rlock
 -> [c1e0a358] &x->wait
 -> [c1e098a4] &(&sighand->siglock)->rlock
 -> [c1e098ac] &x->wait#16

c1e098ec FD:    1 BD:   73 ......: &p->mems_allowed_seq

c1c7e698 FD:   11 BD:    2 +.+.+.: ioapic_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1c87bf8] irq_domain_mutex

c1e098a4 FD:   21 BD:   76 -.....: &(&sighand->siglock)->rlock
 -> [c1e098d4] &sig->wait_chldexit
 -> [c1cd86c8] input_pool.lock
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c1e098c4] &(&(&sig->stats_lock)->lock)->rlock
 -> [c25d60a4] &(&tty->ctrl_lock)->rlock
 -> [c1e098e4] &p->pi_lock
 -> [c1e098dc] &prev->lock
 -> [c1e0989c] &sighand->signalfd_wqh
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c7eaf850] hrtimer_bases.lock
 -> [c7eeb850] hrtimer_bases.lock#4
 -> [c7ec3850] hrtimer_bases.lock#2
 -> [c7ed7850] hrtimer_bases.lock#3

c1c7e5b0 FD:    1 BD:   71 ......: vector_lock

c1c87b98 FD:    1 BD:    4 +.+.+.: revmap_trees_mutex

c1c7b770 FD:    1 BD:    1 ......: &nmi_desc[0].lock

c1c830d8 FD:   10 BD:   28 +.+.+.: smpboot_threads_lock
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a8bc] &rq->lock
 -> [c1e0a358] &x->wait
 -> [c1e0a348] (complete)&self.parked

c1c82a10 FD:    1 BD:   35 +.+...: kthread_create_lock

c1e0a360 FD:    7 BD:   34 +.+...: (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1e0a8bc] &rq->lock

c1e0a358 FD:    6 BD:   99 ......: &x->wait
 -> [c1e098e4] &p->pi_lock

c1e0a348 FD:    7 BD:   29 +.+...: (complete)&self.parked
 -> [c1e0a358] &x->wait
 -> [c1e0a8bc] &rq->lock

c1c827b8 FD:   26 BD:   28 +.+.+.: wq_pool_mutex
 -> [c1e0a16c] &wq->mutex
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1e0a184] &pool->attach_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c25bc904] &(&zone->lock)->rlock

c1e0a184 FD:   12 BD:   31 +.+...: &pool->attach_mutex
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c25b3b90] &(&stopper->lock)->rlock
 -> [c25b3ba0] (complete)&done->completion
 -> [c1e0a8bc] &rq->lock
 -> [c25b3b98] &x->wait#7

c1e0a1a4 FD:    8 BD:  141 -.-...: &(&pool->lock)->rlock
 -> [c1e098e4] &p->pi_lock
 -> [c1c82750] wq_mayday_lock
 -> [c259f24c] &(&base->lock)->rlock

c1e0a16c FD:   11 BD:   29 +.+...: &wq->mutex
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c1e0a1a5] &pool->lock/1
 -> [c1e0a1cc] &x->wait#13

c1e0a1a5 FD:    8 BD:   81 -.-...: &pool->lock/1
 -> [c1e098e4] &p->pi_lock
 -> [c1c82750] wq_mayday_lock
 -> [c259f24c] &(&base->lock)->rlock

c259f190 FD:    6 BD:   62 ..-...: &rsp->gp_wq
 -> [c1e098e4] &p->pi_lock

c25b3b90 FD:    6 BD:   37 ..-...: &(&stopper->lock)->rlock
 -> [c1e098e4] &p->pi_lock

c1cd8648 FD:    1 BD:    1 ......: blocking_pool.lock

c25c5748 FD:   17 BD:    7 +.+...: &(&newf->file_lock)->rlock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c82750 FD:    6 BD:  151 ..-...: wq_mayday_lock
 -> [c1e098e4] &p->pi_lock

c1c7fe54 FD:    1 BD:   27 ......: cpu_hotplug.wq.lock

c1c8cf98 FD:    1 BD:   27 +.+...: relay_channels_mutex

c1c7c378 FD:    1 BD:   27 +.+...: smp_alt

c1c878d8 FD:    6 BD:   27 +.+...: sparse_irq_lock
 -> [c1c7c830] rtc_lock
 -> [c1e0a8bc] &rq->lock

c1e09d9d FD:   30 BD:   27 +.+...: (complete)&st->done
 -> [c1e09d95] &x->wait#2
 -> [c1e0a8bc] &rq->lock
 -> [c1c827b8] wq_pool_mutex
 -> [c1e0a184] &pool->attach_mutex
 -> [c1c830d8] smpboot_threads_lock

c7ec3850 FD:    1 BD:   77 -.-...: hrtimer_bases.lock#2

c1e09d95 FD:    6 BD:   28 ......: &x->wait#2
 -> [c1e098e4] &p->pi_lock

c1e0a9e8 FD:    2 BD:  362 ......: &rt_b->rt_runtime_lock
 -> [c1e0a9d8] &rt_rq->rt_runtime_lock

c1e0a9d8 FD:    1 BD:  363 ......: &rt_rq->rt_runtime_lock

c7ed7850 FD:    1 BD:   79 -.-...: hrtimer_bases.lock#3

c7eeb850 FD:    1 BD:   77 -.-...: hrtimer_bases.lock#4

c1c85298 FD:   10 BD:    1 +.+.+.: sched_domains_mutex
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1e0aa3c] &dl_b->dl_runtime_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1c934f0] pcpu_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c9dc50] sysctl_lock

c1e0aa54 FD:    1 BD:  362 ......: &cp->lock

c1c92e65 FD:   31 BD:    1 +.+.+.: &type->s_umount_key#5/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1ccb9d0] percpu_counters_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c92e8c] &sb->s_type->i_lock_key#5
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock

c259f254 FD:    6 BD:   17 +.-.-.: ((&timer))
 -> [c1e098e4] &p->pi_lock

c259f127 FD:    1 BD:    1 ......: rcu_callback

c1c92e8c FD:   17 BD:   12 +.+...: &sb->s_type->i_lock_key#5
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1cde880 FD:   70 BD:    1 +.+...: (complete)setup_done
 -> [c1cde894] (setup_done).wait.lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25c57bc] &(&fs->lock)->rlock
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c1c6cacc] &sb->s_type->i_lock_key
 -> [c1c6cad4] &sb->s_type->i_mutex_key#2
 -> [c1c635a8] mount_lock
 -> [c1cde925] &type->s_umount_key#6/1
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c99970] sb_lock
 -> [c1c99ef0] mnt_id_lock
 -> [c1c99f80] (mnt_id_ida).idr.lock
 -> [c25ca4c0] &(&avc_cache.slots_lock[i])->rlock
 -> [c1cc46d0] notif_lock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c1c99eb8] namespace_sem
 -> [c1c9db90] proc_inum_lock
 -> [c1c9dbe0] (proc_inum_ida).idr.lock

c1cde894 FD:    6 BD:    2 ......: (setup_done).wait.lock
 -> [c1e098e4] &p->pi_lock

c1c99eb8 FD:   27 BD:   47 +++++.: namespace_sem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c99f80] (mnt_id_ida).idr.lock
 -> [c1c99ef0] mnt_id_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c635a8] mount_lock
 -> [c1c63528] rename_lock

c25c57b4 FD:    1 BD:   74 +.+...: &fs->seq

c1cde925 FD:   31 BD:    2 +.+.+.: &type->s_umount_key#6/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1ccb9d0] percpu_counters_lock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c25bca94 FD:    1 BD:   46 +.+...: &(&sbinfo->stat_lock)->rlock

c1cde94c FD:   17 BD:   46 +.+...: &sb->s_type->i_lock_key#6
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c6cad4 FD:   29 BD:    5 ++++++: &sb->s_type->i_mutex_key#2
 -> [c1c99eb8] namespace_sem
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c1c6cacc] &sb->s_type->i_lock_key

c1c63528 FD:   18 BD:   53 +.+...: rename_lock
 -> [c1c63504] rename_lock#2

c1c63504 FD:   17 BD:   54 +.+...: rename_lock#2
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25c462a] &(&dentry->d_lockref.lock)->rlock/2
 -> [c25c462b] &(&dentry->d_lockref.lock)->rlock/3

c25c575c FD:    1 BD:   55 ......: &new_ns->poll

c25c4620 FD:    2 BD:  175 +.+...: &dentry->d_seq
 -> [c25c4621] &dentry->d_seq/1

c25c57bc FD:   18 BD:   73 +.+...: &(&fs->lock)->rlock
 -> [c25c57b4] &fs->seq
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1cde990 FD:    1 BD:   34 +.+...: req_lock

c25db814 FD:    1 BD:   43 ......: &x->wait#3

c25d2da8 FD:    1 BD:   54 +.+...: &(&k->list_lock)->rlock

c25db698 FD:    3 BD:   47 ......: &(&dev->power.lock)->rlock
 -> [c25db699] &(&dev->power.lock)->rlock/1
 -> [c25db754] &dev->power.wait_queue

c1cdee58 FD:    1 BD:   45 +.+...: dpm_list_mtx

c1ccb6d8 FD:   12 BD:   64 +.+.+.: uevent_sock_mutex
 -> [c25e1d68] &(&net->nsid_lock)->rlock
 -> [c25e16dc] &(&list->lock)->rlock
 -> [c1d01e50] nl_table_wait.lock
 -> [c1e0a8bc] &rq->lock
 -> [c25bc904] &(&zone->lock)->rlock

c1c820b0 FD:    1 BD:   59 ......: running_helpers_waitq.lock

c1c87c58 FD:    4 BD:   12 +.+.+.: register_lock
 -> [c1c9dbe0] (proc_inum_ida).idr.lock
 -> [c1c9db90] proc_inum_lock
 -> [c25bc904] &(&zone->lock)->rlock

c1c82218 FD:    2 BD:    1 +.+...: umhelper_sem
 -> [c1c82070] usermodehelper_disabled_waitq.lock

c1c82070 FD:    1 BD:    2 ......: usermodehelper_disabled_waitq.lock

c1cff938 FD:  107 BD:    7 +.+.+.: rtnl_mutex
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25e2924] subsys mutex#11
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1cdedf8] dev_hotplug_mutex
 -> [c1cff694] dev_base_lock
 -> [c1cd86c8] input_pool.lock
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c1d01e50] nl_table_wait.lock
 -> [c2607354] &(&reg_requests_lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c260734c] &(&reg_pending_beacons_lock)->rlock
 -> [c25e24e4] &tbl->lock
 -> [c1c9dc50] sysctl_lock
 -> [c2607344] &(&reg_indoor_lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c1d020d8] nf_hook_mutex
 -> [c1c90058] jump_label_mutex
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c1cff9b0] lweventlist_lock
 -> [c1c9dbe0] (proc_inum_ida).idr.lock
 -> [c1c9db90] proc_inum_lock
 -> [c25efb40] &ndev->lock
 -> [c25efcf2] &(&idev->mc_lock)->rlock
 -> [c25efd02] &(&mc->mca_lock)->rlock
 -> [c1d047d8] (inetaddr_chain).rwsem
 -> [c25e2148] _xmit_LOOPBACK
 -> [c25ee798] &(&in_dev->mc_tomb_lock)->rlock
 -> [c1d04a90] fib_info_lock
 -> [c25efb58] &(&ifa->lock)->rlock

c1cf5718 FD:    1 BD:    1 +.+...: cpufreq_fast_switch_lock

c1cddf58 FD:    1 BD:    1 +.+...: syscore_ops_lock

c1e0a18c FD:   26 BD:    1 +.+.+.: &pool->manager_arb
 -> [c259f24c] &(&base->lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a8bc] &rq->lock
 -> [c1e0a358] &x->wait
 -> [c1e0a184] &pool->attach_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1e0a194] ((&pool->mayday_timer))
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)

c1c90578 FD:    2 BD:    1 +.+...: zonelists_mutex
 -> [c25bc904] &(&zone->lock)->rlock

c1c99a94 FD:    1 BD:    1 ++++..: binfmt_lock

c1e0a194 FD:   10 BD:    5 +.-...: ((&pool->mayday_timer))
 -> [c1e0a1a5] &pool->lock/1
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1e0a144 FD:    1 BD:    1 .+.+.+: "events_unbound"

c1e0a0f8 FD:  133 BD:    3 +.+.+.: (&sub_info->work)
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25b38d4] &cgroup_threadgroup_rwsem
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a8bc] &rq->lock
 -> [c25c4058] &(&n->list_lock)->rlock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c1e098a4] &(&sighand->siglock)->rlock
 -> [c1e098d4] &sig->wait_chldexit
 -> [c1e098dc] &prev->lock
 -> [c1e0a0e8] &x->wait#12

c1cfdb85 FD:   27 BD:    1 +.+.+.: &type->s_umount_key#7/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1cfdbac] &sb->s_type->i_lock_key#7
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1cfdbac FD:   17 BD:    3 +.+...: &sb->s_type->i_lock_key#7
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c82250 FD:    1 BD:    1 +.+...: umh_sysctl_lock

c1e098bc FD:  141 BD:    2 +.+.+.: &sig->cred_guard_mutex
 -> [c25c57bc] &(&fs->lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c6cad4] &sb->s_type->i_mutex_key#2
 -> [c1e0a8bc] &rq->lock
 -> [c1c9c0d4] init_fs.lock
 -> [c1c9e3fc] &type->i_mutex_dir_key
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c1e098e4] &p->pi_lock
 -> [c25b3b90] &(&stopper->lock)->rlock
 -> [c25b3ba0] (complete)&done->completion
 -> [c25b3b98] &x->wait#7
 -> [c1c7f3f0] pgd_lock
 -> [c1e09914] &mm->mmap_sem
 -> [c25c874c] &ei->xattr_sem
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c8744] &ei->i_data_sem
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c1e0990c] &(&mm->page_table_lock)->rlock
 -> [c25c3ce8] &anon_vma->rwsem
 -> [c1e0980c] &(ptlock_ptr(page))->rlock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c25bc924] zone->wait_table + i
 -> [c1e098a4] &(&sighand->siglock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25c5748] &(&newf->file_lock)->rlock
 -> [c25c873c] &ei->i_mmap_sem
 -> [c1dfe42c] &mm->context.lock
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c25c567c] &mapping->i_mmap_rwsem
 -> [c25c3cdc] key#3
 -> [c1c9e3f4] &sb->s_type->i_mutex_key#8
 -> [c25b4244] &(kretprobe_table_locks[i].lock)

c1cfdcb8 FD:    1 BD:    1 +.+...: proto_list_mutex

c25c4600 FD:    6 BD:  173 ......: &wq
 -> [c1e098e4] &p->pi_lock

c1cd8790 FD:    1 BD:   16 ..-...: random_write_wait.lock

c25bc6c4 FD:    1 BD:    1 +.+...: &child->perf_event_mutex

c1d01e14 FD:    1 BD:    4 .+.+..: nl_table_lock

c1e098d4 FD:    6 BD:   77 ......: &sig->wait_chldexit
 -> [c1e098e4] &p->pi_lock

c1d01e50 FD:    1 BD:   69 ......: nl_table_wait.lock

c1cfdbf0 FD:    1 BD:    1 +.+...: net_family_lock

c1e098c4 FD:    3 BD:   77 ......: &(&(&sig->stats_lock)->lock)->rlock
 -> [c1e098cc] &(&sig->stats_lock)->seqcount

c1e098cc FD:    2 BD:   78 ......: &(&sig->stats_lock)->seqcount
 -> [c1c63250] pidmap_lock

c25e1900 FD:    1 BD:    3 +.....: slock-AF_NETLINK

c25e1a60 FD:   12 BD:    2 +.+...: sk_lock-AF_NETLINK
 -> [c25e1900] slock-AF_NETLINK

c1c80150 FD:    1 BD:    1 +.+...: low_water_lock

c25d2dfd FD:   10 BD:    5 +.....: &(&tbl->locks[i])->rlock
 -> [c25d2dfe] &(&tbl->locks[i])->rlock/1
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1c99ff0 FD:    1 BD:   11 +.+...: pin_fs_lock

c1cc3085 FD:   27 BD:    1 +.+.+.: &type->s_umount_key#8/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1cc30ac] &sb->s_type->i_lock_key#8
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1cc30ac FD:   17 BD:   10 +.+...: &sb->s_type->i_lock_key#8
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1cc30b4 FD:   22 BD:    8 +.+.+.: &sb->s_type->i_mutex_key#3
 -> [c1cc30ac] &sb->s_type->i_lock_key#8
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25bc904] &(&zone->lock)->rlock

c1cddc78 FD:   34 BD:   43 +.+.+.: gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccb630] kobj_ns_type_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1e098e4] &p->pi_lock

c1c9dfd0 FD:    1 BD:   61 +.+...: sysfs_symlink_target_lock

c25d70dc FD:    8 BD:    1 +.+...: subsys mutex
 -> [c2607b50] &(&k->k_lock)->rlock

c2607b50 FD:    7 BD:   78 +.+...: &(&k->k_lock)->rlock
 -> [c1d19ed0] klist_remove_lock

c1cdf9d8 FD:    1 BD:    1 +.+...: regmap_debugfs_early_lock

c1cf1c38 FD:    1 BD:    1 +.+...: __i2c_board_lock

c1cf1eb8 FD:    8 BD:    1 +.+...: core_lock
 -> [c2607b50] &(&k->k_lock)->rlock

c1cf5c58 FD:   14 BD:    1 +.+...: cpuidle_lock
 -> [c1e0a8bc] &rq->lock
 -> [c259f118] (complete)&rs_array[i].completion
 -> [c259f110] &x->wait#4

c259f118 FD:   11 BD:    4 +.+...: (complete)&rs_array[i].completion
 -> [c1e0a8bc] &rq->lock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c7ed7850] hrtimer_bases.lock#3
 -> [c1c898b0] timekeeper_lock
 -> [c1c632e8] jiffies_lock

c259f110 FD:    6 BD:    4 ..-...: &x->wait#4
 -> [c1e098e4] &p->pi_lock

c1ccd680 FD:    1 BD:   45 ++++..: bus_type_sem

c25df800 FD:    8 BD:    1 +.+...: subsys mutex#2
 -> [c2607b50] &(&k->k_lock)->rlock

c1c805f0 FD:    1 BD:   17 +.+...: bootmem_resource_lock

c1cfd730 FD:    1 BD:   15 ......: pci_config_lock

c1e0baa0 FD:    1 BD:    1 ......: &dev->mutex

c1cde52c FD:   52 BD:    2 +.+.+.: subsys mutex#3
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccff18] performance_mutex
 -> [c1cf5814] cpufreq_driver_lock
 -> [c25df56c] &policy->rwsem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c1cf5558] s_active#7
 -> [c1cf5654] s_active#8
 -> [c1cf561c] s_active#9
 -> [c1cf5638] s_active#10
 -> [c1cf5670] s_active#11
 -> [c1cf568c] s_active#12
 -> [c1cf5574] s_active#13
 -> [c1cf55ac] s_active#14
 -> [c1cf5590] s_active#15
 -> [c1cf55c8] s_active#16
 -> [c1cf553c] s_active#17
 -> [c25df544] &x->wait#14
 -> [c25df554] (complete)&policy->kobj_unregister

c1c81bf0 FD:    1 BD:    1 ......: uidhash_lock

c1e0a324 FD:    5 BD:    1 +.+...: s_active
 -> [c1e0a8bc] &rq->lock

c1cd1050 FD:    1 BD:    1 +.+...: sysrq_key_table_lock

c1c888b0 FD:    1 BD:    1 ....-.: freezer_lock

c1c90350 FD:    1 BD:    1 ......: oom_reaper_wait.lock

c1c8234c FD:    1 BD:    1 +.+...: subsys mutex#4

c25bc90c FD:    1 BD:    1 ......: &pgdat->kcompactd_wait

c1cc91d8 FD:   71 BD:    7 +.+.+.: bio_slab_lock

c1ccaf18 FD:    1 BD:   11 +.+.+.: block_class_lock

c1c99a58 FD:    1 BD:   23 +.+...: chrdevs_lock

c25d5eec FD:    1 BD:    1 ......: &(*(&acpi_gbl_hardware_lock))->rlock

c25d5ef4 FD:    1 BD:    2 ......: &(*(&acpi_gbl_gpe_lock))->rlock

c1c87358 FD:    1 BD:    1 +.+...: pm_mutex

c1ccd868 FD:   81 BD:    1 +.+.+.: acpi_scan_lock
 -> [c25d57d4] semaphore->lock
 -> [c25db814] &x->wait#3
 -> [c1ccd814] acpi_device_lock
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccd618] subsys mutex#5
 -> [c1e0a8bc] &rq->lock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25d5ee4] &(*(&acpi_gbl_reference_count_lock))->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1cfd730] pci_config_lock
 -> [c25d5b88] &(*(&acpi_gbl_global_lock_pending_lock))->rlock
 -> [c1ccd33c] acpi_ioremap_lock
 -> [c1ccdbe4] osc_lock
 -> [c1ccc1f8] pci_bus_sem
 -> [c1cfd658] pci_mmcfg_lock
 -> [c1c80634] resource_lock
 -> [c25d5adc] &device->physical_node_lock
 -> [c1cddc78] gdp_mutex
 -> [c25d2e48] subsys mutex#6
 -> [c1ccbdd0] pci_lock
 -> [c1ccc6f8] pci_slot_mutex
 -> [c1ccbf10] resource_alignment_lock
 -> [c1ccd5b8] acpi_pm_notifier_lock
 -> [c1ccc0ac] subsys mutex#7
 -> [c1ccbe38] pci_rescan_remove_lock
 -> [c1cddfec] subsys mutex#8
 -> [c1ccdcd8] acpi_link_lock
 -> [c1cde6cc] subsys mutex#9
 -> [c1cdefb0] events_lock
 -> [c25d5ef4] &(*(&acpi_gbl_gpe_lock))->rlock

c1ccd814 FD:    1 BD:    2 +.+.+.: acpi_device_lock

c1ccd618 FD:    1 BD:    2 +.+...: subsys mutex#5

c25d5b88 FD:    1 BD:    2 ......: &(*(&acpi_gbl_global_lock_pending_lock))->rlock

c1ccdbe4 FD:    3 BD:    2 +.+.+.: osc_lock
 -> [c25d57d4] semaphore->lock
 -> [c25d5ee4] &(*(&acpi_gbl_reference_count_lock))->rlock

c1ccc1f8 FD:    1 BD:    2 ++++..: pci_bus_sem

c1cfd658 FD:    1 BD:    2 +.+...: pci_mmcfg_lock

c25d5adc FD:   33 BD:    8 +.+.+.: &device->physical_node_lock
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex

c25d2e48 FD:    8 BD:    2 +.+...: subsys mutex#6
 -> [c2607b50] &(&k->k_lock)->rlock

c1ccbdd0 FD:    2 BD:   13 ......: pci_lock
 -> [c1cfd730] pci_config_lock

c1ccc6f8 FD:    1 BD:    2 +.+...: pci_slot_mutex

c1ccbf10 FD:    1 BD:    2 +.+...: resource_alignment_lock

c25db699 FD:    1 BD:   48 ......: &(&dev->power.lock)->rlock/1

c1ccd5b8 FD:    4 BD:    2 +.+.+.: acpi_pm_notifier_lock
 -> [c1cdefb0] events_lock
 -> [c25d57d4] semaphore->lock
 -> [c25d5ee4] &(*(&acpi_gbl_reference_count_lock))->rlock

c1cdefb0 FD:    1 BD:    3 ......: events_lock

c1ccc0ac FD:    1 BD:    2 +.+...: subsys mutex#7

c1ccbe38 FD:   11 BD:    2 +.+...: pci_rescan_remove_lock

c1cddfec FD:    1 BD:    2 +.+...: subsys mutex#8

c1ccdcd8 FD:    4 BD:    2 +.+.+.: acpi_link_lock
 -> [c25d57d4] semaphore->lock
 -> [c25d5ee4] &(*(&acpi_gbl_reference_count_lock))->rlock
 -> [c1cfd730] pci_config_lock

c1cde6cc FD:    1 BD:    2 +.+...: subsys mutex#9

c1cd9458 FD:  145 BD:    1 +.+.+.: misc_mtx
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1cde990] req_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c1e0a8bc] &rq->lock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25dae24] subsys mutex#10
 -> [c25bc904] &(&zone->lock)->rlock

c25db718 FD:  114 BD:   33 +.+...: (complete)&req.done
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1cde990] req_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c1cde954] &sb->s_type->i_mutex_key#5
 -> [c25b4244] &(kretprobe_table_locks[i].lock)

c25db710 FD:    6 BD:   33 ......: &x->wait#5
 -> [c1e098e4] &p->pi_lock

c1cde934 FD:  148 BD:    2 .+.+.+: sb_writers
 -> [c1cde955] &sb->s_type->i_mutex_key#4/1
 -> [c1cde954] &sb->s_type->i_mutex_key#5
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c5780] &p->lock
 -> [c1e0a8bc] &rq->lock
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25cf4f0] &(&sbsec->isec_lock)->rlock
 -> [c1c999f0] cdev_lock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c25bca84] &(&info->lock)->rlock
 -> [c25bcb38] &(&wb->list_lock)->rlock

c1cde955 FD:  121 BD:    3 +.+.+.: &sb->s_type->i_mutex_key#4/1
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c1cde954] &sb->s_type->i_mutex_key#5
 -> [c1e0a8bc] &rq->lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c1cde958] &sb->s_type->i_mutex_key#5/4
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25cf4f0] &(&sbsec->isec_lock)->rlock
 -> [c25efa68] &u->readlock

c1cde954 FD:  111 BD:   37 ++++++: &sb->s_type->i_mutex_key#5
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25bca7c] &(&xattrs->lock)->rlock
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25bca84] &(&info->lock)->rlock
 -> [c25c566c] &inode->i_size_seqcount
 -> [c25bc8c0] (PG_locked)page
 -> [c1e0a8bc] &rq->lock
 -> [c1cde958] &sb->s_type->i_mutex_key#5/4
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c99eb8] namespace_sem

c25dae24 FD:    8 BD:    2 +.+...: subsys mutex#10
 -> [c2607b50] &(&k->k_lock)->rlock

c1cddb70 FD:    3 BD:    1 ......: vga_lock#2
 -> [c1ccbdd0] pci_lock

c1cde578 FD:   55 BD:    3 +.+.+.: attribute_container_mutex
 -> [c25db814] &x->wait#3
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25db6fc] subsys mutex#29
 -> [c1e0a8bc] &rq->lock

c1cdf0d8 FD:   32 BD:    1 +.+.+.: drivers_dir_mutex
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1e0a8bc] &rq->lock

c1cfa3b8 FD:    3 BD:    1 +.+.+.: info_mutex
 -> [c1c9dbe0] (proc_inum_ida).idr.lock
 -> [c1c9db90] proc_inum_lock

c1ccb630 FD:    1 BD:   44 +.+...: kobj_ns_type_lock

c25e2924 FD:    8 BD:    8 +.+...: subsys mutex#11
 -> [c2607b50] &(&k->k_lock)->rlock

c1cdedf8 FD:    4 BD:   15 +.+...: dev_hotplug_mutex
 -> [c25db698] &(&dev->power.lock)->rlock

c1cff694 FD:    1 BD:    8 +.....: dev_base_lock

c25dd080 FD:    1 BD:    1 ......: &syncp->seq

c1d01b94 FD:    1 BD:    1 +.+...: qdisc_mod_lock

c1d01fd8 FD:    8 BD:    1 +.+.+.: cb_lock
 -> [c1d02038] genl_mutex

c1d02038 FD:    7 BD:    2 +.+.+.: genl_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1d01e14] nl_table_lock
 -> [c1d01e50] nl_table_wait.lock

c1d02138 FD:    1 BD:    1 +.+...: afinfo_mutex

c1d01dd0 FD:    1 BD:    1 ......: netlink_chain.lock

c2607354 FD:    1 BD:    8 +.+...: &(&reg_requests_lock)->rlock

c1e0a15c FD:    1 BD:    1 .+.+.+: "events"

c1d0c400 FD:  108 BD:    1 +.+.+.: reg_work
 -> [c1cff938] rtnl_mutex

c260734c FD:    1 BD:    8 +.....: &(&reg_pending_beacons_lock)->rlock

c1d141b8 FD:    1 BD:    1 +.+.+.: rate_ctrl_mutex

c1d19970 FD:    1 BD:    1 +.+...: netlbl_domhsh_lock

c1d19a90 FD:    1 BD:    1 +.+...: netlbl_unlhsh_lock

c1cf0d98 FD:  155 BD:    5 +.+.+.: input_mutex
 -> [c1cf0d30] input_devices_poll_wait.lock
 -> [c25deac8] &dev->mutex#2
 -> [c1cf0de0] (input_ida).idr.lock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1e0a8bc] &rq->lock
 -> [c25db814] &x->wait#3
 -> [c1c99a58] chrdevs_lock
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1cde990] req_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25deab0] subsys mutex#24
 -> [c25df718] subsys mutex#37
 -> [c1cf5f38] leds_list_lock
 -> [c1cf6018] triggers_list_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25deb11] &mousedev->mutex/1

c1cf0d30 FD:    1 BD:    6 ......: input_devices_poll_wait.lock

c1e08e10 FD:   25 BD:    3 +.+...: (complete)&work.complete
 -> [c1e08e20] (&(&work.work)->work)
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1e0a194] ((&pool->mayday_timer))
 -> [c1e0a184] &pool->attach_mutex
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a358] &x->wait
 -> [c1e0a8bc] &rq->lock
 -> [c1c82a10] kthread_create_lock
 -> [c1ccb5f0] simple_ida_lock
 -> [c25d2d9c] &(&idp->lock)->rlock

c1e08e08 FD:    6 BD:    5 ......: &x->wait#6
 -> [c1e098e4] &p->pi_lock

c1e08e20 FD:    7 BD:    4 +.+...: (&(&work.work)->work)
 -> [c1e08e08] &x->wait#6

c1c8c858 FD:   10 BD:    3 +.+...: stop_cpus_mutex
 -> [c1c8c88c] stop_cpus_lock
 -> [c25b3ba0] (complete)&done->completion
 -> [c1e0a8bc] &rq->lock
 -> [c25b3b98] &x->wait#7

c1c8c88c FD:    7 BD:    4 +.+...: stop_cpus_lock
 -> [c25b3b90] &(&stopper->lock)->rlock

c25b3ba0 FD:    8 BD:   35 +.+...: (complete)&done->completion
 -> [c25b3b98] &x->wait#7
 -> [c25b3b90] &(&stopper->lock)->rlock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a8bc] &rq->lock

c25b3b98 FD:    6 BD:   36 ......: &x->wait#7
 -> [c1e098e4] &p->pi_lock

c1cc3205 FD:   26 BD:    1 +.+.+.: &type->s_umount_key#9/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1cc322c] &sb->s_type->i_lock_key#9
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1cc322c FD:   17 BD:    6 +.+...: &sb->s_type->i_lock_key#9
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1cc3234 FD:   22 BD:    4 +.+.+.: &sb->s_type->i_mutex_key#6
 -> [c1cc322c] &sb->s_type->i_lock_key#9
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25bc904] &(&zone->lock)->rlock

c1c8da78 FD:   26 BD:    1 +.+.+.: event_mutex
 -> [c1c99ff0] pin_fs_lock
 -> [c1cc3234] &sb->s_type->i_mutex_key#6
 -> [c1c8d6d8] trace_event_sem

c1c99b05 FD:   26 BD:    1 +.+.+.: &type->s_umount_key#10/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1c99b2c] &sb->s_type->i_lock_key#10
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c99b2c FD:   17 BD:    2 +.+...: &sb->s_type->i_lock_key#10
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c635d0 FD:    1 BD:    7 +.+...: bdev_lock

c1c9c585 FD:   26 BD:    1 +.+.+.: &type->s_umount_key#11/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1c9c5ac] &sb->s_type->i_lock_key#11
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c9c5ac FD:   17 BD:    2 +.+...: &sb->s_type->i_lock_key#11
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c9dd74 FD:    1 BD:    1 +.+...: kclist_lock

c1ca7585 FD:   27 BD:    1 +.+.+.: &type->s_umount_key#12/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1ca75ac] &sb->s_type->i_lock_key#12
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1ca75ac FD:   17 BD:    2 +.+...: &sb->s_type->i_lock_key#12
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c25d5768 FD:    8 BD:   12 +.+...: subsys mutex#12
 -> [c2607b50] &(&k->k_lock)->rlock

c1cd0318 FD:    1 BD:    1 +.+.+.: pnp_lock

c1cd03ec FD:    1 BD:    1 +.+...: subsys mutex#13

c25daa40 FD:    8 BD:    1 +.+...: subsys mutex#14
 -> [c2607b50] &(&k->k_lock)->rlock

c25d6084 FD:    8 BD:   12 +.+...: subsys mutex#15
 -> [c2607b50] &(&k->k_lock)->rlock

c25d63b0 FD:    8 BD:    1 +.+...: subsys mutex#16
 -> [c2607b50] &(&k->k_lock)->rlock

c1cd0d58 FD:  194 BD:    1 +.+.+.: tty_mutex
 -> [c1c87750] (console_sem).lock
 -> [c1c8772c] console_lock
 -> [c1c87710] logbuf_lock
 -> [c1cd0eb0] tty_ldiscs_lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25d60ec] &tty->legacy_mutex
 -> [c25d6218] (&buf->work)
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c1e0a8bc] &rq->lock

c1cf6018 FD:   25 BD:    6 ++++.+: triggers_list_lock
 -> [c25df728] &led_cdev->trigger_lock

c1cf5f38 FD:    1 BD:    6 ++++..: leds_list_lock

c25df734 FD:    1 BD:    8 .+.?..: &trig->leddev_list_lock

c25ddce0 FD:    8 BD:    1 +.+...: subsys mutex#17
 -> [c2607b50] &(&k->k_lock)->rlock

c1cf3958 FD:    2 BD:    1 +.+...: thermal_governor_lock
 -> [c1cf39b8] thermal_list_lock

c1cf39b8 FD:    1 BD:    2 +.+...: thermal_list_lock

c1cf5778 FD:    1 BD:    1 +.+...: cpufreq_governor_mutex

c1cfd5d0 FD:    1 BD:    1 ......: pcibios_fwaddrmap_lock

c1cff6d0 FD:    1 BD:    1 +.+...: offload_lock

c1d049f0 FD:    1 BD:    1 +.....: inetsw_lock

c1cff710 FD:    1 BD:    1 +.+...: ptype_lock

c25e24e4 FD:    2 BD:    9 +.....: &tbl->lock
 -> [c259f24c] &(&base->lock)->rlock

c1e0a134 FD:    1 BD:    1 .+.+.+: "events_power_efficient"

c1d04720 FD:    2 BD:    1 +.+...: (check_lifetime_work).work
 -> [c259f24c] &(&base->lock)->rlock

c25e29b0 FD:    1 BD:    2 +.+...: &(&net->rules_mod_lock)->rlock

c1d06790 FD:    1 BD:    1 +.....: xfrm_state_afinfo_lock

c1d06650 FD:    1 BD:    1 +.+...: xfrm_policy_afinfo_lock

c1d067d0 FD:    1 BD:    1 +.....: xfrm_input_afinfo_lock

c1d038d4 FD:    1 BD:    2 +.....: raw_v4_hashinfo.lock

c1d035f0 FD:    1 BD:    1 +.+...: tcp_cong_list_lock

c1cfdda0 FD:    1 BD:    2 ......: (net_generic_ids).idr.lock

c1d0bbf0 FD:    1 BD:    3 +.+...: cache_list_lock

c26067c8 FD:    3 BD:    1 +.+...: (&(&cache_cleaner)->work)
 -> [c1d0bbf0] cache_list_lock
 -> [c259f24c] &(&base->lock)->rlock

c1d0bc58 FD:    1 BD:    1 +.+...: (rpc_pipefs_notifier_list).rwsem

c1d0bd30 FD:    1 BD:    1 +.+...: svc_xprt_class_lock

c1d092f0 FD:    1 BD:    1 +.+...: xprt_list_lock

c1c6cab4 FD:   34 BD:    1 .+.+.+: sb_writers#2
 -> [c1c6cad5] &sb->s_type->i_mutex_key#2/1
 -> [c1c6cad4] &sb->s_type->i_mutex_key#2

c1c6cad5 FD:   21 BD:    2 +.+.+.: &sb->s_type->i_mutex_key#2/1
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c6cacc] &sb->s_type->i_lock_key
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock

c1c7c400 FD:   45 BD:    1 +.+...: (tsc_irqwork).work
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1c89db8] clocksource_mutex

c26067c0 FD:    9 BD:    1 ..-...: (&(&cache_cleaner)->timer)
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1e06400 FD:    8 BD:    3 +.+...: subsys mutex#18
 -> [c2607b50] &(&k->k_lock)->rlock

c1e0640c FD:    8 BD:    3 +.+...: subsys mutex#19
 -> [c2607b50] &(&k->k_lock)->rlock

c1c0039a FD:    9 BD:    1 ..-...: net/wireless/reg.c:533
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1d0c380 FD:  108 BD:    1 +.+.+.: (crda_timeout).work
 -> [c1cff938] rtnl_mutex

c2607344 FD:    1 BD:    8 +.+...: &(&reg_indoor_lock)->rlock

c1c7ee00 FD:    2 BD:    1 +.+...: (bios_check_work).work
 -> [c259f24c] &(&base->lock)->rlock

c1cc79f8 FD:   11 BD:    1 +.+.+.: crypto_alg_sem
 -> [c25d1140] &x->wait#8
 -> [c1cc7998] (crypto_chain).rwsem

c1cc7998 FD:    9 BD:    2 .+.+.+: (crypto_chain).rwsem
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1e0a8bc] &rq->lock

c25d1148 FD:    1 BD:    1 +.+...: (complete)&larval->completion

c25d1140 FD:    6 BD:    2 ......: &x->wait#8
 -> [c1e098e4] &p->pi_lock

c1c89c8c FD:    1 BD:    1 +.+...: subsys mutex#20

c25debe4 FD:    9 BD:    1 +.+...: subsys mutex#21
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c1c89f30] rtcdev_lock

c1cddef8 FD:    1 BD:    6 +.+...: deferred_probe_mutex

c1cdde50 FD:    6 BD:    5 ......: probe_waitqueue.lock
 -> [c1e098e4] &p->pi_lock

c1c8a2cc FD:    1 BD:    1 +.+...: subsys mutex#22

c1c8ca10 FD:    1 BD:    1 ......: audit_freelist_lock

c1c87670 FD:    1 BD:    1 ......: printk_ratelimit_state.lock

c25b4244 FD:    1 BD:   57 ......: &(kretprobe_table_locks[i].lock)

c1c8f9ec FD:    1 BD:    2 +.+...: subsys mutex#23

c25bc91c FD:    1 BD:    1 ....-.: &pgdat->kswapd_wait

c1b4e159 FD:    9 BD:    1 ..-...: arch/x86/kernel/tsc.c:1128
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25a0120 FD:   10 BD:    1 +.-...: (&watchdog_timer)
 -> [c1c89d10] watchdog_lock

c1b6ba22 FD:    9 BD:    1 ..-...: mm/vmstat.c:1493
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1c92f40 FD:   41 BD:    1 +.+...: (shepherd).work
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1e0a8bc] &rq->lock

c1c89d40 FD:    9 BD:    1 +.+.+.: watchdog_work
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1e0a8bc] &rq->lock

c25bcaf8 FD:    1 BD:    1 .+.+..: "vmstat"

c25bcb08 FD:    2 BD:    1 +.+...: (&(({ do { const void *__vpp_verify = (typeof((&vmstat_work) + 0))((void *)0); (void)__vpp_verify; } while (0); ({ unsigned long __ptr; __asm__ ("" : "=r"(__ptr) : "0"((typeof(*((&vmstat_work))) *)((&vmstat_work)))); (typeof((typeof(*((&vmstat_work))) *)((&vmstat_work)))) (__ptr + (((__per_cpu_offset[(cpu)])))); }); }))->work)
 -> [c259f24c] &(&base->lock)->rlock

c1c9c645 FD:   26 BD:    1 +.+.+.: &type->s_umount_key#13/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1c9c66c] &sb->s_type->i_lock_key#13
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c9c66c FD:   17 BD:    2 +.+...: &sb->s_type->i_lock_key#13
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c63690 FD:    1 BD:    1 +.+...: dq_list_lock

c1ca7910 FD:    1 BD:    1 +.+...: nfs_version_lock

c1cc3b30 FD:    1 BD:    2 +.+...: key_user_lock

c1cc3b70 FD:    1 BD:    2 +.+...: key_serial_lock

c1cc3a78 FD:    2 BD:    2 +.+...: key_construction_mutex
 -> [c1cc3c74] keyring_name_lock

c1cc3c74 FD:    1 BD:    3 ++++..: keyring_name_lock

c1cc3ad8 FD:    1 BD:    1 +.+...: key_types_sem

c1cc2eb0 FD:    1 BD:    1 +.+...: nls_lock

c1cc3705 FD:   27 BD:    1 +.+.+.: &type->s_umount_key#14/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1cc372c] &sb->s_type->i_lock_key#14
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1cc372c FD:   17 BD:    2 +.+...: &sb->s_type->i_lock_key#14
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1d020d8 FD:    1 BD:    8 +.+...: nf_hook_mutex

c1cc7305 FD:   27 BD:    1 +.+.+.: &type->s_umount_key#15/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1cc732c] &sb->s_type->i_lock_key#15
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1cc732c FD:   17 BD:    2 +.+...: &sb->s_type->i_lock_key#15
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1cc9230 FD:    1 BD:    8 +.+...: elv_list_lock

c25d36fc FD:    1 BD:    1 +.+...: &(&drv->dynids.lock)->rlock

c25deab0 FD:    8 BD:    6 +.+...: subsys mutex#24
 -> [c2607b50] &(&k->k_lock)->rlock

c25deac8 FD:    1 BD:    6 +.+...: &dev->mutex#2

c1ccfa9c FD:   45 BD:    1 +.+.+.: register_count_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock

c1cf3a38 FD:    1 BD:    1 +.+.+.: thermal_idr_lock

c25dee90 FD:    8 BD:    1 +.+...: subsys mutex#25
 -> [c2607b50] &(&k->k_lock)->rlock

c1c83070 FD:    1 BD:    3 ......: async_lock

c25bcb00 FD:    9 BD:    1 ..-...: (&(({ do { const void *__vpp_verify = (typeof((&vmstat_work) + 0))((void *)0); (void)__vpp_verify; } while (0); ({ unsigned long __ptr; __asm__ ("" : "=r"(__ptr) : "0"((typeof(*((&vmstat_work))) *)((&vmstat_work)))); (typeof((typeof(*((&vmstat_work))) *)((&vmstat_work)))) (__ptr + (((__per_cpu_offset[(cpu)])))); }); }))->timer)
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1e0a3a4 FD:  231 BD:    1 +.+.+.: (&entry->work)
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1c9de78] kernfs_mutex
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c1c83070] async_lock
 -> [c1c83030] async_done.lock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c7ed7850] hrtimer_bases.lock#3
 -> [c25db814] &x->wait#3
 -> [c25dbbe0] &(shost->host_lock)->rlock
 -> [c1cde578] attribute_container_mutex
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c25dbbd0] &shost->scan_mutex
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25d2788] (complete)&wait
 -> [c25d2780] &x->wait#10
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c1cddc78] gdp_mutex
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1cdee58] dpm_list_mtx
 -> [c25bcb50] subsys mutex#26
 -> [c1c99ff0] pin_fs_lock
 -> [c1cc30b4] &sb->s_type->i_mutex_key#3
 -> [c1c930d0] bdi_lock
 -> [c1ccaf18] block_class_lock
 -> [c1cde990] req_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c25d280c] subsys mutex#27
 -> [c1cdedf8] dev_hotplug_mutex
 -> [c1c63550] inode_hash_lock
 -> [c1c635d0] bdev_lock
 -> [c1c9c30c] &sb->s_type->i_lock_key#2
 -> [c25d2824] &ev->block_mutex
 -> [c25c57e4] &bdev->bd_mutex
 -> [c25d282c] &(&ev->lock)->rlock
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c1ccac58] disk_events_mutex

c1c83030 FD:    6 BD:    2 ......: async_done.lock
 -> [c1e098e4] &p->pi_lock

c1cd4cd8 FD:  163 BD:    1 +.+.+.: serial_mutex
 -> [c1cd4ab8] port_mutex

c1cd4ab8 FD:  162 BD:    2 +.+.+.: port_mutex
 -> [c25d6238] &port->mutex

c25d6238 FD:  161 BD:   11 +.+.+.: &port->mutex
 -> [c1c805f0] bootmem_resource_lock
 -> [c1c80634] resource_lock
 -> [c25d74d4] &port_lock_key
 -> [c1c99a58] chrdevs_lock
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1cde990] req_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c1e0a8bc] &rq->lock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25d6084] subsys mutex#15
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25d6228] &(&port->lock)->rlock
 -> [c1cd4dd8] hash_mutex
 -> [c25d7500] &(&i->lock)->rlock
 -> [c259f0c4] &irq_desc_lock_class
 -> [c1c87c58] register_lock
 -> [c1c9dbe0] (proc_inum_ida).idr.lock
 -> [c1c9db90] proc_inum_lock
 -> [c25d6240] &port->delta_msr_wait
 -> [c25d7508] (&up->timer)
 -> [c259f24c] &(&base->lock)->rlock
 -> [c25d6248] &port->open_wait
 -> [c25d60c4] &tty->write_wait

c25db6a0 FD:    1 BD:    6 ......: &(&dev->devres_lock)->rlock

c1cddde8 FD:    1 BD:    1 +.+...: s_active#2

c1cdde04 FD:    1 BD:    1 +.+...: s_active#3

c1ccc19c FD:    1 BD:    1 +.+...: s_active#4

c1ccc180 FD:    1 BD:    1 +.+...: s_active#5

c1cddd94 FD:    1 BD:    1 +.+...: s_active#6

c1d19ed0 FD:    6 BD:   79 +.+...: klist_remove_lock
 -> [c1e098e4] &p->pi_lock

c25db6b4 FD:    1 BD:    3 .+.+..: &(&priv->bus_notifier)->rwsem

c25db660 FD:    1 BD:    1 +.....: &(&dev->queue_lock)->rlock

c1cdfdf8 FD:  180 BD:    4 +.+.+.: loop_index_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1cc9300] (blk_queue_ida).idr.lock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1cc91d8] bio_slab_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1e0a16c] &wq->mutex
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1c827b8] wq_pool_mutex
 -> [c1ccb9d0] percpu_counters_lock
 -> [c1ccabd0] blk_mq_cpu_notify_lock
 -> [c1cca998] all_q_mutex
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25bcb50] subsys mutex#26
 -> [c1c99ff0] pin_fs_lock
 -> [c1cc30b4] &sb->s_type->i_mutex_key#3
 -> [c1c930d0] bdi_lock
 -> [c1ccaf18] block_class_lock
 -> [c1cde990] req_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c25d280c] subsys mutex#27
 -> [c1cdedf8] dev_hotplug_mutex
 -> [c25d2718] &(&q->__queue_lock)->rlock

c1cc9300 FD:    1 BD:   81 ......: (blk_queue_ida).idr.lock

c1ccabd0 FD:    1 BD:    5 +.+...: blk_mq_cpu_notify_lock

c1cca998 FD:   48 BD:    5 +.+.+.: all_q_mutex
 -> [c25d2794] &set->tag_list_lock
 -> [c25d2720] &q->sysfs_lock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c25bc904] &(&zone->lock)->rlock

c25d2794 FD:    1 BD:    6 +.+...: &set->tag_list_lock

c25d2720 FD:   21 BD:    7 +.+.+.: &q->sysfs_lock
 -> [c1cc9230] elv_list_lock
 -> [c25d2718] &(&q->__queue_lock)->rlock

c25bcb50 FD:    8 BD:    8 +.+...: subsys mutex#26
 -> [c2607b50] &(&k->k_lock)->rlock

c1c930d0 FD:    1 BD:    8 +.....: bdi_lock

c25d280c FD:    8 BD:    8 +.+...: subsys mutex#27
 -> [c2607b50] &(&k->k_lock)->rlock

c25d2718 FD:   19 BD:   80 -.-...: &(&q->__queue_lock)->rlock
 -> [c25dbc3c] &(&sdev->list_lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c25d2780] &x->wait#10
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25d2774] &(&ioc->lock)->rlock
 -> [c25dbbe0] &(shost->host_lock)->rlock
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c25d26bc] &x->wait#15
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c25bc924] zone->wait_table + i
 -> [c1cd86c8] input_pool.lock
 -> [c1cd87d0] random_read_wait.lock

c25dbd88 FD:  147 BD:    3 +.+.+.: subsys mutex#28
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c25db814] &x->wait#3
 -> [c1ce48d4] sg_index_lock
 -> [c1c99a58] chrdevs_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1e098e4] &p->pi_lock
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1cde990] req_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25dbf04] subsys mutex#34

c25dbfa8 FD:   23 BD:   74 -.-...: &(&host->lock)->rlock
 -> [c25dbbe0] &(shost->host_lock)->rlock
 -> [c25dbfc0] &ap->eh_wait_q
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c25dbfe8] &x->wait#9
 -> [c1ce6650] ata_scsi_rbuf_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25d2718] &(&q->__queue_lock)->rlock

c1ce4970 FD:    1 BD:    1 +.+...: lock

c25db6fc FD:    8 BD:    4 +.+...: subsys mutex#29
 -> [c2607b50] &(&k->k_lock)->rlock

c1ce3420 FD:    1 BD:   81 ......: (host_index_ida).idr.lock

c1ce0578 FD:   71 BD:    1 +.+.+.: host_cmd_pool_mutex

c1ce3a8c FD:    1 BD:    3 +.+...: subsys mutex#30

c25dbbc0 FD:    8 BD:    1 +.+...: subsys mutex#31
 -> [c2607b50] &(&k->k_lock)->rlock

c25db754 FD:    1 BD:   48 ......: &dev->power.wait_queue

c25dbbe0 FD:    6 BD:   82 -.-...: &(shost->host_lock)->rlock
 -> [c1e098e4] &p->pi_lock

c25dbfc0 FD:    6 BD:   75 ......: &ap->eh_wait_q
 -> [c1e098e4] &p->pi_lock

c25dd034 FD:   24 BD:   10 +.+...: (&(&ap->sff_pio_task)->work)
 -> [c25dbfa8] &(&host->lock)->rlock

c25dbfa0 FD:   29 BD:    9 +.+...: &host->eh_mutex
 -> [c25dbfb0] (&ap->fastdrain_timer)
 -> [c259f24c] &(&base->lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c1ce7850] piix_lock
 -> [c1ccbdd0] pci_lock
 -> [c25dd034] (&(&ap->sff_pio_task)->work)
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25dbfb0 FD:    1 BD:   10 ......: (&ap->fastdrain_timer)

c1ce7850 FD:    3 BD:   10 ......: piix_lock
 -> [c1ccbdd0] pci_lock

c1cff650 FD:    1 BD:    1 +.+...: napi_hash_lock

c1ce9ab8 FD:    5 BD:    1 +.+...: e1000_eeprom_lock
 -> [c1e0a8bc] &rq->lock

c25dd024 FD:    1 BD:    1 .+.+..: "ata_sff"

c25dbfe8 FD:    6 BD:   75 -.-...: &x->wait#9
 -> [c1e098e4] &p->pi_lock

c25dbbd8 FD:    6 BD:    5 ......: &shost->host_wait
 -> [c1e098e4] &p->pi_lock

c25dbbd0 FD:  208 BD:    2 +.+.+.: &shost->scan_mutex
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c25dbbe0] &(shost->host_lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1cc9300] (blk_queue_ida).idr.lock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1cc91d8] bio_slab_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1e0a16c] &wq->mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1c827b8] wq_pool_mutex
 -> [c1ccb9d0] percpu_counters_lock
 -> [c25d2720] &q->sysfs_lock
 -> [c25db814] &x->wait#3
 -> [c1cde578] attribute_container_mutex
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25d2788] (complete)&wait
 -> [c25d2780] &x->wait#10
 -> [c25dbc34] &sdev->inquiry_mutex
 -> [c259f118] (complete)&rs_array[i].completion
 -> [c259f110] &x->wait#4
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c1ce3a8c] subsys mutex#30
 -> [c1cddc78] gdp_mutex
 -> [c25dbd88] subsys mutex#28
 -> [c1ccb178] bsg_mutex
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)

c25dbc3c FD:    1 BD:   81 ..-...: &(&sdev->list_lock)->rlock

c1ce6650 FD:    1 BD:   75 -.-...: ata_scsi_rbuf_lock

c25d2780 FD:    6 BD:   81 ..-...: &x->wait#10
 -> [c1e098e4] &p->pi_lock

c25d2788 FD:   37 BD:    8 +.+...: (complete)&wait
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c259f1d0] rcu_node_0
 -> [c259f0c4] &irq_desc_lock_class
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25dbbe0] &(shost->host_lock)->rlock
 -> [c25dbfa0] &host->eh_mutex
 -> [c25dbfe8] &x->wait#9
 -> [c259f254] ((&timer))
 -> [c1e0a8bc] &rq->lock
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c25dd034] (&(&ap->sff_pio_task)->work)
 -> [c1cd87d0] random_read_wait.lock
 -> [c1cd86c8] input_pool.lock
 -> [c7eaf850] hrtimer_bases.lock

c1ce9a50 FD:    1 BD:    1 ......: e1000_phy_lock

c25dbc34 FD:    1 BD:    3 +.+...: &sdev->inquiry_mutex

c1cff9b0 FD:    9 BD:    8 ......: lweventlist_lock
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1cff9e0 FD:  108 BD:    1 +.+...: (linkwatch_work).work
 -> [c1cff938] rtnl_mutex

c1ce45c0 FD:    1 BD:    4 ......: (sd_index_ida).idr.lock

c1ce45f0 FD:    2 BD:    3 +.+...: sd_index_lock
 -> [c1ce45c0] (sd_index_ida).idr.lock

c25dbebc FD:    8 BD:    3 +.+...: subsys mutex#32
 -> [c2607b50] &(&k->k_lock)->rlock

c25ba114 FD:    1 BD:   69 ......: &(&tsk->delays->lock)->rlock

c25de438 FD:    8 BD:    1 +.+...: subsys mutex#33
 -> [c2607b50] &(&k->k_lock)->rlock

c1cebbf8 FD:    1 BD:    1 +.+...: usb_bus_idr_lock

c1ce48d4 FD:    1 BD:    4 ......: sg_index_lock

c25dbf04 FD:    8 BD:    4 +.+...: subsys mutex#34
 -> [c2607b50] &(&k->k_lock)->rlock

c1ccb178 FD:  145 BD:    3 +.+.+.: bsg_mutex
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c1ccb5f0] simple_ida_lock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1cde990] req_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25d2cd8] subsys mutex#35

c1c63550 FD:   21 BD:   81 +.+...: inode_hash_lock
 -> [c1c9c30c] &sb->s_type->i_lock_key#2
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c1c9e04c] &sb->s_type->i_lock_key#17

c1cf0730 FD:    2 BD:    7 -.-...: i8042_lock
 -> [c25de860] &x->wait#11

c25d2824 FD:   80 BD:    5 +.+...: &ev->block_mutex
 -> [c25d282c] &(&ev->lock)->rlock
 -> [c25d281c] (&(&ev->dwork)->work)
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25d282c FD:    9 BD:    8 ......: &(&ev->lock)->rlock
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25c57e4 FD:  206 BD:    3 +.+.+.: &bdev->bd_mutex
 -> [c1ce4578] sd_ref_mutex
 -> [c1c9c314] &sb->s_type->i_mutex_key#7
 -> [c1c63550] inode_hash_lock
 -> [c1c99970] sb_lock
 -> [c1e0a8bc] &rq->lock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25d2788] (complete)&wait
 -> [c25d2780] &x->wait#10
 -> [c1c972d0] vmap_area_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c25bc924] zone->wait_table + i
 -> [c25d282c] &(&ev->lock)->rlock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c1c9c30c] &sb->s_type->i_lock_key#2
 -> [c1cf4930] all_mddevs_lock
 -> [c25def74] &mddev->open_mutex
 -> [c1c635d0] bdev_lock
 -> [c1ce47f8] sr_mutex
 -> [c1cdfdf8] loop_index_mutex
 -> [c25dbab8] &lo->lo_ctl_mutex

c1ce4578 FD:    1 BD:    7 +.+...: sd_ref_mutex

c1c9c314 FD:    2 BD:    4 +.+...: &sb->s_type->i_mutex_key#7
 -> [c25c566c] &inode->i_size_seqcount

c25c566c FD:    1 BD:   63 +.+...: &inode->i_size_seqcount

c25d2cd8 FD:    8 BD:    4 +.+...: subsys mutex#35
 -> [c2607b50] &(&k->k_lock)->rlock

c25de860 FD:    1 BD:    8 -.....: &x->wait#11

c25c6f84 FD:    1 BD:   13 +.+...: &(&ent->pde_unload_lock)->rlock

c1cf05b0 FD:    9 BD:    3 ......: serio_event_lock
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1e0a14c FD:    1 BD:    1 .+.+.+: "events_long"

c1cf0560 FD:  174 BD:    1 +.+.+.: serio_event_work
 -> [c1cf0618] serio_mutex
 -> [c1e0a8bc] &rq->lock

c1cf0618 FD:  173 BD:    2 +.+.+.: serio_mutex
 -> [c1cf05b0] serio_event_lock
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c25db6b4] &(&priv->bus_notifier)->rwsem
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c1cf03ac] subsys mutex#36
 -> [c1e098e4] &p->pi_lock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c25bc904] &(&zone->lock)->rlock

c1cf0de0 FD:    1 BD:   81 ......: (input_ida).idr.lock

c25bc8c0 FD:   93 BD:   61 +.+...: (PG_locked)page
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c259f0c4] &irq_desc_lock_class
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25bc924] zone->wait_table + i
 -> [c1e0980c] &(ptlock_ptr(page))->rlock
 -> [c7eeb850] hrtimer_bases.lock#4
 -> [c7eaf850] hrtimer_bases.lock
 -> [c1c898b0] timekeeper_lock
 -> [c1c632e8] jiffies_lock
 -> [c7ed7850] hrtimer_bases.lock#3
 -> [c7ec3850] hrtimer_bases.lock#2
 -> [c25c566c] &inode->i_size_seqcount
 -> [c25bca84] &(&info->lock)->rlock
 -> [c259f1d0] rcu_node_0
 -> [c259f190] &rsp->gp_wq
 -> [c25d2d68] (&cfqd->idle_slice_timer)
 -> [c1cd87d0] random_read_wait.lock
 -> [c25c88c0] &(&sbi->s_bal_lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c25c8744] &ei->i_data_sem
 -> [c25c89a0] &(&journal->j_list_lock)->rlock
 -> [c25c879c] &(&ei->i_raw_lock)->rlock

c25c5684 FD:    5 BD:   69 ..-...: &(&mapping->tree_lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25d2d74] &pl->lock

c25c5674 FD:    1 BD:   65 +.+...: &(&mapping->private_lock)->rlock

c259f12f FD:    1 BD:    1 ......: rcu_read_lock_sched

c25deb11 FD:    1 BD:    6 +.+...: &mousedev->mutex/1

c25bc924 FD:    6 BD:   90 ..-...: zone->wait_table + i
 -> [c1e098e4] &p->pi_lock

c25bc8fc FD:    1 BD:   71 ......: &(&zone->lru_lock)->rlock

c1cf03ac FD:    1 BD:    3 +.+...: subsys mutex#36

c1ccac58 FD:    1 BD:    3 +.+...: disk_events_mutex

c1e0a12c FD:    1 BD:    1 .+.+.+: "events_freezable_power_efficient"

c25d281c FD:   79 BD:    6 +.+.+.: (&(&ev->dwork)->work)
 -> [c1ce4578] sd_ref_mutex
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25d2788] (complete)&wait
 -> [c25d2780] &x->wait#10
 -> [c1e0a8bc] &rq->lock
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c25d282c] &(&ev->lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock

c25d2774 FD:    9 BD:   81 ......: &(&ioc->lock)->rlock
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1ce46d0 FD:    1 BD:    3 +.+...: sr_index_lock

c25de840 FD:  167 BD:    3 +.+.+.: &serio->drv_mutex
 -> [c25db814] &x->wait#3
 -> [c25de848] &serio->lock
 -> [c25deaa7] &ps2dev->cmd_mutex
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1c9de78] kernfs_mutex
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25deab0] subsys mutex#24
 -> [c1cf0d98] input_mutex
 -> [c1cf0730] i8042_lock
 -> [c25db6a0] &(&dev->devres_lock)->rlock
 -> [c1cf12f8] psmouse_mutex

c25dbbb0 FD:    9 BD:    1 ..-...: (&(&cmd->abort_work)->timer)
 -> [c1e0a1a5] &pool->lock/1

c25de848 FD:   16 BD:    7 -.-...: &serio->lock
 -> [c25dea9f] &ps2dev->wait
 -> [c25deac0] &(&dev->event_lock)->rlock

c25deaa7 FD:   22 BD:    5 +.+...: &ps2dev->cmd_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1cf06f8] i8042_mutex

c25dbbc8 FD:    1 BD:    1 .+.+..: "scsi_tmf_%d"shost->host_no

c25dbbb8 FD:    7 BD:    1 +.+...: (&(&cmd->abort_work)->work)
 -> [c25dbbe0] &(shost->host_lock)->rlock

c1cf06f8 FD:   21 BD:    6 +.+...: i8042_mutex
 -> [c25de848] &serio->lock
 -> [c1cf0730] i8042_lock
 -> [c1e0a8bc] &rq->lock
 -> [c25dea9f] &ps2dev->wait
 -> [c259f24c] &(&base->lock)->rlock
 -> [c259f254] ((&timer))
 -> [c25b4244] &(kretprobe_table_locks[i].lock)

c25dea9f FD:    6 BD:    8 -.-...: &ps2dev->wait
 -> [c1e098e4] &p->pi_lock

c1ceb158 FD:    1 BD:    3 +.+...: cdrom_mutex

c1cf1980 FD:    1 BD:   81 ......: (rtc_ida).idr.lock

c25d2738 FD:    9 BD:    1 +.-...: ((&q->timeout))
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25dec0c FD:    2 BD:    1 +.+...: &rtc->ops_lock
 -> [c1c7c830] rtc_lock

c25d2700 FD:    1 BD:    1 .+.+..: "kblockd"

c25d2708 FD:   20 BD:    1 +.+...: (&q->timeout_work)
 -> [c25d2718] &(&q->__queue_lock)->rlock

c1c89f30 FD:    1 BD:    2 ......: rtcdev_lock

c1cf4ef8 FD:    1 BD:    1 +.+...: _lock

c25df718 FD:    8 BD:    6 +.+...: subsys mutex#37
 -> [c2607b50] &(&k->k_lock)->rlock

c25df728 FD:   24 BD:    7 +.+.+.: &led_cdev->trigger_lock
 -> [c1e0a8bc] &rq->lock
 -> [c25df734] &trig->leddev_list_lock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock

c25deac0 FD:   14 BD:   11 -.-...: &(&dev->event_lock)->rlock
 -> [c1cd86c8] input_pool.lock
 -> [c1cd87d0] random_read_wait.lock

c1cf53f0 FD:    1 BD:    1 +.+...: _lock#2

c1cf12f8 FD:  165 BD:    4 +.+.+.: psmouse_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c25db814] &x->wait#3
 -> [c25de848] &serio->lock
 -> [c25deaa7] &ps2dev->cmd_mutex
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c2607b50] &(&k->k_lock)->rlock
 -> [c25deab0] subsys mutex#24
 -> [c1cf0d98] input_mutex

c1cfa318 FD:    1 BD:    1 +.+...: snd_ioctl_rwsem

c1cfa418 FD:    1 BD:    1 +.+.+.: strings

c1cfa5b8 FD:    1 BD:    1 +.+...: register_mutex

c1cfa198 FD:  144 BD:    2 +.+.+.: sound_mutex
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1cde990] req_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25db718] (complete)&req.done
 -> [c1e0a8bc] &rq->lock
 -> [c25db710] &x->wait#5
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25dfbc4] subsys mutex#38

c25dfbc4 FD:    8 BD:    5 +.+...: subsys mutex#38
 -> [c2607b50] &(&k->k_lock)->rlock

c1cfa6b8 FD:    1 BD:    1 +.+...: register_mutex#2

c1cfaed8 FD:  146 BD:    1 +.+.+.: register_mutex#3
 -> [c1cfa198] sound_mutex
 -> [c1cfaf10] clients_lock
 -> [c25bc904] &(&zone->lock)->rlock

c1cfaf10 FD:    1 BD:    4 ......: clients_lock

c25e09c0 FD:    2 BD:    1 +.+...: &client->ports_mutex
 -> [c25e09c8] &client->ports_lock

c25e09c8 FD:    1 BD:    2 .+.+..: &client->ports_lock

c1cfb078 FD:  147 BD:    1 +.+.+.: register_mutex#4
 -> [c1cfa478] sound_oss_mutex

c1cfa478 FD:  146 BD:    2 +.+.+.: sound_oss_mutex
 -> [c1cfa130] sound_loader_lock
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1cde990] req_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c1e0a8bc] &rq->lock
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c25dfbc4] subsys mutex#38

c1cfa130 FD:    1 BD:    3 +.+...: sound_loader_lock

c25e1160 FD:    4 BD:    1 ++++.+: &grp->list_mutex
 -> [c25e1168] &grp->list_lock
 -> [c1cfaf10] clients_lock
 -> [c1cfb1f0] register_lock#2

c25e1168 FD:    1 BD:    2 ......: &grp->list_lock

c1cfb100 FD:  147 BD:    1 +.+.+.: async_lookup_work
 -> [c1cfaf10] clients_lock
 -> [c1cfa258] snd_card_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1e0a1a5] &pool->lock/1
 -> [c1e0a0f0] (complete)&done#2
 -> [c1e0a0e8] &x->wait#12
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c1cfafa0] autoload_work
 -> [c1e0a1bc] (complete)&barr->done
 -> [c1e0a1cc] &x->wait#13

c1cfa258 FD:    1 BD:    2 +.+...: snd_card_mutex

c1e0a0f0 FD:  135 BD:    2 +.+...: (complete)&done#2
 -> [c1e0a0f8] (&sub_info->work)
 -> [c1e0a1a5] &pool->lock/1
 -> [c1e0a8bc] &rq->lock

c1e0a0e8 FD:    6 BD:    4 ......: &x->wait#12
 -> [c1e098e4] &p->pi_lock

c1cfb1f0 FD:    1 BD:    2 ......: register_lock#2

c1e098dc FD:    1 BD:   77 ......: &prev->lock

c25e3620 FD:    1 BD:    1 +.+...: &table[i].mutex

c1d021b8 FD:    1 BD:    1 +.+...: nf_log_mutex

c1cfafa0 FD:    8 BD:    7 +.+...: autoload_work
 -> [c2607b50] &(&k->k_lock)->rlock

c1e0a1bc FD:   15 BD:    6 +.+...: (complete)&barr->done
 -> [c1e0a1c4] (&barr->work)
 -> [c1e0a1a4] &(&pool->lock)->rlock
 -> [c1cfafa0] autoload_work
 -> [c1e0a8bc] &rq->lock
 -> [c1e0a1a5] &pool->lock/1

c1e0a1cc FD:    6 BD:   37 ......: &x->wait#13
 -> [c1e098e4] &p->pi_lock

c1e0a1c4 FD:    7 BD:    7 +.+...: (&barr->work)
 -> [c1e0a1cc] &x->wait#13

c1d02bb8 FD:    1 BD:    1 +.+...: nf_ct_ext_type_mutex

c1d02538 FD:    1 BD:    1 +.+...: nf_ct_helper_mutex

c25e3ad0 FD:    1 BD:    2 +.+.+.: &xt[i].mutex

c1d02238 FD:    1 BD:    1 +.+...: nf_sockopt_mutex

c1d02598 FD:    1 BD:    1 +.+.+.: nf_ct_proto_mutex

c1d06690 FD:    1 BD:    1 +.....: xfrm_km_lock

c1d06f50 FD:    1 BD:    1 +.....: inetsw6_lock

c1d07ab4 FD:    1 BD:    2 +.....: raw_v6_hashinfo.lock

c25efc40 FD:    1 BD:    2 +.+...: &(&ip6addrlbl_table.lock)->rlock

c25efb40 FD:    4 BD:    8 ++....: &ndev->lock
 -> [c1cd8790] random_write_wait.lock
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c259f24c] &(&base->lock)->rlock

c25efcf2 FD:    1 BD:    8 +.....: &(&idev->mc_lock)->rlock

c25efd02 FD:    2 BD:    8 +.....: &(&mc->mca_lock)->rlock
 -> [c25e2048] _xmit_ETHER

c259f137 FD:    1 BD:    1 ......: rcu_read_lock_bh

c25e2048 FD:    1 BD:    9 +.....: _xmit_ETHER

c25efb20 FD:    1 BD:    1 .+.+..: "%s"("ipv6_addrconf")

c1d07000 FD:  108 BD:    1 +.+...: (addr_chk_work).work
 -> [c1cff938] rtnl_mutex

c1d06710 FD:    1 BD:    1 +.....: xfrm_type_lock

c1d08c78 FD:    1 BD:    1 +.+...: xfrm6_protocol_mutex

c1d066d0 FD:    1 BD:    1 +.....: xfrm_mode_lock

c26043d0 FD:    1 BD:    1 ......: &syncp->seq#2

c25efb28 FD:    1 BD:    1 ......: &syncp->seq#3

c1d05b98 FD:    1 BD:    1 +.+...: tunnel4_mutex

c1d0b450 FD:    1 BD:    1 +.+...: rpc_authflavor_lock

c1d0b6b0 FD:    1 BD:    1 +.+...: authtab_lock

c1c7e0b8 FD:   53 BD:    1 +.+.+.: microcode_mutex
 -> [c1cde52c] subsys mutex#3

c1cc3d78 FD:   10 BD:    1 +.+.+.: key_user_keyring_mutex
 -> [c1cc3b30] key_user_lock
 -> [c1cc3d08] root_key_user.lock
 -> [c1cd8790] random_write_wait.lock
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c1cc3b70] key_serial_lock
 -> [c1cc3a78] key_construction_mutex
 -> [c1cc3c40] &type->lock_class

c1cc3d08 FD:    1 BD:    4 +.+...: root_key_user.lock

c1cc3c40 FD:    3 BD:    2 +.+.+.: &type->lock_class
 -> [c1cc3bd8] keyring_serialise_link_sem

c1cc3bd8 FD:    2 BD:    3 +.+.+.: keyring_serialise_link_sem
 -> [c1cc3d08] root_key_user.lock

c1ccb850 FD:    3 BD:    1 ......: lock#2
 -> [c1cd8790] random_write_wait.lock
 -> [c1cd85c8] nonblocking_pool.lock

c25db6c4 FD:    1 BD:    1 ++++..: "%s""deferwq"

c1cdde80 FD:    2 BD:    1 +.+...: deferred_probe_work
 -> [c1cddef8] deferred_probe_mutex

c1c9def0 FD:    9 BD:    2 ......: kernfs_notify_lock
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1c9dea0 FD:   37 BD:    1 +.+...: kernfs_notify_work
 -> [c1c9def0] kernfs_notify_lock
 -> [c1c9df90] kernfs_open_node_lock
 -> [c1c9de78] kernfs_mutex

c1c9df90 FD:    1 BD:   18 ......: kernfs_open_node_lock

c1ccff18 FD:    2 BD:    3 +.+.+.: performance_mutex
 -> [c25d57d4] semaphore->lock

c1cf5814 FD:    1 BD:    3 ......: cpufreq_driver_lock

c25df56c FD:    1 BD:    3 +.+...: &policy->rwsem

c1cf5558 FD:    1 BD:    3 +.+...: s_active#7

c1cf5654 FD:    1 BD:    3 +.+...: s_active#8

c1cf561c FD:    1 BD:    3 +.+...: s_active#9

c1cf5638 FD:    1 BD:    3 +.+...: s_active#10

c1cf5670 FD:    1 BD:    3 +.+...: s_active#11

c1cf568c FD:    1 BD:    3 +.+...: s_active#12

c1cf5574 FD:    1 BD:    3 +.+...: s_active#13

c1cf55ac FD:    1 BD:    3 +.+...: s_active#14

c1cf5590 FD:    1 BD:    3 +.+...: s_active#15

c1cf55c8 FD:    1 BD:    3 +.+...: s_active#16

c1cf553c FD:    1 BD:    3 +.+...: s_active#17

c25df544 FD:    1 BD:    3 ......: &x->wait#14

c25df554 FD:    1 BD:    3 +.+...: (complete)&policy->kobj_unregister

c1c87359 FD:    1 BD:    1 +.+...: pm_mutex/1

c1c999f0 FD:    1 BD:    3 +.+...: cdev_lock

c25d60ec FD:  193 BD:    2 +.+.+.: &tty->legacy_mutex
 -> [c1c972d0] vmap_area_lock
 -> [c25d60c4] &tty->write_wait
 -> [c25d60bc] &tty->read_wait
 -> [c25d60dc] &tty->termios_rwsem
 -> [c25d6094] &(&tty->files_lock)->rlock
 -> [c25d6228] &(&port->lock)->rlock
 -> [c25d6238] &port->mutex
 -> [c25d74d4] &port_lock_key
 -> [c1c87750] (console_sem).lock
 -> [c1c8772c] console_lock
 -> [c1c87710] logbuf_lock
 -> [c25c40d4] &(&f->f_lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c259f254] ((&timer))
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25d60cc] &tty->ldisc_sem
 -> [c25d60a4] &(&tty->ctrl_lock)->rlock
 -> [c25d6248] &port->open_wait

c25d60c4 FD:    6 BD:   18 -.-...: &tty->write_wait
 -> [c1e098e4] &p->pi_lock

c25d60bc FD:    6 BD:    9 ......: &tty->read_wait
 -> [c1e098e4] &p->pi_lock

c25d60dc FD:  178 BD:    8 ++++..: &tty->termios_rwsem
 -> [c25d6238] &port->mutex
 -> [c25d60c4] &tty->write_wait
 -> [c25d60bc] &tty->read_wait
 -> [c25d6180] &ldata->output_lock
 -> [c25d74d4] &port_lock_key
 -> [c1c87750] (console_sem).lock
 -> [c1c8772c] console_lock
 -> [c1c87710] logbuf_lock
 -> [c1e0a8bc] &rq->lock
 -> [c1c7f3f0] pgd_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25d609c] &(&tty->flow_lock)->rlock
 -> [c25d60e4] &tty->throttle_mutex
 -> [c255ba50] &sem->wait_lock
 -> [c25d60a4] &(&tty->ctrl_lock)->rlock

c25d6094 FD:    3 BD:    4 +.+...: &(&tty->files_lock)->rlock
 -> [c25c40d4] &(&f->f_lock)->rlock

c25d6228 FD:    1 BD:   12 ......: &(&port->lock)->rlock

c1cd4dd8 FD:   17 BD:   12 +.+.+.: hash_mutex
 -> [c259f0c4] &irq_desc_lock_class
 -> [c1c9dc14] proc_subdir_lock
 -> [c25c6f84] &(&ent->pde_unload_lock)->rlock
 -> [c1c9db90] proc_inum_lock
 -> [c25d7500] &(&i->lock)->rlock

c25d7500 FD:    8 BD:   13 -.-...: &(&i->lock)->rlock
 -> [c25d74d4] &port_lock_key

c1cf4930 FD:    1 BD:    4 +.+...: all_mddevs_lock

c25def0c FD:    1 BD:    1 +.+...: "md_misc"

c1cf4398 FD:  176 BD:    1 +.+.+.: disks_mutex
 -> [c1cc9300] (blk_queue_ida).idr.lock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1cc91d8] bio_slab_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1e0a16c] &wq->mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c1c827b8] wq_pool_mutex
 -> [c1ccb9d0] percpu_counters_lock
 -> [c25db814] &x->wait#3
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25def74] &mddev->open_mutex

c25def74 FD:  152 BD:    5 +.+.+.: &mddev->open_mutex
 -> [c25db814] &x->wait#3
 -> [c1cddc78] gdp_mutex
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c1ccd680] bus_type_sem
 -> [c1c9dfd0] sysfs_symlink_target_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25db698] &(&dev->power.lock)->rlock
 -> [c1cdee58] dpm_list_mtx
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a1a5] &pool->lock/1
 -> [c1c820b0] running_helpers_waitq.lock
 -> [c1e0a8bc] &rq->lock
 -> [c25bcb50] subsys mutex#26
 -> [c1c99ff0] pin_fs_lock
 -> [c1cc30b4] &sb->s_type->i_mutex_key#3
 -> [c1c930d0] bdi_lock
 -> [c1ccaf18] block_class_lock
 -> [c1cde990] req_lock
 -> [c1e098e4] &p->pi_lock
 -> [c25db718] (complete)&req.done
 -> [c25db710] &x->wait#5
 -> [c25d280c] subsys mutex#27
 -> [c1cdedf8] dev_hotplug_mutex
 -> [c25d2718] &(&q->__queue_lock)->rlock

c25c57dc FD:  164 BD:    1 +.+.+.: &bdev->bd_fsfreeze_mutex
 -> [c1c99970] sb_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1c9e4c5] &type->s_umount_key#16/1
 -> [c1c9e545] &type->s_umount_key#17/1
 -> [c1c9e3c5] &type->s_umount_key#18/1

c1c9e4c5 FD:   64 BD:    2 +.+.+.: &type->s_umount_key#16/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c1e0a8bc] &rq->lock

c25d2d60 FD:   24 BD:    1 +.+...: (&cfqd->unplug_work)
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock

c1c9e545 FD:   32 BD:    2 +.+.+.: &type->s_umount_key#17/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c1e0a8bc] &rq->lock

c1c9e3c5 FD:  161 BD:    2 +.+.+.: &type->s_umount_key#18/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25bc924] zone->wait_table + i
 -> [c1cd8790] random_write_wait.lock
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1ccb9d0] percpu_counters_lock
 -> [c1c63550] inode_hash_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c1c9dbe0] (proc_inum_ida).idr.lock
 -> [c1c9db90] proc_inum_lock
 -> [c25c8744] &ei->i_data_sem
 -> [c25c8998] &journal->j_state_lock
 -> [c1ca6398] jbd2_slab_create_mutex
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c25c8954] &(&journal->j_revoke_lock)->rlock
 -> [c1c9c30c] &sb->s_type->i_lock_key#2
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c25bcb30] &(&wb->work_lock)->rlock
 -> [c25bc8c0] (PG_locked)page
 -> [c25d26c4] (complete)&ret.event
 -> [c25d26bc] &x->wait#15
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c25c89b0] &journal->j_checkpoint_mutex
 -> [c1c82a10] kthread_create_lock
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a360] (complete)&done
 -> [c1e0a358] &x->wait
 -> [c25c89d8] &journal->j_wait_done_commit
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c1c827b8] wq_pool_mutex
 -> [c25cf500] &isec->lock
 -> [c1ca5df8] ext4_grpinfo_slab_create_mutex
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25c875c] &ext4_li_mtx
 -> [c25d2da8] &(&k->list_lock)->rlock
 -> [c25d2d9c] &(&idp->lock)->rlock
 -> [c1ccb5f0] simple_ida_lock
 -> [c1c9de78] kernfs_mutex
 -> [c25c89b8] &journal->j_barrier
 -> [c25c89e0] &journal->j_wait_transaction_locked
 -> [c259f24c] &(&base->lock)->rlock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c25c876c FD:    3 BD:    1 +.+...: &(&bgl->locks[i].lock)->rlock
 -> [c25c88c0] &(&sbi->s_bal_lock)->rlock
 -> [c25c88c8] &(&sbi->s_md_lock)->rlock

c1c9e3ec FD:   17 BD:  105 +.+...: &sb->s_type->i_lock_key#16
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25bcc48] &(&lru->node[i].lock)->rlock

c1b7b789 FD:    9 BD:    1 ..-...: fs/file_table.c:262
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1c99840 FD:  207 BD:    1 +.+...: (delayed_fput_work).work
 -> [c1e0a8bc] &rq->lock
 -> [c25c57e4] &bdev->bd_mutex

c25d2d68 FD:   20 BD:   62 +.-...: (&cfqd->idle_slice_timer)
 -> [c25d2718] &(&q->__queue_lock)->rlock

c25c41bc FD:    3 BD:    1 +.+...: (&s->destroy_work)
 -> [c259f150] &rsp->gp_wait
 -> [c1c934f0] pcpu_lock

c259f150 FD:    1 BD:    2 ......: &rsp->gp_wait

c25c878c FD:    5 BD:   63 ++++..: &ei->i_es_lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c8920] &(&sbi->s_es_lock)->rlock
 -> [c25c8910] key#5
 -> [c25c8918] key#6

c25c8744 FD:   76 BD:   62 ++++..: &ei->i_data_sem
 -> [c25c878c] &ei->i_es_lock
 -> [c1e0a8bc] &rq->lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25bc924] zone->wait_table + i
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c25c8794] &(&ei->i_prealloc_lock)->rlock
 -> [c25c879c] &(&ei->i_raw_lock)->rlock
 -> [c25c89a0] &(&journal->j_list_lock)->rlock
 -> [c25c8954] &(&journal->j_revoke_lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c25c8784] &(&(ei->i_block_reservation_lock))->rlock

c25c8920 FD:    1 BD:   64 +.+...: &(&sbi->s_es_lock)->rlock

c25c8998 FD:   31 BD:   16 ++++..: &journal->j_state_lock
 -> [c25c89d8] &journal->j_wait_done_commit
 -> [c25c89d0] &journal->j_wait_commit
 -> [c259f24c] &(&base->lock)->rlock
 -> [c25c8940] &(&transaction->t_handle_lock)->rlock
 -> [c25c89a0] &(&journal->j_list_lock)->rlock
 -> [c25c89e0] &journal->j_wait_transaction_locked

c1ca6398 FD:   71 BD:    3 +.+.+.: jbd2_slab_create_mutex

c25c8954 FD:    1 BD:   63 +.+...: &(&journal->j_revoke_lock)->rlock

c25bcb38 FD:   19 BD:   69 +.+...: &(&wb->list_lock)->rlock
 -> [c1c9c30c] &sb->s_type->i_lock_key#2
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16

c25bcb30 FD:    2 BD:   67 +.....: &(&wb->work_lock)->rlock
 -> [c259f24c] &(&base->lock)->rlock

c25d26c4 FD:    1 BD:    3 +.+...: (complete)&ret.event

c25d26bc FD:    6 BD:   81 ..-...: &x->wait#15
 -> [c1e098e4] &p->pi_lock

c25d2730 FD:   20 BD:    1 +.+...: (&(&q->delay_work)->work)
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbbe0] &(shost->host_lock)->rlock

c25c89b0 FD:   49 BD:    4 +.+...: &journal->j_checkpoint_mutex
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25bc924] zone->wait_table + i
 -> [c1e0a8bc] &rq->lock
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c25c8998] &journal->j_state_lock

c25c89d8 FD:    6 BD:   17 ......: &journal->j_wait_done_commit
 -> [c1e098e4] &p->pi_lock

c25c89d0 FD:    6 BD:   17 ......: &journal->j_wait_commit
 -> [c1e098e4] &p->pi_lock

c1ca5df8 FD:   71 BD:    3 +.+.+.: ext4_grpinfo_slab_create_mutex

c25c875c FD:    1 BD:    4 +.+...: &ext4_li_mtx

c25c89b8 FD:   52 BD:    3 +.+...: &journal->j_barrier
 -> [c25c8998] &journal->j_state_lock
 -> [c25c89a0] &(&journal->j_list_lock)->rlock
 -> [c25c89b0] &journal->j_checkpoint_mutex
 -> [c25c87c4] key
 -> [c25c87bc] key#2
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25bc924] zone->wait_table + i
 -> [c1e0a8bc] &rq->lock
 -> [c25ba114] &(&tsk->delays->lock)->rlock

c25c89a0 FD:   26 BD:   66 +.+...: &(&journal->j_list_lock)->rlock
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c1c9c30c] &sb->s_type->i_lock_key#2
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c25bcb30] &(&wb->work_lock)->rlock

c25c87c4 FD:    1 BD:    5 ......: key

c25c87bc FD:    1 BD:    5 ......: key#2

c25c89e0 FD:    1 BD:   17 ......: &journal->j_wait_transaction_locked

c25c8764 FD:    1 BD:    1 ......: &rs->lock

c1c9e3fc FD:  121 BD:    4 ++++++: &type->i_mutex_dir_key
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25c8744] &ei->i_data_sem
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25bc924] zone->wait_table + i
 -> [c1c63550] inode_hash_lock
 -> [c1e0a8bc] &rq->lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c25cf500] &isec->lock
 -> [c1c99eb8] namespace_sem
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c1c7f3f0] pgd_lock
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25c8948] jbd2_handle

c1cde924 FD:   22 BD:    1 +.+...: &type->s_umount_key#19
 -> [c1c99970] sb_lock
 -> [c25bcc48] &(&lru->node[i].lock)->rlock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c25bcc48 FD:    1 BD:  174 +.+...: &(&lru->node[i].lock)->rlock

c25c45f8 FD:    6 BD:  173 ......: &wq#2
 -> [c1e098e4] &p->pi_lock

c1e09914 FD:  103 BD:    7 ++++++: &mm->mmap_sem
 -> [c25c3ce8] &anon_vma->rwsem
 -> [c1e0980c] &(ptlock_ptr(page))->rlock
 -> [c25c567c] &mapping->i_mmap_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0990c] &(&mm->page_table_lock)->rlock
 -> [c25c873c] &ei->i_mmap_sem
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c25bc8c0] (PG_locked)page
 -> [c25c3cdc] key#3
 -> [c1e09915] &mm->mmap_sem/1
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c25bc924] zone->wait_table + i
 -> [c25ba114] &(&tsk->delays->lock)->rlock

c25c874c FD:    1 BD:   12 .+.+..: &ei->xattr_sem

c1cd8710 FD:    1 BD:    1 ..-...: random_ready_list_lock

c1cd8750 FD:    1 BD:    1 ..-...: urandom_init_wait.lock

c1e0990c FD:    1 BD:   14 +.+...: &(&mm->page_table_lock)->rlock

c25c3ce8 FD:    4 BD:   13 +.+...: &anon_vma->rwsem
 -> [c1e0990c] &(&mm->page_table_lock)->rlock
 -> [c25c3cdc] key#3
 -> [c25bc904] &(&zone->lock)->rlock

c1e0980c FD:   10 BD:   70 +.+...: &(ptlock_ptr(page))->rlock
 -> [c1e0980d] &(ptlock_ptr(page))->rlock/1
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c25bc924] zone->wait_table + i

c1c9d554 FD:    1 BD:    1 .+.+..: entries_lock

c25c3cdc FD:    1 BD:   14 ......: key#3

c25c567c FD:   11 BD:    9 +.+...: &mapping->i_mmap_rwsem
 -> [c25c3ce8] &anon_vma->rwsem
 -> [c1e0a8bc] &rq->lock
 -> [c255ba50] &sem->wait_lock

c25c873c FD:   95 BD:    8 .+.+.+: &ei->i_mmap_sem
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c8744] &ei->i_data_sem
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25bc8c0] (PG_locked)page
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25bc924] zone->wait_table + i
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c1c7f3f0] pgd_lock

c1c82fb8 FD:    1 BD:    1 +.+...: reboot_mutex

c1cd17f0 FD:    1 BD:    1 ......: vt_spawn_con.lock

c25c40d4 FD:    2 BD:    7 +.+...: &(&f->f_lock)->rlock
 -> [c1c99b70] fasync_lock

c1c99b70 FD:    1 BD:    8 +.+...: fasync_lock

c25d60cc FD:  188 BD:    3 ++++++: &tty->ldisc_sem
 -> [c1c972d0] vmap_area_lock
 -> [c1cd0eb0] tty_ldiscs_lock
 -> [c25d6220] &buf->lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25d60dc] &tty->termios_rwsem
 -> [c25d60c4] &tty->write_wait
 -> [c25d60bc] &tty->read_wait
 -> [c25d74d4] &port_lock_key
 -> [c25d609c] &(&tty->flow_lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25d6188] &ldata->atomic_read_lock
 -> [c25d6218] (&buf->work)
 -> [c1e0a1a5] &pool->lock/1
 -> [c1e0a1bc] (complete)&barr->done
 -> [c1e0a1cc] &x->wait#13

c25d608c FD:    1 BD:    1 +.+...: (&tty->SAK_work)

c25d60b4 FD:    1 BD:    1 +.+...: (&tty->hangup_work)

c25d6218 FD:  180 BD:    5 +.+...: (&buf->work)

c25d60f4 FD:    4 BD:    1 +.+...: (&tty->hangup_work)#2
 -> [c25d6094] &(&tty->files_lock)->rlock

c25d6220 FD:  179 BD:    6 +.+...: &buf->lock
 -> [c25d60dc] &tty->termios_rwsem

c25d6240 FD:    1 BD:   12 ......: &port->delta_msr_wait

c25d7508 FD:    1 BD:   12 ......: (&up->timer)

c25d6248 FD:    1 BD:   12 ......: &port->open_wait

c1c9e3d4 FD:  124 BD:    1 .+.+.+: sb_writers#3
 -> [c1e0a8bc] &rq->lock
 -> [c25c8948] jbd2_handle
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c1c9e3fd] &type->i_mutex_dir_key/1
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c1c63550] inode_hash_lock
 -> [c25cf4f0] &(&sbsec->isec_lock)->rlock
 -> [c1c9e3fc] &type->i_mutex_dir_key
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c9e3f4] &sb->s_type->i_mutex_key#8

c1cd0cf0 FD:    1 BD:    1 +.+...: redirect_lock

c25d60ac FD:  179 BD:    1 +.+.+.: &tty->atomic_write_lock
 -> [c25d60dc] &tty->termios_rwsem
 -> [c255ba50] &sem->wait_lock

c25d6180 FD:   69 BD:    9 +.+...: &ldata->output_lock
 -> [c25d74d4] &port_lock_key
 -> [c1c87750] (console_sem).lock
 -> [c1c8772c] console_lock
 -> [c1c87710] logbuf_lock
 -> [c1e0a8bc] &rq->lock

c1dfe42c FD:    1 BD:    7 +.+...: &mm->context.lock

c1e09915 FD:   17 BD:    8 +.+.+.: &mm->mmap_sem/1
 -> [c25c567c] &mapping->i_mmap_rwsem
 -> [c25c3ce8] &anon_vma->rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0990c] &(&mm->page_table_lock)->rlock
 -> [c1e0980c] &(ptlock_ptr(page))->rlock
 -> [c25c3cdc] key#3
 -> [c1e0a8bc] &rq->lock
 -> [c1c7f3f0] pgd_lock

c1e0980d FD:    1 BD:   71 +.+...: &(ptlock_ptr(page))->rlock/1

c25c45e9 FD:   13 BD:    1 +.+.+.: &pipe->mutex/1
 -> [c25c45f0] &pipe->wait
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a8bc] &rq->lock

c25c45f0 FD:    7 BD:    4 ......: &pipe->wait
 -> [c1e098e4] &p->pi_lock
 -> [c25c5980] &(&ep->lock)->rlock

c25d60a4 FD:    1 BD:   77 ......: &(&tty->ctrl_lock)->rlock

c1c81f98 FD:    2 BD:    1 ++++..: uts_sem
 -> [c1c8d154] hostname_poll.wait.lock

c1c9e3f4 FD:  110 BD:    5 +.+.+.: &sb->s_type->i_mutex_key#8
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c8948] jbd2_handle
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25c874c] &ei->xattr_sem
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25bc8c0] (PG_locked)page
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c25bcb30] &(&wb->work_lock)->rlock

c1c9e3fd FD:  111 BD:    2 +.+...: &type->i_mutex_dir_key/1
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c9e3f4] &sb->s_type->i_mutex_key#8
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16

c1c9db24 FD:   21 BD:    1 +.+...: &type->s_umount_key#20
 -> [c1c99970] sb_lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c25d2775 FD:    2 BD:    2 ......: &(&ioc->lock)->rlock/1

c25d276c FD:    3 BD:    1 +.+...: (&ioc->release_work)
 -> [c25d2775] &(&ioc->lock)->rlock/1

c25c5780 FD:   85 BD:    3 +.+.+.: &p->lock
 -> [c1c99eb8] namespace_sem
 -> [c1c99a58] chrdevs_lock
 -> [c1ccaf18] block_class_lock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c1e098a4] &(&sighand->siglock)->rlock
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c25c8090] &of->mutex
 -> [c1c635d0] bdev_lock
 -> [c1c98e70] swap_lock
 -> [c1e0a8bc] &rq->lock

c1cd87d0 FD:    1 BD:   87 -.-...: random_read_wait.lock

c1c9db34 FD:   50 BD:    1 .+.+.+: sb_writers#4
 -> [c1c9db4c] &sb->s_type->i_lock_key#4
 -> [c1c9db54] &sb->s_type->i_mutex_key
 -> [c1c9dc50] sysctl_lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock

c1c9e025 FD:   37 BD:    1 +.+.+.: &type->s_umount_key#21/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1c9de78] kernfs_mutex
 -> [c25cf500] &isec->lock
 -> [c1c9e04c] &sb->s_type->i_lock_key#17
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c9e04c FD:   17 BD:   82 +.+...: &sb->s_type->i_lock_key#17
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c9e05c FD:   43 BD:    1 ++++++: &type->i_mutex_dir_key#2
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c9de78] kernfs_mutex
 -> [c1c99eb8] namespace_sem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c1e098e4] &p->pi_lock

c1cc3084 FD:   21 BD:    1 +.+...: &type->s_umount_key#22
 -> [c1c99970] sb_lock
 -> [c25bcc48] &(&lru->node[i].lock)->rlock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c25c4629 FD:    2 BD:  173 +.+...: &(&dentry->d_lockref.lock)->rlock/1
 -> [c25bcc48] &(&lru->node[i].lock)->rlock

c1c92e74 FD:  115 BD:    2 .+.+.+: sb_writers#5
 -> [c1c92e95] &sb->s_type->i_mutex_key#9/1
 -> [c1c92e94] &sb->s_type->i_mutex_key#10
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c92e8c] &sb->s_type->i_lock_key#5
 -> [c1e0a8bc] &rq->lock
 -> [c255ba50] &sem->wait_lock

c1c92e95 FD:  114 BD:    3 +.+.+.: &sb->s_type->i_mutex_key#9/1
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock
 -> [c1c92e8c] &sb->s_type->i_lock_key#5
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25efa68] &u->readlock
 -> [c1c92e98] &sb->s_type->i_mutex_key#10/4
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c92e94] &sb->s_type->i_mutex_key#10
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25bca84] &(&info->lock)->rlock
 -> [c25cf4f0] &(&sbsec->isec_lock)->rlock
 -> [c255ba50] &sem->wait_lock

c1c92e94 FD:  105 BD:    4 ++++++: &sb->s_type->i_mutex_key#10
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock
 -> [c1c92e8c] &sb->s_type->i_lock_key#5
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25bca7c] &(&xattrs->lock)->rlock
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25bca84] &(&info->lock)->rlock
 -> [c25c566c] &inode->i_size_seqcount
 -> [c25bc8c0] (PG_locked)page
 -> [c1c92e98] &sb->s_type->i_mutex_key#10/4
 -> [c1e0a8bc] &rq->lock
 -> [c255ba50] &sem->wait_lock
 -> [c25b4244] &(kretprobe_table_locks[i].lock)
 -> [c25bc8fc] &(&zone->lru_lock)->rlock

c25bca7c FD:    1 BD:   41 +.+...: &(&xattrs->lock)->rlock

c25bca84 FD:    1 BD:   62 +.+...: &(&info->lock)->rlock

c1c99b14 FD:    1 BD:    1 .+.+..: sb_writers#6

c25c40cc FD:  154 BD:    1 +.+.+.: &f->f_pos_lock
 -> [c1cde934] sb_writers
 -> [c1c92e74] sb_writers#5
 -> [c1e0a8bc] &rq->lock
 -> [c25c5780] &p->lock

c25bca40 FD:    4 BD:    1 +.-...: (&dom->period_timer)
 -> [c25d2d94] key#4
 -> [c25d2d8c] &p->sequence
 -> [c259f24c] &(&base->lock)->rlock

c25d2d94 FD:    1 BD:    2 ..-...: key#4

c25d2d8c FD:    1 BD:    2 ..-...: &p->sequence

c25c6f7c FD:    1 BD:  173 ......: &wq#3

c1d06dd0 FD:    1 BD:    7 +.+...: unix_table_lock

c25efa68 FD:   31 BD:    6 +.+.+.: &u->readlock
 -> [c25bca94] &(&sbinfo->stat_lock)->rlock
 -> [c1c92e8c] &sb->s_type->i_lock_key#5
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1d06dd0] unix_table_lock
 -> [c25efa78] &af_unix_sk_receive_queue_lock_key
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c25efa60] &u->peer_wait

c25efa70 FD:   21 BD:    1 +.+...: &(&u->lock)->rlock
 -> [c25e1728] clock-AF_UNIX
 -> [c25efa71] &(&u->lock)->rlock/1
 -> [c25efa78] &af_unix_sk_receive_queue_lock_key
 -> [c25efa60] &u->peer_wait

c1c82c70 FD:    7 BD:    1 .+.+.+: s_active#18
 -> [c1c9df58] kernfs_open_file_mutex

c1c9df58 FD:    6 BD:   16 +.+...: kernfs_open_file_mutex
 -> [c1c9df90] kernfs_open_node_lock
 -> [c1e0a8bc] &rq->lock

c25c8090 FD:   40 BD:    5 +.+.+.: &of->mutex

c1c92e98 FD:   19 BD:    5 +.+...: &sb->s_type->i_mutex_key#10/4
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c63528] rename_lock

c25c462a FD:    4 BD:  173 +.+...: &(&dentry->d_lockref.lock)->rlock/2
 -> [c25c462b] &(&dentry->d_lockref.lock)->rlock/3

c25c462b FD:    3 BD:  174 +.+...: &(&dentry->d_lockref.lock)->rlock/3
 -> [c25c4620] &dentry->d_seq

c25c4621 FD:    1 BD:  176 +.+...: &dentry->d_seq/1

c25e1728 FD:    1 BD:    2 +.....: clock-AF_UNIX

c25efa60 FD:    6 BD:    8 +.+...: &u->peer_wait
 -> [c1e098e4] &p->pi_lock

c25efa78 FD:    1 BD:    8 +.+...: &af_unix_sk_receive_queue_lock_key

c25efa71 FD:   17 BD:    2 +.+...: &(&u->lock)->rlock/1
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c25e1680 FD:    7 BD:   71 ......: &wq->wait
 -> [c1e098e4] &p->pi_lock
 -> [c25c5980] &(&ep->lock)->rlock

c25c8910 FD:    1 BD:   64 ......: key#5

c25c8918 FD:    1 BD:   64 ......: key#6

c25c5978 FD:   32 BD:    2 +.+.+.: &ep->mtx
 -> [c25e1680] &wq->wait
 -> [c25c40d4] &(&f->f_lock)->rlock
 -> [c25c5980] &(&ep->lock)->rlock
 -> [c25c5834] &group->notification_waitq
 -> [c25c583c] &group->notification_mutex
 -> [c1e0989c] &sighand->signalfd_wqh
 -> [c1e098a4] &(&sighand->siglock)->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25c45f0] &pipe->wait

c25c5980 FD:    6 BD:  106 ......: &(&ep->lock)->rlock
 -> [c1e098e4] &p->pi_lock

c25c5834 FD:    1 BD:    3 ......: &group->notification_waitq

c25c583c FD:    1 BD:    3 +.+...: &group->notification_mutex

c1e0989c FD:    7 BD:   77 ......: &sighand->signalfd_wqh
 -> [c25c5980] &(&ep->lock)->rlock

c1c98e70 FD:    1 BD:    4 +.+...: swap_lock

c25e1888 FD:    1 BD:    2 +.....: slock-AF_UNIX

c25e19e8 FD:    2 BD:    1 +.+...: sk_lock-AF_UNIX
 -> [c25e1888] slock-AF_UNIX

c1c9e034 FD:   42 BD:    1 .+.+.+: sb_writers#7
 -> [c1c9e04c] &sb->s_type->i_lock_key#17
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c25c8090] &of->mutex
 -> [c1e0a8bc] &rq->lock

c25e1d68 FD:    1 BD:   65 ......: &(&net->nsid_lock)->rlock

c25e16dc FD:    1 BD:   65 ..-...: &(&list->lock)->rlock

c25e2a20 FD:    1 BD:    1 ......: &nlk->wait

c1cddce4 FD:   42 BD:    1 .+.+.+: s_active#19
 -> [c1c9df58] kernfs_open_file_mutex
 -> [c1ccb6d8] uevent_sock_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1e098e4] &p->pi_lock
 -> [c25d5adc] &device->physical_node_lock
 -> [c25bc904] &(&zone->lock)->rlock

c25bcb20 FD:    9 BD:    1 ..-...: (&(&wb->dwork)->timer)
 -> [c1e0a1a5] &pool->lock/1

c25bcb48 FD:    1 BD:    1 .+.+.+: "writeback"

c25bcb28 FD:  111 BD:    1 +.+.+.: (&(&wb->dwork)->work)
 -> [c25bcb30] &(&wb->work_lock)->rlock
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c25d2d74] &pl->lock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock

c25d2d74 FD:    2 BD:   70 ..-...: &pl->lock
 -> [c25d2d7c] key#7

c25d2d7c FD:    1 BD:   71 ..-...: key#7

c1cf0bb4 FD:    7 BD:    1 .+.+.+: s_active#20
 -> [c1c9df58] kernfs_open_file_mutex

c1cf0b60 FD:    7 BD:    1 .+.+.+: s_active#21
 -> [c1c9df58] kernfs_open_file_mutex

c1cf0b7c FD:    7 BD:    1 .+.+.+: s_active#22
 -> [c1c9df58] kernfs_open_file_mutex

c1cf0b98 FD:    8 BD:    1 .+.+.+: s_active#23
 -> [c1c9df58] kernfs_open_file_mutex
 -> [c1e098e4] &p->pi_lock
 -> [c1e0a8bc] &rq->lock

c1cf0d04 FD:    7 BD:    1 .+.+.+: s_active#24
 -> [c1c9df58] kernfs_open_file_mutex

c1cf630c FD:    8 BD:    1 .+.+.+: s_active#25
 -> [c1c9df58] kernfs_open_file_mutex
 -> [c1e098e4] &p->pi_lock

c255ba50 FD:    6 BD:   22 ......: &sem->wait_lock
 -> [c1e098e4] &p->pi_lock

c25c40c0 FD:    1 BD:    1 ......: key#8

c25c582c FD:   22 BD:    1 +.+.+.: &group->mark_mutex
 -> [c1c92e8c] &sb->s_type->i_lock_key#5
 -> [c1e0a8bc] &rq->lock
 -> [c25c5944] &(&group->inotify_data.idr_lock)->rlock
 -> [c25c5938] &(&mark->lock)->rlock
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16

c25c5944 FD:    1 BD:    2 +.+...: &(&group->inotify_data.idr_lock)->rlock

c25c5938 FD:   20 BD:    3 +.+...: &(&mark->lock)->rlock
 -> [c1c92e8c] &sb->s_type->i_lock_key#5
 -> [c1cde94c] &sb->s_type->i_lock_key#6
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16

c1ce39ec FD:    7 BD:    1 .+.+.+: s_active#26
 -> [c1e0a8bc] &rq->lock
 -> [c1c9df58] kernfs_open_file_mutex

c1ccc62c FD:    7 BD:    1 .+.+.+: s_active#27
 -> [c1c9df58] kernfs_open_file_mutex

c1ce3a24 FD:    7 BD:    1 .+.+.+: s_active#28
 -> [c1c9df58] kernfs_open_file_mutex

c1ccc610 FD:    7 BD:    1 .+.+.+: s_active#29
 -> [c1c9df58] kernfs_open_file_mutex

c1c9c538 FD:   33 BD:    1 +.+...: epmutex
 -> [c1e0a8bc] &rq->lock
 -> [c25c5978] &ep->mtx

c1ce47f8 FD:   86 BD:    4 +.+.+.: sr_mutex
 -> [c1ce4698] sr_ref_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c25d2824] &ev->block_mutex
 -> [c25d282c] &(&ev->lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25d2788] (complete)&wait
 -> [c25d2780] &x->wait#10
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c1c99970] sb_lock
 -> [c25dbbd8] &shost->host_wait
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25dbbe0] &(shost->host_lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock

c1ce4698 FD:    1 BD:    5 +.+...: sr_ref_mutex

c1cf1a28 FD:    7 BD:    1 .+.+.+: s_active#30
 -> [c1c9df58] kernfs_open_file_mutex

c25d2728 FD:    9 BD:    1 ..-...: (&(&q->delay_work)->timer)
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25c582d FD:   21 BD:    1 +.+...: &group->mark_mutex/1
 -> [c25c5938] &(&mark->lock)->rlock

c1c9c3d0 FD:    1 BD:    1 +.+...: destroy_lock

c259f178 FD:    1 BD:    1 ......: &(&sp->queue_lock)->rlock

c1c9e3c4 FD:  117 BD:    1 ++++.+: &type->s_umount_key#23
 -> [c25bcc48] &(&lru->node[i].lock)->rlock
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c25c8998] &journal->j_state_lock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25c87c4] key
 -> [c25c87bc] key#2
 -> [c1e0a8bc] &rq->lock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25dbfa8] &(&host->lock)->rlock
 -> [c25bc924] zone->wait_table + i
 -> [c25ba114] &(&tsk->delays->lock)->rlock
 -> [c25c875c] &ext4_li_mtx
 -> [c1c635a8] mount_lock
 -> [c25c87a4] &sbi->s_journal_flag_rwsem
 -> [c25bcb38] &(&wb->list_lock)->rlock

c25c8948 FD:  109 BD:   11 +.+...: jbd2_handle
 -> [c25c89a0] &(&journal->j_list_lock)->rlock
 -> [c25c8954] &(&journal->j_revoke_lock)->rlock
 -> [c25c5674] &(&mapping->private_lock)->rlock
 -> [c25c89c8] &journal->j_wait_updates
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c879c] &(&ei->i_raw_lock)->rlock
 -> [c25c8998] &journal->j_state_lock
 -> [c25c87d4] &sbi->s_orphan_lock
 -> [c25c8744] &ei->i_data_sem
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c25c8794] &(&ei->i_prealloc_lock)->rlock
 -> [c25c878c] &ei->i_es_lock
 -> [c25c88d0] &meta_group_info[i]->alloc_sem
 -> [c1c63550] inode_hash_lock
 -> [c25c87e4] &(&sbi->s_next_gen_lock)->rlock
 -> [c25c874c] &ei->xattr_sem
 -> [c1c9e3ec] &sb->s_type->i_lock_key#16
 -> [c25cf500] &isec->lock
 -> [c25bc8c0] (PG_locked)page
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c25c566c] &inode->i_size_seqcount
 -> [c25bc8fc] &(&zone->lru_lock)->rlock

c25c89c8 FD:    1 BD:   12 ......: &journal->j_wait_updates

c1cde958 FD:   19 BD:   38 +.+...: &sb->s_type->i_mutex_key#5/4
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c63528] rename_lock

c1ce3a08 FD:    8 BD:    1 .+.+.+: s_active#31
 -> [c1c9df58] kernfs_open_file_mutex
 -> [c1e098e4] &p->pi_lock

c1ccadfc FD:    7 BD:    1 .+.+.+: s_active#32
 -> [c1c9df58] kernfs_open_file_mutex
 -> [c1e0a8bc] &rq->lock

c25e17a0 FD:    1 BD:    1 +.....: clock-AF_NETLINK

c25d2de5 FD:   15 BD:    1 +.+.+.: (&ht->run_work)
 -> [c25d2df5] &ht->mutex

c25d2df5 FD:   14 BD:    2 +.+.+.: &ht->mutex
 -> [c1e0a8bc] &rq->lock
 -> [c1cd8790] random_write_wait.lock
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c25d2dfd] &(&tbl->locks[i])->rlock
 -> [c25d2ded] &(&ht->lock)->rlock

c25d2dfe FD:    1 BD:    6 +.....: &(&tbl->locks[i])->rlock/1

c25d2ded FD:    1 BD:    3 +.+...: &(&ht->lock)->rlock

c25dbab8 FD:    7 BD:    4 +.+...: &lo->lo_ctl_mutex
 -> [c1e0a8bc] &rq->lock
 -> [c25d2710] &q->mq_freeze_wq

c25d2710 FD:    6 BD:    5 ..-...: &q->mq_freeze_wq
 -> [c1e098e4] &p->pi_lock

c1ccb8f0 FD:    1 BD:    1 ..-...: percpu_ref_switch_waitq.lock

c25e24d4 FD:    9 BD:    1 ..-...: (&(&tbl->gc_work)->timer)
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25e24dc FD:    3 BD:    1 +.+...: (&(&tbl->gc_work)->work)
 -> [c25e24e4] &tbl->lock

c25c879c FD:    1 BD:   63 +.+...: &(&ei->i_raw_lock)->rlock

c25c8988 FD:    6 BD:    1 +.-...: ((&journal->j_commit_timer))
 -> [c1e098e4] &p->pi_lock

c25c8940 FD:    1 BD:   17 +.+...: &(&transaction->t_handle_lock)->rlock

c25c88c8 FD:    1 BD:   64 +.+...: &(&sbi->s_md_lock)->rlock

c25c8990 FD:    1 BD:    1 +.+...: &(&journal->j_history_lock)->rlock

c25c87d4 FD:    1 BD:   12 +.+...: &sbi->s_orphan_lock

c1c9e3e4 FD:  110 BD:    1 .+.+..: sb_internal
 -> [c25c8948] jbd2_handle

c25c8794 FD:    1 BD:   63 +.+...: &(&ei->i_prealloc_lock)->rlock

c25c88c0 FD:    1 BD:   64 +.+...: &(&sbi->s_bal_lock)->rlock

c25c88d0 FD:    1 BD:   12 .+.+..: &meta_group_info[i]->alloc_sem

c25c87e4 FD:    1 BD:   12 +.+...: &(&sbi->s_next_gen_lock)->rlock

c25c8784 FD:    1 BD:   63 +.+...: &(&(ei->i_block_reservation_lock))->rlock

c1c9e0a5 FD:   27 BD:    1 +.+.+.: &type->s_umount_key#24/1
 -> [c1c99970] sb_lock
 -> [c1c90a18] shrinker_rwsem
 -> [c1c9e0cc] &sb->s_type->i_lock_key#18
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock
 -> [c1c9e0d4] &sb->s_type->i_mutex_key#11
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c25cf4f8] &sbsec->lock

c1c9e0cc FD:   17 BD:    3 +.+...: &sb->s_type->i_lock_key#18
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock

c1c9e0d4 FD:   21 BD:    2 +.+.+.: &sb->s_type->i_mutex_key#11
 -> [c25c4628] &(&dentry->d_lockref.lock)->rlock
 -> [c1c9e0cc] &sb->s_type->i_lock_key#18
 -> [c25c41a4] &(&s->s_inode_list_lock)->rlock
 -> [c25cf500] &isec->lock

c255e580 FD:    2 BD:    1 +.+...: &user->lock
 -> [c1c87710] logbuf_lock

c1c9c2e4 FD:   94 BD:    1 .+.+..: &type->s_umount_key#25
 -> [c1c9c30c] &sb->s_type->i_lock_key#2
 -> [c25bc8c0] (PG_locked)page
 -> [c25c5684] &(&mapping->tree_lock)->rlock
 -> [c1e098f4] &(&p->alloc_lock)->rlock
 -> [c25d2718] &(&q->__queue_lock)->rlock
 -> [c25bc8fc] &(&zone->lru_lock)->rlock
 -> [c25bcb38] &(&wb->list_lock)->rlock
 -> [c25bc904] &(&zone->lock)->rlock

c1c8d154 FD:    1 BD:    2 ......: hostname_poll.wait.lock

c25dec04 FD:    1 BD:    1 ......: &(&rtc->irq_lock)->rlock

c25debfc FD:    1 BD:    1 ......: &(&rtc->irq_task_lock)->rlock

c25c5ac0 FD:    2 BD:    1 +.+...: &(&ctx->flc_lock)->rlock
 -> [c1c9c724] file_lock_lglock

c1c9c724 FD:    1 BD:    2 .+.+..: file_lock_lglock

c1e098b4 FD:  142 BD:    1 +.+...: (complete)&vfork
 -> [c1e098bc] &sig->cred_guard_mutex
 -> [c1e0980c] &(ptlock_ptr(page))->rlock
 -> [c1e0a8bc] &rq->lock
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c1c7f3f0] pgd_lock

c1e098ac FD:    6 BD:   73 ......: &x->wait#16
 -> [c1e098e4] &p->pi_lock

c1d047d8 FD:    5 BD:    8 .+.+.+: (inetaddr_chain).rwsem
 -> [c1d04a90] fib_info_lock
 -> [c1c934b8] pcpu_alloc_mutex
 -> [c1d01e50] nl_table_wait.lock

c1d04a90 FD:    1 BD:    9 +.....: fib_info_lock

c25e2148 FD:    1 BD:    8 +.....: _xmit_LOOPBACK

c1d005e4 FD:    1 BD:    1 ......: netpoll_srcu

c25ee798 FD:    1 BD:    8 +.....: &(&in_dev->mc_tomb_lock)->rlock

c1d07090 FD:    2 BD:    8 +.....: addrconf_hash_lock
 -> [c1c934f0] pcpu_lock

c25efb58 FD:    1 BD:    8 +.....: &(&ifa->lock)->rlock

c25efca0 FD:    3 BD:    8 +.....: &tb->tb6_lock
 -> [c1d01e50] nl_table_wait.lock
 -> [c25efcb0] &net->ipv6.fib6_walker_lock

c25efcb0 FD:    1 BD:    9 +.....: &net->ipv6.fib6_walker_lock

c25e1890 FD:    1 BD:    1 +.....: slock-AF_INET

c25e1730 FD:    1 BD:    1 +.....: clock-AF_INET

c1c876d0 FD:    6 BD:    1 -.....: log_wait.lock
 -> [c1e098e4] &p->pi_lock

c25d609c FD:    1 BD:    9 ......: &(&tty->flow_lock)->rlock

c25d6188 FD:  185 BD:    4 +.+...: &ldata->atomic_read_lock
 -> [c25d60dc] &tty->termios_rwsem
 -> [c25d6218] (&buf->work)
 -> [c1e0a8bc] &rq->lock
 -> [c1c7f3f0] pgd_lock
 -> [c25d60bc] &tty->read_wait
 -> [c1e0a1a5] &pool->lock/1
 -> [c1e0a1bc] (complete)&barr->done
 -> [c1e0a1cc] &x->wait#13

c1b8863b FD:    5 BD:    1 +.-...: lib/random32.c:217
 -> [c1cd8790] random_write_wait.lock
 -> [c1cd85c8] nonblocking_pool.lock
 -> [c259f24c] &(&base->lock)->rlock
 -> [c1cd86c8] input_pool.lock

c1b50661 FD:    9 BD:    1 ..-...: arch/x86/kernel/check.c:145
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25c87a4 FD:  110 BD:    3 .+.+.+: &sbi->s_journal_flag_rwsem
 -> [c25bc904] &(&zone->lock)->rlock
 -> [c25c8948] jbd2_handle
 -> [c25d2718] &(&q->__queue_lock)->rlock

c1cd25a0 FD:   69 BD:    1 +.+...: console_work
 -> [c1c87750] (console_sem).lock
 -> [c1c8772c] console_lock
 -> [c1c87710] logbuf_lock
 -> [c1e0a8bc] &rq->lock

c25d60e4 FD:    1 BD:    9 +.+...: &tty->throttle_mutex

c25d60d4 FD:    1 BD:    1 +.+...: &tty->winsize_mutex

c1c003d1 FD:    9 BD:    1 ..-...: net/wireless/reg.c:212
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1d0c440 FD:  108 BD:    1 +.+...: (reg_check_chans).work
 -> [c1cff938] rtnl_mutex

c1bfe51f FD:    9 BD:    1 ..-...: net/ipv6/addrconf.c:150
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1bfc462 FD:    9 BD:    1 ..-...: net/ipv4/devinet.c:438
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1e0a19c FD:   10 BD:    1 +.-...: (&pool->idle_timer)
 -> [c1e0a1a5] &pool->lock/1
 -> [c1e0a1a4] &(&pool->lock)->rlock

c25c87dc FD:    2 BD:    1 +.-...: ((&sbi->s_err_report))
 -> [c259f24c] &(&base->lock)->rlock

c25e28ec FD:    2 BD:    1 +.-...: ((&fc->rnd_timer))
 -> [c259f24c] &(&base->lock)->rlock

c1b956b4 FD:    9 BD:   12 +.-...: drivers/tty/vt/vt.c:231
 -> [c1e0a1a4] &(&pool->lock)->rlock

c1cd1250 FD:    1 BD:   12 ......: vt_event_lock

c1b95555 FD:    1 BD:   12 +.-...: drivers/tty/vt/keyboard.c:252

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2016-12-09  5:12 ` [PATCH v4 15/15] lockdep: Crossrelease feature documentation Byungchul Park
@ 2017-01-10 20:08   ` Peter Zijlstra
  2017-01-11  1:29     ` Byungchul Park
  2017-01-18  6:42   ` Boqun Feng
  1 sibling, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-10 20:08 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin


First off my sincere apologies for being so horribly slow with this :/

I did spend some time thinking about this thing during the Christmas
holidays, but have not yet managed to write a coherent text on it like I
promised I'd do.

That said; I think I now mostly understand what and why.

But I still feel this document is very hard to read and presents things
backwards.

> +Let's take a look at more complicated example.
> +
> +   TASK X			   TASK Y
> +   ------			   ------
> +   acquire B
> +
> +   release B
> +
> +   acquire C
> +
> +   release C
> +   (1)
> +   fork Y
> +				   acquire AX
> +   acquire D
> +   /* A dependency 'AX -> D' exists */
> +				   acquire F
> +   release D
> +				   acquire G
> +				   /* A dependency 'F -> G' exists */
> +   acquire E
> +   /* A dependency 'AX -> E' exists */
> +				   acquire H
> +				   /* A dependency 'G -> H' exists */
> +   release E
> +				   release H
> +   release AX held by Y
> +				   release G
> +
> +				   release F
> +
> +   where AX, B, C,..., H are different lock classes, and a suffix 'X' is
> +   added on crosslocks.
> +
> +Does a dependency 'AX -> B' exist? Nope.

I think the above without the "fork Y" line is a much more interesting
example, because then the answer becomes: maybe.

This all boils down to the asynchonous nature of the primitive. There is
no well defined point other than what is observed (as I think you tried
to point out in our earlier exchanges).

The "acquire AX" point is entirely random wrt any action in other
threads, _however_ the time between "acquire" and "release" of any
'lock' is the only time we can be certain of things.

> +==============
> +Implementation
> +==============
> +
> +Data structures
> +---------------
> +
> +Crossrelease feature introduces two main data structures.
> +
> +1. pend_lock

I'm not sure 'pending' is the right name here, but I'll consider that
more when I review the code patches.

> +
> +   This is an array embedded in task_struct, for keeping locks queued so
> +   that real dependencies can be added using them at commit step. Since
> +   it's local data, it can be accessed locklessly in the owner context.
> +   The array is filled at acquire step and consumed at commit step. And
> +   it's managed in circular manner.
> +
> +2. cross_lock
> +
> +   This is a global linked list, for keeping all crosslocks in progress.
> +   The list grows at acquire step and is shrunk at release step.

FWIW, this is a perfect example of why I say the document is written
backwards. At this point there is no demonstrated need or use for this
list.

> +
> +CONCLUSION
> +
> +Crossrelease feature introduces two main data structures.
> +
> +1. A pend_lock array for queueing typical locks in circular manner.
> +2. A cross_lock linked list for managing crosslocks in progress.
> +
> +
> +How crossrelease works
> +----------------------
> +
> +Let's take a look at how crossrelease feature works step by step,
> +starting from how lockdep works without crossrelease feaure.
> +

> +
> +Let's look at how commit works for crosslocks.
> +
> +   AX's RELEASE CONTEXT		   AX's ACQUIRE CONTEXT
> +   --------------------		   --------------------
> +				   acquire AX
> +				   /*
> +				    * 1. Mark AX as started
> +				    *
> +				    * (No queuing for crosslocks)
> +				    *
> +				    * In pend_lock: Empty
> +				    * In graph: Empty
> +				    */
> +
> +   (serialized by some means e.g. barrier)
> +
> +   acquire D
> +   /*
> +    * (No marking for typical locks)
> +    *
> +    * 1. Queue D
> +    *
> +    * In pend_lock: D
> +    * In graph: Empty
> +    */
> +				   acquire B
> +				   /*
> +				    * (No marking for typical locks)
> +				    *
> +				    * 1. Queue B
> +				    *
> +				    * In pend_lock: B
> +				    * In graph: Empty
> +				    */
> +   release D
> +   /*
> +    * (No commit for typical locks)
> +    *
> +    * In pend_lock: D
> +    * In graph: Empty
> +    */
> +				   acquire C
> +				   /*
> +				    * (No marking for typical locks)
> +				    *
> +				    * 1. Add 'B -> C' of TT type
> +				    * 2. Queue C
> +				    *
> +				    * In pend_lock: B, C
> +				    * In graph: 'B -> C'
> +				    */
> +   acquire E
> +   /*
> +    * (No marking for typical locks)
> +    *
> +    * 1. Queue E
> +    *
> +    * In pend_lock: D, E
> +    * In graph: 'B -> C'
> +    */
> +				   acquire D
> +				   /*
> +				    * (No marking for typical locks)
> +				    *
> +				    * 1. Add 'C -> D' of TT type
> +				    * 2. Queue D
> +				    *
> +				    * In pend_lock: B, C, D
> +				    * In graph: 'B -> C', 'C -> D'
> +				    */
> +   release E
> +   /*
> +    * (No commit for typical locks)
> +    *
> +    * In pend_lock: D, E
> +    * In graph: 'B -> C', 'C -> D'
> +    */
> +				   release D
> +				   /*
> +				    * (No commit for typical locks)
> +				    *
> +				    * In pend_lock: B, C, D
> +				    * In graph: 'B -> C', 'C -> D'
> +				    */
> +   release AX
> +   /*
> +    * 1. Commit AX (= Add 'AX -> ?')
> +    *   a. What queued since AX was marked: D, E
> +    *   b. Add 'AX -> D' of CT type
> +    *   c. Add 'AX -> E' of CT type

OK, so commit adds multiple dependencies, that makes more sense.
Previously I understood commit to only add a single dependency, which
does not make sense (except in the special case where there is but one).

I dislike how I have to reconstruct this from an example instead of
first having had the rules stated though.

> +    *
> +    * In pend_lock: D, E
> +    * In graph: 'B -> C', 'C -> D',
> +    *           'AX -> D', 'AX -> E'
> +    */

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 04/15] lockdep: Add a function building a chain between two classes
  2016-12-09  5:12 ` [PATCH v4 04/15] lockdep: Add a function building a chain between two classes Byungchul Park
@ 2017-01-10 21:00   ` Peter Zijlstra
  2017-01-12  1:41     ` Byungchul Park
  0 siblings, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-10 21:00 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Fri, Dec 09, 2016 at 02:12:00PM +0900, Byungchul Park wrote:
> add_chain_cache() should be used in the context where the hlock is
> owned since it might be racy in another context. However crossrelease
> feature needs to build a chain between two locks regardless of context.
> So introduce a new function making it possible.
> 
> Signed-off-by: Byungchul Park <byungchul.park@lge.com>
> ---
>  kernel/locking/lockdep.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 56 insertions(+)
> 
> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index 5df56aa..111839f 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c
> @@ -2105,6 +2105,62 @@ static int check_no_collision(struct task_struct *curr,
>  	return 1;
>  }
>  
> +/*
> + * This is for building a chain between just two different classes,
> + * instead of adding a new hlock upon current, which is done by
> + * add_chain_cache().
> + *
> + * This can be called in any context with two classes, while
> + * add_chain_cache() must be done within the lock owener's context
> + * since it uses hlock which might be racy in another context.
> + */
> +static inline int add_chain_cache_classes(unsigned int prev,
> +					  unsigned int next,
> +					  unsigned int irq_context,
> +					  u64 chain_key)
> +{
> +	struct hlist_head *hash_head = chainhashentry(chain_key);
> +	struct lock_chain *chain;
> +
> +	/*
> +	 * Allocate a new chain entry from the static array, and add
> +	 * it to the hash:
> +	 */
> +
> +	/*
> +	 * We might need to take the graph lock, ensure we've got IRQs
> +	 * disabled to make this an IRQ-safe lock.. for recursion reasons
> +	 * lockdep won't complain about its own locking errors.
> +	 */
> +	if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
> +		return 0;
> +
> +	if (unlikely(nr_lock_chains >= MAX_LOCKDEP_CHAINS)) {
> +		if (!debug_locks_off_graph_unlock())
> +			return 0;
> +
> +		print_lockdep_off("BUG: MAX_LOCKDEP_CHAINS too low!");
> +		dump_stack();
> +		return 0;
> +	}
> +
> +	chain = lock_chains + nr_lock_chains++;
> +	chain->chain_key = chain_key;
> +	chain->irq_context = irq_context;
> +	chain->depth = 2;
> +	if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) {
> +		chain->base = nr_chain_hlocks;
> +		nr_chain_hlocks += chain->depth;
> +		chain_hlocks[chain->base] = prev - 1;
> +		chain_hlocks[chain->base + 1] = next -1;
> +	}

You didn't copy this part right. There is no error when >
MAX_LOCKDEP_CHAIN_HLOCKS.


> +	hlist_add_head_rcu(&chain->entry, hash_head);
> +	debug_atomic_inc(chain_lookup_misses);
> +	inc_chains();
> +
> +	return 1;
> +}
> +
>  static inline int add_chain_cache(struct task_struct *curr,
>  				  struct held_lock *hlock,
>  				  u64 chain_key)
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-10 20:08   ` Peter Zijlstra
@ 2017-01-11  1:29     ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-11  1:29 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Jan 10, 2017 at 09:08:50PM +0100, Peter Zijlstra wrote:
> But I still feel this document is very hard to read and presents things
> backwards.

I admit it. I think I need to modify the document more.. I will try it.

> 
> > +Let's take a look at more complicated example.
> > +
> > +   TASK X			   TASK Y
> > +   ------			   ------
> > +   acquire B
> > +
> > +   release B
> > +
> > +   acquire C
> > +
> > +   release C
> > +   (1)
> > +   fork Y
> > +				   acquire AX
> > +   acquire D
> > +   /* A dependency 'AX -> D' exists */
> > +				   acquire F
> > +   release D
> > +				   acquire G
> > +				   /* A dependency 'F -> G' exists */
> > +   acquire E
> > +   /* A dependency 'AX -> E' exists */
> > +				   acquire H
> > +				   /* A dependency 'G -> H' exists */
> > +   release E
> > +				   release H
> > +   release AX held by Y
> > +				   release G
> > +
> > +				   release F
> > +
> > +   where AX, B, C,..., H are different lock classes, and a suffix 'X' is
> > +   added on crosslocks.
> > +
> > +Does a dependency 'AX -> B' exist? Nope.
> 
> I think the above without the "fork Y" line is a much more interesting
> example, because then the answer becomes: maybe.

Sure. The dependency 'AX -> B' might exist in that case. Then we can
add the dependency once we detect it, in other words, once we prove it's
a true dependency. But we cannot add it before we prove it, though it
might be a true one, because it might not be a true one.

> This all boils down to the asynchonous nature of the primitive. There is
> no well defined point other than what is observed (as I think you tried
> to point out in our earlier exchanges).

Exactly.

> The "acquire AX" point is entirely random wrt any action in other
> threads, _however_ the time between "acquire" and "release" of any
> 'lock' is the only time we can be certain of things.
> 
> > +==============
> > +Implementation
> > +==============
> > +
> > +Data structures
> > +---------------
> > +
> > +Crossrelease feature introduces two main data structures.
> > +
> > +1. pend_lock
> 
> I'm not sure 'pending' is the right name here, but I'll consider that
> more when I review the code patches.

Thank you.

> > +
> > +   This is an array embedded in task_struct, for keeping locks queued so
> > +   that real dependencies can be added using them at commit step. Since
> > +   it's local data, it can be accessed locklessly in the owner context.
> > +   The array is filled at acquire step and consumed at commit step. And
> > +   it's managed in circular manner.
> > +
> > +2. cross_lock
> > +
> > +   This is a global linked list, for keeping all crosslocks in progress.
> > +   The list grows at acquire step and is shrunk at release step.
> 
> FWIW, this is a perfect example of why I say the document is written
> backwards. At this point there is no demonstrated need or use for this
> list.

I will consider that more.

> OK, so commit adds multiple dependencies, that makes more sense.
> Previously I understood commit to only add a single dependency, which
> does not make sense (except in the special case where there is but one).
> 
> I dislike how I have to reconstruct this from an example instead of
> first having had the rules stated though.

So do I.

> 
> > +    *
> > +    * In pend_lock: D, E
> > +    * In graph: 'B -> C', 'C -> D',
> > +    *           'AX -> D', 'AX -> E'
> > +    */

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 04/15] lockdep: Add a function building a chain between two classes
  2017-01-10 21:00   ` Peter Zijlstra
@ 2017-01-12  1:41     ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-12  1:41 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Jan 10, 2017 at 10:00:38PM +0100, Peter Zijlstra wrote:
> > +static inline int add_chain_cache_classes(unsigned int prev,
> > +					  unsigned int next,
> > +					  unsigned int irq_context,
> > +					  u64 chain_key)
> > +{
> > +	struct hlist_head *hash_head = chainhashentry(chain_key);
> > +	struct lock_chain *chain;
> > +
> > +	/*
> > +	 * Allocate a new chain entry from the static array, and add
> > +	 * it to the hash:
> > +	 */
> > +
> > +	/*
> > +	 * We might need to take the graph lock, ensure we've got IRQs
> > +	 * disabled to make this an IRQ-safe lock.. for recursion reasons
> > +	 * lockdep won't complain about its own locking errors.
> > +	 */
> > +	if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
> > +		return 0;
> > +
> > +	if (unlikely(nr_lock_chains >= MAX_LOCKDEP_CHAINS)) {
> > +		if (!debug_locks_off_graph_unlock())
> > +			return 0;
> > +
> > +		print_lockdep_off("BUG: MAX_LOCKDEP_CHAINS too low!");
> > +		dump_stack();
> > +		return 0;
> > +	}
> > +
> > +	chain = lock_chains + nr_lock_chains++;
> > +	chain->chain_key = chain_key;
> > +	chain->irq_context = irq_context;
> > +	chain->depth = 2;
> > +	if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) {
> > +		chain->base = nr_chain_hlocks;
> > +		nr_chain_hlocks += chain->depth;
> > +		chain_hlocks[chain->base] = prev - 1;
> > +		chain_hlocks[chain->base + 1] = next -1;
> > +	}
> 
> You didn't copy this part right. There is no error when >
> MAX_LOCKDEP_CHAIN_HLOCKS.

Oh my god! I am sorry. I missed it.

Thank you,
Byungchul

> 
> 
> > +	hlist_add_head_rcu(&chain->entry, hash_head);
> > +	debug_atomic_inc(chain_lookup_misses);
> > +	inc_chains();
> > +
> > +	return 1;
> > +}
> > +
> >  static inline int add_chain_cache(struct task_struct *curr,
> >  				  struct held_lock *hlock,
> >  				  u64 chain_key)
> > -- 
> > 1.9.1
> > 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2016-12-09  5:12 ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
@ 2017-01-12 16:16   ` Peter Zijlstra
  2017-01-13  2:45     ` Byungchul Park
                       ` (2 more replies)
  0 siblings, 3 replies; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-12 16:16 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Fri, Dec 09, 2016 at 02:12:01PM +0900, Byungchul Park wrote:
> check_prev_add() saves a stack trace of the current. But crossrelease
> feature needs to use a separate stack trace of another context in
> check_prev_add(). So make it use a separate stack trace instead of one
> of the current.
> 

So I was thinking, can't we make check_prevs_add() create the stack
trace unconditionally but record if we used it or not, and then return
the entries when unused. All that is serialized by graph_lock anyway and
that way we already pass a stack into check_prev_add() so we can easily
pass in a different one.

I think that removes a bunch of tricky and avoids all the new tricky.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 06/15] lockdep: Make save_trace can skip stack tracing of the current
  2016-12-09  5:12 ` [PATCH v4 06/15] lockdep: Make save_trace can skip stack tracing of the current Byungchul Park
@ 2017-01-12 16:37   ` Peter Zijlstra
  2017-01-13  0:18     ` Byungchul Park
  0 siblings, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-12 16:37 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Fri, Dec 09, 2016 at 02:12:02PM +0900, Byungchul Park wrote:
> Currently, save_trace() always performs save_stack_trace() for the
> current. However, crossrelease needs to use stack trace data of another
> context instead of the current. So add a parameter for skipping stack
> tracing of the current and make it use trace data, which is already
> saved by crossrelease framework.
> 
> Signed-off-by: Byungchul Park <byungchul.park@lge.com>
> ---
>  kernel/locking/lockdep.c | 33 ++++++++++++++++++++-------------
>  1 file changed, 20 insertions(+), 13 deletions(-)
> 
> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index 3eaa11c..11580ec 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c
> @@ -387,15 +387,22 @@ static void print_lockdep_off(const char *bug_msg)
>  #endif
>  }
>  
> -static int save_trace(struct stack_trace *trace)
> +static int save_trace(struct stack_trace *trace, int skip_tracing)
>  {
> -	trace->nr_entries = 0;
> -	trace->max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
> -	trace->entries = stack_trace + nr_stack_trace_entries;
> +	unsigned int nr_avail = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
>  
> -	trace->skip = 3;
> -
> -	save_stack_trace(trace);
> +	if (skip_tracing) {
> +		trace->nr_entries = min(trace->nr_entries, nr_avail);
> +		memcpy(stack_trace + nr_stack_trace_entries, trace->entries,
> +				trace->nr_entries * sizeof(trace->entries[0]));
> +		trace->entries = stack_trace + nr_stack_trace_entries;
> +	} else {
> +		trace->nr_entries = 0;
> +		trace->max_entries = nr_avail;
> +		trace->entries = stack_trace + nr_stack_trace_entries;
> +		trace->skip = 3;
> +		save_stack_trace(trace);
> +	}
>  
>  	/*
>  	 * Some daft arches put -1 at the end to indicate its a full trace.

That's pretty nasty semantics.. so when skip_tracing it modifies trace
in-place.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 06/15] lockdep: Make save_trace can skip stack tracing of the current
  2017-01-12 16:37   ` Peter Zijlstra
@ 2017-01-13  0:18     ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-13  0:18 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Thu, Jan 12, 2017 at 05:37:57PM +0100, Peter Zijlstra wrote:
> On Fri, Dec 09, 2016 at 02:12:02PM +0900, Byungchul Park wrote:
> > Currently, save_trace() always performs save_stack_trace() for the
> > current. However, crossrelease needs to use stack trace data of another
> > context instead of the current. So add a parameter for skipping stack
> > tracing of the current and make it use trace data, which is already
> > saved by crossrelease framework.
> > 
> > Signed-off-by: Byungchul Park <byungchul.park@lge.com>
> > ---
> >  kernel/locking/lockdep.c | 33 ++++++++++++++++++++-------------
> >  1 file changed, 20 insertions(+), 13 deletions(-)
> > 
> > diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> > index 3eaa11c..11580ec 100644
> > --- a/kernel/locking/lockdep.c
> > +++ b/kernel/locking/lockdep.c
> > @@ -387,15 +387,22 @@ static void print_lockdep_off(const char *bug_msg)
> >  #endif
> >  }
> >  
> > -static int save_trace(struct stack_trace *trace)
> > +static int save_trace(struct stack_trace *trace, int skip_tracing)
> >  {
> > -	trace->nr_entries = 0;
> > -	trace->max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
> > -	trace->entries = stack_trace + nr_stack_trace_entries;
> > +	unsigned int nr_avail = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
> >  
> > -	trace->skip = 3;
> > -
> > -	save_stack_trace(trace);
> > +	if (skip_tracing) {
> > +		trace->nr_entries = min(trace->nr_entries, nr_avail);
> > +		memcpy(stack_trace + nr_stack_trace_entries, trace->entries,
> > +				trace->nr_entries * sizeof(trace->entries[0]));
> > +		trace->entries = stack_trace + nr_stack_trace_entries;
> > +	} else {
> > +		trace->nr_entries = 0;
> > +		trace->max_entries = nr_avail;
> > +		trace->entries = stack_trace + nr_stack_trace_entries;
> > +		trace->skip = 3;
> > +		save_stack_trace(trace);
> > +	}
> >  
> >  	/*
> >  	 * Some daft arches put -1 at the end to indicate its a full trace.
> 
> That's pretty nasty semantics.. so when skip_tracing it modifies trace
> in-place.

I agree. Let me think more and enhance it.

Thank you,
Byungchul

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2017-01-12 16:16   ` Peter Zijlstra
@ 2017-01-13  2:45     ` Byungchul Park
  2017-01-13  4:09     ` [PATCH] lockdep: Make a stack_trace instance passed to check_prev_add as an arg Byungchul Park
  2017-01-13 10:11     ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
  2 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-13  2:45 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Thu, Jan 12, 2017 at 05:16:43PM +0100, Peter Zijlstra wrote:
> On Fri, Dec 09, 2016 at 02:12:01PM +0900, Byungchul Park wrote:
> > check_prev_add() saves a stack trace of the current. But crossrelease
> > feature needs to use a separate stack trace of another context in
> > check_prev_add(). So make it use a separate stack trace instead of one
> > of the current.
> > 
> 
> So I was thinking, can't we make check_prevs_add() create the stack
> trace unconditionally but record if we used it or not, and then return
> the entries when unused. All that is serialized by graph_lock anyway and
> that way we already pass a stack into check_prev_add() so we can easily
> pass in a different one.
> 
> I think that removes a bunch of tricky and avoids all the new tricky.

Looks very good.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [PATCH] lockdep: Make a stack_trace instance passed to check_prev_add as an arg
  2017-01-12 16:16   ` Peter Zijlstra
  2017-01-13  2:45     ` Byungchul Park
@ 2017-01-13  4:09     ` Byungchul Park
  2017-01-13  4:38       ` Byungchul Park
  2017-01-13 10:11     ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
  2 siblings, 1 reply; 55+ messages in thread
From: Byungchul Park @ 2017-01-13  4:09 UTC (permalink / raw)
  To: peterz, mingo; +Cc: linux-kernel

Like this. Right?

----->8-----
>From c6173f29ff9bf801649f3cbeb80a914fdf1b998b Mon Sep 17 00:00:00 2001
From: Byungchul Park <byungchul.park@lge.com>
Date: Fri, 13 Jan 2017 12:02:02 +0900
Subject: [PATCH] lockdep: Make a stack_trace instance passed to check_prev_add
 as an arg

Saving stack_trace within check_prev_add needs many tricky codes. To
avoid these, this patch makes the stack_trace instance created out of
check_prev_add, but by caller and passed as an argument.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 30 +++++++++---------------------
 1 file changed, 9 insertions(+), 21 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2081c31..049fc71 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1797,20 +1797,13 @@ static inline void inc_chains(void)
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance, int *stack_saved)
+	       struct held_lock *next, int distance,
+	       struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	int ret;
 	struct lock_list this;
 	struct lock_list *uninitialized_var(target_entry);
-	/*
-	 * Static variable, serialized by the graph_lock().
-	 *
-	 * We use this static variable to save the stack trace in case
-	 * we call into this function multiple times due to encountering
-	 * trylocks in the held lock stack.
-	 */
-	static struct stack_trace trace;
 
 	/*
 	 * Prove that the new <prev> -> <next> dependency would not
@@ -1858,26 +1851,20 @@ static inline void inc_chains(void)
 		}
 	}
 
-	if (!*stack_saved) {
-		if (!save_trace(&trace))
-			return 0;
-		*stack_saved = 1;
-	}
-
 	/*
 	 * Ok, all validations passed, add the new lock
 	 * to the previous lock's dependency list:
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, trace);
 	if (!ret)
 		return 0;
 
@@ -1885,8 +1872,6 @@ static inline void inc_chains(void)
 	 * Debugging printouts:
 	 */
 	if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) {
-		/* We drop graph lock, so another thread can overwrite trace. */
-		*stack_saved = 0;
 		graph_unlock();
 		printk("\n new dependency: ");
 		print_lock_name(hlock_class(prev));
@@ -1909,8 +1894,8 @@ static inline void inc_chains(void)
 check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
 	int depth = curr->lockdep_depth;
-	int stack_saved = 0;
 	struct held_lock *hlock;
+	struct stack_trace trace;
 
 	/*
 	 * Debugging checks.
@@ -1927,6 +1912,9 @@ static inline void inc_chains(void)
 			curr->held_locks[depth-1].irq_context)
 		goto out_bug;
 
+	if (!save_trace(&trace))
+		return 0;
+
 	for (;;) {
 		int distance = curr->lockdep_depth - depth + 1;
 		hlock = curr->held_locks + depth - 1;
@@ -1936,7 +1924,7 @@ static inline void inc_chains(void)
 		 */
 		if (hlock->read != 2 && hlock->check) {
 			if (!check_prev_add(curr, hlock, next,
-						distance, &stack_saved))
+						distance, &trace))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [PATCH] lockdep: Make a stack_trace instance passed to check_prev_add as an arg
  2017-01-13  4:09     ` [PATCH] lockdep: Make a stack_trace instance passed to check_prev_add as an arg Byungchul Park
@ 2017-01-13  4:38       ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-13  4:38 UTC (permalink / raw)
  To: peterz, mingo; +Cc: linux-kernel

On Fri, Jan 13, 2017 at 01:09:41PM +0900, Byungchul Park wrote:
> Like this. Right?

Ignore this. We need to make save_trace work in different way first.

> 
> ----->8-----
> >From c6173f29ff9bf801649f3cbeb80a914fdf1b998b Mon Sep 17 00:00:00 2001
> From: Byungchul Park <byungchul.park@lge.com>
> Date: Fri, 13 Jan 2017 12:02:02 +0900
> Subject: [PATCH] lockdep: Make a stack_trace instance passed to check_prev_add
>  as an arg
> 
> Saving stack_trace within check_prev_add needs many tricky codes. To
> avoid these, this patch makes the stack_trace instance created out of
> check_prev_add, but by caller and passed as an argument.
> 
> Signed-off-by: Byungchul Park <byungchul.park@lge.com>
> ---
>  kernel/locking/lockdep.c | 30 +++++++++---------------------
>  1 file changed, 9 insertions(+), 21 deletions(-)
> 
> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index 2081c31..049fc71 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c
> @@ -1797,20 +1797,13 @@ static inline void inc_chains(void)
>   */
>  static int
>  check_prev_add(struct task_struct *curr, struct held_lock *prev,
> -	       struct held_lock *next, int distance, int *stack_saved)
> +	       struct held_lock *next, int distance,
> +	       struct stack_trace *trace)
>  {
>  	struct lock_list *entry;
>  	int ret;
>  	struct lock_list this;
>  	struct lock_list *uninitialized_var(target_entry);
> -	/*
> -	 * Static variable, serialized by the graph_lock().
> -	 *
> -	 * We use this static variable to save the stack trace in case
> -	 * we call into this function multiple times due to encountering
> -	 * trylocks in the held lock stack.
> -	 */
> -	static struct stack_trace trace;
>  
>  	/*
>  	 * Prove that the new <prev> -> <next> dependency would not
> @@ -1858,26 +1851,20 @@ static inline void inc_chains(void)
>  		}
>  	}
>  
> -	if (!*stack_saved) {
> -		if (!save_trace(&trace))
> -			return 0;
> -		*stack_saved = 1;
> -	}
> -
>  	/*
>  	 * Ok, all validations passed, add the new lock
>  	 * to the previous lock's dependency list:
>  	 */
>  	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
>  			       &hlock_class(prev)->locks_after,
> -			       next->acquire_ip, distance, &trace);
> +			       next->acquire_ip, distance, trace);
>  
>  	if (!ret)
>  		return 0;
>  
>  	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
>  			       &hlock_class(next)->locks_before,
> -			       next->acquire_ip, distance, &trace);
> +			       next->acquire_ip, distance, trace);
>  	if (!ret)
>  		return 0;
>  
> @@ -1885,8 +1872,6 @@ static inline void inc_chains(void)
>  	 * Debugging printouts:
>  	 */
>  	if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) {
> -		/* We drop graph lock, so another thread can overwrite trace. */
> -		*stack_saved = 0;
>  		graph_unlock();
>  		printk("\n new dependency: ");
>  		print_lock_name(hlock_class(prev));
> @@ -1909,8 +1894,8 @@ static inline void inc_chains(void)
>  check_prevs_add(struct task_struct *curr, struct held_lock *next)
>  {
>  	int depth = curr->lockdep_depth;
> -	int stack_saved = 0;
>  	struct held_lock *hlock;
> +	struct stack_trace trace;
>  
>  	/*
>  	 * Debugging checks.
> @@ -1927,6 +1912,9 @@ static inline void inc_chains(void)
>  			curr->held_locks[depth-1].irq_context)
>  		goto out_bug;
>  
> +	if (!save_trace(&trace))
> +		return 0;
> +
>  	for (;;) {
>  		int distance = curr->lockdep_depth - depth + 1;
>  		hlock = curr->held_locks + depth - 1;
> @@ -1936,7 +1924,7 @@ static inline void inc_chains(void)
>  		 */
>  		if (hlock->read != 2 && hlock->check) {
>  			if (!check_prev_add(curr, hlock, next,
> -						distance, &stack_saved))
> +						distance, &trace))
>  				return 0;
>  			/*
>  			 * Stop after the first non-trylock entry,
> -- 
> 1.9.1

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2016-12-09  5:12 ` [PATCH v4 07/15] lockdep: Implement crossrelease feature Byungchul Park
@ 2017-01-13  4:39   ` Lai Jiangshan
  2017-01-13  5:02     ` Byungchul Park
  2017-01-16 15:10   ` Peter Zijlstra
  2017-01-16 15:13   ` Peter Zijlstra
  2 siblings, 1 reply; 55+ messages in thread
From: Lai Jiangshan @ 2017-01-13  4:39 UTC (permalink / raw)
  To: Byungchul Park
  Cc: Peter Zijlstra, Ingo Molnar, Thomas Gleixner, walken, Boqun Feng,
	kirill, LKML, linux-mm, iamjoonsoo.kim, akpm, npiggin

> +
> +/*
> + * No contention. Irq disable is only required.
> + */
> +static int same_context_plock(struct pend_lock *plock)
> +{
> +       struct task_struct *curr = current;
> +       int cpu = smp_processor_id();
> +
> +       /* In the case of hardirq context */
> +       if (curr->hardirq_context) {
> +               if (plock->hardirq_id != per_cpu(hardirq_id, cpu) ||
> +                   plock->hardirq_context != curr->hardirq_context)
> +                       return 0;
> +       /* In the case of softriq context */
> +       } else if (curr->softirq_context) {
> +               if (plock->softirq_id != per_cpu(softirq_id, cpu) ||
> +                   plock->softirq_context != curr->softirq_context)
> +                       return 0;
> +       /* In the case of process context */
> +       } else {
> +               if (plock->hardirq_context != 0 ||
> +                   plock->softirq_context != 0)
> +                       return 0;
> +       }
> +       return 1;
> +}
>

I have not read the code yet...
but different work functions in workqueues are different "contexts" IMO,
does commit operation work well in work functions?

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-13  4:39   ` Lai Jiangshan
@ 2017-01-13  5:02     ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-13  5:02 UTC (permalink / raw)
  To: Lai Jiangshan
  Cc: Peter Zijlstra, Ingo Molnar, Thomas Gleixner, walken, Boqun Feng,
	kirill, LKML, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Fri, Jan 13, 2017 at 12:39:04PM +0800, Lai Jiangshan wrote:
> > +
> > +/*
> > + * No contention. Irq disable is only required.
> > + */
> > +static int same_context_plock(struct pend_lock *plock)
> > +{
> > +       struct task_struct *curr = current;
> > +       int cpu = smp_processor_id();
> > +
> > +       /* In the case of hardirq context */
> > +       if (curr->hardirq_context) {
> > +               if (plock->hardirq_id != per_cpu(hardirq_id, cpu) ||
> > +                   plock->hardirq_context != curr->hardirq_context)
> > +                       return 0;
> > +       /* In the case of softriq context */
> > +       } else if (curr->softirq_context) {
> > +               if (plock->softirq_id != per_cpu(softirq_id, cpu) ||
> > +                   plock->softirq_context != curr->softirq_context)
> > +                       return 0;
> > +       /* In the case of process context */
> > +       } else {
> > +               if (plock->hardirq_context != 0 ||
> > +                   plock->softirq_context != 0)
> > +                       return 0;
> > +       }
> > +       return 1;
> > +}
> >
> 
> I have not read the code yet...
> but different work functions in workqueues are different "contexts" IMO,
> does commit operation work well in work functions?

Hello,

Yes. I also think it should be considered since each work might be run in
different context from another, thanks to concurrency support of workqueue.
I will reflect it.

Thanks,
Byungchul

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2017-01-12 16:16   ` Peter Zijlstra
  2017-01-13  2:45     ` Byungchul Park
  2017-01-13  4:09     ` [PATCH] lockdep: Make a stack_trace instance passed to check_prev_add as an arg Byungchul Park
@ 2017-01-13 10:11     ` Byungchul Park
  2017-01-13 10:17       ` [PATCH 1/2] lockdep: Refactor save_trace() Byungchul Park
                         ` (2 more replies)
  2 siblings, 3 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-13 10:11 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

What do you think about the following patches doing it?

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [PATCH 1/2] lockdep: Refactor save_trace()
  2017-01-13 10:11     ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
@ 2017-01-13 10:17       ` Byungchul Park
  2017-01-13 10:17       ` [PATCH 2/2] lockdep: Pass a callback arg to check_prev_add() to handle stack_trace Byungchul Park
  2017-01-17 15:54       ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Peter Zijlstra
  2 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-13 10:17 UTC (permalink / raw)
  To: peterz, mingo; +Cc: linux-kernel

Actually, this 1/2 patch will be useful only with combining crossrelease
implementation. However, you can easily guess the way to handle
stack_trace in crossrelease with these two patches.

----->8-----
>From 8093188ebba720a1a79cd73c52e992bad7d15bfd Mon Sep 17 00:00:00 2001
From: Byungchul Park <byungchul.park@lge.com>
Date: Fri, 13 Jan 2017 13:54:51 +0900
Subject: [PATCH 1/2] lockdep: Refactor save_trace()

Currently, save_trace() allocates a buffer for saving stack_trace from
the global buffer, and then saves the trace. However, it would be more
useful if a separate buffer can be used. Actually, crossrelease needs
to use separate temporal buffers where to save stack_traces.

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2081c31..e63ff97 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -392,13 +392,13 @@ static void print_lockdep_off(const char *bug_msg)
 #endif
 }
 
-static int save_trace(struct stack_trace *trace)
+static unsigned int __save_trace(struct stack_trace *trace, unsigned long *buf,
+				 unsigned long max_nr, int skip)
 {
 	trace->nr_entries = 0;
-	trace->max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
-	trace->entries = stack_trace + nr_stack_trace_entries;
-
-	trace->skip = 3;
+	trace->max_entries = max_nr;
+	trace->entries = buf;
+	trace->skip = skip;
 
 	save_stack_trace(trace);
 
@@ -415,7 +415,15 @@ static int save_trace(struct stack_trace *trace)
 
 	trace->max_entries = trace->nr_entries;
 
-	nr_stack_trace_entries += trace->nr_entries;
+	return trace->nr_entries;
+}
+
+static int save_trace(struct stack_trace *trace)
+{
+	unsigned long *buf = stack_trace + nr_stack_trace_entries;
+	unsigned long max_nr = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
+
+	nr_stack_trace_entries += __save_trace(trace, buf, max_nr, 3);
 
 	if (nr_stack_trace_entries >= MAX_STACK_TRACE_ENTRIES-1) {
 		if (!debug_locks_off_graph_unlock())
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH 2/2] lockdep: Pass a callback arg to check_prev_add() to handle stack_trace
  2017-01-13 10:11     ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
  2017-01-13 10:17       ` [PATCH 1/2] lockdep: Refactor save_trace() Byungchul Park
@ 2017-01-13 10:17       ` Byungchul Park
  2017-01-17 15:54       ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Peter Zijlstra
  2 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-13 10:17 UTC (permalink / raw)
  To: peterz, mingo; +Cc: linux-kernel

Currently, a separate stack_trace instance cannot be used in
check_prev_add(). The simplest way to achieve it is to pass a
stack_trace instance to check_prev_add() as an argument after
saving it. However, unnecessary saving can happen if so implemented.

The proper solution is to pass a callback function additionally along
with a stack_trace so that a caller can decide the way to save, for
example, doing nothing, calling save_trace() or doing something else.

Actually, crossrelease don't need to save stack_trace of current but
only need to copy stack_traces from temporary buffers to the global
stack_trace[].

Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
 kernel/locking/lockdep.c | 38 ++++++++++++++++++--------------------
 1 file changed, 18 insertions(+), 20 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index e63ff97..75dc14a 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1805,20 +1805,13 @@ static inline void inc_chains(void)
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance, int *stack_saved)
+	       struct held_lock *next, int distance, struct stack_trace *trace,
+	       int (*save)(struct stack_trace *trace))
 {
 	struct lock_list *entry;
 	int ret;
 	struct lock_list this;
 	struct lock_list *uninitialized_var(target_entry);
-	/*
-	 * Static variable, serialized by the graph_lock().
-	 *
-	 * We use this static variable to save the stack trace in case
-	 * we call into this function multiple times due to encountering
-	 * trylocks in the held lock stack.
-	 */
-	static struct stack_trace trace;
 
 	/*
 	 * Prove that the new <prev> -> <next> dependency would not
@@ -1866,11 +1859,8 @@ static inline void inc_chains(void)
 		}
 	}
 
-	if (!*stack_saved) {
-		if (!save_trace(&trace))
-			return 0;
-		*stack_saved = 1;
-	}
+	if (save && !save(trace))
+		return 0;
 
 	/*
 	 * Ok, all validations passed, add the new lock
@@ -1878,14 +1868,14 @@ static inline void inc_chains(void)
 	 */
 	ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, trace);
 	if (!ret)
 		return 0;
 
@@ -1893,8 +1883,6 @@ static inline void inc_chains(void)
 	 * Debugging printouts:
 	 */
 	if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) {
-		/* We drop graph lock, so another thread can overwrite trace. */
-		*stack_saved = 0;
 		graph_unlock();
 		printk("\n new dependency: ");
 		print_lock_name(hlock_class(prev));
@@ -1917,8 +1905,10 @@ static inline void inc_chains(void)
 check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
 	int depth = curr->lockdep_depth;
-	int stack_saved = 0;
 	struct held_lock *hlock;
+	struct stack_trace trace;
+	unsigned long start_nr = nr_stack_trace_entries;
+	int (*save)(struct stack_trace *trace) = save_trace;
 
 	/*
 	 * Debugging checks.
@@ -1944,8 +1934,16 @@ static inline void inc_chains(void)
 		 */
 		if (hlock->read != 2 && hlock->check) {
 			if (!check_prev_add(curr, hlock, next,
-						distance, &stack_saved))
+						distance, &trace, save))
 				return 0;
+
+			/*
+			 * Stop saving stack_trace if save_trace() was
+			 * called at least once:
+			 */
+			if (save && start_nr != nr_stack_trace_entries)
+				save = NULL;
+
 			/*
 			 * Stop after the first non-trylock entry,
 			 * as non-trylock entries have added their
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2016-12-09  5:12 ` [PATCH v4 07/15] lockdep: Implement crossrelease feature Byungchul Park
  2017-01-13  4:39   ` Lai Jiangshan
@ 2017-01-16 15:10   ` Peter Zijlstra
  2017-01-17  2:05     ` Byungchul Park
  2017-01-16 15:13   ` Peter Zijlstra
  2 siblings, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-16 15:10 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:

> @@ -143,6 +149,9 @@ struct lock_class_stats lock_stats(struct lock_class *class);
>  void clear_lock_stats(struct lock_class *class);
>  #endif
>  
> +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> +struct cross_lock;
> +#endif

That seems like pointless wrappery, unused (fwd) declarations are
harmless.

>  /*
>   * Map the lock object (the lock instance) to the lock-class object.
>   * This is embedded into specific lock instances:
> @@ -155,6 +164,9 @@ struct lockdep_map {
>  	int				cpu;
>  	unsigned long			ip;
>  #endif
> +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> +	struct cross_lock		*xlock;
> +#endif

The use of this escapes me; why does the lockdep_map need a pointer to
this?

>  };
>  
>  static inline void lockdep_copy_map(struct lockdep_map *to,
> @@ -258,7 +270,82 @@ struct held_lock {
>  	unsigned int hardirqs_off:1;
>  	unsigned int references:12;					/* 32 bits */
>  	unsigned int pin_count;
> +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> +	/*
> +	 * This is used to find out the first plock among plocks having
> +	 * been acquired since a crosslock was held. Crossrelease feature
> +	 * uses chain cache between the crosslock and the first plock to
> +	 * avoid building unnecessary dependencies, like how lockdep uses
> +	 * a sort of chain cache for normal locks.
> +	 */
> +	unsigned int gen_id;
> +#endif
> +};

Makes sense, except we'll have a bunch of different generation numbers
(see below), so I think it makes sense to name it more explicitly.

> +
> +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> +#define MAX_PLOCK_TRACE_ENTRIES		5

Why 5? ;-)

> +/*
> + * This is for keeping locks waiting for commit to happen so that
> + * dependencies are actually built later at commit step.
> + *
> + * Every task_struct has an array of pend_lock. Each entiry will be
> + * added with a lock whenever lock_acquire() is called for normal lock.
> + */
> +struct pend_lock {

Like said before, I'm not sure "pending" is the right word, we track
locks that have very much been released. Would something like "lock
history" make more sense?

> +	/*
> +	 * prev_gen_id is used to check whether any other hlock in the
> +	 * current is already dealing with the xlock, with which commit
> +	 * is performed. If so, this plock can be skipped.
> +	 */
> +	unsigned int		prev_gen_id;

Confused..

> +	/*
> +	 * A kind of global timestamp increased and set when this plock
> +	 * is inserted.
> +	 */
> +	unsigned int		gen_id;

Right, except you also have pend_lock::hlock::gen_id, which I think is
the very same generation number, no?

> +
> +	int			hardirq_context;
> +	int			softirq_context;

This would fit in 2 bit, why do you use 8 bytes?

> +
> +	/*
> +	 * Whenever irq happens, these are updated so that we can
> +	 * distinguish each irq context uniquely.
> +	 */
> +	unsigned int		hardirq_id;
> +	unsigned int		softirq_id;

An alternative approach would be to 'unwind' or discard all historical
events from a nested context once we exit it.

After all, all we care about is the history of the release context, once
the context is gone, we don't care.

> +
> +	/*
> +	 * Seperate stack_trace data. This will be used at commit step.
> +	 */
> +	struct stack_trace	trace;
> +	unsigned long		trace_entries[MAX_PLOCK_TRACE_ENTRIES];
> +
> +	/*
> +	 * Seperate hlock instance. This will be used at commit step.
> +	 */
> +	struct held_lock	hlock;
> +};
> +
> +/*
> + * One cross_lock per one lockdep_map.
> + *
> + * To initialize a lock as crosslock, lockdep_init_map_crosslock() should
> + * be used instead of lockdep_init_map(), where the pointer of cross_lock
> + * instance should be passed as a parameter.
> + */
> +struct cross_lock {
> +	unsigned int		gen_id;

Again, you already have hlock::gen_id for this, no?

> +	struct list_head	xlock_entry;
> +
> +	/*
> +	 * Seperate hlock instance. This will be used at commit step.
> +	 */
> +	struct held_lock	hlock;
> +
> +	int			ref; /* reference count */
>  };

Why not do something like:

struct lockdep_map_cross {
	struct lockdep_map	map;
	struct held_lock	hlock;
}

That saves at least that pointer.

But I still have to figure out why we need this hlock.

Also note that a full hlock contains superfluous information:

 - prev_chain_key; not required, we can compute the 2 entry chain hash
 		   on demand when we need.
 - instance: we already have the lockdep_map right here, pointers to
 	     self are kinda pointless
 - nest_lock: not sure that makes sense wrt this stuff.
 - class_idx: can recompute if we have lockdep_map
 - reference: see nest_lock

> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index 253538f..592ee368 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1719,6 +1719,11 @@ struct task_struct {
>  	struct held_lock held_locks[MAX_LOCK_DEPTH];
>  	gfp_t lockdep_reclaim_gfp;
>  #endif
> +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> +#define MAX_PLOCKS_NR 1024UL
> +	int plock_index;
> +	struct pend_lock *plocks;
> +#endif

That's a giant heap of memory.. why 1024?


> diff --git a/kernel/fork.c b/kernel/fork.c
> index 4a7ec0c..91ab81b 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c

> @@ -1443,6 +1451,10 @@ static struct task_struct *copy_process(unsigned long clone_flags,
>  	p->lockdep_depth = 0; /* no locks held yet */
>  	p->curr_chain_key = 0;
>  	p->lockdep_recursion = 0;
> +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> +	p->plock_index = 0;
> +	p->plocks = vzalloc(sizeof(struct pend_lock) * MAX_PLOCKS_NR);

And while I understand why you need vmalloc for that amount of memory,
do realize that on 32bit kernels you'll very quickly run out of space
this way.

That pend_lock thing could maybe be shrunk to 128 bytes, at which point
you can fit 64 in two pages, is that not sufficient?


> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index 11580ec..2c8b2c1 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c

> +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> +
> +static LIST_HEAD(xlocks_head);

Still not explanation for what this list is for...

> +
> +/*
> + * Whenever a crosslock is held, cross_gen_id will be increased.
> + */
> +static atomic_t cross_gen_id; /* Can be wrapped */
> +
> +/* Implement a circular buffer - for internal use */
> +#define cir_p(n, i)		((i) ? (i) - 1 : (n) - 1)

This is broken, I think you'll want something like: (((i)-1) % (n))

> +#define cir_n(n, i)		((i) == (n) - 1 ? 0 : (i) + 1)

Idem

> +/*
> + * Crossrelease needs to distinguish each hardirq context.
> + */
> +static DEFINE_PER_CPU(unsigned int, hardirq_id);
> +void crossrelease_hardirq_start(void)
> +{
> +	per_cpu(hardirq_id, smp_processor_id())++;
> +}
> +
> +/*
> + * Crossrelease needs to distinguish each softirq context.
> + */
> +static DEFINE_PER_CPU(unsigned int, softirq_id);
> +void crossrelease_softirq_start(void)
> +{
> +	per_cpu(softirq_id, smp_processor_id())++;
> +}

See above, I don't think we need to retain the plock stuff once a
context finishes, and therefore we don't need context generation numbers
to differentiate them.

> +/*
> + * To find the earlist crosslock among all crosslocks not released yet.
> + */
> +static unsigned int gen_id_begin(void)
> +{
> +	struct cross_lock *xlock = list_entry_rcu(xlocks_head.next,
> +			struct cross_lock, xlock_entry);
> +
> +	/* If empty */
> +	if (&xlock->xlock_entry == &xlocks_head)
> +		return (unsigned int)atomic_read(&cross_gen_id) + 1;
> +
> +	return READ_ONCE(xlock->gen_id);
> +}
> +
> +/*
> + * To find the latest crosslock among all crosslocks already released.
> + */
> +static inline unsigned int gen_id_done(void)
> +{
> +	return gen_id_begin() - 1;
> +}

I'm not sure about these... if you increment the generation count on any
cross action (both acquire and release) and tag all hlocks with the
current reading, you have all the ordering required, no?

> +/*
> + * No contention. Irq disable is only required.
> + */
> +static void add_plock(struct held_lock *hlock, unsigned int prev_gen_id,
> +		unsigned int gen_id_done)
> +{
> +	struct task_struct *curr = current;
> +	int cpu = smp_processor_id();
> +	struct pend_lock *plock;
> +	/*
> +	 *	CONTEXT 1		CONTEXT 2
> +	 *	---------		---------
> +	 *	acquire A (cross)
> +	 *	X = atomic_inc_return()
> +	 *	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ serialize
> +	 *				Y = atomic_read_acquire()
> +	 *				acquire B
> +	 *				acquire C
> +	 *
> +	 * For ordering between this and all following LOCKs.
> +	 * This way we ensure the order A -> B -> C when CONTEXT 2
> +	 * can see Y is equal to or greater than X.
> +	 *
> +	 * Pairs with atomic_inc_return() in add_xlock().
> +	 */
> +	unsigned int gen_id = (unsigned int)atomic_read_acquire(&cross_gen_id);

fails to explain why this is important.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2016-12-09  5:12 ` [PATCH v4 07/15] lockdep: Implement crossrelease feature Byungchul Park
  2017-01-13  4:39   ` Lai Jiangshan
  2017-01-16 15:10   ` Peter Zijlstra
@ 2017-01-16 15:13   ` Peter Zijlstra
  2017-01-17  2:33     ` Byungchul Park
  2 siblings, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-16 15:13 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:
> +	/*
> +	 * We assign class_idx here redundantly even though following
> +	 * memcpy will cover it, in order to ensure a rcu reader can
> +	 * access the class_idx atomically without lock.
> +	 *
> +	 * Here we assume setting a word-sized variable is atomic.

which one, where?

> +	 */
> +	xlock->hlock.class_idx = hlock->class_idx;
> +	gen_id = (unsigned int)atomic_inc_return(&cross_gen_id);
> +	WRITE_ONCE(xlock->gen_id, gen_id);
> +	memcpy(&xlock->hlock, hlock, sizeof(struct held_lock));
> +	INIT_LIST_HEAD(&xlock->xlock_entry);
> +	list_add_tail_rcu(&xlock->xlock_entry, &xlocks_head);

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-16 15:10   ` Peter Zijlstra
@ 2017-01-17  2:05     ` Byungchul Park
  2017-01-17  7:12       ` Peter Zijlstra
  2017-01-17  7:14       ` Peter Zijlstra
  0 siblings, 2 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-17  2:05 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Mon, Jan 16, 2017 at 04:10:01PM +0100, Peter Zijlstra wrote:
> On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:
> 
> > @@ -143,6 +149,9 @@ struct lock_class_stats lock_stats(struct lock_class *class);
> >  void clear_lock_stats(struct lock_class *class);
> >  #endif
> >  
> > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > +struct cross_lock;
> > +#endif
> 
> That seems like pointless wrappery, unused (fwd) declarations are
> harmless.

OK.

> >  /*
> >   * Map the lock object (the lock instance) to the lock-class object.
> >   * This is embedded into specific lock instances:
> > @@ -155,6 +164,9 @@ struct lockdep_map {
> >  	int				cpu;
> >  	unsigned long			ip;
> >  #endif
> > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > +	struct cross_lock		*xlock;
> > +#endif
> 
> The use of this escapes me; why does the lockdep_map need a pointer to
> this?

Lockdep interfaces e.g. lock_acquire(), lock_release() and lock_commit()
use lockdep_map as an arg, but crossrelease need to extract cross_lock
instances from that.

> 
> >  };
> >  
> >  static inline void lockdep_copy_map(struct lockdep_map *to,
> > @@ -258,7 +270,82 @@ struct held_lock {
> >  	unsigned int hardirqs_off:1;
> >  	unsigned int references:12;					/* 32 bits */
> >  	unsigned int pin_count;
> > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > +	/*
> > +	 * This is used to find out the first plock among plocks having
> > +	 * been acquired since a crosslock was held. Crossrelease feature
> > +	 * uses chain cache between the crosslock and the first plock to
> > +	 * avoid building unnecessary dependencies, like how lockdep uses
> > +	 * a sort of chain cache for normal locks.
> > +	 */
> > +	unsigned int gen_id;
> > +#endif
> > +};
> 
> Makes sense, except we'll have a bunch of different generation numbers
> (see below), so I think it makes sense to name it more explicitly.

Right. I will try it.

> > +
> > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > +#define MAX_PLOCK_TRACE_ENTRIES		5
> 
> Why 5? ;-)

What nr are you recommanding to store one stack_trace?

> 
> > +/*
> > + * This is for keeping locks waiting for commit to happen so that
> > + * dependencies are actually built later at commit step.
> > + *
> > + * Every task_struct has an array of pend_lock. Each entiry will be
> > + * added with a lock whenever lock_acquire() is called for normal lock.
> > + */
> > +struct pend_lock {
> 
> Like said before, I'm not sure "pending" is the right word, we track
> locks that have very much been released. Would something like "lock
> history" make more sense?

Good idea. But I want to avoid using 'h'istory lock because it could be
confused with 'h'eld lock when I call something like hlock_class(). But
I will use "lock history" if I cannot find better words. What about
"logged lock"?

> 
> > +	/*
> > +	 * prev_gen_id is used to check whether any other hlock in the
> > +	 * current is already dealing with the xlock, with which commit
> > +	 * is performed. If so, this plock can be skipped.
> > +	 */
> > +	unsigned int		prev_gen_id;
> 
> Confused..

Sorry. My explanation is insufficient. I will try to make it sufficient.

For example,

Context X
---------
gen_id : 10 when acquiring AX (crosslock)

Context Y
---------
gen_id : 9 when acquiring B
gen_id : 12 when acquiring C (Original lockdep adds B -> C)
gen_id : 15 when acquiring D (Original lockdep adds C -> D)
gen_id : 19 when acquiring E (Original lockdep adds D -> E)
release AX <- focus here!
(will release E, D, C and B in future..)

In this situation, it's enough to connect only AX and C, "AX -> C".
"AX -> D" and "AX -> E" are unnecessary because it can be covered by
"AX -> C", "C -> D" and "D -> E", even though "AX -> D" and "AX -> E"
are also true dependencies, but unnecessary.

To handle this optimization, I decided to keep prev_gen_id in pend_lock
which stores gen_id of the previous lock in held_locks so that I can
decide whether the previous lock can be handled with the xlock. If so,
I can skip this dependency because this can be covered by
"the xlock -> the previous lock" and "the previous lock -> current lock".

> 
> > +	/*
> > +	 * A kind of global timestamp increased and set when this plock
> > +	 * is inserted.

Sorry. This comment is wrong. I will fix it. This should be,

"This will be set to a value of a global timestamp, cross_gen_id, when
inserting this plock."

> > +	 */
> > +	unsigned int		gen_id;
> 
> Right, except you also have pend_lock::hlock::gen_id, which I think is
> the very same generation number, no?

pend_lock::gen_id is equal to or greater than pend_lock::hlock::gen_id
because atomic_inc_return(&cross_gen_id) can happen between these two
stores.

pend_lock::gen_id is used to compare with cross_lock::gen_id when
identifying dependencies.

held_lock::gen_id is used to decide whether the previous lock in held_locks
can handle necessary dependencies on behalf of current lock.

> > +
> > +	int			hardirq_context;
> > +	int			softirq_context;
> 
> This would fit in 2 bit, why do you use 8 bytes?

Right. 2 bits are enough. I will change it.

> 
> > +
> > +	/*
> > +	 * Whenever irq happens, these are updated so that we can
> > +	 * distinguish each irq context uniquely.
> > +	 */
> > +	unsigned int		hardirq_id;
> > +	unsigned int		softirq_id;
> 
> An alternative approach would be to 'unwind' or discard all historical
> events from a nested context once we exit it.

That's one of what I considered. However, it would make code complex to
detect if pend_lock ring buffer was wrapped.

> 
> After all, all we care about is the history of the release context, once
> the context is gone, we don't care.

We must care it and decide if the next plock in the ring buffer might be
valid one or not.

> 
> > +
> > +	/*
> > +	 * Seperate stack_trace data. This will be used at commit step.
> > +	 */
> > +	struct stack_trace	trace;
> > +	unsigned long		trace_entries[MAX_PLOCK_TRACE_ENTRIES];
> > +
> > +	/*
> > +	 * Seperate hlock instance. This will be used at commit step.
> > +	 */
> > +	struct held_lock	hlock;
> > +};
> > +
> > +/*
> > + * One cross_lock per one lockdep_map.
> > + *
> > + * To initialize a lock as crosslock, lockdep_init_map_crosslock() should
> > + * be used instead of lockdep_init_map(), where the pointer of cross_lock
> > + * instance should be passed as a parameter.
> > + */
> > +struct cross_lock {
> > +	unsigned int		gen_id;
> 
> Again, you already have hlock::gen_id for this, no?

(Not implemented yet though) To add "xlock -> xlock", xlock should be both a
pend_lock and a cross_lock.

hlock::gen_id should have the time holding it to decide representative locks
among held_locks to handle dependencies in a optimized way.

xlock::gen_id should have increased value when acquiring crosslock and be
used to compare with plock::gen_id and decide true dependencies.

I think both are necessary.

> > +	struct list_head	xlock_entry;
> > +
> > +	/*
> > +	 * Seperate hlock instance. This will be used at commit step.
> > +	 */
> > +	struct held_lock	hlock;
> > +
> > +	int			ref; /* reference count */
> >  };
> 
> Why not do something like:
> 
> struct lockdep_map_cross {
> 	struct lockdep_map	map;
> 	struct held_lock	hlock;
> }
> 
> That saves at least that pointer.
> 
> But I still have to figure out why we need this hlock.
> 
> Also note that a full hlock contains superfluous information:
> 
>  - prev_chain_key; not required, we can compute the 2 entry chain hash
>  		   on demand when we need.
>  - instance: we already have the lockdep_map right here, pointers to
>  	     self are kinda pointless
>  - nest_lock: not sure that makes sense wrt this stuff.
>  - class_idx: can recompute if we have lockdep_map
>  - reference: see nest_lock

I agree with you. I will try to extract only necessary fields from hlock
and remove held_lock from xlock.

> 
> > diff --git a/include/linux/sched.h b/include/linux/sched.h
> > index 253538f..592ee368 100644
> > --- a/include/linux/sched.h
> > +++ b/include/linux/sched.h
> > @@ -1719,6 +1719,11 @@ struct task_struct {
> >  	struct held_lock held_locks[MAX_LOCK_DEPTH];
> >  	gfp_t lockdep_reclaim_gfp;
> >  #endif
> > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > +#define MAX_PLOCKS_NR 1024UL
> > +	int plock_index;
> > +	struct pend_lock *plocks;
> > +#endif
> 
> That's a giant heap of memory.. why 1024?

Could you recommand the nr which is the size of ring buffer for plocks?

> 
> 
> > diff --git a/kernel/fork.c b/kernel/fork.c
> > index 4a7ec0c..91ab81b 100644
> > --- a/kernel/fork.c
> > +++ b/kernel/fork.c
> 
> > @@ -1443,6 +1451,10 @@ static struct task_struct *copy_process(unsigned long clone_flags,
> >  	p->lockdep_depth = 0; /* no locks held yet */
> >  	p->curr_chain_key = 0;
> >  	p->lockdep_recursion = 0;
> > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > +	p->plock_index = 0;
> > +	p->plocks = vzalloc(sizeof(struct pend_lock) * MAX_PLOCKS_NR);
> 
> And while I understand why you need vmalloc for that amount of memory,
> do realize that on 32bit kernels you'll very quickly run out of space
> this way.

OK. I also think it should be smaller. Please recommand proper nr.

> That pend_lock thing could maybe be shrunk to 128 bytes, at which point
> you can fit 64 in two pages, is that not sufficient?

Do you think 64 is sufficient? I will apply it. Actually the size is not
much important, it only causes missing some dependencies. By the way, I
forgot how much nr was used while running crossrelease. I will check it
again and let you know.

> 
> 
> > diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> > index 11580ec..2c8b2c1 100644
> > --- a/kernel/locking/lockdep.c
> > +++ b/kernel/locking/lockdep.c
> 
> > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > +
> > +static LIST_HEAD(xlocks_head);
> 
> Still not explanation for what this list is for...

This is for tracking crosslocks in progress so that crossrelase can
decide gen_id_done. Is there other ways?

Actually every thing becomes simple if ring buffer was not used. But
I think I should use ring buffer for plocks. Any alternatives?

> 
> > +
> > +/*
> > + * Whenever a crosslock is held, cross_gen_id will be increased.
> > + */
> > +static atomic_t cross_gen_id; /* Can be wrapped */
> > +
> > +/* Implement a circular buffer - for internal use */
> > +#define cir_p(n, i)		((i) ? (i) - 1 : (n) - 1)
> 
> This is broken, I think you'll want something like: (((i)-1) % (n))

This is not broken because 0 <= i < MAX_PLOCKS_NR. And the result must be
MAX_PLOCKS_NR - 1 when i = 0. What you recommand provides wrong result.

> 
> > +#define cir_n(n, i)		((i) == (n) - 1 ? 0 : (i) + 1)
> 
> Idem

I can change this as you recommand.

> 
> > +/*
> > + * Crossrelease needs to distinguish each hardirq context.
> > + */
> > +static DEFINE_PER_CPU(unsigned int, hardirq_id);
> > +void crossrelease_hardirq_start(void)
> > +{
> > +	per_cpu(hardirq_id, smp_processor_id())++;
> > +}
> > +
> > +/*
> > + * Crossrelease needs to distinguish each softirq context.
> > + */
> > +static DEFINE_PER_CPU(unsigned int, softirq_id);
> > +void crossrelease_softirq_start(void)
> > +{
> > +	per_cpu(softirq_id, smp_processor_id())++;
> > +}
> 
> See above, I don't think we need to retain the plock stuff once a
> context finishes, and therefore we don't need context generation numbers
> to differentiate them.

I stongly want to implement as you recommand. But it makes ring buffer
implementation difficult. Unwinding when finishing nested contexts needs
some more jobs to distinguish between wrapped case and unwinded case.
If you prefer the latter, then I will re-work to replace the former way
with the latter.

> 
> > +/*
> > + * To find the earlist crosslock among all crosslocks not released yet.
> > + */
> > +static unsigned int gen_id_begin(void)
> > +{
> > +	struct cross_lock *xlock = list_entry_rcu(xlocks_head.next,
> > +			struct cross_lock, xlock_entry);
> > +
> > +	/* If empty */
> > +	if (&xlock->xlock_entry == &xlocks_head)
> > +		return (unsigned int)atomic_read(&cross_gen_id) + 1;
> > +
> > +	return READ_ONCE(xlock->gen_id);
> > +}
> > +
> > +/*
> > + * To find the latest crosslock among all crosslocks already released.
> > + */
> > +static inline unsigned int gen_id_done(void)
> > +{
> > +	return gen_id_begin() - 1;
> > +}
> 
> I'm not sure about these... if you increment the generation count on any
> cross action (both acquire and release) and tag all hlocks with the
> current reading, you have all the ordering required, no?

This is for implementing ring buffer to distinguish between plock entries,
whether it is in progress or finished. This might be removed if we just
overwrite plocks when overflowing the ring. It seems to be better. I will
change it so that crossrelease just overwrite old ones when overflowing.

And I will remove gen_id_begin() and gen_id_done(). Let me check it.

> 
> > +/*
> > + * No contention. Irq disable is only required.
> > + */
> > +static void add_plock(struct held_lock *hlock, unsigned int prev_gen_id,
> > +		unsigned int gen_id_done)
> > +{
> > +	struct task_struct *curr = current;
> > +	int cpu = smp_processor_id();
> > +	struct pend_lock *plock;
> > +	/*
> > +	 *	CONTEXT 1		CONTEXT 2
> > +	 *	---------		---------
> > +	 *	acquire A (cross)
> > +	 *	X = atomic_inc_return()
> > +	 *	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ serialize
> > +	 *				Y = atomic_read_acquire()
> > +	 *				acquire B
> > +	 *				acquire C
> > +	 *
> > +	 * For ordering between this and all following LOCKs.
> > +	 * This way we ensure the order A -> B -> C when CONTEXT 2
> > +	 * can see Y is equal to or greater than X.
> > +	 *
> > +	 * Pairs with atomic_inc_return() in add_xlock().
> > +	 */
> > +	unsigned int gen_id = (unsigned int)atomic_read_acquire(&cross_gen_id);
> 
> fails to explain why this is important.

It's important if context 2 can see acquire A when acquiring B, because it
has a dependency only in that case. So I needed to prove it via
RELEASE-ACQUIRE of cross_gen_id. Wrong?

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-16 15:13   ` Peter Zijlstra
@ 2017-01-17  2:33     ` Byungchul Park
  2017-01-17  6:24       ` Boqun Feng
  0 siblings, 1 reply; 55+ messages in thread
From: Byungchul Park @ 2017-01-17  2:33 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Mon, Jan 16, 2017 at 04:13:19PM +0100, Peter Zijlstra wrote:
> On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:
> > +	/*
> > +	 * We assign class_idx here redundantly even though following
> > +	 * memcpy will cover it, in order to ensure a rcu reader can
> > +	 * access the class_idx atomically without lock.
> > +	 *
> > +	 * Here we assume setting a word-sized variable is atomic.
> 
> which one, where?

I meant xlock_class(xlock) in check_add_plock().

I was not sure about the following two.

1. Is it ordered between following a and b?
   a. memcpy -> list_add_tail_rcu
   b. list_for_each_entry_rcu -> load class_idx (xlock_class)
   I assumed that it's not ordered.
2. Does memcpy guarantee atomic store for each word?
   I assumed that it doesn't.

But I think I was wrong.. The first might be ordered. I will remove
the following redundant statement. It'd be orderd, right?

> 
> > +	 */
> > +	xlock->hlock.class_idx = hlock->class_idx;
> > +	gen_id = (unsigned int)atomic_inc_return(&cross_gen_id);
> > +	WRITE_ONCE(xlock->gen_id, gen_id);
> > +	memcpy(&xlock->hlock, hlock, sizeof(struct held_lock));
> > +	INIT_LIST_HEAD(&xlock->xlock_entry);
> > +	list_add_tail_rcu(&xlock->xlock_entry, &xlocks_head);
> 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-17  2:33     ` Byungchul Park
@ 2017-01-17  6:24       ` Boqun Feng
  2017-01-17  7:43         ` Byungchul Park
  0 siblings, 1 reply; 55+ messages in thread
From: Boqun Feng @ 2017-01-17  6:24 UTC (permalink / raw)
  To: Byungchul Park
  Cc: Peter Zijlstra, mingo, tglx, walken, kirill, linux-kernel,
	linux-mm, iamjoonsoo.kim, akpm, npiggin

[-- Attachment #1: Type: text/plain, Size: 1644 bytes --]

On Tue, Jan 17, 2017 at 11:33:41AM +0900, Byungchul Park wrote:
> On Mon, Jan 16, 2017 at 04:13:19PM +0100, Peter Zijlstra wrote:
> > On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:
> > > +	/*
> > > +	 * We assign class_idx here redundantly even though following
> > > +	 * memcpy will cover it, in order to ensure a rcu reader can
> > > +	 * access the class_idx atomically without lock.
> > > +	 *
> > > +	 * Here we assume setting a word-sized variable is atomic.
> > 
> > which one, where?
> 
> I meant xlock_class(xlock) in check_add_plock().
> 
> I was not sure about the following two.
> 
> 1. Is it ordered between following a and b?
>    a. memcpy -> list_add_tail_rcu
>    b. list_for_each_entry_rcu -> load class_idx (xlock_class)
>    I assumed that it's not ordered.
> 2. Does memcpy guarantee atomic store for each word?
>    I assumed that it doesn't.
> 
> But I think I was wrong.. The first might be ordered. I will remove
> the following redundant statement. It'd be orderd, right?
> 

Yes, a and b are ordered, IOW, they could be paired, meaning when we
got the item in a list_for_each_entry_rcu() loop, all memory operations
before the corresponding list_add_tail_rcu() should be observed by us.

Regards,
Boqun

> > 
> > > +	 */
> > > +	xlock->hlock.class_idx = hlock->class_idx;
> > > +	gen_id = (unsigned int)atomic_inc_return(&cross_gen_id);
> > > +	WRITE_ONCE(xlock->gen_id, gen_id);
> > > +	memcpy(&xlock->hlock, hlock, sizeof(struct held_lock));
> > > +	INIT_LIST_HEAD(&xlock->xlock_entry);
> > > +	list_add_tail_rcu(&xlock->xlock_entry, &xlocks_head);
> > 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-17  2:05     ` Byungchul Park
@ 2017-01-17  7:12       ` Peter Zijlstra
  2017-01-17  7:49         ` Byungchul Park
  2017-01-17  7:14       ` Peter Zijlstra
  1 sibling, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-17  7:12 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Jan 17, 2017 at 11:05:42AM +0900, Byungchul Park wrote:
> On Mon, Jan 16, 2017 at 04:10:01PM +0100, Peter Zijlstra wrote:
> > On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:

> > > +
> > > +	/*
> > > +	 * Whenever irq happens, these are updated so that we can
> > > +	 * distinguish each irq context uniquely.
> > > +	 */
> > > +	unsigned int		hardirq_id;
> > > +	unsigned int		softirq_id;
> > 
> > An alternative approach would be to 'unwind' or discard all historical
> > events from a nested context once we exit it.
> 
> That's one of what I considered. However, it would make code complex to
> detect if pend_lock ring buffer was wrapped.

I'm not sure I see the need for detecting that...

> > 
> > After all, all we care about is the history of the release context, once
> > the context is gone, we don't care.
> 
> We must care it and decide if the next plock in the ring buffer might be
> valid one or not.

So I was thinking this was an overwriting ring buffer; something like
so:

struct pend_lock plocks[64];
unsigned int plocks_idx;

static void plocks_add(..)
{
	unsigned int idx = (plocks_idx++) % 64;

	plocks[idx] = ...;
}

static void plocks_close_context(int ctx)
{
	for (i = 0; i < 64; i++) {
		int idx = (plocks_idx - 1) % 64;
		if (plocks[idx].ctx != ctx)
			break;

		plocks_idx--;
	}
}

Similarly for the release, it need only look at 64 entries and terminate
early if the generation number is too old.

static void plocks_release(unsigned int gen)
{
	for (i = 0; i < 64; i++) {
		int idx = (plocks_idx - 1 - i) % 64;
		if ((int)(plocks[idx].gen_id - gen) < 0)
			break;

		/* do release muck */
	}
}

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-17  2:05     ` Byungchul Park
  2017-01-17  7:12       ` Peter Zijlstra
@ 2017-01-17  7:14       ` Peter Zijlstra
  2017-01-17  7:45         ` Byungchul Park
  1 sibling, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-17  7:14 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Jan 17, 2017 at 11:05:42AM +0900, Byungchul Park wrote:
> On Mon, Jan 16, 2017 at 04:10:01PM +0100, Peter Zijlstra wrote:
> > On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:

> > > @@ -155,6 +164,9 @@ struct lockdep_map {
> > >  	int				cpu;
> > >  	unsigned long			ip;
> > >  #endif
> > > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > > +	struct cross_lock		*xlock;
> > > +#endif
> > 
> > The use of this escapes me; why does the lockdep_map need a pointer to
> > this?
> 
> Lockdep interfaces e.g. lock_acquire(), lock_release() and lock_commit()
> use lockdep_map as an arg, but crossrelease need to extract cross_lock
> instances from that.

> > Why not do something like:
> > 
> > struct lockdep_map_cross {
> > 	struct lockdep_map	map;
> > 	struct held_lock	hlock;
> > }

Using a structure like that, you can pass lockdep_map_cross around just
fine, since the lockdep_map is the first member, so the pointers are
interchangeable. At worst we might need to munge a few typecasts.

But then the cross release code can simply cast to the bigger type and
have access to the extra data it knows to be there.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-17  6:24       ` Boqun Feng
@ 2017-01-17  7:43         ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-17  7:43 UTC (permalink / raw)
  To: Boqun Feng
  Cc: Peter Zijlstra, mingo, tglx, walken, kirill, linux-kernel,
	linux-mm, iamjoonsoo.kim, akpm, npiggin

On Tue, Jan 17, 2017 at 02:24:08PM +0800, Boqun Feng wrote:
> On Tue, Jan 17, 2017 at 11:33:41AM +0900, Byungchul Park wrote:
> > On Mon, Jan 16, 2017 at 04:13:19PM +0100, Peter Zijlstra wrote:
> > > On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:
> > > > +	/*
> > > > +	 * We assign class_idx here redundantly even though following
> > > > +	 * memcpy will cover it, in order to ensure a rcu reader can
> > > > +	 * access the class_idx atomically without lock.
> > > > +	 *
> > > > +	 * Here we assume setting a word-sized variable is atomic.
> > > 
> > > which one, where?
> > 
> > I meant xlock_class(xlock) in check_add_plock().
> > 
> > I was not sure about the following two.
> > 
> > 1. Is it ordered between following a and b?
> >    a. memcpy -> list_add_tail_rcu
> >    b. list_for_each_entry_rcu -> load class_idx (xlock_class)
> >    I assumed that it's not ordered.
> > 2. Does memcpy guarantee atomic store for each word?
> >    I assumed that it doesn't.
> > 
> > But I think I was wrong.. The first might be ordered. I will remove
> > the following redundant statement. It'd be orderd, right?
> > 
> 
> Yes, a and b are ordered, IOW, they could be paired, meaning when we
> got the item in a list_for_each_entry_rcu() loop, all memory operations
> before the corresponding list_add_tail_rcu() should be observed by us.

Thank you for confirming it.

> 
> Regards,
> Boqun
> 
> > > 
> > > > +	 */
> > > > +	xlock->hlock.class_idx = hlock->class_idx;
> > > > +	gen_id = (unsigned int)atomic_inc_return(&cross_gen_id);
> > > > +	WRITE_ONCE(xlock->gen_id, gen_id);
> > > > +	memcpy(&xlock->hlock, hlock, sizeof(struct held_lock));
> > > > +	INIT_LIST_HEAD(&xlock->xlock_entry);
> > > > +	list_add_tail_rcu(&xlock->xlock_entry, &xlocks_head);
> > > 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-17  7:14       ` Peter Zijlstra
@ 2017-01-17  7:45         ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-17  7:45 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Jan 17, 2017 at 08:14:56AM +0100, Peter Zijlstra wrote:
> On Tue, Jan 17, 2017 at 11:05:42AM +0900, Byungchul Park wrote:
> > On Mon, Jan 16, 2017 at 04:10:01PM +0100, Peter Zijlstra wrote:
> > > On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:
> 
> > > > @@ -155,6 +164,9 @@ struct lockdep_map {
> > > >  	int				cpu;
> > > >  	unsigned long			ip;
> > > >  #endif
> > > > +#ifdef CONFIG_LOCKDEP_CROSSRELEASE
> > > > +	struct cross_lock		*xlock;
> > > > +#endif
> > > 
> > > The use of this escapes me; why does the lockdep_map need a pointer to
> > > this?
> > 
> > Lockdep interfaces e.g. lock_acquire(), lock_release() and lock_commit()
> > use lockdep_map as an arg, but crossrelease need to extract cross_lock
> > instances from that.
> 
> > > Why not do something like:
> > > 
> > > struct lockdep_map_cross {
> > > 	struct lockdep_map	map;
> > > 	struct held_lock	hlock;
> > > }
> 
> Using a structure like that, you can pass lockdep_map_cross around just
> fine, since the lockdep_map is the first member, so the pointers are
> interchangeable. At worst we might need to munge a few typecasts.
> 
> But then the cross release code can simply cast to the bigger type and
> have access to the extra data it knows to be there.

Right. I will apply it.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 07/15] lockdep: Implement crossrelease feature
  2017-01-17  7:12       ` Peter Zijlstra
@ 2017-01-17  7:49         ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-17  7:49 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Jan 17, 2017 at 08:12:20AM +0100, Peter Zijlstra wrote:
> On Tue, Jan 17, 2017 at 11:05:42AM +0900, Byungchul Park wrote:
> > On Mon, Jan 16, 2017 at 04:10:01PM +0100, Peter Zijlstra wrote:
> > > On Fri, Dec 09, 2016 at 02:12:03PM +0900, Byungchul Park wrote:
> 
> > > > +
> > > > +	/*
> > > > +	 * Whenever irq happens, these are updated so that we can
> > > > +	 * distinguish each irq context uniquely.
> > > > +	 */
> > > > +	unsigned int		hardirq_id;
> > > > +	unsigned int		softirq_id;
> > > 
> > > An alternative approach would be to 'unwind' or discard all historical
> > > events from a nested context once we exit it.
> > 
> > That's one of what I considered. However, it would make code complex to
> > detect if pend_lock ring buffer was wrapped.
> 
> I'm not sure I see the need for detecting that...
> 
> > > 
> > > After all, all we care about is the history of the release context, once
> > > the context is gone, we don't care.
> > 
> > We must care it and decide if the next plock in the ring buffer might be
> > valid one or not.
> 
> So I was thinking this was an overwriting ring buffer; something like
> so:

OK. I am making code just overwrite ring buffer when overflowing it,
instead of warning the situation.

Thank you very much.

> 
> struct pend_lock plocks[64];
> unsigned int plocks_idx;
> 
> static void plocks_add(..)
> {
> 	unsigned int idx = (plocks_idx++) % 64;
> 
> 	plocks[idx] = ...;
> }
> 
> static void plocks_close_context(int ctx)
> {
> 	for (i = 0; i < 64; i++) {
> 		int idx = (plocks_idx - 1) % 64;
> 		if (plocks[idx].ctx != ctx)
> 			break;
> 
> 		plocks_idx--;
> 	}
> }
> 
> Similarly for the release, it need only look at 64 entries and terminate
> early if the generation number is too old.
> 
> static void plocks_release(unsigned int gen)
> {
> 	for (i = 0; i < 64; i++) {
> 		int idx = (plocks_idx - 1 - i) % 64;
> 		if ((int)(plocks[idx].gen_id - gen) < 0)
> 			break;
> 
> 		/* do release muck */
> 	}
> }

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2017-01-13 10:11     ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
  2017-01-13 10:17       ` [PATCH 1/2] lockdep: Refactor save_trace() Byungchul Park
  2017-01-13 10:17       ` [PATCH 2/2] lockdep: Pass a callback arg to check_prev_add() to handle stack_trace Byungchul Park
@ 2017-01-17 15:54       ` Peter Zijlstra
  2017-01-18  2:04         ` Byungchul Park
  2 siblings, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-17 15:54 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Fri, Jan 13, 2017 at 07:11:43PM +0900, Byungchul Park wrote:
> What do you think about the following patches doing it?

I was more thinking about something like so...

Also, I think I want to muck with struct stack_trace; the members:
max_nr_entries and skip are input arguments to save_stack_trace() and
bloat the structure for no reason.

---
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 7c38f8f3d97b..f2df300a96ee 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -430,6 +430,21 @@ static int save_trace(struct stack_trace *trace)
 	return 1;
 }
 
+static bool return_trace(struct stack_trace *trace)
+{
+	/*
+	 * If @trace is the last trace generated by save_trace(), then we can
+	 * return the entries by simply subtracting @nr_stack_trace_entries
+	 * again.
+	 */
+	if (trace->entries != stack_trace + nr_stack_trace_entries - trace->nr_entres)
+		return false;
+
+	nr_stack_trace_entries -= trace->nr_entries;
+	trace->entries = NULL;
+	return true;
+}
+
 unsigned int nr_hardirq_chains;
 unsigned int nr_softirq_chains;
 unsigned int nr_process_chains;
@@ -1797,20 +1812,12 @@ static inline void inc_chains(void)
  */
 static int
 check_prev_add(struct task_struct *curr, struct held_lock *prev,
-	       struct held_lock *next, int distance, int *stack_saved)
+	       struct held_lock *next, int distance, struct stack_trace *trace)
 {
 	struct lock_list *entry;
 	int ret;
 	struct lock_list this;
 	struct lock_list *uninitialized_var(target_entry);
-	/*
-	 * Static variable, serialized by the graph_lock().
-	 *
-	 * We use this static variable to save the stack trace in case
-	 * we call into this function multiple times due to encountering
-	 * trylocks in the held lock stack.
-	 */
-	static struct stack_trace trace;
 
 	/*
 	 * Prove that the new <prev> -> <next> dependency would not
@@ -1858,11 +1865,7 @@ static inline void inc_chains(void)
 		}
 	}
 
-	if (!*stack_saved) {
-		if (!save_trace(&trace))
-			return 0;
-		*stack_saved = 1;
-	}
+	trace->skip = 1; /* mark used */
 
 	/*
 	 * Ok, all validations passed, add the new lock
@@ -1870,14 +1873,14 @@ static inline void inc_chains(void)
 	 */
 	ret = add_lock_to_list(hlock_class(next),
 			       &hlock_class(prev)->locks_after,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, trace);
 
 	if (!ret)
 		return 0;
 
 	ret = add_lock_to_list(hlock_class(prev),
 			       &hlock_class(next)->locks_before,
-			       next->acquire_ip, distance, &trace);
+			       next->acquire_ip, distance, trace);
 	if (!ret)
 		return 0;
 
@@ -1885,8 +1888,6 @@ static inline void inc_chains(void)
 	 * Debugging printouts:
 	 */
 	if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) {
-		/* We drop graph lock, so another thread can overwrite trace. */
-		*stack_saved = 0;
 		graph_unlock();
 		printk("\n new dependency: ");
 		print_lock_name(hlock_class(prev));
@@ -1908,10 +1909,15 @@ static inline void inc_chains(void)
 static int
 check_prevs_add(struct task_struct *curr, struct held_lock *next)
 {
+	struct stack_trace trace = { .nr_entries = 0, .skip = 0, };
 	int depth = curr->lockdep_depth;
-	int stack_saved = 0;
 	struct held_lock *hlock;
 
+	if (!save_trace(&trace))
+		goto out_bug;
+
+	trace.skip = 0; /* abuse to mark usage */
+
 	/*
 	 * Debugging checks.
 	 *
@@ -1936,7 +1942,7 @@ static inline void inc_chains(void)
 		 */
 		if (hlock->read != 2 && hlock->check) {
 			if (!check_prev_add(curr, hlock, next,
-						distance, &stack_saved))
+					    distance, &trace))
 				return 0;
 			/*
 			 * Stop after the first non-trylock entry,
@@ -1962,6 +1968,9 @@ static inline void inc_chains(void)
 	}
 	return 1;
 out_bug:
+	if (trace.nr_entries && !trace.skip)
+		return_trace(&trace);
+
 	if (!debug_locks_off_graph_unlock())
 		return 0;
 

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2017-01-17 15:54       ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Peter Zijlstra
@ 2017-01-18  2:04         ` Byungchul Park
  2017-01-18 15:10           ` Peter Zijlstra
  0 siblings, 1 reply; 55+ messages in thread
From: Byungchul Park @ 2017-01-18  2:04 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Jan 17, 2017 at 04:54:31PM +0100, Peter Zijlstra wrote:
> On Fri, Jan 13, 2017 at 07:11:43PM +0900, Byungchul Park wrote:
> > What do you think about the following patches doing it?
> 
> I was more thinking about something like so...
> 
> Also, I think I want to muck with struct stack_trace; the members:
> max_nr_entries and skip are input arguments to save_stack_trace() and
> bloat the structure for no reason.

With your approach, save_trace() must be called whenever check_prevs_add()
is called, which might be unnecessary.

Frankly speaking, I think what I proposed resolved it neatly. Don't you
think so?

> 
> ---
> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index 7c38f8f3d97b..f2df300a96ee 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c
> @@ -430,6 +430,21 @@ static int save_trace(struct stack_trace *trace)
>  	return 1;
>  }
>  
> +static bool return_trace(struct stack_trace *trace)
> +{
> +	/*
> +	 * If @trace is the last trace generated by save_trace(), then we can
> +	 * return the entries by simply subtracting @nr_stack_trace_entries
> +	 * again.
> +	 */
> +	if (trace->entries != stack_trace + nr_stack_trace_entries - trace->nr_entres)
> +		return false;
> +
> +	nr_stack_trace_entries -= trace->nr_entries;
> +	trace->entries = NULL;
> +	return true;
> +}
> +
>  unsigned int nr_hardirq_chains;
>  unsigned int nr_softirq_chains;
>  unsigned int nr_process_chains;
> @@ -1797,20 +1812,12 @@ static inline void inc_chains(void)
>   */
>  static int
>  check_prev_add(struct task_struct *curr, struct held_lock *prev,
> -	       struct held_lock *next, int distance, int *stack_saved)
> +	       struct held_lock *next, int distance, struct stack_trace *trace)
>  {
>  	struct lock_list *entry;
>  	int ret;
>  	struct lock_list this;
>  	struct lock_list *uninitialized_var(target_entry);
> -	/*
> -	 * Static variable, serialized by the graph_lock().
> -	 *
> -	 * We use this static variable to save the stack trace in case
> -	 * we call into this function multiple times due to encountering
> -	 * trylocks in the held lock stack.
> -	 */
> -	static struct stack_trace trace;
>  
>  	/*
>  	 * Prove that the new <prev> -> <next> dependency would not
> @@ -1858,11 +1865,7 @@ static inline void inc_chains(void)
>  		}
>  	}
>  
> -	if (!*stack_saved) {
> -		if (!save_trace(&trace))
> -			return 0;
> -		*stack_saved = 1;
> -	}
> +	trace->skip = 1; /* mark used */
>  
>  	/*
>  	 * Ok, all validations passed, add the new lock
> @@ -1870,14 +1873,14 @@ static inline void inc_chains(void)
>  	 */
>  	ret = add_lock_to_list(hlock_class(next),
>  			       &hlock_class(prev)->locks_after,
> -			       next->acquire_ip, distance, &trace);
> +			       next->acquire_ip, distance, trace);
>  
>  	if (!ret)
>  		return 0;
>  
>  	ret = add_lock_to_list(hlock_class(prev),
>  			       &hlock_class(next)->locks_before,
> -			       next->acquire_ip, distance, &trace);
> +			       next->acquire_ip, distance, trace);
>  	if (!ret)
>  		return 0;
>  
> @@ -1885,8 +1888,6 @@ static inline void inc_chains(void)
>  	 * Debugging printouts:
>  	 */
>  	if (verbose(hlock_class(prev)) || verbose(hlock_class(next))) {
> -		/* We drop graph lock, so another thread can overwrite trace. */
> -		*stack_saved = 0;
>  		graph_unlock();
>  		printk("\n new dependency: ");
>  		print_lock_name(hlock_class(prev));
> @@ -1908,10 +1909,15 @@ static inline void inc_chains(void)
>  static int
>  check_prevs_add(struct task_struct *curr, struct held_lock *next)
>  {
> +	struct stack_trace trace = { .nr_entries = 0, .skip = 0, };
>  	int depth = curr->lockdep_depth;
> -	int stack_saved = 0;
>  	struct held_lock *hlock;
>  
> +	if (!save_trace(&trace))
> +		goto out_bug;
> +
> +	trace.skip = 0; /* abuse to mark usage */
> +
>  	/*
>  	 * Debugging checks.
>  	 *
> @@ -1936,7 +1942,7 @@ static inline void inc_chains(void)
>  		 */
>  		if (hlock->read != 2 && hlock->check) {
>  			if (!check_prev_add(curr, hlock, next,
> -						distance, &stack_saved))
> +					    distance, &trace))
>  				return 0;
>  			/*
>  			 * Stop after the first non-trylock entry,
> @@ -1962,6 +1968,9 @@ static inline void inc_chains(void)
>  	}
>  	return 1;
>  out_bug:
> +	if (trace.nr_entries && !trace.skip)
> +		return_trace(&trace);
> +
>  	if (!debug_locks_off_graph_unlock())
>  		return 0;
>  

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2016-12-09  5:12 ` [PATCH v4 15/15] lockdep: Crossrelease feature documentation Byungchul Park
  2017-01-10 20:08   ` Peter Zijlstra
@ 2017-01-18  6:42   ` Boqun Feng
  2017-01-18 10:53     ` Byungchul Park
  1 sibling, 1 reply; 55+ messages in thread
From: Boqun Feng @ 2017-01-18  6:42 UTC (permalink / raw)
  To: Byungchul Park
  Cc: peterz, mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

[-- Attachment #1: Type: text/plain, Size: 1298 bytes --]

On Fri, Dec 09, 2016 at 02:12:11PM +0900, Byungchul Park wrote:
[...]
> +Example 1:
> +
> +   CONTEXT X		   CONTEXT Y
> +   ---------		   ---------
> +   mutext_lock A
> +			   lock_page B
> +   lock_page B
> +			   mutext_lock A /* DEADLOCK */

s/mutext_lock/mutex_lock

> +   unlock_page B
> +			   mutext_unlock A
> +   mutex_unlock A
> +			   unlock_page B
> +
> +   where A is a lock class and B is a page lock.
> +
> +No, we cannot.
> +
> +Example 2:
> +
> +   CONTEXT X	   CONTEXT Y	   CONTEXT Z
> +   ---------	   ---------	   ----------
> +		   mutex_lock A
> +   lock_page B
> +		   lock_page B
> +				   mutext_lock A /* DEADLOCK */
> +				   mutext_unlock A

Ditto.

> +				   unlock_page B held by X
> +		   unlock_page B
> +		   mutex_unlock A
> +
> +   where A is a lock class and B is a page lock.
> +
> +No, we cannot.
> +
> +Example 3:
> +
> +   CONTEXT X		   CONTEXT Y
> +   ---------		   ---------
> +			   mutex_lock A
> +   mutex_lock A
> +   mutex_unlock A
> +			   wait_for_complete B /* DEADLOCK */

I think this part better be:

   CONTEXT X		   CONTEXT Y
   ---------		   ---------
   			   mutex_lock A
   mutex_lock A
   			   wait_for_complete B /* DEADLOCK */
   mutex_unlock A

, right? Because Y triggers DEADLOCK before X could run mutex_unlock().

Regards,
Boqun

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-18  6:42   ` Boqun Feng
@ 2017-01-18 10:53     ` Byungchul Park
  2017-01-18 11:03       ` Peter Zijlstra
  0 siblings, 1 reply; 55+ messages in thread
From: Byungchul Park @ 2017-01-18 10:53 UTC (permalink / raw)
  To: Boqun Feng
  Cc: peterz, mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Wed, Jan 18, 2017 at 02:42:30PM +0800, Boqun Feng wrote:
> On Fri, Dec 09, 2016 at 02:12:11PM +0900, Byungchul Park wrote:
> [...]
> > +Example 1:
> > +
> > +   CONTEXT X		   CONTEXT Y
> > +   ---------		   ---------
> > +   mutext_lock A
> > +			   lock_page B
> > +   lock_page B
> > +			   mutext_lock A /* DEADLOCK */
> 
> s/mutext_lock/mutex_lock

Thank you.

> > +Example 3:
> > +
> > +   CONTEXT X		   CONTEXT Y
> > +   ---------		   ---------
> > +			   mutex_lock A
> > +   mutex_lock A
> > +   mutex_unlock A
> > +			   wait_for_complete B /* DEADLOCK */
> 
> I think this part better be:
> 
>    CONTEXT X		   CONTEXT Y
>    ---------		   ---------
>    			   mutex_lock A
>    mutex_lock A
>    			   wait_for_complete B /* DEADLOCK */
>    mutex_unlock A
> 
> , right? Because Y triggers DEADLOCK before X could run mutex_unlock().

There's no different between two examples.

No matter which one is chosen, mutex_lock A in CONTEXT X cannot be passed.

> 
> Regards,
> Boqun

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-18 10:53     ` Byungchul Park
@ 2017-01-18 11:03       ` Peter Zijlstra
  2017-01-18 11:54         ` Byungchul Park
  0 siblings, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-18 11:03 UTC (permalink / raw)
  To: Byungchul Park
  Cc: Boqun Feng, mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Wed, Jan 18, 2017 at 07:53:47PM +0900, Byungchul Park wrote:
> On Wed, Jan 18, 2017 at 02:42:30PM +0800, Boqun Feng wrote:
> > On Fri, Dec 09, 2016 at 02:12:11PM +0900, Byungchul Park wrote:
> > [...]
> > > +Example 1:
> > > +
> > > +   CONTEXT X		   CONTEXT Y
> > > +   ---------		   ---------
> > > +   mutext_lock A
> > > +			   lock_page B
> > > +   lock_page B
> > > +			   mutext_lock A /* DEADLOCK */
> > 
> > s/mutext_lock/mutex_lock
> 
> Thank you.
> 
> > > +Example 3:
> > > +
> > > +   CONTEXT X		   CONTEXT Y
> > > +   ---------		   ---------
> > > +			   mutex_lock A
> > > +   mutex_lock A
> > > +   mutex_unlock A
> > > +			   wait_for_complete B /* DEADLOCK */
> > 
> > I think this part better be:
> > 
> >    CONTEXT X		   CONTEXT Y
> >    ---------		   ---------
> >    			   mutex_lock A
> >    mutex_lock A
> >    			   wait_for_complete B /* DEADLOCK */
> >    mutex_unlock A
> > 
> > , right? Because Y triggers DEADLOCK before X could run mutex_unlock().
> 
> There's no different between two examples.

There is..

> No matter which one is chosen, mutex_lock A in CONTEXT X cannot be passed.

But your version shows it does mutex_unlock() before CONTEXT Y does
wait_for_completion().

The thing about these diagrams is that both columns are assumed to have
the same timeline.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-18 11:03       ` Peter Zijlstra
@ 2017-01-18 11:54         ` Byungchul Park
  2017-01-18 12:07           ` Peter Zijlstra
  0 siblings, 1 reply; 55+ messages in thread
From: Byungchul Park @ 2017-01-18 11:54 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Boqun Feng, mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Wed, Jan 18, 2017 at 12:03:17PM +0100, Peter Zijlstra wrote:
> On Wed, Jan 18, 2017 at 07:53:47PM +0900, Byungchul Park wrote:
> > On Wed, Jan 18, 2017 at 02:42:30PM +0800, Boqun Feng wrote:
> > > On Fri, Dec 09, 2016 at 02:12:11PM +0900, Byungchul Park wrote:
> > > [...]
> > > > +Example 1:
> > > > +
> > > > +   CONTEXT X		   CONTEXT Y
> > > > +   ---------		   ---------
> > > > +   mutext_lock A
> > > > +			   lock_page B
> > > > +   lock_page B
> > > > +			   mutext_lock A /* DEADLOCK */
> > > 
> > > s/mutext_lock/mutex_lock
> > 
> > Thank you.
> > 
> > > > +Example 3:
> > > > +
> > > > +   CONTEXT X		   CONTEXT Y
> > > > +   ---------		   ---------
> > > > +			   mutex_lock A
> > > > +   mutex_lock A
> > > > +   mutex_unlock A
> > > > +			   wait_for_complete B /* DEADLOCK */
> > > 
> > > I think this part better be:
> > > 
> > >    CONTEXT X		   CONTEXT Y
> > >    ---------		   ---------
> > >    			   mutex_lock A
> > >    mutex_lock A
> > >    			   wait_for_complete B /* DEADLOCK */
> > >    mutex_unlock A
> > > 
> > > , right? Because Y triggers DEADLOCK before X could run mutex_unlock().
> > 
> > There's no different between two examples.
> 
> There is..
> 
> > No matter which one is chosen, mutex_lock A in CONTEXT X cannot be passed.
> 
> But your version shows it does mutex_unlock() before CONTEXT Y does
> wait_for_completion().
> 
> The thing about these diagrams is that both columns are assumed to have
> the same timeline.

X cannot acquire mutex A because Y already acquired it.

In order words, all statements below mutex_lock A in X cannot run.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-18 11:54         ` Byungchul Park
@ 2017-01-18 12:07           ` Peter Zijlstra
  2017-01-18 12:14             ` byungchul.park
  2017-01-18 12:49             ` byungchul.park
  0 siblings, 2 replies; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-18 12:07 UTC (permalink / raw)
  To: Byungchul Park
  Cc: Boqun Feng, mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Wed, Jan 18, 2017 at 08:54:28PM +0900, Byungchul Park wrote:
> On Wed, Jan 18, 2017 at 12:03:17PM +0100, Peter Zijlstra wrote:
> > On Wed, Jan 18, 2017 at 07:53:47PM +0900, Byungchul Park wrote:
> > > On Wed, Jan 18, 2017 at 02:42:30PM +0800, Boqun Feng wrote:
> > > > On Fri, Dec 09, 2016 at 02:12:11PM +0900, Byungchul Park wrote:
> > > > [...]
> > > > > +Example 1:
> > > > > +
> > > > > +   CONTEXT X		   CONTEXT Y
> > > > > +   ---------		   ---------
> > > > > +   mutext_lock A
> > > > > +			   lock_page B
> > > > > +   lock_page B
> > > > > +			   mutext_lock A /* DEADLOCK */
> > > > 
> > > > s/mutext_lock/mutex_lock
> > > 
> > > Thank you.
> > > 
> > > > > +Example 3:
> > > > > +
> > > > > +   CONTEXT X		   CONTEXT Y
> > > > > +   ---------		   ---------
> > > > > +			   mutex_lock A
> > > > > +   mutex_lock A
> > > > > +   mutex_unlock A
> > > > > +			   wait_for_complete B /* DEADLOCK */
> > > > 
> > > > I think this part better be:
> > > > 
> > > >    CONTEXT X		   CONTEXT Y
> > > >    ---------		   ---------
> > > >    			   mutex_lock A
> > > >    mutex_lock A
> > > >    			   wait_for_complete B /* DEADLOCK */
> > > >    mutex_unlock A
> > > > 
> > > > , right? Because Y triggers DEADLOCK before X could run mutex_unlock().
> > > 
> > > There's no different between two examples.
> > 
> > There is..
> > 
> > > No matter which one is chosen, mutex_lock A in CONTEXT X cannot be passed.
> > 
> > But your version shows it does mutex_unlock() before CONTEXT Y does
> > wait_for_completion().
> > 
> > The thing about these diagrams is that both columns are assumed to have
> > the same timeline.
> 
> X cannot acquire mutex A because Y already acquired it.
> 
> In order words, all statements below mutex_lock A in X cannot run.

But your timeline shows it does, which is the error that Boqun pointed
out.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* RE: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-18 12:07           ` Peter Zijlstra
@ 2017-01-18 12:14             ` byungchul.park
  2017-01-18 14:12               ` Peter Zijlstra
  2017-01-18 12:49             ` byungchul.park
  1 sibling, 1 reply; 55+ messages in thread
From: byungchul.park @ 2017-01-18 12:14 UTC (permalink / raw)
  To: 'Peter Zijlstra'
  Cc: 'Boqun Feng',
	mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

> -----Original Message-----
> From: Peter Zijlstra [mailto:peterz@infradead.org]
> Sent: Wednesday, January 18, 2017 9:08 PM
> To: Byungchul Park
> Cc: Boqun Feng; mingo@kernel.org; tglx@linutronix.de; walken@google.com;
> kirill@shutemov.name; linux-kernel@vger.kernel.org; linux-mm@kvack.org;
> iamjoonsoo.kim@lge.com; akpm@linux-foundation.org; npiggin@gmail.com
> Subject: Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
> 
> On Wed, Jan 18, 2017 at 08:54:28PM +0900, Byungchul Park wrote:
> > On Wed, Jan 18, 2017 at 12:03:17PM +0100, Peter Zijlstra wrote:
> > > On Wed, Jan 18, 2017 at 07:53:47PM +0900, Byungchul Park wrote:
> > > > On Wed, Jan 18, 2017 at 02:42:30PM +0800, Boqun Feng wrote:
> > > > > On Fri, Dec 09, 2016 at 02:12:11PM +0900, Byungchul Park wrote:
> > > > > [...]
> > > > > > +Example 1:
> > > > > > +
> > > > > > +   CONTEXT X		   CONTEXT Y
> > > > > > +   ---------		   ---------
> > > > > > +   mutext_lock A
> > > > > > +			   lock_page B
> > > > > > +   lock_page B
> > > > > > +			   mutext_lock A /* DEADLOCK */
> > > > >
> > > > > s/mutext_lock/mutex_lock
> > > >
> > > > Thank you.
> > > >
> > > > > > +Example 3:
> > > > > > +
> > > > > > +   CONTEXT X		   CONTEXT Y
> > > > > > +   ---------		   ---------
> > > > > > +			   mutex_lock A
> > > > > > +   mutex_lock A
> > > > > > +   mutex_unlock A
> > > > > > +			   wait_for_complete B /* DEADLOCK */
> > > > >
> > > > > I think this part better be:
> > > > >
> > > > >    CONTEXT X		   CONTEXT Y
> > > > >    ---------		   ---------
> > > > >    			   mutex_lock A
> > > > >    mutex_lock A
> > > > >    			   wait_for_complete B /* DEADLOCK */
> > > > >    mutex_unlock A
> > > > >
> > > > > , right? Because Y triggers DEADLOCK before X could run
> mutex_unlock().
> > > >
> > > > There's no different between two examples.
> > >
> > > There is..
> > >
> > > > No matter which one is chosen, mutex_lock A in CONTEXT X cannot be
> passed.
> > >
> > > But your version shows it does mutex_unlock() before CONTEXT Y does
> > > wait_for_completion().
> > >
> > > The thing about these diagrams is that both columns are assumed to
> have
> > > the same timeline.
> >
> > X cannot acquire mutex A because Y already acquired it.
> >
> > In order words, all statements below mutex_lock A in X cannot run.
> 
> But your timeline shows it does, which is the error that Boqun pointed
> out.

I am sorry for not understanding what you are talking about.

Do you mean that I should remove all statements below mutex_lock A in X?

Or should I move mutex_unlock as Boqun said? What will change?

^ permalink raw reply	[flat|nested] 55+ messages in thread

* RE: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-18 12:07           ` Peter Zijlstra
  2017-01-18 12:14             ` byungchul.park
@ 2017-01-18 12:49             ` byungchul.park
  1 sibling, 0 replies; 55+ messages in thread
From: byungchul.park @ 2017-01-18 12:49 UTC (permalink / raw)
  To: 'Peter Zijlstra'
  Cc: 'Boqun Feng',
	mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

> -----Original Message-----
> From: byungchul.park [mailto:byungchul.park@lge.com]
> Sent: Wednesday, January 18, 2017 9:15 PM
> To: 'Peter Zijlstra'
> Cc: 'Boqun Feng'; 'mingo@kernel.org'; 'tglx@linutronix.de';
> 'walken@google.com'; 'kirill@shutemov.name'; 'linux-
> kernel@vger.kernel.org'; 'linux-mm@kvack.org'; 'iamjoonsoo.kim@lge.com';
> 'akpm@linux-foundation.org'; 'npiggin@gmail.com'
> Subject: RE: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
> 
> > -----Original Message-----
> > From: Peter Zijlstra [mailto:peterz@infradead.org]
> > Sent: Wednesday, January 18, 2017 9:08 PM
> > To: Byungchul Park
> > Cc: Boqun Feng; mingo@kernel.org; tglx@linutronix.de; walken@google.com;
> > kirill@shutemov.name; linux-kernel@vger.kernel.org; linux-mm@kvack.org;
> > iamjoonsoo.kim@lge.com; akpm@linux-foundation.org; npiggin@gmail.com
> > Subject: Re: [PATCH v4 15/15] lockdep: Crossrelease feature
> documentation
> >
> > On Wed, Jan 18, 2017 at 08:54:28PM +0900, Byungchul Park wrote:
> > > On Wed, Jan 18, 2017 at 12:03:17PM +0100, Peter Zijlstra wrote:
> > > > On Wed, Jan 18, 2017 at 07:53:47PM +0900, Byungchul Park wrote:
> > > > > On Wed, Jan 18, 2017 at 02:42:30PM +0800, Boqun Feng wrote:
> > > > > > On Fri, Dec 09, 2016 at 02:12:11PM +0900, Byungchul Park wrote:
> > > > > > [...]
> > > > > > > +Example 1:
> > > > > > > +
> > > > > > > +   CONTEXT X		   CONTEXT Y
> > > > > > > +   ---------		   ---------
> > > > > > > +   mutext_lock A
> > > > > > > +			   lock_page B
> > > > > > > +   lock_page B
> > > > > > > +			   mutext_lock A /* DEADLOCK */
> > > > > >
> > > > > > s/mutext_lock/mutex_lock
> > > > >
> > > > > Thank you.
> > > > >
> > > > > > > +Example 3:
> > > > > > > +
> > > > > > > +   CONTEXT X		   CONTEXT Y
> > > > > > > +   ---------		   ---------
> > > > > > > +			   mutex_lock A
> > > > > > > +   mutex_lock A
> > > > > > > +   mutex_unlock A
> > > > > > > +			   wait_for_complete B /* DEADLOCK */
> > > > > >
> > > > > > I think this part better be:
> > > > > >
> > > > > >    CONTEXT X		   CONTEXT Y
> > > > > >    ---------		   ---------
> > > > > >    			   mutex_lock A
> > > > > >    mutex_lock A
> > > > > >    			   wait_for_complete B /* DEADLOCK */
> > > > > >    mutex_unlock A
> > > > > >
> > > > > > , right? Because Y triggers DEADLOCK before X could run
> > mutex_unlock().
> > > > >
> > > > > There's no different between two examples.
> > > >
> > > > There is..
> > > >
> > > > > No matter which one is chosen, mutex_lock A in CONTEXT X cannot be
> > passed.
> > > >
> > > > But your version shows it does mutex_unlock() before CONTEXT Y does
> > > > wait_for_completion().
> > > >
> > > > The thing about these diagrams is that both columns are assumed to
> > have
> > > > the same timeline.
> > >
> > > X cannot acquire mutex A because Y already acquired it.
> > >
> > > In order words, all statements below mutex_lock A in X cannot run.
> >
> > But your timeline shows it does, which is the error that Boqun pointed
> > out.
> 
> I am sorry for not understanding what you are talking about.
> 
> Do you mean that I should remove all statements below mutex_lock A in X?
> 
> Or should I move mutex_unlock as Boqun said? What will change?

Anyway, I will change it as he said even though I don't understand what is
different between them. :/ But I am just curious. It would be appreciated
if you answer my question.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-18 12:14             ` byungchul.park
@ 2017-01-18 14:12               ` Peter Zijlstra
  2017-01-19  1:54                 ` Byungchul Park
  0 siblings, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-18 14:12 UTC (permalink / raw)
  To: byungchul.park
  Cc: 'Boqun Feng',
	mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Wed, Jan 18, 2017 at 09:14:59PM +0900, byungchul.park wrote:

> +Example 3:
> +
> +   CONTEXT X		   CONTEXT Y
> +   ---------		   ---------
> +			   mutex_lock A
> +   mutex_lock A
> +   mutex_unlock A
> +			   wait_for_complete B /* DEADLOCK */

Each line (across both columns) is a distinct point in time after the
line before.

Therefore, this states that "mutex_unlock A" happens before
"wait_for_completion B", which is clearly impossible.

You don't have to remove everything after mutex_lock A, but the unlock
must not happen before context Y does the unlock.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2017-01-18  2:04         ` Byungchul Park
@ 2017-01-18 15:10           ` Peter Zijlstra
  2017-01-19  2:47             ` Byungchul Park
  0 siblings, 1 reply; 55+ messages in thread
From: Peter Zijlstra @ 2017-01-18 15:10 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Wed, Jan 18, 2017 at 11:04:32AM +0900, Byungchul Park wrote:
> On Tue, Jan 17, 2017 at 04:54:31PM +0100, Peter Zijlstra wrote:
> > On Fri, Jan 13, 2017 at 07:11:43PM +0900, Byungchul Park wrote:
> > > What do you think about the following patches doing it?
> > 
> > I was more thinking about something like so...
> > 
> > Also, I think I want to muck with struct stack_trace; the members:
> > max_nr_entries and skip are input arguments to save_stack_trace() and
> > bloat the structure for no reason.
> 
> With your approach, save_trace() must be called whenever check_prevs_add()
> is called, which might be unnecessary.

True.. but since we hold the graph_lock this is a slow path anyway, so I
didn't care much.

Then again, I forgot to clean up in a bunch of paths.

> Frankly speaking, I think what I proposed resolved it neatly. Don't you
> think so?

My initial reaction was to your patches being radically different to
what I had proposed. But after fixing mine I don't particularly like
either one of them.

Also, I think yours has a hole in, you check nr_stack_trace_entries
against an older copy to check we did save_stack(), this is not accurate
as check_prev_add() can drop graph_lock in the verbose case and then
someone else could have done save_stack().


Let me see if I can find something simpler..

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 15/15] lockdep: Crossrelease feature documentation
  2017-01-18 14:12               ` Peter Zijlstra
@ 2017-01-19  1:54                 ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-19  1:54 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: 'Boqun Feng',
	mingo, tglx, walken, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Wed, Jan 18, 2017 at 03:12:55PM +0100, Peter Zijlstra wrote:
> On Wed, Jan 18, 2017 at 09:14:59PM +0900, byungchul.park wrote:
> 
> > +Example 3:
> > +
> > +   CONTEXT X		   CONTEXT Y
> > +   ---------		   ---------
> > +			   mutex_lock A
> > +   mutex_lock A
> > +   mutex_unlock A
> > +			   wait_for_complete B /* DEADLOCK */
> 
> Each line (across both columns) is a distinct point in time after the
> line before.
> 
> Therefore, this states that "mutex_unlock A" happens before
> "wait_for_completion B", which is clearly impossible.

I meant that all statements below mutex_lock A in X are already impossible.
So the order of those are meaningless. But.. I got what you mean.

> You don't have to remove everything after mutex_lock A, but the unlock
> must not happen before context Y does the unlock.

I will apply what you and boqun recommanded, from the next spin.

Thank you,
Byungchul

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2017-01-18 15:10           ` Peter Zijlstra
@ 2017-01-19  2:47             ` Byungchul Park
  0 siblings, 0 replies; 55+ messages in thread
From: Byungchul Park @ 2017-01-19  2:47 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Wed, Jan 18, 2017 at 04:10:53PM +0100, Peter Zijlstra wrote:
> On Wed, Jan 18, 2017 at 11:04:32AM +0900, Byungchul Park wrote:
> > On Tue, Jan 17, 2017 at 04:54:31PM +0100, Peter Zijlstra wrote:
> > > On Fri, Jan 13, 2017 at 07:11:43PM +0900, Byungchul Park wrote:
> > > > What do you think about the following patches doing it?
> > > 
> > > I was more thinking about something like so...
> > > 
> > > Also, I think I want to muck with struct stack_trace; the members:
> > > max_nr_entries and skip are input arguments to save_stack_trace() and
> > > bloat the structure for no reason.
> > 
> > With your approach, save_trace() must be called whenever check_prevs_add()
> > is called, which might be unnecessary.
> 
> True.. but since we hold the graph_lock this is a slow path anyway, so I
> didn't care much.

If we don't need to care it, the problem becomes easy to solve. But IMHO,
it'd be better to care it as original lockdep code did, because
save_trace() might have bigger overhead than we expect and
check_prevs_add() can be called frequently, so it'd be better to avoid it
when possible.

> Then again, I forgot to clean up in a bunch of paths.
> 
> > Frankly speaking, I think what I proposed resolved it neatly. Don't you
> > think so?
> 
> My initial reaction was to your patches being radically different to
> what I had proposed. But after fixing mine I don't particularly like
> either one of them.
> 
> Also, I think yours has a hole in, you check nr_stack_trace_entries
> against an older copy to check we did save_stack(), this is not accurate
> as check_prev_add() can drop graph_lock in the verbose case and then
> someone else could have done save_stack().

Right. My mistake..

Then.. The following patch on top of my patch 2/2 can solve it. Right?

---

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 49b9386..0f5bded 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1892,7 +1892,7 @@ static inline void inc_chains(void)
 		if (entry->class == hlock_class(next)) {
 			if (distance == 1)
 				entry->distance = 1;
-			return 2;
+			return 1;
 		}
 	}
 
@@ -1927,9 +1927,10 @@ static inline void inc_chains(void)
 		print_lock_name(hlock_class(next));
 		printk(KERN_CONT "\n");
 		dump_stack();
-		return graph_lock();
+		if (!graph_lock())
+			return 0;
 	}
-	return 1;
+	return 2;
 }
 
 /*
@@ -1975,15 +1976,16 @@ static inline void inc_chains(void)
 			 * added:
 			 */
 			if (hlock->read != 2 && hlock->check) {
-				if (!check_prev_add(curr, hlock, next,
-							distance, &trace, save))
+				int ret = check_prev_add(curr, hlock, next,
+							distance, &trace, save);
+				if (!ret)
 					return 0;
 
 				/*
 				 * Stop saving stack_trace if save_trace() was
 				 * called at least once:
 				 */
-				if (save && start_nr != nr_stack_trace_entries)
+				if (save && ret == 2)
 					save = NULL;
 
 				/*

^ permalink raw reply related	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2017-01-19  3:18 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-09  5:11 [PATCH v4 00/15] lockdep: Implement crossrelease feature Byungchul Park
2016-12-09  5:11 ` [PATCH v4 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
2016-12-09  5:11 ` [PATCH v4 02/15] x86/dumpstack: Add save_stack_trace()_fast() Byungchul Park
2016-12-09  5:11 ` [PATCH v4 03/15] lockdep: Refactor lookup_chain_cache() Byungchul Park
2016-12-09  5:12 ` [PATCH v4 04/15] lockdep: Add a function building a chain between two classes Byungchul Park
2017-01-10 21:00   ` Peter Zijlstra
2017-01-12  1:41     ` Byungchul Park
2016-12-09  5:12 ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
2017-01-12 16:16   ` Peter Zijlstra
2017-01-13  2:45     ` Byungchul Park
2017-01-13  4:09     ` [PATCH] lockdep: Make a stack_trace instance passed to check_prev_add as an arg Byungchul Park
2017-01-13  4:38       ` Byungchul Park
2017-01-13 10:11     ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
2017-01-13 10:17       ` [PATCH 1/2] lockdep: Refactor save_trace() Byungchul Park
2017-01-13 10:17       ` [PATCH 2/2] lockdep: Pass a callback arg to check_prev_add() to handle stack_trace Byungchul Park
2017-01-17 15:54       ` [PATCH v4 05/15] lockdep: Make check_prev_add can use a separate stack_trace Peter Zijlstra
2017-01-18  2:04         ` Byungchul Park
2017-01-18 15:10           ` Peter Zijlstra
2017-01-19  2:47             ` Byungchul Park
2016-12-09  5:12 ` [PATCH v4 06/15] lockdep: Make save_trace can skip stack tracing of the current Byungchul Park
2017-01-12 16:37   ` Peter Zijlstra
2017-01-13  0:18     ` Byungchul Park
2016-12-09  5:12 ` [PATCH v4 07/15] lockdep: Implement crossrelease feature Byungchul Park
2017-01-13  4:39   ` Lai Jiangshan
2017-01-13  5:02     ` Byungchul Park
2017-01-16 15:10   ` Peter Zijlstra
2017-01-17  2:05     ` Byungchul Park
2017-01-17  7:12       ` Peter Zijlstra
2017-01-17  7:49         ` Byungchul Park
2017-01-17  7:14       ` Peter Zijlstra
2017-01-17  7:45         ` Byungchul Park
2017-01-16 15:13   ` Peter Zijlstra
2017-01-17  2:33     ` Byungchul Park
2017-01-17  6:24       ` Boqun Feng
2017-01-17  7:43         ` Byungchul Park
2016-12-09  5:12 ` [PATCH v4 08/15] lockdep: Make crossrelease use save_stack_trace_fast() Byungchul Park
2016-12-09  5:12 ` [PATCH v4 09/15] lockdep: Make print_circular_bug() crosslock-aware Byungchul Park
2016-12-09  5:12 ` [PATCH v4 10/15] lockdep: Apply crossrelease to completion operation Byungchul Park
2016-12-09  5:12 ` [PATCH v4 11/15] pagemap.h: Remove trailing white space Byungchul Park
2016-12-09  5:12 ` [PATCH v4 12/15] lockdep: Apply crossrelease to PG_locked lock Byungchul Park
2016-12-09  5:12 ` [PATCH v4 13/15] lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked Byungchul Park
2016-12-09  5:12 ` [PATCH v4 14/15] lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to page_ext Byungchul Park
2016-12-09  5:12 ` [PATCH v4 15/15] lockdep: Crossrelease feature documentation Byungchul Park
2017-01-10 20:08   ` Peter Zijlstra
2017-01-11  1:29     ` Byungchul Park
2017-01-18  6:42   ` Boqun Feng
2017-01-18 10:53     ` Byungchul Park
2017-01-18 11:03       ` Peter Zijlstra
2017-01-18 11:54         ` Byungchul Park
2017-01-18 12:07           ` Peter Zijlstra
2017-01-18 12:14             ` byungchul.park
2017-01-18 14:12               ` Peter Zijlstra
2017-01-19  1:54                 ` Byungchul Park
2017-01-18 12:49             ` byungchul.park
2016-12-09  5:21 ` [FYI] Output of 'cat /proc/lockdep' after applying crossrelease Byungchul Park

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).