linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/15] lockdep: Implement crossrelease feature
@ 2016-09-13  9:44 Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
                   ` (16 more replies)
  0 siblings, 17 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:44 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.

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

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 | 785 ++++++++++++++++++++++++++++++++
 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, 2124 insertions(+), 135 deletions(-)
 create mode 100644 Documentation/locking/crossrelease.txt

-- 
1.9.1

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

* [PATCH v3 01/15] x86/dumpstack: Optimize save_stack_trace
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13 13:18   ` Josh Poimboeuf
  2016-09-13  9:45 ` [PATCH v3 02/15] x86/dumpstack: Add save_stack_trace()_fast() Byungchul Park
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 02/15] x86/dumpstack: Add save_stack_trace()_fast()
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13 13:20   ` Josh Poimboeuf
  2016-09-13  9:45 ` [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache() Byungchul Park
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache()
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 02/15] x86/dumpstack: Add save_stack_trace()_fast() Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-15 15:33   ` Nilay Vaish
  2016-09-13  9:45 ` [PATCH v3 04/15] lockdep: Add a function building a chain between two classes Byungchul Park
                   ` (13 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 04/15] lockdep: Add a function building a chain between two classes
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (2 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache() Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 05/15] lockdep: Make check_prev_add can use a separate stack_trace
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (3 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 04/15] lockdep: Add a function building a chain between two classes Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 06/15] lockdep: Make save_trace can skip stack tracing of the current Byungchul Park
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 06/15] lockdep: Make save_trace can skip stack tracing of the current
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (4 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 07/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (5 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 06/15] lockdep: Make save_trace can skip stack tracing of the current Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13 10:05   ` Peter Zijlstra
  2016-09-13 15:05   ` Peter Zijlstra
  2016-09-13  9:45 ` [PATCH v3 08/15] lockdep: Make crossrelease use save_stack_trace_fast() Byungchul Park
                   ` (9 subsequent siblings)
  16 siblings, 2 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 08/15] lockdep: Make crossrelease use save_stack_trace_fast()
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (6 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 07/15] lockdep: Implement crossrelease feature Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 09/15] lockdep: Make print_circular_bug() crosslock-aware Byungchul Park
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 09/15] lockdep: Make print_circular_bug() crosslock-aware
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (7 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 08/15] lockdep: Make crossrelease use save_stack_trace_fast() Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 10/15] lockdep: Apply crossrelease to completion operation Byungchul Park
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 10/15] lockdep: Apply crossrelease to completion operation
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (8 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 09/15] lockdep: Make print_circular_bug() crosslock-aware Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 11/15] pagemap.h: Remove trailing white space Byungchul Park
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 11/15] pagemap.h: Remove trailing white space
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (9 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 10/15] lockdep: Apply crossrelease to completion operation Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 12/15] lockdep: Apply crossrelease to PG_locked lock Byungchul Park
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 12/15] lockdep: Apply crossrelease to PG_locked lock
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (10 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 11/15] pagemap.h: Remove trailing white space Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 13/15] lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked Byungchul Park
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 13/15] lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (11 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 12/15] lockdep: Apply crossrelease to PG_locked lock Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 14/15] lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to page_ext Byungchul Park
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 14/15] lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to page_ext
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (12 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 13/15] lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-13  9:45 ` [PATCH v3 15/15] lockdep: Crossrelease feature documentation Byungchul Park
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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] 49+ messages in thread

* [PATCH v3 15/15] lockdep: Crossrelease feature documentation
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (13 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 14/15] lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to page_ext Byungchul Park
@ 2016-09-13  9:45 ` Byungchul Park
  2016-09-15 17:25   ` Nilay Vaish
  2016-09-16 15:47   ` Nilay Vaish
  2016-09-13  9:58 ` [FYI] Output of 'cat /proc/lockdep' after applying crossrelease Byungchul Park
  2016-11-02  5:42 ` [REVISED DOC on v3] Crossrelease Lockdep Byungchul Park
  16 siblings, 2 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:45 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 | 785 +++++++++++++++++++++++++++++++++
 1 file changed, 785 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..78558af
--- /dev/null
+++ b/Documentation/locking/crossrelease.txt
@@ -0,0 +1,785 @@
+Crossrelease
+============
+
+Started by Byungchul Park <byungchul.park@lge.com>
+
+Contents:
+
+ (*) Background.
+
+     - What causes deadlock.
+     - What lockdep detects.
+     - How lockdep works.
+
+ (*) Limitation.
+
+     - Limit to typical lock.
+     - Pros from the limitation.
+     - Cons from the limitation.
+
+ (*) Generalization.
+
+     - Relax the limitation.
+
+ (*) Crossrelease.
+
+     - Introduce crossrelease.
+     - Introduce commit.
+
+ (*) Implementation.
+
+     - Data structures.
+     - How crossrelease works.
+
+ (*) Optimizations.
+
+     - Avoid duplication.
+     - Avoid lock contention.
+
+
+==========
+Background
+==========
+
+What causes deadlock
+--------------------
+
+A deadlock occurs when a context is waiting for an event to be issued
+which cannot be issued because the context or another context who can
+issue the event is also waiting for an event to be issued which cannot
+be issued. Single context or more than one context both waiting for an
+event and issuing an event may paricipate in a deadlock.
+
+For example,
+
+A context who can issue event D is waiting for event A to be issued.
+A context who can issue event A is waiting for event B to be issued.
+A context who can issue event B is waiting for event C to be issued.
+A context who can issue event C is waiting for event D to be issued.
+
+A deadlock occurs when these four operations are run at a time because
+event D cannot be issued if event A isn't issued which in turn cannot be
+issued if event B isn't issued which in turn cannot be issued if event C
+isn't issued which in turn cannot be issued if event D isn't issued. No
+event can be issued since any of them never meets its precondition.
+
+We can easily recognize that each wait operation creates a dependency
+between two issuings e.g. between issuing D and issuing A like, 'event D
+cannot be issued if event A isn't issued', in other words, 'issuing
+event D depends on issuing event A'. So the whole example can be
+rewritten in terms of dependency,
+
+Do an operation making 'event D cannot be issued if event A isn't issued'.
+Do an operation making 'event A cannot be issued if event B isn't issued'.
+Do an operation making 'event B cannot be issued if event C isn't issued'.
+Do an operation making 'event C cannot be issued if event D isn't issued'.
+
+or,
+
+Do an operation making 'issuing event D depends on issuing event A'.
+Do an operation making 'issuing event A depends on issuing event B'.
+Do an operation making 'issuing event B depends on issuing event C'.
+Do an operation making 'issuing event C depends on issuing event D'.
+
+What causes a deadlock is a set of dependencies a chain of which forms a
+cycle, which means that issuing event D depending on issuing event A
+depending on issuing event B depending on issuing event C depending on
+issuing event D, finally depends on issuing event D itself, which means
+no event can be issued.
+
+Any set of operations creating dependencies causes a deadlock. The set
+of lock operations e.g. acquire and release is an example. Waiting for a
+lock to be released corresponds to waiting for an event and releasing a
+lock corresponds to issuing an event. So the description of dependency
+above can be altered to one in terms of lock.
+
+In terms of event, issuing event A depends on issuing event B if,
+
+	Event A cannot be issued if event B isn't issued.
+
+In terms of lock, releasing lock A depends on releasing lock B if,
+
+	Lock A cannot be released if lock B isn't released.
+
+CONCLUSION
+
+A set of dependencies a chain of which forms a cycle, causes a deadlock,
+no matter what creates the dependencies.
+
+
+What lockdep detects
+--------------------
+
+A deadlock actually occurs only when all operations creating problematic
+dependencies are run at a time. However, even if it has not happend, the
+deadlock potentially can occur if the problematic dependencies obviously
+exist. Thus it's meaningful to detect not only an actual deadlock but
+also its possibility. Lockdep does the both.
+
+Whether a deadlock actually occurs or not depends on several factors,
+which means a deadlock may not occur even though problematic
+dependencies exist. For example, what order contexts are switched in is
+a factor. A deadlock will occur when contexts are switched so that all
+operations causing a deadlock become run simultaneously.
+
+Lockdep tries to detect a deadlock or its possibility aggressively,
+though it also tries to avoid false positive detections. So lockdep is
+designed to consider all possible combinations of dependencies so that
+it can detect all potential possibilities of deadlock in advance. What
+lockdep tries in order to consider all possibilities are,
+
+1. Use a global dependency graph including all dependencies.
+
+   What lockdep checks is based on dependencies instead of what actually
+   happened. So no matter which context or call path a new dependency is
+   detected in, it's just referred to as a global factor.
+
+2. Use lock classes than lock instances when checking dependencies.
+
+   What actually causes a deadlock is lock instances. However, lockdep
+   uses lock classes than its instances when checking dependencies since
+   any instance of a same lock class can be altered anytime.
+
+So lockdep detects both an actual deadlock and its possibility. But the
+latter is more valuable than the former. When a deadlock actually
+occures, 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 does, the fisrt one is more valuable,
+
+1. Detecting and reporting deadlock possibility.
+2. Detecting and reporting a deadlock actually occured.
+
+
+How lockdep works
+-----------------
+
+What lockdep should do, to detect a deadlock or its possibility are,
+
+1. Detect a new dependency created.
+2. Keep the dependency in a global data structure esp. graph.
+3. Check if any of all possible chains of dependencies forms a cycle.
+4. Report a deadlock or its possibility if a cycle is detected.
+
+A graph used by lockdep to keep all dependencies looks like,
+
+A -> B -        -> F -> G
+        \      /
+         -> E -        -> L
+        /      \      /
+C -> D -        -> H -
+                      \
+                       -> I -> K
+                      /
+                   J -
+
+where A, B,..., L are different lock classes.
+
+Lockdep adds a dependency into graph when a new dependency is detected.
+For example, it adds a dependency 'A -> B' when a dependency between
+releasing lock A and releasing lock B, which has not been added yet, is
+detected. It does same thing on other dependencies, too. See 'What
+causes deadlock' section.
+
+NOTE: Precisely speaking, a dependency is one between releasing a lock
+and releasing another lock as described in 'What causes deadlock'
+section. However from now on, we will describe a dependency as if it's
+one between a lock and another lock for simplicity. Then 'A -> B' can be
+described as a dependency between lock A and lock B.
+
+We already checked how a problematic set of dependencies causes a
+deadlock in 'What causes deadlock' section. This time let's check if a
+deadlock or its possibility can be detected using a problematic set of
+dependencies. Assume that 'A -> B', 'B -> E' and 'E -> A' were added in
+the sequence into graph. Then the graph finally will be,
+
+ -> A -> B -> E -
+/                \
+\                /
+ ----------------
+
+where A, B and E are different lock classes.
+
+From adding three dependencies, a cycle was created which means, by
+definition of dependency, the situation 'lock E must be released to
+release lock B which in turn must be released to release lock A which in
+turn must be released to release lock E which in turn must be released
+to release B and so on infinitely' can happen.
+
+Once the situation happens, no lock can be released since any of them
+can never meet each precondition. It's a deadlock. Lockdep can detect a
+deadlock or its possibility with checking if a cycle was created after
+adding each dependency into graph. This is how lockdep detects a
+deadlock or its possibility.
+
+CONCLUSION
+
+Lockdep detects a deadlock or its possibility with checking if a cycle
+was created after adding each dependency into graph.
+
+
+==========
+Limitation
+==========
+
+Limit to typical lock
+---------------------
+
+Limiting what lockdep has to consider to only ones satisfying the
+following condition, the implementation of adding dependencies becomes
+simple while its capacity for detection becomes limited. Typical lock
+e.g. spin lock and mutex is the case. Let's check what pros and cons of
+it are, in next section.
+
+	A lock should be released within the context holding the lock.
+
+CONCLUSION
+
+Limiting what lockdep has to consider to typical lock e.g. spin lock and
+mutex, the implmentation becomes simple while it has a limited capacity.
+
+
+Pros from the limitation
+------------------------
+
+Given the limitation, when acquiring a lock, any lock being in
+held_locks of the acquire context cannot be released if the lock to
+acquire was not released yet. Yes. It's the exact case to add a new
+dependency 'A -> B' into graph, where lock A represents each lock being
+in held_locks and lock B represents the lock to acquire.
+
+For example, only considering typical lock,
+
+	PROCESS 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, there's nothing in held_locks of PROCESS X thus
+no dependency is added. When acquiring lock B, lockdep detects and adds
+a new dependency 'A -> B' between lock A being in held_locks and lock B.
+And when acquiring lock C, lockdep also adds another dependency 'B -> C'
+for same reason. They are added when acquiring each lock, simply.
+
+NOTE: Even though every lock being in held_locks depends on the lock to
+acquire, lockdep does not add all dependencies between them because all
+of them can be covered by other dependencies except one dependency
+between the lock on top of held_locks and the lock to acquire, which
+must be added.
+
+Besides, we can expect several advantages from the limitation.
+
+1. Any lock being in held_locks cannot be released unconditionally if
+   the context is stuck, thus we can easily identify a dependency when
+   acquiring a lock.
+
+2. Considering only locks being in local held_locks of a single context
+   makes some races avoidable, even though it fails of course when
+   modifying its global dependency graph.
+
+3. To build a dependency graph, lockdep only needs to keep locks not
+   released yet. However relaxing the limitation, it might need to keep
+   even locks already released, additionally. See 'Crossrelease' section.
+
+CONCLUSION
+
+Given the limitation, the implementation becomes simple and efficient.
+
+
+Cons from the limitation
+------------------------
+
+Given the limitation, lockdep is applicable only to typical lock. For
+example, page lock for page access or completion for synchronization
+cannot play with lockdep having the limitation. However since page lock
+or completion also causes a deadlock, it would be better to detect a
+deadlock or its possibility even for them.
+
+Can we detect deadlocks below with lockdep having the limitation?
+
+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 held by 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.
+
+CONCLUSION
+
+Given the limitation, lockdep cannot detect a deadlock or its
+possibility caused by page lock or completion.
+
+
+==============
+Generalization
+==============
+
+Relax the limitation
+--------------------
+
+Detecting and adding new 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. More dependencies lockdep adds, more
+throughly it can work. Therefore Lockdep has to do its best to add as
+many true dependencies as possible.
+
+Relaxing the limitation, lockdep can add additional dependencies since
+it makes lockdep deal with additional ones creating the dependencies e.g.
+page lock or completion, which might be released in any context. Even so,
+it needs to be noted that behaviors adding dependencies created by
+typical lock don't need to be changed at all.
+
+For example, only considering typical lock, 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, and upper case letters
+represent typical lock.
+
+After the relaxing, the graph will have additional dependencies like,
+
+A -> B -        -> F -> G
+        \      /
+         -> E -        -> L -> c
+        /      \      /
+C -> D -        -> H -
+               /      \
+            a -        -> I -> K
+                      /
+              b -> J -
+
+where a, b, c, A, B,..., L are different lock classes, and upper case
+letters represent typical lock while lower case letters represent
+non-typical lock e.g. page lock and completion.
+
+However, it might suffer performance degradation since relaxing the
+limitation with which design and implementation of lockdep become
+efficient might introduce inefficiency inevitably. Each option, that is,
+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.
+
+In the latter, of course, some contexts are not allowed if they
+themselves cause a deadlock. 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 a cycle of a dependency chain, meaning a
+deadlock. Otherwise, any contexts are allowed to release it.
+
+CONCLUSION
+
+Relaxing the limitation, lockdep adds additional dependencies and gets
+additional chances to check if they cause a deadlock. It makes lockdep
+additionally deal with what might be released in any context.
+
+
+============
+Crossrelease
+============
+
+Introduce crossrelease
+----------------------
+
+To allow lockdep to add additional dependencies created by what might be
+released in any context, which we call 'crosslock', it's necessary to
+introduce a new feature which makes it possible to identify and add the
+dependencies. We call the feature 'crossrelease'. Crossrelease feature
+has to do,
+
+1. Identify a new dependency created by crosslock.
+2. Add the dependency into graph when identifying it.
+
+That's all. Once a meaningful dependency is added into graph, lockdep
+will work with the graph as it did. So the most important thing to do is
+to identify a dependency created by crosslock. Remind what a dependency
+is. For example, Lock A depends on lock B if 'lock A cannot be released
+if lock B isn't released'. See 'What causes deadlock' section.
+
+By definition, a lock depends on every lock having been added into
+held_locks in the lock's release context since the lock was acquired,
+because the lock cannot be released if the release context is stuck by
+any of dependent locks, not released. So lockdep should technically
+consider release contexts of locks to identify dependencies.
+
+It's no matter of course to typical lock because acquire context is same
+as release context for typical lock, which means lockdep would work with
+considering only acquire contexts for typical lock. However, for
+crosslock, lockdep cannot identify release context and any dependency
+until the crosslock will be actually released.
+
+Regarding crosslock, lockdep has to record all history by queueing all
+locks potentially creating dependencies so that real dependencies can be
+added using the history recorded when identifying release context. We
+call it 'commit', that is, to add dependencies in batches. See
+'Introduce commit' section.
+
+Of course, some actual deadlocks caused by crosslock cannot be detected
+at the time it happened, because the deadlocks cannot be indentified and
+detected until the crosslock will be actually released. But this way
+deadlock possibility can be detected and it's worth just possibility
+detection of deadlock. See 'What lockdep does' section.
+
+CONCLUSION
+
+With crossrelease feature, lockdep can works with what might be released
+in any context, namely, crosslock.
+
+
+Introduce commit
+----------------
+
+Crossrelease feature names it 'commit' to identify and add dependencies
+into graph in batches. Lockdep is already doing what commit does when
+acquiring a lock, for typical lock. However, that way must be changed
+for crosslock so that it identifies the crosslock's release context
+first and then does commit.
+
+The main reason why lockdep performs additional step, namely commit, for
+crosslock is that some dependencies by crosslock cannot be identified
+until the crosslock's release context is eventually identified, though
+some other dependencies by crosslock can. There are four kinds of
+dependencies to consider.
+
+1. 'typical lock A -> typical lock B' dependency
+
+   Just when acquiring lock B, lockdep can identify the dependency
+   between lock A and lock B as it did. Commit is unnecessary.
+
+2. 'typical lock A -> crosslock b' dependency
+
+   Just when acquiring crosslock b, lockdep can identify the dependency
+   between lock A and crosslock B as well. Commit is unnecessary, too.
+
+3. 'crosslock a -> typical lock B' dependency
+
+   When acquiring lock B, lockdep cannot identify the dependency. It can
+   be identified only when crosslock a is released. Commit is necessary.
+
+4. 'crosslock a -> crosslock b' dependency
+
+   Creating this kind of dependency directly is unnecessary since it can
+   be covered by other kinds of dependencies.
+
+Lockdep works without commit during dealing with only typical locks.
+However, it needs to perform commit step, once at least one crosslock is
+acquired, until all crosslocks in progress are released. Introducing
+commit, lockdep performs three steps i.e. acquire, commit and release.
+What lockdep should do in each step is like,
+
+1. Acquire
+
+   1) For typical lock
+
+	Lockdep does what it originally does and queues the lock so
+	that lockdep can check dependencies using it at commit step.
+
+   2) For crosslock
+
+	The crosslock is added to a global linked list so that lockdep
+	can check dependencies using it at commit step.
+
+2. Commit
+
+   1) For typical lock
+
+	N/A.
+
+   2) For crosslock
+
+	Lockdep checks and adds dependencies using data saved at acquire
+	step, as if the dependencies were added without commit 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
+
+Lockdep can detect a deadlock or its possibility caused by what might be
+released in any context, using commit step, where it checks and adds
+dependencies in batches.
+
+
+==============
+Implementation
+==============
+
+Data structures
+---------------
+
+Crossrelease feature introduces two main data structures.
+
+1. pend_lock (or plock)
+
+   This is an array embedded in task_struct, for keeping locks queued so
+   that real dependencies can be added using them at commit step. So
+   this data can be accessed locklessly within the owner context. The
+   array is filled when acquiring a typical lock and consumed when doing
+   commit. And it's managed in circular manner.
+
+2. cross_lock (or xlock)
+
+   This is a global linked list, for keeping all crosslocks in progress.
+   The list grows when acquiring a crosslock and is shrunk when
+   releasing the crosslock. lockdep_init_map_crosslock() should be used
+   to initialize a crosslock instance instead of lockdep_init_map() so
+   that lockdep can recognize it as crosslock.
+
+CONCLUSION
+
+Crossrelease feature uses 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 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 lock.
+
+	RELEASE CONTEXT of A (= ACQUIRE CONTEXT of A)
+	--------------------
+	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, and upper case letters
+represent typical lock.
+
+After adding 'A -> B', the dependency graph will be,
+
+A -> B
+
+where A and B are different lock classes, and upper case letters
+represent typical lock.
+
+And after adding 'B -> C', the graph will be,
+
+A -> B -> C
+
+where A, B and C are different lock classes, and upper case letters
+represent typical lock.
+
+What if applying commit on typical locks? It's not necessary for typical
+lock. Just for showing what commit does.
+
+	RELEASE CONTEXT of A (= ACQUIRE CONTEXT of A)
+	--------------------
+	acquire A -> mark A as started (nothing before, no queueing)
+
+	acquire B -> mark B as started and queue B
+
+	acquire C -> mark C as started and queue C
+
+	release C -> commit C (nothing queued since C started)
+
+	release B -> commit B -> add a dependency 'B -> C'
+
+	release A -> commit A -> add dependencies 'A -> B' and 'A -> C'
+
+where A, B and C are different lock classes, and upper case letters
+represent typical lock.
+
+After doing commit A, B and C, the dependency graph becomes like,
+
+A -> B -> C
+
+where A, B and C are different lock classes, and upper case letters
+represent typical lock.
+
+NOTE: A dependency 'A -> C' is optimized out.
+
+Here we can see the final graph is same as the graph built without
+commit. Of course the former way leads to finish building the graph
+earlier than the latter way, 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 using commit, for crosslock.
+
+Let's look at how commit works for crosslock.
+
+	RELEASE CONTEXT of a	ACQUIRE CONTEXT of a
+	--------------------	--------------------
+				acquire a -> mark a as started
+
+	(serialized by some means e.g. barrier)
+
+	acquire D -> queue D
+				acquire B -> queue B
+	release D
+				acquire C -> add 'B -> C' and queue C
+	acquire E -> queue E
+				acquire D -> add 'C -> D' and queue D
+	release E
+				release D
+	release a -> commit a -> add 'a -> D' and 'a -> E'
+				release C
+
+				release B
+
+where a, B,..., E are different lock classes, and upper case letters
+represent typical lock while lower case letters represent crosslock.
+
+When acquiring crosslock a, no dependency can be added since there's
+nothing in the held_locks. However, crossrelease feature marks the
+crosslock as started, which means all locks to acquire from now are
+candidates which might create new dependencies later when identifying
+release context.
+
+When acquiring lock B, lockdep does what it originally does for typical
+lock and additionally queues the lock for later commit to refer to
+because it might be a dependent lock of the crosslock. It does same
+thing on lock C, D and E. And then two dependencies 'a -> D' and 'a -> E'
+are added when identifying the release context, at commit step.
+
+The final graph is, with crossrelease feature using commit,
+
+B -> C -
+        \
+         -> D
+        /
+     a -
+        \
+         -> E
+
+where a, B,..., E are different lock classes, and upper case letters
+represent typical lock while lower case letters represent crosslock.
+
+However, without crossrelease feature, the final graph will be,
+
+B -> C -> D
+
+where B and C are different lock classes, and upper case letters
+represent typical lock.
+
+The former graph has two more dependencies 'a -> D' and 'a -> E' giving
+additional chances to check if they cause a deadlock. This way lockdep
+can detect a deadlock or its possibility caused by crosslock. Again,
+behaviors adding dependencies created by only typical locks are not
+changed at all.
+
+CONCLUSION
+
+Crossrelease works using commit for crosslock, leaving behaviors adding
+dependencies between only typical locks unchanged.
+
+
+=============
+Optimizations
+=============
+
+Avoid duplication
+-----------------
+
+Crossrelease feature uses a cache like what lockdep already uses for
+dependency chains, but this time it's for caching one dependency like
+'crosslock -> typical lock' crossing between two different context. Once
+that dependency is cached, same dependency will never be added any more.
+Even queueing unnecessary locks is also prevented based on the cache.
+
+CONCLUSION
+
+Crossrelease does not add any duplicate dependency.
+
+
+Avoid lock contention
+---------------------
+
+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 each array to be accessed only within each own
+context. It's like how held_locks is accessed. Lockless implmentation is
+important since typical locks are very frequently accessed.
+
+CONCLUSION
+
+Crossrelease avoids lock contection as far as possible.
-- 
1.9.1

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

* [FYI] Output of 'cat /proc/lockdep' after applying crossrelease
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (14 preceding siblings ...)
  2016-09-13  9:45 ` [PATCH v3 15/15] lockdep: Crossrelease feature documentation Byungchul Park
@ 2016-09-13  9:58 ` Byungchul Park
  2016-11-02  5:42 ` [REVISED DOC on v3] Crossrelease Lockdep Byungchul Park
  16 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13  9:58 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] 49+ messages in thread

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13  9:45 ` [PATCH v3 07/15] lockdep: Implement crossrelease feature Byungchul Park
@ 2016-09-13 10:05   ` Peter Zijlstra
  2016-09-13 12:09     ` Peter Zijlstra
  2016-09-13 15:14     ` Byungchul Park
  2016-09-13 15:05   ` Peter Zijlstra
  1 sibling, 2 replies; 49+ messages in thread
From: Peter Zijlstra @ 2016-09-13 10:05 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 13, 2016 at 06:45:06PM +0900, Byungchul Park wrote:
> 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.
> 

Completely inadequate.

Please explain how cross-release does what it does. Talk about lock
graphs and such.

I do not have time to reverse engineer this stuff.

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13 10:05   ` Peter Zijlstra
@ 2016-09-13 12:09     ` Peter Zijlstra
  2016-09-13 15:14     ` Byungchul Park
  1 sibling, 0 replies; 49+ messages in thread
From: Peter Zijlstra @ 2016-09-13 12:09 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 13, 2016 at 12:05:20PM +0200, Peter Zijlstra wrote:
> On Tue, Sep 13, 2016 at 06:45:06PM +0900, Byungchul Park wrote:
> > 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.
> > 
> 
> Completely inadequate.
> 
> Please explain how cross-release does what it does. Talk about lock
> graphs and such.
> 
> I do not have time to reverse engineer this stuff.

Blergh, I'll do it anyway.. hold on for an email asking specific
questions.

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

* Re: [PATCH v3 01/15] x86/dumpstack: Optimize save_stack_trace
  2016-09-13  9:45 ` [PATCH v3 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
@ 2016-09-13 13:18   ` Josh Poimboeuf
  2016-09-13 14:54     ` Byungchul Park
  0 siblings, 1 reply; 49+ messages in thread
From: Josh Poimboeuf @ 2016-09-13 13:18 UTC (permalink / raw)
  To: Byungchul Park
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill, linux-kernel,
	linux-mm, iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 13, 2016 at 06:45:00PM +0900, Byungchul Park wrote:
> 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.

This code will (probably) be obsoleted soon with my new unwinder.

Also, my previous comment was ignored:

  Instead of adding a new callback, why not just check the ops->address()
  return value?  It already returns an error if the array is full. 
   
  I think that would be cleaner and would help prevent more callback
  sprawl.

-- 
Josh

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

* Re: [PATCH v3 02/15] x86/dumpstack: Add save_stack_trace()_fast()
  2016-09-13  9:45 ` [PATCH v3 02/15] x86/dumpstack: Add save_stack_trace()_fast() Byungchul Park
@ 2016-09-13 13:20   ` Josh Poimboeuf
  0 siblings, 0 replies; 49+ messages in thread
From: Josh Poimboeuf @ 2016-09-13 13:20 UTC (permalink / raw)
  To: Byungchul Park
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill, linux-kernel,
	linux-mm, iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 13, 2016 at 06:45:01PM +0900, Byungchul Park wrote:
> 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.

Again this code will (probably) be obsolete soon.  And another quote
from my previous review:

  So how about we change save_stack_trace() to use print_context_stack()
  for CONFIG_FRAME_POINTER=n and print_context_stack_bp() for
  CONFIG_FRAME_POINTER=y?  That would preserve the existing behavior, no?

-- 
Josh

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

* Re: [PATCH v3 01/15] x86/dumpstack: Optimize save_stack_trace
  2016-09-13 13:18   ` Josh Poimboeuf
@ 2016-09-13 14:54     ` Byungchul Park
  0 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13 14:54 UTC (permalink / raw)
  To: Josh Poimboeuf
  Cc: Byungchul Park, peterz, Ingo Molnar, tglx, walken, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 13, 2016 at 10:18 PM, Josh Poimboeuf <jpoimboe@redhat.com> wrote:
> On Tue, Sep 13, 2016 at 06:45:00PM +0900, Byungchul Park wrote:
>> 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.
>
> This code will (probably) be obsoleted soon with my new unwinder.

Hello,

You are right.

I also think this will probably be obsoleted with yours.
So I didn't modify any details of the patch.
I will take your comment into account if it becomes necessary.

Anyway, crossrelease needs this patch to work smoothly.
That's only reason why I included this patch in the thread.

Thank you,
Byungchul

> Also, my previous comment was ignored:
>
>   Instead of adding a new callback, why not just check the ops->address()
>   return value?  It already returns an error if the array is full.
>
>   I think that would be cleaner and would help prevent more callback
>   sprawl.
>
> --
> Josh



-- 
Thanks,
Byungchul

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13  9:45 ` [PATCH v3 07/15] lockdep: Implement crossrelease feature Byungchul Park
  2016-09-13 10:05   ` Peter Zijlstra
@ 2016-09-13 15:05   ` Peter Zijlstra
  2016-09-13 17:12     ` Byungchul Park
  1 sibling, 1 reply; 49+ messages in thread
From: Peter Zijlstra @ 2016-09-13 15:05 UTC (permalink / raw)
  To: Byungchul Park
  Cc: mingo, tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin



So the idea is to add support for non-owner serialization primitives,
like completions/semaphores, and so far I've not looked at the code yet.
I did spend 2+ hours trying to decipher your documentation thing, but am
still confused, that thing is exceedingly hard to parse/read.

So the typical scenario would be something like:

L a	L a
	U a
W b	C b

where L := lock, U := unlock, W := wait_for_completion and C :=
complete.

On the left, the blocking thread we can easily observe the 'b depends on
a' relation, since we block while holding a.

On the right however that same relation is hard to see, since by the
time we would run complete, a has already been released.

I _think_ you propose to keep track of all prior held locks and then use
the union of the held list on the block-chain with the prior held list
from the complete context.

The document describes you have a prior held list, and that its a
circular thing, but it then completely fails to specify what gets added
and how its used.

Also, by it being a circular list (of indeterminate, but finite size),
there is the possibility of missing dependencies if they got spooled out
by recent activity.

The document has an example in the section 'How cross release works'
(the 3rd) that simply cannot be. It lists lock action _after_ acquire,
but you place the acquire in wait_for_completion. We _block_ there.

Is this the general idea?

If so, I cannot see how something like:

W a	W b
C b	C a

would work, somewhere in that document it states that this would be
handled by the existing dependencies, but I cannot see how. The blocking
thread (either one) has no held context, therefore the previously
mentioned union of held and prev-held is empty.

The alternative is not doing the union, but then you end up with endless
pointless dependencies afaict.


On the whole 'release context' thing you want to cast lockdep in, please
consider something like:

L a	L b
L b	L a
U a	U a
U b	U b

Note that the release order of locks is immaterial and can generate
'wrong' dependencies. Note how on both sides a is released under b,
failing to observe the stil obvious AB-BA deadlock.

Note that lockdep doesn't model release or anything, it models
blocked-on relations, like PI does.

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13 10:05   ` Peter Zijlstra
  2016-09-13 12:09     ` Peter Zijlstra
@ 2016-09-13 15:14     ` Byungchul Park
  1 sibling, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-13 15:14 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, walken, boqun.feng, kirill,
	linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 13, 2016 at 7:05 PM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Tue, Sep 13, 2016 at 06:45:06PM +0900, Byungchul Park wrote:
>> 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.
>>
>
> Completely inadequate.
>
> Please explain how cross-release does what it does. Talk about lock
> graphs and such.

Hello,

Could you tell me about what you intend, in detail?
I'm now asking you since I really don't know it.

The reason I reworked on documentation was to
answer your requests like "explain mathematically",
"tell how it works with graph" and so on. Should I
do anything else? I really don't know it.

If I missed something, please let me know. Then I
can do whatever you want if it's necessary.

> I do not have time to reverse engineer this stuff.

Why don't you read the document in the last patch
first? The document is my answer for your requests
you asked in version 1 thread. Insufficient?

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13 15:05   ` Peter Zijlstra
@ 2016-09-13 17:12     ` Byungchul Park
  2016-09-13 19:38       ` Peter Zijlstra
  0 siblings, 1 reply; 49+ messages in thread
From: Byungchul Park @ 2016-09-13 17:12 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, walken, boqun.feng, kirill,
	linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Wed, Sep 14, 2016 at 12:05 AM, Peter Zijlstra <peterz@infradead.org> wrote:
>
>
> So the idea is to add support for non-owner serialization primitives,
> like completions/semaphores, and so far I've not looked at the code yet.
> I did spend 2+ hours trying to decipher your documentation thing, but am
> still confused, that thing is exceedingly hard to parse/read.
>
> So the typical scenario would be something like:
>
> L a     L a
>         U a
> W b     C b
>
> where L := lock, U := unlock, W := wait_for_completion and C :=
> complete.
>
> On the left, the blocking thread we can easily observe the 'b depends on
> a' relation, since we block while holding a.

I think 'a depends on b' relation.

Why does b depend on a? b depends on a's what?

> On the right however that same relation is hard to see, since by the

Yes, there's no dependency on the right side of your example.

> time we would run complete, a has already been released.

I will change your example a little bit.

W b
~~~~~~~ <- serialized
        L a
        U a
        C b

(Remind crossrelease considers dependencies in only case that
lock sequence serialized is observable.)

There's a dependency in this case, like 'b depends on a' relation.
'C b' may not be hit, depending on the result of 'L a', that is,
acquired or not.

> I _think_ you propose to keep track of all prior held locks and then use
> the union of the held list on the block-chain with the prior held list
> from the complete context.

Almost right. Only thing we need to do to consider the union is to
connect two chains of two contexts by adding one dependency 'b -> a'.

> The document describes you have a prior held list, and that its a
> circular thing, but it then completely fails to specify what gets added
> and how its used.

Why does it fail? It keeps them in the form of hlock. This data is enough
to generate dependencies.

> Also, by it being a circular list (of indeterminate, but finite size),
> there is the possibility of missing dependencies if they got spooled out
> by recent activity.

Yes, right. They might be missed. It means just missing some chances to
check a deadlock. It's all. Better than do nothing.

Furthermore, It just only needs 10~50 entries on qemu-i386 4core since
it's optimized as far as possible so that it only considers essential ones
instead of all the prior held list. The number of entries might need to be
changed on a large system. It's future work.

> The document has an example in the section 'How cross release works'
> (the 3rd) that simply cannot be. It lists lock action _after_ acquire,

Could you tell me more? Why cannot it be?

> but you place the acquire in wait_for_completion. We _block_ there.
>
> Is this the general idea?
>
> If so, I cannot see how something like:
>
> W a     W b
> C b     C a

I didn't tell it works in this case. But it can be a future work. I'm not
sure but I don't think making it work is impossible at all. But anyway
current implementation cannot deal with this case.

> would work, somewhere in that document it states that this would be
> handled by the existing dependencies, but I cannot see how. The blocking

Nowhere I mentioned it can be handled.

However it will work if we can identify and add 'a -> b' and 'b -> a'
dependencies. It's fairly possible.

> thread (either one) has no held context, therefore the previously
> mentioned union of held and prev-held is empty.

Right, in current implementation.

> The alternative is not doing the union, but then you end up with endless
> pointless dependencies afaict.

That's a matter of whether or not we can identify additional dependencies.
I proposed the way to find certain and additional dependencies which original
lockdep missed. Of course there might be other dependencies which cannot
be identified with current implementation. It must be a future work.

> On the whole 'release context' thing you want to cast lockdep in, please
> consider something like:
>
> L a     L b
> L b     L a
> U a     U a
> U b     U b
>
> Note that the release order of locks is immaterial and can generate
> 'wrong' dependencies. Note how on both sides a is released under b,

Who said the release order is important? Wrong for what?

Using the way crossrelease works, AB-BA deadlock of course can be
detected, even though typical lock like 'a' and 'b' does not need to use
crossrelease feature at all.

On left side,
L a : queue a
L b : queue b
U a : add 'a -> b' (consider only locks having been tried since L a was held.)
U b : nop

On right side,
L b : queue b
L a : queue a
U a : nop
U b : add 'b -> a' (consider only locks having been tried since L b was held.)

AB-BA deadlock can be detected with dependencies 'a -> b' and 'b -> a'.
Fairly true dependencies can be generated with this concept, and never fail
to observe this kind of deadlock.

> failing to observe the stil obvious AB-BA deadlock.
>
> Note that lockdep doesn't model release or anything, it models
> blocked-on relations, like PI does.

As you said, current lockdep _implementation_ doesn't model it.
There are 2 parts roughly in lockdep.

1. detect(identify) dependency
2. check dependencies in graph to detect deadlock

Precisely, The first part doesn't model it. I think it's good for
optimized behavior. But it misses some dependencies.
So that's what I proposed to provide the chance to users
between more perfect detector and optimized behavior.

I think the second part works perfectly at the moment.

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13 17:12     ` Byungchul Park
@ 2016-09-13 19:38       ` Peter Zijlstra
  2016-09-13 21:42         ` Peter Zijlstra
  2016-09-14  2:27         ` Byungchul Park
  0 siblings, 2 replies; 49+ messages in thread
From: Peter Zijlstra @ 2016-09-13 19:38 UTC (permalink / raw)
  To: Byungchul Park
  Cc: Byungchul Park, Ingo Molnar, tglx, walken, boqun.feng, kirill,
	linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Wed, Sep 14, 2016 at 02:12:51AM +0900, Byungchul Park wrote:
> On Wed, Sep 14, 2016 at 12:05 AM, Peter Zijlstra <peterz@infradead.org> wrote:
> >
> >
> > So the idea is to add support for non-owner serialization primitives,
> > like completions/semaphores, and so far I've not looked at the code yet.
> > I did spend 2+ hours trying to decipher your documentation thing, but am
> > still confused, that thing is exceedingly hard to parse/read.
> >
> > So the typical scenario would be something like:
> >
> > L a     L a
> >         U a
> > W b     C b
> >
> > where L := lock, U := unlock, W := wait_for_completion and C :=
> > complete.
> >
> > On the left, the blocking thread we can easily observe the 'b depends on
> > a' relation, since we block while holding a.
> 
> I think 'a depends on b' relation.
> 
> Why does b depend on a? b depends on a's what?

b blocks while holding a. In any case, for the graph it doesn't matter,
its not a directed graph as such, all we care about is acyclic.

> > On the right however that same relation is hard to see, since by the
> 
> Yes, there's no dependency on the right side of your example.

Well, there is, its just not trivially observable. We must be able to
acquire a in order to complete b, therefore there is a dependency.

> > time we would run complete, a has already been released.
> 
> I will change your example a little bit.
> 
> W b
> ~~~~~~~ <- serialized
>         L a
>         U a
>         C b
> 
> (Remind crossrelease considers dependencies in only case that
> lock sequence serialized is observable.)

What does that mean? Any why? This is a random point in time without
actual meaning.

> There's a dependency in this case, like 'b depends on a' relation.
> 'C b' may not be hit, depending on the result of 'L a', that is,
> acquired or not.

With or without a random reference point, the dependency is there.

> > I _think_ you propose to keep track of all prior held locks and then use
> > the union of the held list on the block-chain with the prior held list
> > from the complete context.
> 
> Almost right. Only thing we need to do to consider the union is to
> connect two chains of two contexts by adding one dependency 'b -> a'.

Sure, but how do you arrive at which connection to make. The document is
entirely silent on this crucial point.

The union between the held-locks of the blocked and prev-held-locks of
the release should give a fair indication I think, but then, I've not
thought too hard on this yet.

> > The document describes you have a prior held list, and that its a
> > circular thing, but it then completely fails to specify what gets added
> > and how its used.
> 
> Why does it fail? It keeps them in the form of hlock. This data is enough
> to generate dependencies.

It fails to explain. It just barely mentions you keep them, it doesn't
mention how they're used or why.

> > Also, by it being a circular list (of indeterminate, but finite size),
> > there is the possibility of missing dependencies if they got spooled out
> > by recent activity.
> 
> Yes, right. They might be missed. It means just missing some chances to
> check a deadlock. It's all. Better than do nothing.

Sure, but you didn't specify. Again, the document is very ambiguous and
ill specified.

> > The document has an example in the section 'How cross release works'
> > (the 3rd) that simply cannot be. It lists lock action _after_ acquire,
> 
> Could you tell me more? Why cannot it be?

Once you block you cannot take more locks, that simply doesnt make
sense.

> > but you place the acquire in wait_for_completion. We _block_ there.
> >
> > Is this the general idea?
> >
> > If so, I cannot see how something like:
> >
> > W a     W b
> > C b     C a
> 
> I didn't tell it works in this case. But it can be a future work. I'm not
> sure but I don't think making it work is impossible at all. But anyway
> current implementation cannot deal with this case.

+4. 'crosslock a -> crosslock b' dependency
+
+   Creating this kind of dependency directly is unnecessary since it can
+   be covered by other kinds of dependencies.

Says something different, doesn't it?

> > On the whole 'release context' thing you want to cast lockdep in, please
> > consider something like:
> >
> > L a     L b
> > L b     L a
> > U a     U a
> > U b     U b
> >
> > Note that the release order of locks is immaterial and can generate
> > 'wrong' dependencies. Note how on both sides a is released under b,
> 
> Who said the release order is important? Wrong for what?

Well, again, you mention 'release context' without actually specifying
what you mean with that.

L a
L b
U b

and

L a
L b
U a

Here the unlocks obviously have different context. Without further
specification its simply impossible to tell what you mean.

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13 19:38       ` Peter Zijlstra
@ 2016-09-13 21:42         ` Peter Zijlstra
  2016-09-14  1:01           ` Byungchul Park
  2016-09-14  2:27         ` Byungchul Park
  1 sibling, 1 reply; 49+ messages in thread
From: Peter Zijlstra @ 2016-09-13 21:42 UTC (permalink / raw)
  To: Byungchul Park
  Cc: Byungchul Park, Ingo Molnar, tglx, walken, boqun.feng, kirill,
	linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 13, 2016 at 09:38:29PM +0200, Peter Zijlstra wrote:

> > > I _think_ you propose to keep track of all prior held locks and then use
> > > the union of the held list on the block-chain with the prior held list
> > > from the complete context.
> > 
> > Almost right. Only thing we need to do to consider the union is to
> > connect two chains of two contexts by adding one dependency 'b -> a'.
> 
> Sure, but how do you arrive at which connection to make. The document is
> entirely silent on this crucial point.
> 
> The union between the held-locks of the blocked and prev-held-locks of
> the release should give a fair indication I think, but then, I've not
> thought too hard on this yet.

s/union/intersection/

those that are in both sets.

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13 21:42         ` Peter Zijlstra
@ 2016-09-14  1:01           ` Byungchul Park
  0 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-14  1:01 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Wed, Sep 14, 2016 at 6:42 AM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Tue, Sep 13, 2016 at 09:38:29PM +0200, Peter Zijlstra wrote:
>
>> > > I _think_ you propose to keep track of all prior held locks and then use
>> > > the union of the held list on the block-chain with the prior held list
>> > > from the complete context.
>> >
>> > Almost right. Only thing we need to do to consider the union is to
>> > connect two chains of two contexts by adding one dependency 'b -> a'.
>>
>> Sure, but how do you arrive at which connection to make. The document is
>> entirely silent on this crucial point.
>>
>> The union between the held-locks of the blocked and prev-held-locks of
>> the release should give a fair indication I think, but then, I've not
>> thought too hard on this yet.
>
> s/union/intersection/
>
> those that are in both sets.

Precisely speaking, I introduces separate chains.

For example,

1. Held-locks of the blocked,
A -> B -> C (which original lockdep builds)

2. Prev-held-locks of the release
G -> H -> I (which original lockdep builds, too)

3. Cross chain (which I introduced newly)
C -> G

Then the 'A -> B -> C -> G -> H -> I' can be traversed
when bfs is performed.

-- 
Thanks,
Byungchul

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-13 19:38       ` Peter Zijlstra
  2016-09-13 21:42         ` Peter Zijlstra
@ 2016-09-14  2:27         ` Byungchul Park
  2016-09-14  4:49           ` Byungchul Park
  2016-09-14  8:11           ` Peter Zijlstra
  1 sibling, 2 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-14  2:27 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Wed, Sep 14, 2016 at 4:38 AM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Wed, Sep 14, 2016 at 02:12:51AM +0900, Byungchul Park wrote:
>> On Wed, Sep 14, 2016 at 12:05 AM, Peter Zijlstra <peterz@infradead.org> wrote:
>> >
>> >
>> > So the idea is to add support for non-owner serialization primitives,
>> > like completions/semaphores, and so far I've not looked at the code yet.
>> > I did spend 2+ hours trying to decipher your documentation thing, but am
>> > still confused, that thing is exceedingly hard to parse/read.
>> >
>> > So the typical scenario would be something like:
>> >
>> > L a     L a
>> >         U a
>> > W b     C b
>> >
>> > where L := lock, U := unlock, W := wait_for_completion and C :=
>> > complete.
>> >
>> > On the left, the blocking thread we can easily observe the 'b depends on
>> > a' relation, since we block while holding a.
>>
>> I think 'a depends on b' relation.
>>
>> Why does b depend on a? b depends on a's what?
>
> b blocks while holding a. In any case, for the graph it doesn't matter,
> its not a directed graph as such, all we care about is acyclic.

It's a directed graph.The meaning of backward traverse is different
from the meaning of forward traverse, isn't it?

>> > On the right however that same relation is hard to see, since by the
>>
>> Yes, there's no dependency on the right side of your example.
>
> Well, there is, its just not trivially observable. We must be able to
> acquire a in order to complete b, therefore there is a dependency.

No. We cannot say there is a dependency unconditionally. There can
be a dependency or not.

L a     L a
        U a
~~~~~~~~~ what if serialized by something?
W b     C b

If something we don't recognize serializes locks, which ensures
'W b' happens after 'L a , U a' in the other context, then there's
no dependency here.

We should say 'b depends on a' in only case that the sequence
'W b and then L a and then C b, where last two ops are in same
context' _actually_ happened at least once. Otherwise, it might
add a false dependency.

It's same as how original lockdep works with typical locks. It adds
a dependency only when a lock is actually hit.

>> > time we would run complete, a has already been released.
>>
>> I will change your example a little bit.
>>
>> W b
>> ~~~~~~~ <- serialized
>>         L a
>>         U a
>>         C b
>>
>> (Remind crossrelease considers dependencies in only case that
>> lock sequence serialized is observable.)
>
> What does that mean? Any why? This is a random point in time without
> actual meaning.

It's not random point. We have to consider meaningful sequences among
those which are globally observable. That's why we need to serialize
those locks. For example,

W b
L a
U a
C b

Once this sequence is observable globally, we can say 'It's possible to
run in this sequence. Is this sequence problematic or not?'.

L a
U a
W b
C b

If only this sequence can be observable, we should not assume
this sequence can be changed. However once the former sequence
happens, it has a possibility to hit the same sequence again later.
So we can check deadlock possibility with the sequence,

_not randomly_.

>> There's a dependency in this case, like 'b depends on a' relation.
>> 'C b' may not be hit, depending on the result of 'L a', that is,
>> acquired or not.
>
> With or without a random reference point, the dependency is there.

The dependency might not be there.

>> > I _think_ you propose to keep track of all prior held locks and then use
>> > the union of the held list on the block-chain with the prior held list
>> > from the complete context.
>>
>> Almost right. Only thing we need to do to consider the union is to
>> connect two chains of two contexts by adding one dependency 'b -> a'.
>
> Sure, but how do you arrive at which connection to make. The document is
> entirely silent on this crucial point.

We need to connect between the crosslock and the first lock among
locks having been acquired since the crosslock was held. Others will be
connected each other by original lockdep.

By the way, does my document miss this description? If so, sorry.
I will check and update it.

> The union between the held-locks of the blocked and prev-held-locks of
> the release should give a fair indication I think, but then, I've not
> thought too hard on this yet.

I make the union only If actually the lock sequence happened once so
that it's globally visible.

>> > The document describes you have a prior held list, and that its a
>> > circular thing, but it then completely fails to specify what gets added
>> > and how its used.
>>
>> Why does it fail? It keeps them in the form of hlock. This data is enough
>> to generate dependencies.
>
> It fails to explain. It just barely mentions you keep them, it doesn't
> mention how they're used or why.

Okay. I need to explain more about that.

>> > Also, by it being a circular list (of indeterminate, but finite size),
>> > there is the possibility of missing dependencies if they got spooled out
>> > by recent activity.
>>
>> Yes, right. They might be missed. It means just missing some chances to
>> check a deadlock. It's all. Better than do nothing.
>
> Sure, but you didn't specify. Again, the document is very ambiguous and
> ill specified.

Yes, I need to supplement the latter half as you said. I will do it.

>
>> > The document has an example in the section 'How cross release works'
>> > (the 3rd) that simply cannot be. It lists lock action _after_ acquire,
>>
>> Could you tell me more? Why cannot it be?
>
> Once you block you cannot take more locks, that simply doesnt make
> sense.

No. Peter.. lockdep check possibilities. All lockdep operations are
performed assuming this same sequence can happen again later but in
a little bit different order between more than two contexts so that actual
deadlock can happen then.

No doubt, it cannot take more locks while blocked at the time. But it's
not important for lockdep to work.

>> > but you place the acquire in wait_for_completion. We _block_ there.
>> >
>> > Is this the general idea?
>> >
>> > If so, I cannot see how something like:
>> >
>> > W a     W b
>> > C b     C a
>>
>> I didn't tell it works in this case. But it can be a future work. I'm not
>> sure but I don't think making it work is impossible at all. But anyway
>> current implementation cannot deal with this case.
>
> +4. 'crosslock a -> crosslock b' dependency
> +
> +   Creating this kind of dependency directly is unnecessary since it can
> +   be covered by other kinds of dependencies.
>
> Says something different, doesn't it?

Really sorry. I made you much confused. I will update it.

>> > On the whole 'release context' thing you want to cast lockdep in, please
>> > consider something like:
>> >
>> > L a     L b
>> > L b     L a
>> > U a     U a
>> > U b     U b
>> >
>> > Note that the release order of locks is immaterial and can generate
>> > 'wrong' dependencies. Note how on both sides a is released under b,
>>
>> Who said the release order is important? Wrong for what?
>
> Well, again, you mention 'release context' without actually specifying
> what you mean with that.
>
> L a
> L b
> U b
>
> and
>
> L a
> L b
> U a
>
> Here the unlocks obviously have different context. Without further
> specification its simply impossible to tell what you mean.

I mentioned what the release context means in the document. Or
I don't understand what you are asking now.

-- 
Thanks,
Byungchul

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-14  2:27         ` Byungchul Park
@ 2016-09-14  4:49           ` Byungchul Park
  2016-09-14  8:11           ` Peter Zijlstra
  1 sibling, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-14  4:49 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Wed, Sep 14, 2016 at 11:27 AM, Byungchul Park
<max.byungchul.park@gmail.com> wrote:
> On Wed, Sep 14, 2016 at 4:38 AM, Peter Zijlstra <peterz@infradead.org> wrote:
>> On Wed, Sep 14, 2016 at 02:12:51AM +0900, Byungchul Park wrote:
>>> On Wed, Sep 14, 2016 at 12:05 AM, Peter Zijlstra <peterz@infradead.org> wrote:
>>> >
>>> >
>>> > So the idea is to add support for non-owner serialization primitives,
>>> > like completions/semaphores, and so far I've not looked at the code yet.
>>> > I did spend 2+ hours trying to decipher your documentation thing, but am
>>> > still confused, that thing is exceedingly hard to parse/read.
>>> >
>>> > So the typical scenario would be something like:
>>> >
>>> > L a     L a
>>> >         U a
>>> > W b     C b
>>> >
>>> > where L := lock, U := unlock, W := wait_for_completion and C :=
>>> > complete.
>>> >
>>> > On the left, the blocking thread we can easily observe the 'b depends on
>>> > a' relation, since we block while holding a.
>>>
>>> I think 'a depends on b' relation.
>>>
>>> Why does b depend on a? b depends on a's what?
>>
>> b blocks while holding a. In any case, for the graph it doesn't matter,
>> its not a directed graph as such, all we care about is acyclic.
>
> It's a directed graph.The meaning of backward traverse is different
> from the meaning of forward traverse, isn't it?
>
>>> > On the right however that same relation is hard to see, since by the
>>>
>>> Yes, there's no dependency on the right side of your example.
>>
>> Well, there is, its just not trivially observable. We must be able to
>> acquire a in order to complete b, therefore there is a dependency.
>
> No. We cannot say there is a dependency unconditionally. There can
> be a dependency or not.
>
> L a     L a
>         U a
> ~~~~~~~~~ what if serialized by something?
> W b     C b
>
> If something we don't recognize serializes locks, which ensures
> 'W b' happens after 'L a , U a' in the other context, then there's
> no dependency here.
>
> We should say 'b depends on a' in only case that the sequence
> 'W b and then L a and then C b, where last two ops are in same
> context' _actually_ happened at least once. Otherwise, it might
> add a false dependency.
>
> It's same as how original lockdep works with typical locks. It adds
> a dependency only when a lock is actually hit.
>
>>> > time we would run complete, a has already been released.
>>>
>>> I will change your example a little bit.
>>>
>>> W b
>>> ~~~~~~~ <- serialized
>>>         L a
>>>         U a
>>>         C b
>>>
>>> (Remind crossrelease considers dependencies in only case that
>>> lock sequence serialized is observable.)
>>
>> What does that mean? Any why? This is a random point in time without
>> actual meaning.
>
> It's not random point. We have to consider meaningful sequences among
> those which are globally observable. That's why we need to serialize
> those locks. For example,
>
> W b
> L a
> U a
> C b
>
> Once this sequence is observable globally, we can say 'It's possible to
> run in this sequence. Is this sequence problematic or not?'.
>
> L a
> U a
> W b
> C b
>
> If only this sequence can be observable, we should not assume
> this sequence can be changed. However once the former sequence
> happens, it has a possibility to hit the same sequence again later.
> So we can check deadlock possibility with the sequence,
>
> _not randomly_.
>
>>> There's a dependency in this case, like 'b depends on a' relation.
>>> 'C b' may not be hit, depending on the result of 'L a', that is,
>>> acquired or not.
>>
>> With or without a random reference point, the dependency is there.
>
> The dependency might not be there.
>
>>> > I _think_ you propose to keep track of all prior held locks and then use
>>> > the union of the held list on the block-chain with the prior held list
>>> > from the complete context.
>>>
>>> Almost right. Only thing we need to do to consider the union is to
>>> connect two chains of two contexts by adding one dependency 'b -> a'.
>>
>> Sure, but how do you arrive at which connection to make. The document is
>> entirely silent on this crucial point.
>
> We need to connect between the crosslock and the first lock among
> locks having been acquired since the crosslock was held. Others will be
> connected each other by original lockdep.
>
> By the way, does my document miss this description? If so, sorry.
> I will check and update it.
>
>> The union between the held-locks of the blocked and prev-held-locks of
>> the release should give a fair indication I think, but then, I've not
>> thought too hard on this yet.
>
> I make the union only If actually the lock sequence happened once so
> that it's globally visible.
>
>>> > The document describes you have a prior held list, and that its a
>>> > circular thing, but it then completely fails to specify what gets added
>>> > and how its used.
>>>
>>> Why does it fail? It keeps them in the form of hlock. This data is enough
>>> to generate dependencies.
>>
>> It fails to explain. It just barely mentions you keep them, it doesn't
>> mention how they're used or why.
>
> Okay. I need to explain more about that.
>
>>> > Also, by it being a circular list (of indeterminate, but finite size),
>>> > there is the possibility of missing dependencies if they got spooled out
>>> > by recent activity.
>>>
>>> Yes, right. They might be missed. It means just missing some chances to
>>> check a deadlock. It's all. Better than do nothing.
>>
>> Sure, but you didn't specify. Again, the document is very ambiguous and
>> ill specified.
>
> Yes, I need to supplement the latter half as you said. I will do it.
>
>>
>>> > The document has an example in the section 'How cross release works'
>>> > (the 3rd) that simply cannot be. It lists lock action _after_ acquire,
>>>
>>> Could you tell me more? Why cannot it be?
>>
>> Once you block you cannot take more locks, that simply doesnt make
>> sense.
>
> No. Peter.. lockdep check possibilities. All lockdep operations are
> performed assuming this same sequence can happen again later but in
> a little bit different order between more than two contexts so that actual
> deadlock can happen then.

I can see what you are talking about. you are only talking about
wait_for_completion. wait_for_completion is a case more complicated
than lock_page. Could we talk about lock_page first if you don't mind?
And then move to wait_for_complete next?

Anyway even in wait_complete, It's possible like,
(even though it's not much meaningful in case of wait_for_completion)

(Triger complete context to start)
acquire A
wait_for_completion A
   acquire another (e.g. wait.lock)
   release it
   ...
   (actually sleep)

Not much meaningful in wait_for_completion, though it's not wrong.
And this is much meaningful to another crosslock like lock_page.
The target I'm mainly focusing is page lock.

> No doubt, it cannot take more locks while blocked at the time. But it's
> not important for lockdep to work.
>
>>> > but you place the acquire in wait_for_completion. We _block_ there.
>>> >
>>> > Is this the general idea?
>>> >
>>> > If so, I cannot see how something like:
>>> >
>>> > W a     W b
>>> > C b     C a
>>>
>>> I didn't tell it works in this case. But it can be a future work. I'm not
>>> sure but I don't think making it work is impossible at all. But anyway
>>> current implementation cannot deal with this case.
>>
>> +4. 'crosslock a -> crosslock b' dependency
>> +
>> +   Creating this kind of dependency directly is unnecessary since it can
>> +   be covered by other kinds of dependencies.
>>
>> Says something different, doesn't it?
>
> Really sorry. I made you much confused. I will update it.
>
>>> > On the whole 'release context' thing you want to cast lockdep in, please
>>> > consider something like:
>>> >
>>> > L a     L b
>>> > L b     L a
>>> > U a     U a
>>> > U b     U b
>>> >
>>> > Note that the release order of locks is immaterial and can generate
>>> > 'wrong' dependencies. Note how on both sides a is released under b,
>>>
>>> Who said the release order is important? Wrong for what?
>>
>> Well, again, you mention 'release context' without actually specifying
>> what you mean with that.
>>
>> L a
>> L b
>> U b
>>
>> and
>>
>> L a
>> L b
>> U a
>>
>> Here the unlocks obviously have different context. Without further
>> specification its simply impossible to tell what you mean.
>
> I mentioned what the release context means in the document. Or
> I don't understand what you are asking now.
>
> --
> Thanks,
> Byungchul



-- 
Thanks,
Byungchul

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-14  2:27         ` Byungchul Park
  2016-09-14  4:49           ` Byungchul Park
@ 2016-09-14  8:11           ` Peter Zijlstra
  2016-09-19  2:41             ` Byungchul Park
  1 sibling, 1 reply; 49+ messages in thread
From: Peter Zijlstra @ 2016-09-14  8:11 UTC (permalink / raw)
  To: Byungchul Park
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Wed, Sep 14, 2016 at 11:27:22AM +0900, Byungchul Park wrote:
> > Well, there is, its just not trivially observable. We must be able to
> > acquire a in order to complete b, therefore there is a dependency.
> 
> No. We cannot say there is a dependency unconditionally. There can
> be a dependency or not.
> 
> L a     L a
>         U a
> ~~~~~~~~~ what if serialized by something?

Well, there's no serialization in the example, so no what if.

> W b     C b
> 
> If something we don't recognize serializes locks, which ensures
> 'W b' happens after 'L a , U a' in the other context, then there's
> no dependency here.

Its not there.

> We should say 'b depends on a' in only case that the sequence
> 'W b and then L a and then C b, where last two ops are in same
> context' _actually_ happened at least once. Otherwise, it might
> add a false dependency.
> 
> It's same as how original lockdep works with typical locks. It adds
> a dependency only when a lock is actually hit.

But since these threads are independently scheduled there is no point in
transferring the point in time thread A does W to thread B. There is no
relation there.

B could have already executed the complete or it could not yet have
started execution at all or anything in between, entirely random.

> > What does that mean? Any why? This is a random point in time without
> > actual meaning.
> 
> It's not random point. We have to consider meaningful sequences among
> those which are globally observable. That's why we need to serialize
> those locks.

Serialize how? there is no serialization.

> For example,
> 
> W b
> L a
> U a
> C b
> 
> Once this sequence is observable globally, we can say 'It's possible to
> run in this sequence. Is this sequence problematic or not?'.
> 
> L a
> U a
> W b
> C b
> 
> If only this sequence can be observable, we should not assume
> this sequence can be changed. However once the former sequence
> happens, it has a possibility to hit the same sequence again later.
> So we can check deadlock possibility with the sequence,
> 
> _not randomly_.

I still don't get it.

> We need to connect between the crosslock and the first lock among
> locks having been acquired since the crosslock was held.

Which can be _any_ lock in the history of that thread. It could be
rq->lock from getting the thread scheduled.

> Others will be
> connected each other by original lockdep.
> 
> By the way, does my document miss this description? If so, sorry.
> I will check and update it.

I couldn't find anything useful, but then I could not understand most of
what was written, and I tried hard :-(

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

* Re: [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache()
  2016-09-13  9:45 ` [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache() Byungchul Park
@ 2016-09-15 15:33   ` Nilay Vaish
  2016-09-19  3:05     ` Byungchul Park
  0 siblings, 1 reply; 49+ messages in thread
From: Nilay Vaish @ 2016-09-15 15:33 UTC (permalink / raw)
  To: Byungchul Park
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On 13 September 2016 at 04:45, Byungchul Park <byungchul.park@lge.com> wrote:
> @@ -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;
> +}

Byungchul,  do you think we should increment chain_lookup_misses
before returning NULL from the above function?

--
Nilay

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

* Re: [PATCH v3 15/15] lockdep: Crossrelease feature documentation
  2016-09-13  9:45 ` [PATCH v3 15/15] lockdep: Crossrelease feature documentation Byungchul Park
@ 2016-09-15 17:25   ` Nilay Vaish
  2016-09-19  2:59     ` Byungchul Park
  2016-09-16 15:47   ` Nilay Vaish
  1 sibling, 1 reply; 49+ messages in thread
From: Nilay Vaish @ 2016-09-15 17:25 UTC (permalink / raw)
  To: Byungchul Park
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On 13 September 2016 at 04:45, Byungchul Park <byungchul.park@lge.com> wrote:
> 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 | 785 +++++++++++++++++++++++++++++++++
>  1 file changed, 785 insertions(+)
>  create mode 100644 Documentation/locking/crossrelease.txt

Byungchul, I mostly skimmed through the document.  I suggest that we
split this document.  The initial 1/4 of the document talks about
lockdep's current implementation which I believe should be combined
with the file: Documentation/locking/lockdep-design.txt. Tomorrow I
would try to understand the document in detail and hopefully provide
some useful comments.

--
Nilay

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

* Re: [PATCH v3 15/15] lockdep: Crossrelease feature documentation
  2016-09-13  9:45 ` [PATCH v3 15/15] lockdep: Crossrelease feature documentation Byungchul Park
  2016-09-15 17:25   ` Nilay Vaish
@ 2016-09-16 15:47   ` Nilay Vaish
  2016-09-19  3:00     ` Byungchul Park
  2016-09-20  5:00     ` Byungchul Park
  1 sibling, 2 replies; 49+ messages in thread
From: Nilay Vaish @ 2016-09-16 15:47 UTC (permalink / raw)
  To: Byungchul Park
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On 13 September 2016 at 04:45, Byungchul Park <byungchul.park@lge.com> wrote:
> 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 | 785 +++++++++++++++++++++++++++++++++
>  1 file changed, 785 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..78558af
> --- /dev/null
> +++ b/Documentation/locking/crossrelease.txt
> @@ -0,0 +1,785 @@
> +Crossrelease
> +============
> +
> +Started by Byungchul Park <byungchul.park@lge.com>
> +
> +Contents:
> +
> + (*) Background.
> +
> +     - What causes deadlock.
> +     - What lockdep detects.
> +     - How lockdep works.
> +
> + (*) Limitation.
> +
> +     - Limit to typical lock.
> +     - Pros from the limitation.
> +     - Cons from the limitation.
> +
> + (*) Generalization.
> +
> +     - Relax the limitation.
> +
> + (*) Crossrelease.
> +
> +     - Introduce crossrelease.
> +     - Introduce commit.
> +
> + (*) Implementation.
> +
> +     - Data structures.
> +     - How crossrelease works.
> +
> + (*) Optimizations.
> +
> +     - Avoid duplication.
> +     - Avoid lock contention.
> +
> +
> +==========
> +Background
> +==========
> +
> +What causes deadlock
> +--------------------
> +
> +A deadlock occurs when a context is waiting for an event to be issued
> +which cannot be issued because the context or another context who can
> +issue the event is also waiting for an event to be issued which cannot
> +be issued.

I think 'some event happened' and 'context triggered an event' is
better than 'some event issued' or 'context issued an event'.  I think
'happen' and 'trigger' are more widely used words when we talk about
events.  For example, I would prefer the following version of the
above:

A deadlock occurs when a context is waiting for an event to happen,
which cannot happen because the context which can trigger the event is
also waiting for an event to happen which cannot happen either.

> +Single context or more than one context both waiting for an
> +event and issuing an event may paricipate in a deadlock.

I am not able to make sense of the line above.

> +
> +For example,
> +
> +A context who can issue event D is waiting for event A to be issued.
> +A context who can issue event A is waiting for event B to be issued.
> +A context who can issue event B is waiting for event C to be issued.
> +A context who can issue event C is waiting for event D to be issued.
> +
> +A deadlock occurs when these four operations are run at a time because
> +event D cannot be issued if event A isn't issued which in turn cannot be
> +issued if event B isn't issued which in turn cannot be issued if event C
> +isn't issued which in turn cannot be issued if event D isn't issued. No
> +event can be issued since any of them never meets its precondition.
> +
> +We can easily recognize that each wait operation creates a dependency
> +between two issuings e.g. between issuing D and issuing A like, 'event D
> +cannot be issued if event A isn't issued', in other words, 'issuing
> +event D depends on issuing event A'. So the whole example can be
> +rewritten in terms of dependency,
> +
> +Do an operation making 'event D cannot be issued if event A isn't issued'.
> +Do an operation making 'event A cannot be issued if event B isn't issued'.
> +Do an operation making 'event B cannot be issued if event C isn't issued'.
> +Do an operation making 'event C cannot be issued if event D isn't issued'.
> +
> +or,

I think we can remove the text above.  The example only needs to be
provided once.

> +
> +Do an operation making 'issuing event D depends on issuing event A'.
> +Do an operation making 'issuing event A depends on issuing event B'.
> +Do an operation making 'issuing event B depends on issuing event C'.
> +Do an operation making 'issuing event C depends on issuing event D'.
> +
> +What causes a deadlock is a set of dependencies a chain of which forms a
> +cycle, which means that issuing event D depending on issuing event A
> +depending on issuing event B depending on issuing event C depending on
> +issuing event D, finally depends on issuing event D itself, which means
> +no event can be issued.
> +
> +Any set of operations creating dependencies causes a deadlock. The set
> +of lock operations e.g. acquire and release is an example. Waiting for a
> +lock to be released corresponds to waiting for an event and releasing a
> +lock corresponds to issuing an event. So the description of dependency
> +above can be altered to one in terms of lock.
> +
> +In terms of event, issuing event A depends on issuing event B if,
> +
> +       Event A cannot be issued if event B isn't issued.
> +
> +In terms of lock, releasing lock A depends on releasing lock B if,
> +
> +       Lock A cannot be released if lock B isn't released.
> +
> +CONCLUSION
> +
> +A set of dependencies a chain of which forms a cycle, causes a deadlock,

I think 'a chain of' is not required in the sentence above.

> +no matter what creates the dependencies.
> +
> +
> +What lockdep detects
> +--------------------
> +
> +A deadlock actually occurs only when all operations creating problematic

Instead of 'problematic', I would use 'cyclic'.

> +dependencies are run at a time. However, even if it has not happend, the

dependencies happen at run time.  However, even if they don't, the

> +deadlock potentially can occur if the problematic dependencies obviously

remove obviously

> +exist. Thus it's meaningful to detect not only an actual deadlock but
> +also its possibility. Lockdep does the both.
> +
> +Whether a deadlock actually occurs or not depends on several factors,
> +which means a deadlock may not occur even though problematic

cyclic instead of problematic

> +dependencies exist. For example, what order contexts are switched in is
> +a factor. A deadlock will occur when contexts are switched so that all
> +operations causing a deadlock become run simultaneously.

delete become.

> +
> +Lockdep tries to detect a deadlock or its possibility aggressively,
> +though it also tries to avoid false positive detections. So lockdep is
> +designed to consider all possible combinations of dependencies so that
> +it can detect all potential possibilities of deadlock in advance. What
> +lockdep tries in order to consider all possibilities are,
> +
> +1. Use a global dependency graph including all dependencies.
> +
> +   What lockdep checks is based on dependencies instead of what actually
> +   happened. So no matter which context or call path a new dependency is
> +   detected in, it's just referred to as a global factor.

Can you explain more what 'global factor' means?


> +
> +2. Use lock classes than lock instances when checking dependencies.
> +
> +   What actually causes a deadlock is lock instances. However, lockdep
> +   uses lock classes than its instances when checking dependencies since
> +   any instance of a same lock class can be altered anytime.

I am unable to make sense of the sentence above.  Do you want to say
the following:

However, lockdep uses lock classes than its instances when checking
dependencies since instances from the same lock class behave in the
same (or similar) fashion.

> +
> +So lockdep detects both an actual deadlock and its possibility. But the
> +latter is more valuable than the former. When a deadlock actually
> +occures, 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 does, the fisrt one is more valuable,
> +
> +1. Detecting and reporting deadlock possibility.
> +2. Detecting and reporting a deadlock actually occured.
> +
> +
> +How lockdep works
> +-----------------
> +
> +What lockdep should do, to detect a deadlock or its possibility are,
> +
> +1. Detect a new dependency created.
> +2. Keep the dependency in a global data structure esp. graph.
> +3. Check if any of all possible chains of dependencies forms a cycle.
> +4. Report a deadlock or its possibility if a cycle is detected.
> +
> +A graph used by lockdep to keep all dependencies looks like,
> +
> +A -> B -        -> F -> G
> +        \      /
> +         -> E -        -> L
> +        /      \      /
> +C -> D -        -> H -
> +                      \
> +                       -> I -> K
> +                      /
> +                   J -
> +
> +where A, B,..., L are different lock classes.
> +
> +Lockdep adds a dependency into graph when a new dependency is detected.
> +For example, it adds a dependency 'A -> B' when a dependency between
> +releasing lock A and releasing lock B, which has not been added yet, is
> +detected. It does same thing on other dependencies, too. See 'What
> +causes deadlock' section.

Just from the text above I am not able to understand at what point the
dependency A-> B is added.  If I was implementing lockdep, I would
probably track all the locks that can be acquired between acquisition
and release of A.  For example,

acquire A

if (/* some condition */)
    acquire B
else
    acquire C

release A


I would add to the dependency graph that 'release A' depends on
'acquire B' and 'acquire C'.

> +
> +NOTE: Precisely speaking, a dependency is one between releasing a lock
> +and releasing another lock as described in 'What causes deadlock'
> +section. However from now on, we will describe a dependency as if it's
> +one between a lock and another lock for simplicity. Then 'A -> B' can be
> +described as a dependency between lock A and lock B.
> +
> +We already checked how a problematic set of dependencies causes a
> +deadlock in 'What causes deadlock' section. This time let's check if a
> +deadlock or its possibility can be detected using a problematic set of
> +dependencies. Assume that 'A -> B', 'B -> E' and 'E -> A' were added in
> +the sequence into graph. Then the graph finally will be,
> +
> + -> A -> B -> E -
> +/                \
> +\                /
> + ----------------
> +
> +where A, B and E are different lock classes.
> +
> +From adding three dependencies, a cycle was created which means, by
> +definition of dependency, the situation 'lock E must be released to
> +release lock B which in turn must be released to release lock A which in
> +turn must be released to release lock E which in turn must be released
> +to release B and so on infinitely' can happen.
> +
> +Once the situation happens, no lock can be released since any of them
> +can never meet each precondition. It's a deadlock. Lockdep can detect a
> +deadlock or its possibility with checking if a cycle was created after
> +adding each dependency into graph. This is how lockdep detects a
> +deadlock or its possibility.

I think the text here is just repeating what was said above in the
'What causes deadlock' section.  You probably should remove of the
text here.

> +
> +CONCLUSION
> +
> +Lockdep detects a deadlock or its possibility with checking if a cycle
> +was created after adding each dependency into graph.
> +
> +
> +==========
> +Limitation
> +==========
> +
> +Limit to typical lock
> +---------------------
> +
> +Limiting what lockdep has to consider to only ones satisfying the
> +following condition, the implementation of adding dependencies becomes
> +simple while its capacity for detection becomes limited. Typical lock
> +e.g. spin lock and mutex is the case. Let's check what pros and cons of
> +it are, in next section.
> +
> +       A lock should be released within the context holding the lock.

I would rephrase the above in the following way:  Lockdep is limited
to checking dependencies on locks that are released within the context
that acquired them.  This makes adding dependencies simple but limits
lockdep's capacity for detection.


> +
> +CONCLUSION
> +
> +Limiting what lockdep has to consider to typical lock e.g. spin lock and
> +mutex, the implmentation becomes simple while it has a limited capacity.

I would drop the conclusion altogether.  The above paragraph is too
small to require a conclusion.

> +
> +
> +Pros from the limitation
> +------------------------
> +
> +Given the limitation, when acquiring a lock, any lock being in
> +held_locks of the acquire context cannot be released if the lock to

What does held_locks mean here?  Is it some structure maintained by
the Linux kernel?

> +acquire was not released yet. Yes. It's the exact case to add a new
> +dependency 'A -> B' into graph, where lock A represents each lock being
> +in held_locks and lock B represents the lock to acquire.
> +
> +For example, only considering typical lock,
> +
> +       PROCESS 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, there's nothing in held_locks of PROCESS X thus
> +no dependency is added. When acquiring lock B, lockdep detects and adds
> +a new dependency 'A -> B' between lock A being in held_locks and lock B.
> +And when acquiring lock C, lockdep also adds another dependency 'B -> C'
> +for same reason. They are added when acquiring each lock, simply.
> +
> +NOTE: Even though every lock being in held_locks depends on the lock to
> +acquire, lockdep does not add all dependencies between them because all
> +of them can be covered by other dependencies except one dependency
> +between the lock on top of held_locks and the lock to acquire, which
> +must be added.

I am unable to understand the above sentence.  Can you break it into
two more sentences?

> +
> +Besides, we can expect several advantages from the limitation.
> +
> +1. Any lock being in held_locks cannot be released unconditionally if
> +   the context is stuck, thus we can easily identify a dependency when
> +   acquiring a lock.
> +
> +2. Considering only locks being in local held_locks of a single context
> +   makes some races avoidable, even though it fails of course when
> +   modifying its global dependency graph.
> +
> +3. To build a dependency graph, lockdep only needs to keep locks not
> +   released yet. However relaxing the limitation, it might need to keep
> +   even locks already released, additionally. See 'Crossrelease' section.
> +
> +CONCLUSION
> +
> +Given the limitation, the implementation becomes simple and efficient.
> +
> +
> +Cons from the limitation
> +------------------------
> +
> +Given the limitation, lockdep is applicable only to typical lock. For
> +example, page lock for page access or completion for synchronization
> +cannot play with lockdep having the limitation. However since page lock
> +or completion also causes a deadlock, it would be better to detect a
> +deadlock or its possibility even for them.
> +
> +Can we detect deadlocks below with lockdep having the limitation?
> +
> +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.

I think the formatting is slightly off.  The events corresponding to
process Y should be shifted more towards right.  The vertical ordering
would still clearly indicate the order in which the events happened.


> +
> +No, we cannot.

I do not follow this example.  The context that acquired the mutex A
or lock B also released it.  Should not lockdep be able to detect
this?  I am guessing I have misunderstood which events occurred in
which context.   Here is what I think the example is showing:

X acquired mutex A --> Y acquired lock B --> X tries to acquire B -->
Y tries to acquire A -->  X and Y are in a deadlock since each of them
holds a resource that the other needs.

> +
> +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 held by 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.
> +
> +CONCLUSION
> +
> +Given the limitation, lockdep cannot detect a deadlock or its
> +possibility caused by page lock or completion.
> +
> +
> +==============
> +Generalization
> +==============
> +
> +Relax the limitation
> +--------------------
> +
> +Detecting and adding new 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. More dependencies lockdep adds, more
> +throughly it can work. Therefore Lockdep has to do its best to add as
> +many true dependencies as possible.
> +
> +Relaxing the limitation, lockdep can add additional dependencies since
> +it makes lockdep deal with additional ones creating the dependencies e.g.
> +page lock or completion, which might be released in any context. Even so,
> +it needs to be noted that behaviors adding dependencies created by
> +typical lock don't need to be changed at all.
> +
> +For example, only considering typical lock, 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, and upper case letters
> +represent typical lock.
> +
> +After the relaxing, the graph will have additional dependencies like,
> +
> +A -> B -        -> F -> G
> +        \      /
> +         -> E -        -> L -> c
> +        /      \      /
> +C -> D -        -> H -
> +               /      \
> +            a -        -> I -> K
> +                      /
> +              b -> J -
> +
> +where a, b, c, A, B,..., L are different lock classes, and upper case
> +letters represent typical lock while lower case letters represent
> +non-typical lock e.g. page lock and completion.
> +
> +However, it might suffer performance degradation since relaxing the
> +limitation with which design and implementation of lockdep become
> +efficient might introduce inefficiency inevitably. Each option, that is,
> +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.
> +
> +In the latter, of course, some contexts are not allowed if they
> +themselves cause a deadlock. 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 a cycle of a dependency chain, meaning a
> +deadlock. Otherwise, any contexts are allowed to release it.
> +
> +CONCLUSION
> +
> +Relaxing the limitation, lockdep adds additional dependencies and gets
> +additional chances to check if they cause a deadlock. It makes lockdep
> +additionally deal with what might be released in any context.
> +
> +
> +============
> +Crossrelease
> +============
> +
> +Introduce crossrelease
> +----------------------
> +
> +To allow lockdep to add additional dependencies created by what might be
> +released in any context, which we call 'crosslock', it's necessary to
> +introduce a new feature which makes it possible to identify and add the
> +dependencies. We call the feature 'crossrelease'. Crossrelease feature
> +has to do,
> +
> +1. Identify a new dependency created by crosslock.
> +2. Add the dependency into graph when identifying it.
> +
> +That's all. Once a meaningful dependency is added into graph, lockdep
> +will work with the graph as it did. So the most important thing to do is
> +to identify a dependency created by crosslock. Remind what a dependency
> +is. For example, Lock A depends on lock B if 'lock A cannot be released
> +if lock B isn't released'. See 'What causes deadlock' section.
> +
> +By definition, a lock depends on every lock having been added into
> +held_locks in the lock's release context since the lock was acquired,
> +because the lock cannot be released if the release context is stuck by
> +any of dependent locks, not released. So lockdep should technically
> +consider release contexts of locks to identify dependencies.
> +
> +It's no matter of course to typical lock because acquire context is same
> +as release context for typical lock, which means lockdep would work with
> +considering only acquire contexts for typical lock. However, for
> +crosslock, lockdep cannot identify release context and any dependency
> +until the crosslock will be actually released.
> +
> +Regarding crosslock, lockdep has to record all history by queueing all
> +locks potentially creating dependencies so that real dependencies can be
> +added using the history recorded when identifying release context. We
> +call it 'commit', that is, to add dependencies in batches. See
> +'Introduce commit' section.
> +
> +Of course, some actual deadlocks caused by crosslock cannot be detected
> +at the time it happened, because the deadlocks cannot be indentified and
> +detected until the crosslock will be actually released. But this way
> +deadlock possibility can be detected and it's worth just possibility
> +detection of deadlock. See 'What lockdep does' section.
> +
> +CONCLUSION
> +
> +With crossrelease feature, lockdep can works with what might be released
> +in any context, namely, crosslock.
> +
> +
> +Introduce commit
> +----------------
> +
> +Crossrelease feature names it 'commit' to identify and add dependencies
> +into graph in batches. Lockdep is already doing what commit does when
> +acquiring a lock, for typical lock. However, that way must be changed
> +for crosslock so that it identifies the crosslock's release context
> +first and then does commit.
> +
> +The main reason why lockdep performs additional step, namely commit, for
> +crosslock is that some dependencies by crosslock cannot be identified
> +until the crosslock's release context is eventually identified, though
> +some other dependencies by crosslock can. There are four kinds of
> +dependencies to consider.
> +
> +1. 'typical lock A -> typical lock B' dependency
> +
> +   Just when acquiring lock B, lockdep can identify the dependency
> +   between lock A and lock B as it did. Commit is unnecessary.
> +
> +2. 'typical lock A -> crosslock b' dependency
> +
> +   Just when acquiring crosslock b, lockdep can identify the dependency
> +   between lock A and crosslock B as well. Commit is unnecessary, too.
> +
> +3. 'crosslock a -> typical lock B' dependency
> +
> +   When acquiring lock B, lockdep cannot identify the dependency. It can
> +   be identified only when crosslock a is released. Commit is necessary.
> +
> +4. 'crosslock a -> crosslock b' dependency
> +
> +   Creating this kind of dependency directly is unnecessary since it can
> +   be covered by other kinds of dependencies.
> +
> +Lockdep works without commit during dealing with only typical locks.
> +However, it needs to perform commit step, once at least one crosslock is
> +acquired, until all crosslocks in progress are released. Introducing
> +commit, lockdep performs three steps i.e. acquire, commit and release.
> +What lockdep should do in each step is like,
> +
> +1. Acquire
> +
> +   1) For typical lock
> +
> +       Lockdep does what it originally does and queues the lock so
> +       that lockdep can check dependencies using it at commit step.
> +
> +   2) For crosslock
> +
> +       The crosslock is added to a global linked list so that lockdep
> +       can check dependencies using it at commit step.
> +
> +2. Commit
> +
> +   1) For typical lock
> +
> +       N/A.
> +
> +   2) For crosslock
> +
> +       Lockdep checks and adds dependencies using data saved at acquire
> +       step, as if the dependencies were added without commit 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
> +
> +Lockdep can detect a deadlock or its possibility caused by what might be
> +released in any context, using commit step, where it checks and adds
> +dependencies in batches.
> +
> +
> +==============
> +Implementation
> +==============
> +
> +Data structures
> +---------------
> +
> +Crossrelease feature introduces two main data structures.
> +
> +1. pend_lock (or plock)
> +
> +   This is an array embedded in task_struct, for keeping locks queued so
> +   that real dependencies can be added using them at commit step. So
> +   this data can be accessed locklessly within the owner context. The
> +   array is filled when acquiring a typical lock and consumed when doing
> +   commit. And it's managed in circular manner.
> +
> +2. cross_lock (or xlock)
> +
> +   This is a global linked list, for keeping all crosslocks in progress.
> +   The list grows when acquiring a crosslock and is shrunk when
> +   releasing the crosslock. lockdep_init_map_crosslock() should be used
> +   to initialize a crosslock instance instead of lockdep_init_map() so
> +   that lockdep can recognize it as crosslock.
> +
> +CONCLUSION
> +
> +Crossrelease feature uses 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.
> +

Drop these conclusion, not need at all.


> +
> +How crossrelease works
> +----------------------
> +
> +Let's take 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 lock.
> +
> +       RELEASE CONTEXT of A (= ACQUIRE CONTEXT of A)
> +       --------------------
> +       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, and upper case letters
> +represent typical lock.
> +
> +After adding 'A -> B', the dependency graph will be,
> +
> +A -> B
> +
> +where A and B are different lock classes, and upper case letters
> +represent typical lock.
> +
> +And after adding 'B -> C', the graph will be,
> +
> +A -> B -> C
> +
> +where A, B and C are different lock classes, and upper case letters
> +represent typical lock.
> +
> +What if applying commit on typical locks? It's not necessary for typical
> +lock. Just for showing what commit does.
> +
> +       RELEASE CONTEXT of A (= ACQUIRE CONTEXT of A)
> +       --------------------
> +       acquire A -> mark A as started (nothing before, no queueing)
> +
> +       acquire B -> mark B as started and queue B
> +
> +       acquire C -> mark C as started and queue C
> +
> +       release C -> commit C (nothing queued since C started)
> +
> +       release B -> commit B -> add a dependency 'B -> C'
> +
> +       release A -> commit A -> add dependencies 'A -> B' and 'A -> C'
> +
> +where A, B and C are different lock classes, and upper case letters
> +represent typical lock.
> +
> +After doing commit A, B and C, the dependency graph becomes like,
> +
> +A -> B -> C
> +
> +where A, B and C are different lock classes, and upper case letters
> +represent typical lock.
> +
> +NOTE: A dependency 'A -> C' is optimized out.
> +
> +Here we can see the final graph is same as the graph built without
> +commit. Of course the former way leads to finish building the graph
> +earlier than the latter way, 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 using commit, for crosslock.
> +
> +Let's look at how commit works for crosslock.
> +
> +       RELEASE CONTEXT of a    ACQUIRE CONTEXT of a
> +       --------------------    --------------------
> +                               acquire a -> mark a as started
> +
> +       (serialized by some means e.g. barrier)
> +
> +       acquire D -> queue D
> +                               acquire B -> queue B
> +       release D
> +                               acquire C -> add 'B -> C' and queue C
> +       acquire E -> queue E
> +                               acquire D -> add 'C -> D' and queue D
> +       release E
> +                               release D
> +       release a -> commit a -> add 'a -> D' and 'a -> E'
> +                               release C
> +
> +                               release B
> +
> +where a, B,..., E are different lock classes, and upper case letters
> +represent typical lock while lower case letters represent crosslock.
> +
> +When acquiring crosslock a, no dependency can be added since there's
> +nothing in the held_locks. However, crossrelease feature marks the
> +crosslock as started, which means all locks to acquire from now are
> +candidates which might create new dependencies later when identifying
> +release context.
> +
> +When acquiring lock B, lockdep does what it originally does for typical
> +lock and additionally queues the lock for later commit to refer to
> +because it might be a dependent lock of the crosslock. It does same
> +thing on lock C, D and E. And then two dependencies 'a -> D' and 'a -> E'
> +are added when identifying the release context, at commit step.
> +
> +The final graph is, with crossrelease feature using commit,
> +
> +B -> C -
> +        \
> +         -> D
> +        /
> +     a -
> +        \
> +         -> E
> +

Would not the graph also contain edges a->B and a->C, which lockdep
would add as it usually does?


> +where a, B,..., E are different lock classes, and upper case letters
> +represent typical lock while lower case letters represent crosslock.
> +
> +However, without crossrelease feature, the final graph will be,
> +
> +B -> C -> D
> +
> +where B and C are different lock classes, and upper case letters
> +represent typical lock.
> +
> +The former graph has two more dependencies 'a -> D' and 'a -> E' giving
> +additional chances to check if they cause a deadlock. This way lockdep
> +can detect a deadlock or its possibility caused by crosslock. Again,
> +behaviors adding dependencies created by only typical locks are not
> +changed at all.
> +
> +CONCLUSION
> +
> +Crossrelease works using commit for crosslock, leaving behaviors adding
> +dependencies between only typical locks unchanged.
> +
> +
> +=============
> +Optimizations
> +=============
> +
> +Avoid duplication
> +-----------------
> +
> +Crossrelease feature uses a cache like what lockdep already uses for
> +dependency chains, but this time it's for caching one dependency like
> +'crosslock -> typical lock' crossing between two different context. Once
> +that dependency is cached, same dependency will never be added any more.
> +Even queueing unnecessary locks is also prevented based on the cache.
> +
> +CONCLUSION
> +
> +Crossrelease does not add any duplicate dependency.
> +

No need for conclusion.

> +
> +Avoid lock contention
> +---------------------
> +
> +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 each array to be accessed only within each own
> +context. It's like how held_locks is accessed. Lockless implmentation is
> +important since typical locks are very frequently accessed.
> +
> +CONCLUSION
> +
> +Crossrelease avoids lock contection as far as possible.
> --
> 1.9.1
>


No need for conclusion.


Overall, I think I have generally understood what you are trying to do
and how are doing it.  I think now I'll be able to better review the
patches with actual.

Thanks!
Nilay

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-14  8:11           ` Peter Zijlstra
@ 2016-09-19  2:41             ` Byungchul Park
  2016-09-19  8:50               ` Peter Zijlstra
  0 siblings, 1 reply; 49+ messages in thread
From: Byungchul Park @ 2016-09-19  2:41 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Wed, Sep 14, 2016 at 10:11:17AM +0200, Peter Zijlstra wrote:
> On Wed, Sep 14, 2016 at 11:27:22AM +0900, Byungchul Park wrote:
> > > Well, there is, its just not trivially observable. We must be able to
> > > acquire a in order to complete b, therefore there is a dependency.
> > 
> > No. We cannot say there is a dependency unconditionally. There can
> > be a dependency or not.
> > 
> > L a     L a
> >         U a
> > ~~~~~~~~~ what if serialized by something?
> 
> Well, there's no serialization in the example, so no what if.

It was a korean traditional holliday for a week so I'm late.

I mean we cannot _ensure_ there's no serialization while lockdep works.
In _the_ case you suggested, you'are right if only those code exists.
But it's meaningless.

> > W b     C b
> > 
> > If something we don't recognize serializes locks, which ensures
> > 'W b' happens after 'L a , U a' in the other context, then there's
> > no dependency here.
> 
> Its not there.
> 
> > We should say 'b depends on a' in only case that the sequence
> > 'W b and then L a and then C b, where last two ops are in same
> > context' _actually_ happened at least once. Otherwise, it might
> > add a false dependency.
> > 
> > It's same as how original lockdep works with typical locks. It adds
> > a dependency only when a lock is actually hit.
> 
> But since these threads are independently scheduled there is no point in
> transferring the point in time thread A does W to thread B. There is no
> relation there.
> 
> B could have already executed the complete or it could not yet have
> started execution at all or anything in between, entirely random.

Of course B could have already executed the complete or it could not yet
have started execution at all or anything in between. But it's not entirely
random.

It might be a random point since they are independently scheduled, but it's
not entirely random. And it's a random point among valid points which lockdep
needs to consider. For example,


CONTEXT 1			CONTEXT 2(forked one)
=========			=====================
(a)				acquire F
acquire A			acquire G
acquire B			wait_for_completion Z
acquire C
(b)				acquire H
fork 2				acquire I
acquire D			acquire J
complete Z			acquire K


I can provide countless examples with which I can say you're wrong.
In this case, all acquires between (a) and (b) must be ignored when
generating dependencies with complete operation of Z. It's never random.

Ideally, it would be of course the best to consider all points (not random
points) after (b) which are valid points which lockdep needs to work with.
But I think it's impossible to parse and identify all synchronizations and
forks in kernel code, furthermore, new synchronization interface can be
introduced in future.

So IMHO it would be the second best to consider random points among valid
points, which anyway actually happened so it's guarrented that it has a
depenency with Z.

It's similar to how lockdep works for typical lock e.g. spin lock. Current
lockdep builds dependecy graph based on call paths which actually happened
in each context, which might be different from each run. Even current
lockdep doesn't parse all code and identify dependencies but works based on
actual call paths in runtime which can be random but will eventually cover
it almost (not perfect).

> > > What does that mean? Any why? This is a random point in time without
> > > actual meaning.
> > 
> > It's not random point. We have to consider meaningful sequences among
> > those which are globally observable. That's why we need to serialize
> > those locks.
> 
> Serialize how? there is no serialization.

I mean I did it in my crossrelease implementation.

> 
> > For example,
> > 
> > W b
> > L a
> > U a
> > C b
> > 
> > Once this sequence is observable globally, we can say 'It's possible to
> > run in this sequence. Is this sequence problematic or not?'.
> > 
> > L a
> > U a
> > W b
> > C b
> > 
> > If only this sequence can be observable, we should not assume
> > this sequence can be changed. However once the former sequence
> > happens, it has a possibility to hit the same sequence again later.
> > So we can check deadlock possibility with the sequence,
> > 
> > _not randomly_.
> 
> I still don't get it.
> 
> > We need to connect between the crosslock and the first lock among
> > locks having been acquired since the crosslock was held.
> 
> Which can be _any_ lock in the history of that thread. It could be
> rq->lock from getting the thread scheduled.

I think I already answered it. Right?

> 
> > Others will be
> > connected each other by original lockdep.
> > 
> > By the way, does my document miss this description? If so, sorry.
> > I will check and update it.
> 
> I couldn't find anything useful, but then I could not understand most of
> what was written, and I tried hard :-(

Thank you for trying it.

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

* Re: [PATCH v3 15/15] lockdep: Crossrelease feature documentation
  2016-09-15 17:25   ` Nilay Vaish
@ 2016-09-19  2:59     ` Byungchul Park
  0 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-19  2:59 UTC (permalink / raw)
  To: Nilay Vaish
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Thu, Sep 15, 2016 at 12:25:51PM -0500, Nilay Vaish wrote:
> On 13 September 2016 at 04:45, Byungchul Park <byungchul.park@lge.com> wrote:
> > 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 | 785 +++++++++++++++++++++++++++++++++
> >  1 file changed, 785 insertions(+)
> >  create mode 100644 Documentation/locking/crossrelease.txt
> 
> Byungchul, I mostly skimmed through the document.  I suggest that we
> split this document.  The initial 1/4 of the document talks about
> lockdep's current implementation which I believe should be combined
> with the file: Documentation/locking/lockdep-design.txt. Tomorrow I
> would try to understand the document in detail and hopefully provide
> some useful comments.

Hello,

It was a korean traditional holliday for a week so I'm late.
And I also think 1/4 of it talks about original lockdep.

Thank you,
Byungchul

> 
> --
> Nilay

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

* Re: [PATCH v3 15/15] lockdep: Crossrelease feature documentation
  2016-09-16 15:47   ` Nilay Vaish
@ 2016-09-19  3:00     ` Byungchul Park
  2016-09-20  5:00     ` Byungchul Park
  1 sibling, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-19  3:00 UTC (permalink / raw)
  To: Nilay Vaish
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Fri, Sep 16, 2016 at 10:47:06AM -0500, Nilay Vaish wrote:
> On 13 September 2016 at 04:45, Byungchul Park <byungchul.park@lge.com> wrote:
> > 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 | 785 +++++++++++++++++++++++++++++++++
> >  1 file changed, 785 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..78558af
> > --- /dev/null
> > +++ b/Documentation/locking/crossrelease.txt
> > @@ -0,0 +1,785 @@
> > +Crossrelease
> > +============
> > +
> > +Started by Byungchul Park <byungchul.park@lge.com>
> > +
> > +Contents:
> > +
> > + (*) Background.
> > +
> > +     - What causes deadlock.
> > +     - What lockdep detects.
> > +     - How lockdep works.
> > +
> > + (*) Limitation.
> > +
> > +     - Limit to typical lock.
> > +     - Pros from the limitation.
> > +     - Cons from the limitation.
> > +
> > + (*) Generalization.
> > +
> > +     - Relax the limitation.
> > +
> > + (*) Crossrelease.
> > +
> > +     - Introduce crossrelease.
> > +     - Introduce commit.
> > +
> > + (*) Implementation.
> > +
> > +     - Data structures.
> > +     - How crossrelease works.
> > +
> > + (*) Optimizations.
> > +
> > +     - Avoid duplication.
> > +     - Avoid lock contention.
> > +
> > +
> > +==========
> > +Background
> > +==========
> > +
> > +What causes deadlock
> > +--------------------
> > +
> > +A deadlock occurs when a context is waiting for an event to be issued
> > +which cannot be issued because the context or another context who can
> > +issue the event is also waiting for an event to be issued which cannot
> > +be issued.
> 
> I think 'some event happened' and 'context triggered an event' is
> better than 'some event issued' or 'context issued an event'.  I think
> 'happen' and 'trigger' are more widely used words when we talk about
> events.  For example, I would prefer the following version of the
> above:
> 
> A deadlock occurs when a context is waiting for an event to happen,
> which cannot happen because the context which can trigger the event is
> also waiting for an event to happen which cannot happen either.

Thank you very much for your comments.
I will check it and add my opinions later. :)

Thank you,
Byungchul

> 
> > +Single context or more than one context both waiting for an
> > +event and issuing an event may paricipate in a deadlock.
> 
> I am not able to make sense of the line above.
> 
> > +
> > +For example,
> > +
> > +A context who can issue event D is waiting for event A to be issued.
> > +A context who can issue event A is waiting for event B to be issued.
> > +A context who can issue event B is waiting for event C to be issued.
> > +A context who can issue event C is waiting for event D to be issued.
> > +
> > +A deadlock occurs when these four operations are run at a time because
> > +event D cannot be issued if event A isn't issued which in turn cannot be
> > +issued if event B isn't issued which in turn cannot be issued if event C
> > +isn't issued which in turn cannot be issued if event D isn't issued. No
> > +event can be issued since any of them never meets its precondition.
> > +
> > +We can easily recognize that each wait operation creates a dependency
> > +between two issuings e.g. between issuing D and issuing A like, 'event D
> > +cannot be issued if event A isn't issued', in other words, 'issuing
> > +event D depends on issuing event A'. So the whole example can be
> > +rewritten in terms of dependency,
> > +
> > +Do an operation making 'event D cannot be issued if event A isn't issued'.
> > +Do an operation making 'event A cannot be issued if event B isn't issued'.
> > +Do an operation making 'event B cannot be issued if event C isn't issued'.
> > +Do an operation making 'event C cannot be issued if event D isn't issued'.
> > +
> > +or,
> 
> I think we can remove the text above.  The example only needs to be
> provided once.
> 
> > +
> > +Do an operation making 'issuing event D depends on issuing event A'.
> > +Do an operation making 'issuing event A depends on issuing event B'.
> > +Do an operation making 'issuing event B depends on issuing event C'.
> > +Do an operation making 'issuing event C depends on issuing event D'.
> > +
> > +What causes a deadlock is a set of dependencies a chain of which forms a
> > +cycle, which means that issuing event D depending on issuing event A
> > +depending on issuing event B depending on issuing event C depending on
> > +issuing event D, finally depends on issuing event D itself, which means
> > +no event can be issued.
> > +
> > +Any set of operations creating dependencies causes a deadlock. The set
> > +of lock operations e.g. acquire and release is an example. Waiting for a
> > +lock to be released corresponds to waiting for an event and releasing a
> > +lock corresponds to issuing an event. So the description of dependency
> > +above can be altered to one in terms of lock.
> > +
> > +In terms of event, issuing event A depends on issuing event B if,
> > +
> > +       Event A cannot be issued if event B isn't issued.
> > +
> > +In terms of lock, releasing lock A depends on releasing lock B if,
> > +
> > +       Lock A cannot be released if lock B isn't released.
> > +
> > +CONCLUSION
> > +
> > +A set of dependencies a chain of which forms a cycle, causes a deadlock,
> 
> I think 'a chain of' is not required in the sentence above.
> 
> > +no matter what creates the dependencies.
> > +
> > +
> > +What lockdep detects
> > +--------------------
> > +
> > +A deadlock actually occurs only when all operations creating problematic
> 
> Instead of 'problematic', I would use 'cyclic'.
> 
> > +dependencies are run at a time. However, even if it has not happend, the
> 
> dependencies happen at run time.  However, even if they don't, the
> 
> > +deadlock potentially can occur if the problematic dependencies obviously
> 
> remove obviously
> 
> > +exist. Thus it's meaningful to detect not only an actual deadlock but
> > +also its possibility. Lockdep does the both.
> > +
> > +Whether a deadlock actually occurs or not depends on several factors,
> > +which means a deadlock may not occur even though problematic
> 
> cyclic instead of problematic
> 
> > +dependencies exist. For example, what order contexts are switched in is
> > +a factor. A deadlock will occur when contexts are switched so that all
> > +operations causing a deadlock become run simultaneously.
> 
> delete become.
> 
> > +
> > +Lockdep tries to detect a deadlock or its possibility aggressively,
> > +though it also tries to avoid false positive detections. So lockdep is
> > +designed to consider all possible combinations of dependencies so that
> > +it can detect all potential possibilities of deadlock in advance. What
> > +lockdep tries in order to consider all possibilities are,
> > +
> > +1. Use a global dependency graph including all dependencies.
> > +
> > +   What lockdep checks is based on dependencies instead of what actually
> > +   happened. So no matter which context or call path a new dependency is
> > +   detected in, it's just referred to as a global factor.
> 
> Can you explain more what 'global factor' means?
> 
> 
> > +
> > +2. Use lock classes than lock instances when checking dependencies.
> > +
> > +   What actually causes a deadlock is lock instances. However, lockdep
> > +   uses lock classes than its instances when checking dependencies since
> > +   any instance of a same lock class can be altered anytime.
> 
> I am unable to make sense of the sentence above.  Do you want to say
> the following:
> 
> However, lockdep uses lock classes than its instances when checking
> dependencies since instances from the same lock class behave in the
> same (or similar) fashion.
> 
> > +
> > +So lockdep detects both an actual deadlock and its possibility. But the
> > +latter is more valuable than the former. When a deadlock actually
> > +occures, 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 does, the fisrt one is more valuable,
> > +
> > +1. Detecting and reporting deadlock possibility.
> > +2. Detecting and reporting a deadlock actually occured.
> > +
> > +
> > +How lockdep works
> > +-----------------
> > +
> > +What lockdep should do, to detect a deadlock or its possibility are,
> > +
> > +1. Detect a new dependency created.
> > +2. Keep the dependency in a global data structure esp. graph.
> > +3. Check if any of all possible chains of dependencies forms a cycle.
> > +4. Report a deadlock or its possibility if a cycle is detected.
> > +
> > +A graph used by lockdep to keep all dependencies looks like,
> > +
> > +A -> B -        -> F -> G
> > +        \      /
> > +         -> E -        -> L
> > +        /      \      /
> > +C -> D -        -> H -
> > +                      \
> > +                       -> I -> K
> > +                      /
> > +                   J -
> > +
> > +where A, B,..., L are different lock classes.
> > +
> > +Lockdep adds a dependency into graph when a new dependency is detected.
> > +For example, it adds a dependency 'A -> B' when a dependency between
> > +releasing lock A and releasing lock B, which has not been added yet, is
> > +detected. It does same thing on other dependencies, too. See 'What
> > +causes deadlock' section.
> 
> Just from the text above I am not able to understand at what point the
> dependency A-> B is added.  If I was implementing lockdep, I would
> probably track all the locks that can be acquired between acquisition
> and release of A.  For example,
> 
> acquire A
> 
> if (/* some condition */)
>     acquire B
> else
>     acquire C
> 
> release A
> 
> 
> I would add to the dependency graph that 'release A' depends on
> 'acquire B' and 'acquire C'.
> 
> > +
> > +NOTE: Precisely speaking, a dependency is one between releasing a lock
> > +and releasing another lock as described in 'What causes deadlock'
> > +section. However from now on, we will describe a dependency as if it's
> > +one between a lock and another lock for simplicity. Then 'A -> B' can be
> > +described as a dependency between lock A and lock B.
> > +
> > +We already checked how a problematic set of dependencies causes a
> > +deadlock in 'What causes deadlock' section. This time let's check if a
> > +deadlock or its possibility can be detected using a problematic set of
> > +dependencies. Assume that 'A -> B', 'B -> E' and 'E -> A' were added in
> > +the sequence into graph. Then the graph finally will be,
> > +
> > + -> A -> B -> E -
> > +/                \
> > +\                /
> > + ----------------
> > +
> > +where A, B and E are different lock classes.
> > +
> > +From adding three dependencies, a cycle was created which means, by
> > +definition of dependency, the situation 'lock E must be released to
> > +release lock B which in turn must be released to release lock A which in
> > +turn must be released to release lock E which in turn must be released
> > +to release B and so on infinitely' can happen.
> > +
> > +Once the situation happens, no lock can be released since any of them
> > +can never meet each precondition. It's a deadlock. Lockdep can detect a
> > +deadlock or its possibility with checking if a cycle was created after
> > +adding each dependency into graph. This is how lockdep detects a
> > +deadlock or its possibility.
> 
> I think the text here is just repeating what was said above in the
> 'What causes deadlock' section.  You probably should remove of the
> text here.
> 
> > +
> > +CONCLUSION
> > +
> > +Lockdep detects a deadlock or its possibility with checking if a cycle
> > +was created after adding each dependency into graph.
> > +
> > +
> > +==========
> > +Limitation
> > +==========
> > +
> > +Limit to typical lock
> > +---------------------
> > +
> > +Limiting what lockdep has to consider to only ones satisfying the
> > +following condition, the implementation of adding dependencies becomes
> > +simple while its capacity for detection becomes limited. Typical lock
> > +e.g. spin lock and mutex is the case. Let's check what pros and cons of
> > +it are, in next section.
> > +
> > +       A lock should be released within the context holding the lock.
> 
> I would rephrase the above in the following way:  Lockdep is limited
> to checking dependencies on locks that are released within the context
> that acquired them.  This makes adding dependencies simple but limits
> lockdep's capacity for detection.
> 
> 
> > +
> > +CONCLUSION
> > +
> > +Limiting what lockdep has to consider to typical lock e.g. spin lock and
> > +mutex, the implmentation becomes simple while it has a limited capacity.
> 
> I would drop the conclusion altogether.  The above paragraph is too
> small to require a conclusion.
> 
> > +
> > +
> > +Pros from the limitation
> > +------------------------
> > +
> > +Given the limitation, when acquiring a lock, any lock being in
> > +held_locks of the acquire context cannot be released if the lock to
> 
> What does held_locks mean here?  Is it some structure maintained by
> the Linux kernel?
> 
> > +acquire was not released yet. Yes. It's the exact case to add a new
> > +dependency 'A -> B' into graph, where lock A represents each lock being
> > +in held_locks and lock B represents the lock to acquire.
> > +
> > +For example, only considering typical lock,
> > +
> > +       PROCESS 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, there's nothing in held_locks of PROCESS X thus
> > +no dependency is added. When acquiring lock B, lockdep detects and adds
> > +a new dependency 'A -> B' between lock A being in held_locks and lock B.
> > +And when acquiring lock C, lockdep also adds another dependency 'B -> C'
> > +for same reason. They are added when acquiring each lock, simply.
> > +
> > +NOTE: Even though every lock being in held_locks depends on the lock to
> > +acquire, lockdep does not add all dependencies between them because all
> > +of them can be covered by other dependencies except one dependency
> > +between the lock on top of held_locks and the lock to acquire, which
> > +must be added.
> 
> I am unable to understand the above sentence.  Can you break it into
> two more sentences?
> 
> > +
> > +Besides, we can expect several advantages from the limitation.
> > +
> > +1. Any lock being in held_locks cannot be released unconditionally if
> > +   the context is stuck, thus we can easily identify a dependency when
> > +   acquiring a lock.
> > +
> > +2. Considering only locks being in local held_locks of a single context
> > +   makes some races avoidable, even though it fails of course when
> > +   modifying its global dependency graph.
> > +
> > +3. To build a dependency graph, lockdep only needs to keep locks not
> > +   released yet. However relaxing the limitation, it might need to keep
> > +   even locks already released, additionally. See 'Crossrelease' section.
> > +
> > +CONCLUSION
> > +
> > +Given the limitation, the implementation becomes simple and efficient.
> > +
> > +
> > +Cons from the limitation
> > +------------------------
> > +
> > +Given the limitation, lockdep is applicable only to typical lock. For
> > +example, page lock for page access or completion for synchronization
> > +cannot play with lockdep having the limitation. However since page lock
> > +or completion also causes a deadlock, it would be better to detect a
> > +deadlock or its possibility even for them.
> > +
> > +Can we detect deadlocks below with lockdep having the limitation?
> > +
> > +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.
> 
> I think the formatting is slightly off.  The events corresponding to
> process Y should be shifted more towards right.  The vertical ordering
> would still clearly indicate the order in which the events happened.
> 
> 
> > +
> > +No, we cannot.
> 
> I do not follow this example.  The context that acquired the mutex A
> or lock B also released it.  Should not lockdep be able to detect
> this?  I am guessing I have misunderstood which events occurred in
> which context.   Here is what I think the example is showing:
> 
> X acquired mutex A --> Y acquired lock B --> X tries to acquire B -->
> Y tries to acquire A -->  X and Y are in a deadlock since each of them
> holds a resource that the other needs.
> 
> > +
> > +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 held by 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.
> > +
> > +CONCLUSION
> > +
> > +Given the limitation, lockdep cannot detect a deadlock or its
> > +possibility caused by page lock or completion.
> > +
> > +
> > +==============
> > +Generalization
> > +==============
> > +
> > +Relax the limitation
> > +--------------------
> > +
> > +Detecting and adding new 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. More dependencies lockdep adds, more
> > +throughly it can work. Therefore Lockdep has to do its best to add as
> > +many true dependencies as possible.
> > +
> > +Relaxing the limitation, lockdep can add additional dependencies since
> > +it makes lockdep deal with additional ones creating the dependencies e.g.
> > +page lock or completion, which might be released in any context. Even so,
> > +it needs to be noted that behaviors adding dependencies created by
> > +typical lock don't need to be changed at all.
> > +
> > +For example, only considering typical lock, 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, and upper case letters
> > +represent typical lock.
> > +
> > +After the relaxing, the graph will have additional dependencies like,
> > +
> > +A -> B -        -> F -> G
> > +        \      /
> > +         -> E -        -> L -> c
> > +        /      \      /
> > +C -> D -        -> H -
> > +               /      \
> > +            a -        -> I -> K
> > +                      /
> > +              b -> J -
> > +
> > +where a, b, c, A, B,..., L are different lock classes, and upper case
> > +letters represent typical lock while lower case letters represent
> > +non-typical lock e.g. page lock and completion.
> > +
> > +However, it might suffer performance degradation since relaxing the
> > +limitation with which design and implementation of lockdep become
> > +efficient might introduce inefficiency inevitably. Each option, that is,
> > +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.
> > +
> > +In the latter, of course, some contexts are not allowed if they
> > +themselves cause a deadlock. 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 a cycle of a dependency chain, meaning a
> > +deadlock. Otherwise, any contexts are allowed to release it.
> > +
> > +CONCLUSION
> > +
> > +Relaxing the limitation, lockdep adds additional dependencies and gets
> > +additional chances to check if they cause a deadlock. It makes lockdep
> > +additionally deal with what might be released in any context.
> > +
> > +
> > +============
> > +Crossrelease
> > +============
> > +
> > +Introduce crossrelease
> > +----------------------
> > +
> > +To allow lockdep to add additional dependencies created by what might be
> > +released in any context, which we call 'crosslock', it's necessary to
> > +introduce a new feature which makes it possible to identify and add the
> > +dependencies. We call the feature 'crossrelease'. Crossrelease feature
> > +has to do,
> > +
> > +1. Identify a new dependency created by crosslock.
> > +2. Add the dependency into graph when identifying it.
> > +
> > +That's all. Once a meaningful dependency is added into graph, lockdep
> > +will work with the graph as it did. So the most important thing to do is
> > +to identify a dependency created by crosslock. Remind what a dependency
> > +is. For example, Lock A depends on lock B if 'lock A cannot be released
> > +if lock B isn't released'. See 'What causes deadlock' section.
> > +
> > +By definition, a lock depends on every lock having been added into
> > +held_locks in the lock's release context since the lock was acquired,
> > +because the lock cannot be released if the release context is stuck by
> > +any of dependent locks, not released. So lockdep should technically
> > +consider release contexts of locks to identify dependencies.
> > +
> > +It's no matter of course to typical lock because acquire context is same
> > +as release context for typical lock, which means lockdep would work with
> > +considering only acquire contexts for typical lock. However, for
> > +crosslock, lockdep cannot identify release context and any dependency
> > +until the crosslock will be actually released.
> > +
> > +Regarding crosslock, lockdep has to record all history by queueing all
> > +locks potentially creating dependencies so that real dependencies can be
> > +added using the history recorded when identifying release context. We
> > +call it 'commit', that is, to add dependencies in batches. See
> > +'Introduce commit' section.
> > +
> > +Of course, some actual deadlocks caused by crosslock cannot be detected
> > +at the time it happened, because the deadlocks cannot be indentified and
> > +detected until the crosslock will be actually released. But this way
> > +deadlock possibility can be detected and it's worth just possibility
> > +detection of deadlock. See 'What lockdep does' section.
> > +
> > +CONCLUSION
> > +
> > +With crossrelease feature, lockdep can works with what might be released
> > +in any context, namely, crosslock.
> > +
> > +
> > +Introduce commit
> > +----------------
> > +
> > +Crossrelease feature names it 'commit' to identify and add dependencies
> > +into graph in batches. Lockdep is already doing what commit does when
> > +acquiring a lock, for typical lock. However, that way must be changed
> > +for crosslock so that it identifies the crosslock's release context
> > +first and then does commit.
> > +
> > +The main reason why lockdep performs additional step, namely commit, for
> > +crosslock is that some dependencies by crosslock cannot be identified
> > +until the crosslock's release context is eventually identified, though
> > +some other dependencies by crosslock can. There are four kinds of
> > +dependencies to consider.
> > +
> > +1. 'typical lock A -> typical lock B' dependency
> > +
> > +   Just when acquiring lock B, lockdep can identify the dependency
> > +   between lock A and lock B as it did. Commit is unnecessary.
> > +
> > +2. 'typical lock A -> crosslock b' dependency
> > +
> > +   Just when acquiring crosslock b, lockdep can identify the dependency
> > +   between lock A and crosslock B as well. Commit is unnecessary, too.
> > +
> > +3. 'crosslock a -> typical lock B' dependency
> > +
> > +   When acquiring lock B, lockdep cannot identify the dependency. It can
> > +   be identified only when crosslock a is released. Commit is necessary.
> > +
> > +4. 'crosslock a -> crosslock b' dependency
> > +
> > +   Creating this kind of dependency directly is unnecessary since it can
> > +   be covered by other kinds of dependencies.
> > +
> > +Lockdep works without commit during dealing with only typical locks.
> > +However, it needs to perform commit step, once at least one crosslock is
> > +acquired, until all crosslocks in progress are released. Introducing
> > +commit, lockdep performs three steps i.e. acquire, commit and release.
> > +What lockdep should do in each step is like,
> > +
> > +1. Acquire
> > +
> > +   1) For typical lock
> > +
> > +       Lockdep does what it originally does and queues the lock so
> > +       that lockdep can check dependencies using it at commit step.
> > +
> > +   2) For crosslock
> > +
> > +       The crosslock is added to a global linked list so that lockdep
> > +       can check dependencies using it at commit step.
> > +
> > +2. Commit
> > +
> > +   1) For typical lock
> > +
> > +       N/A.
> > +
> > +   2) For crosslock
> > +
> > +       Lockdep checks and adds dependencies using data saved at acquire
> > +       step, as if the dependencies were added without commit 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
> > +
> > +Lockdep can detect a deadlock or its possibility caused by what might be
> > +released in any context, using commit step, where it checks and adds
> > +dependencies in batches.
> > +
> > +
> > +==============
> > +Implementation
> > +==============
> > +
> > +Data structures
> > +---------------
> > +
> > +Crossrelease feature introduces two main data structures.
> > +
> > +1. pend_lock (or plock)
> > +
> > +   This is an array embedded in task_struct, for keeping locks queued so
> > +   that real dependencies can be added using them at commit step. So
> > +   this data can be accessed locklessly within the owner context. The
> > +   array is filled when acquiring a typical lock and consumed when doing
> > +   commit. And it's managed in circular manner.
> > +
> > +2. cross_lock (or xlock)
> > +
> > +   This is a global linked list, for keeping all crosslocks in progress.
> > +   The list grows when acquiring a crosslock and is shrunk when
> > +   releasing the crosslock. lockdep_init_map_crosslock() should be used
> > +   to initialize a crosslock instance instead of lockdep_init_map() so
> > +   that lockdep can recognize it as crosslock.
> > +
> > +CONCLUSION
> > +
> > +Crossrelease feature uses 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.
> > +
> 
> Drop these conclusion, not need at all.
> 
> 
> > +
> > +How crossrelease works
> > +----------------------
> > +
> > +Let's take 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 lock.
> > +
> > +       RELEASE CONTEXT of A (= ACQUIRE CONTEXT of A)
> > +       --------------------
> > +       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, and upper case letters
> > +represent typical lock.
> > +
> > +After adding 'A -> B', the dependency graph will be,
> > +
> > +A -> B
> > +
> > +where A and B are different lock classes, and upper case letters
> > +represent typical lock.
> > +
> > +And after adding 'B -> C', the graph will be,
> > +
> > +A -> B -> C
> > +
> > +where A, B and C are different lock classes, and upper case letters
> > +represent typical lock.
> > +
> > +What if applying commit on typical locks? It's not necessary for typical
> > +lock. Just for showing what commit does.
> > +
> > +       RELEASE CONTEXT of A (= ACQUIRE CONTEXT of A)
> > +       --------------------
> > +       acquire A -> mark A as started (nothing before, no queueing)
> > +
> > +       acquire B -> mark B as started and queue B
> > +
> > +       acquire C -> mark C as started and queue C
> > +
> > +       release C -> commit C (nothing queued since C started)
> > +
> > +       release B -> commit B -> add a dependency 'B -> C'
> > +
> > +       release A -> commit A -> add dependencies 'A -> B' and 'A -> C'
> > +
> > +where A, B and C are different lock classes, and upper case letters
> > +represent typical lock.
> > +
> > +After doing commit A, B and C, the dependency graph becomes like,
> > +
> > +A -> B -> C
> > +
> > +where A, B and C are different lock classes, and upper case letters
> > +represent typical lock.
> > +
> > +NOTE: A dependency 'A -> C' is optimized out.
> > +
> > +Here we can see the final graph is same as the graph built without
> > +commit. Of course the former way leads to finish building the graph
> > +earlier than the latter way, 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 using commit, for crosslock.
> > +
> > +Let's look at how commit works for crosslock.
> > +
> > +       RELEASE CONTEXT of a    ACQUIRE CONTEXT of a
> > +       --------------------    --------------------
> > +                               acquire a -> mark a as started
> > +
> > +       (serialized by some means e.g. barrier)
> > +
> > +       acquire D -> queue D
> > +                               acquire B -> queue B
> > +       release D
> > +                               acquire C -> add 'B -> C' and queue C
> > +       acquire E -> queue E
> > +                               acquire D -> add 'C -> D' and queue D
> > +       release E
> > +                               release D
> > +       release a -> commit a -> add 'a -> D' and 'a -> E'
> > +                               release C
> > +
> > +                               release B
> > +
> > +where a, B,..., E are different lock classes, and upper case letters
> > +represent typical lock while lower case letters represent crosslock.
> > +
> > +When acquiring crosslock a, no dependency can be added since there's
> > +nothing in the held_locks. However, crossrelease feature marks the
> > +crosslock as started, which means all locks to acquire from now are
> > +candidates which might create new dependencies later when identifying
> > +release context.
> > +
> > +When acquiring lock B, lockdep does what it originally does for typical
> > +lock and additionally queues the lock for later commit to refer to
> > +because it might be a dependent lock of the crosslock. It does same
> > +thing on lock C, D and E. And then two dependencies 'a -> D' and 'a -> E'
> > +are added when identifying the release context, at commit step.
> > +
> > +The final graph is, with crossrelease feature using commit,
> > +
> > +B -> C -
> > +        \
> > +         -> D
> > +        /
> > +     a -
> > +        \
> > +         -> E
> > +
> 
> Would not the graph also contain edges a->B and a->C, which lockdep
> would add as it usually does?
> 
> 
> > +where a, B,..., E are different lock classes, and upper case letters
> > +represent typical lock while lower case letters represent crosslock.
> > +
> > +However, without crossrelease feature, the final graph will be,
> > +
> > +B -> C -> D
> > +
> > +where B and C are different lock classes, and upper case letters
> > +represent typical lock.
> > +
> > +The former graph has two more dependencies 'a -> D' and 'a -> E' giving
> > +additional chances to check if they cause a deadlock. This way lockdep
> > +can detect a deadlock or its possibility caused by crosslock. Again,
> > +behaviors adding dependencies created by only typical locks are not
> > +changed at all.
> > +
> > +CONCLUSION
> > +
> > +Crossrelease works using commit for crosslock, leaving behaviors adding
> > +dependencies between only typical locks unchanged.
> > +
> > +
> > +=============
> > +Optimizations
> > +=============
> > +
> > +Avoid duplication
> > +-----------------
> > +
> > +Crossrelease feature uses a cache like what lockdep already uses for
> > +dependency chains, but this time it's for caching one dependency like
> > +'crosslock -> typical lock' crossing between two different context. Once
> > +that dependency is cached, same dependency will never be added any more.
> > +Even queueing unnecessary locks is also prevented based on the cache.
> > +
> > +CONCLUSION
> > +
> > +Crossrelease does not add any duplicate dependency.
> > +
> 
> No need for conclusion.
> 
> > +
> > +Avoid lock contention
> > +---------------------
> > +
> > +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 each array to be accessed only within each own
> > +context. It's like how held_locks is accessed. Lockless implmentation is
> > +important since typical locks are very frequently accessed.
> > +
> > +CONCLUSION
> > +
> > +Crossrelease avoids lock contection as far as possible.
> > --
> > 1.9.1
> >
> 
> 
> No need for conclusion.
> 
> 
> Overall, I think I have generally understood what you are trying to do
> and how are doing it.  I think now I'll be able to better review the
> patches with actual.
> 
> Thanks!
> Nilay

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

* Re: [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache()
  2016-09-15 15:33   ` Nilay Vaish
@ 2016-09-19  3:05     ` Byungchul Park
  2016-09-19 16:36       ` Nilay Vaish
  0 siblings, 1 reply; 49+ messages in thread
From: Byungchul Park @ 2016-09-19  3:05 UTC (permalink / raw)
  To: Nilay Vaish
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Thu, Sep 15, 2016 at 10:33:46AM -0500, Nilay Vaish wrote:
> On 13 September 2016 at 04:45, Byungchul Park <byungchul.park@lge.com> wrote:
> > @@ -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;
> > +}
> 
> Byungchul,  do you think we should increment chain_lookup_misses
> before returning NULL from the above function?

Hello,

No, I don't think so.
It will be done in add_chain_cache().

Thank you,
Byungchul

> 
> --
> Nilay

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-19  2:41             ` Byungchul Park
@ 2016-09-19  8:50               ` Peter Zijlstra
  2016-09-20  5:50                 ` Byungchul Park
  2016-09-22  2:57                 ` Byungchul Park
  0 siblings, 2 replies; 49+ messages in thread
From: Peter Zijlstra @ 2016-09-19  8:50 UTC (permalink / raw)
  To: Byungchul Park
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Mon, Sep 19, 2016 at 11:41:02AM +0900, Byungchul Park wrote:

> > But since these threads are independently scheduled there is no point in
> > transferring the point in time thread A does W to thread B. There is no
> > relation there.
> > 
> > B could have already executed the complete or it could not yet have
> > started execution at all or anything in between, entirely random.
> 
> Of course B could have already executed the complete or it could not yet
> have started execution at all or anything in between. But it's not entirely
> random.
> 
> It might be a random point since they are independently scheduled, but it's
> not entirely random. And it's a random point among valid points which lockdep
> needs to consider. For example,
> 
> 
> CONTEXT 1			CONTEXT 2(forked one)
> =========			=====================
> (a)				acquire F
> acquire A			acquire G
> acquire B			wait_for_completion Z
> acquire C
> (b)				acquire H
> fork 2				acquire I
> acquire D			acquire J
> complete Z			acquire K
> 

I'm hoping you left out the releases for brevity? Because calling fork()
with locks held is _really_ poor form.

> I can provide countless examples with which I can say you're wrong.
> In this case, all acquires between (a) and (b) must be ignored when
> generating dependencies with complete operation of Z.

I still don't get the point. Why does this matter?

Sure, A-C are irrelevant in this example, but I don't see how they're
differently irrelevant from a whole bunch of other prior state action.


Earlier you said the algorithm for selecting the dependency is the first
acquire observed in the completing thread after the
wait_for_completion(). Is this correct?


				W z

	A a
	for (i<0;i<many;i++) {
	  A x[i]
	  R x[i]
	}
	R a

	<IRQ>
	  A b
	  R b
	  C z
	</IRQ>

That would be 'a' in this case, but that isn't at all related. Its just
as irrelevant as your A-C. And we can pick @many as big as needed to
flush the prev held cyclic buffer (although I've no idea how that
matters either).

What we want here is to link z to b, no? That is the last, not the first
acquire, it also is independent of when W happened.

At the same time, picking the last is no guarantee either, since that
can equally miss dependencies. Suppose the IRQ handler did:

	<IRQ>
	  A c
	  R c
	  A b
	  R b
	  C z
	</IRQ>

instead. We'd miss the z depends on c relation, and since they're
independent lock sections, lockdep wouldn't make a b-c relation either.


Clearly I'm still missing stuff...

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

* Re: [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache()
  2016-09-19  3:05     ` Byungchul Park
@ 2016-09-19 16:36       ` Nilay Vaish
  2016-09-20  2:00         ` Byungchul Park
  0 siblings, 1 reply; 49+ messages in thread
From: Nilay Vaish @ 2016-09-19 16:36 UTC (permalink / raw)
  To: Byungchul Park
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On 18 September 2016 at 22:05, Byungchul Park <byungchul.park@lge.com> wrote:
> On Thu, Sep 15, 2016 at 10:33:46AM -0500, Nilay Vaish wrote:
>> On 13 September 2016 at 04:45, Byungchul Park <byungchul.park@lge.com> wrote:
>> > @@ -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;
>> > +}
>>
>> Byungchul,  do you think we should increment chain_lookup_misses
>> before returning NULL from the above function?
>
> Hello,
>
> No, I don't think so.
> It will be done in add_chain_cache().
>

I think you are assuming that a call to lookup will always be followed
by add.  I thought the point of breaking the original function into
two was that each of the functions can be used individually, without
the other being called.  This means we would not increment the number
of misses when only lookup() gets called, but not add().  Or we would
increment the number of misses when only add() is called and not
lookup().

It really seems odd to me that hits get incremented in lookup and misses don't.

--
Nilay

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

* Re: [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache()
  2016-09-19 16:36       ` Nilay Vaish
@ 2016-09-20  2:00         ` Byungchul Park
  0 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-20  2:00 UTC (permalink / raw)
  To: Nilay Vaish
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Mon, Sep 19, 2016 at 11:36:25AM -0500, Nilay Vaish wrote:
> On 18 September 2016 at 22:05, Byungchul Park <byungchul.park@lge.com> wrote:
> > On Thu, Sep 15, 2016 at 10:33:46AM -0500, Nilay Vaish wrote:
> >> On 13 September 2016 at 04:45, Byungchul Park <byungchul.park@lge.com> wrote:
> >> > @@ -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;
> >> > +}
> >>
> >> Byungchul,  do you think we should increment chain_lookup_misses
> >> before returning NULL from the above function?
> >
> > Hello,
> >
> > No, I don't think so.
> > It will be done in add_chain_cache().
> >
> 
> I think you are assuming that a call to lookup will always be followed
> by add.  I thought the point of breaking the original function into
> two was that each of the functions can be used individually, without
> the other being called.  This means we would not increment the number

Right.

But, we have to remind that counting for cache miss can happen twice if
it's handled in lookup_chain_cache(), because chain_lookup_misses() is
called twice every lookup. One is 'lockless access' for fast path, the
other is 'lock-protected access' for guarranting real miss.

So only when the miss is indentified under lock-protected,
chain_lookup_misses has to be counted. Current chain_lookup_misses means
that "cache miss happened and was _added_ into cache", semantically.
Thus I think it's not bad to handle it in add().

> of misses when only lookup() gets called, but not add().  Or we would

lookup() might be called locklessly for fast path. It would be useful, but
it guarrantees nothing but cache bit. So we cannot count miss in lookup().
Furthermore, we have to assume add() is called when cache miss.

> increment the number of misses when only add() is called and not
> lookup().

Actually add() will not be called without calling lookup(). Anyway, I can
see what you're concerning.. Is there any alterative which is better?

> 
> It really seems odd to me that hits get incremented in lookup and misses don't.
> 
> --
> Nilay

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

* Re: [PATCH v3 15/15] lockdep: Crossrelease feature documentation
  2016-09-16 15:47   ` Nilay Vaish
  2016-09-19  3:00     ` Byungchul Park
@ 2016-09-20  5:00     ` Byungchul Park
  1 sibling, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-20  5:00 UTC (permalink / raw)
  To: Nilay Vaish
  Cc: peterz, mingo, tglx, walken, boqun.feng, kirill,
	Linux Kernel list, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Fri, Sep 16, 2016 at 10:47:06AM -0500, Nilay Vaish wrote:
> > +==========
> > +Background
> > +==========
> > +
> > +What causes deadlock
> > +--------------------
> > +
> > +A deadlock occurs when a context is waiting for an event to be issued
> > +which cannot be issued because the context or another context who can
> > +issue the event is also waiting for an event to be issued which cannot
> > +be issued.
> 
> I think 'some event happened' and 'context triggered an event' is
> better than 'some event issued' or 'context issued an event'.  I think
> 'happen' and 'trigger' are more widely used words when we talk about
> events.  For example, I would prefer the following version of the
> above:
> 
> A deadlock occurs when a context is waiting for an event to happen,
> which cannot happen because the context which can trigger the event is
> also waiting for an event to happen which cannot happen either.

Looks good.

> 
> > +Single context or more than one context both waiting for an
> > +event and issuing an event may paricipate in a deadlock.
> 
> I am not able to make sense of the line above.

I meant that only one context can be in deadlock by itself, like

A
=
lock a <= waiting
lock b
lock a
unlock a <= triggering
unlock b
unlock a

and more than one context also can be in deadlock, like

A		B
=		=
lock b		lock a
lock a		lock b
unlock a	unlock b
unlock b	unlock a

Is there any alterative to describ it?

> 
> > +
> > +For example,
> > +
> > +A context who can issue event D is waiting for event A to be issued.
> > +A context who can issue event A is waiting for event B to be issued.
> > +A context who can issue event B is waiting for event C to be issued.
> > +A context who can issue event C is waiting for event D to be issued.
> > +
> > +A deadlock occurs when these four operations are run at a time because
> > +event D cannot be issued if event A isn't issued which in turn cannot be
> > +issued if event B isn't issued which in turn cannot be issued if event C
> > +isn't issued which in turn cannot be issued if event D isn't issued. No
> > +event can be issued since any of them never meets its precondition.
> > +
> > +We can easily recognize that each wait operation creates a dependency
> > +between two issuings e.g. between issuing D and issuing A like, 'event D
> > +cannot be issued if event A isn't issued', in other words, 'issuing
> > +event D depends on issuing event A'. So the whole example can be
> > +rewritten in terms of dependency,
> > +
> > +Do an operation making 'event D cannot be issued if event A isn't issued'.
> > +Do an operation making 'event A cannot be issued if event B isn't issued'.
> > +Do an operation making 'event B cannot be issued if event C isn't issued'.
> > +Do an operation making 'event C cannot be issued if event D isn't issued'.
> > +
> > +or,
> 
> I think we can remove the text above.  The example only needs to be
> provided once.

I tried not to miss any subtle desciption AFAP. I thought and decided that
I need to explain what a dependecy is, without any hole in logic.

> 
> > +
> > +Do an operation making 'issuing event D depends on issuing event A'.
> > +Do an operation making 'issuing event A depends on issuing event B'.
> > +Do an operation making 'issuing event B depends on issuing event C'.
> > +Do an operation making 'issuing event C depends on issuing event D'.
> > +
> > +What causes a deadlock is a set of dependencies a chain of which forms a
> > +cycle, which means that issuing event D depending on issuing event A
> > +depending on issuing event B depending on issuing event C depending on
> > +issuing event D, finally depends on issuing event D itself, which means
> > +no event can be issued.
> > +
> > +Any set of operations creating dependencies causes a deadlock. The set
> > +of lock operations e.g. acquire and release is an example. Waiting for a
> > +lock to be released corresponds to waiting for an event and releasing a
> > +lock corresponds to issuing an event. So the description of dependency
> > +above can be altered to one in terms of lock.
> > +
> > +In terms of event, issuing event A depends on issuing event B if,
> > +
> > +       Event A cannot be issued if event B isn't issued.
> > +
> > +In terms of lock, releasing lock A depends on releasing lock B if,
> > +
> > +       Lock A cannot be released if lock B isn't released.
> > +
> > +CONCLUSION
> > +
> > +A set of dependencies a chain of which forms a cycle, causes a deadlock,
> 
> I think 'a chain of' is not required in the sentence above.

Do you think so? Actually a chain forms a cycle. I thought dependencies
are not stuffs making a cycle.

> 
> > +no matter what creates the dependencies.
> > +
> > +
> > +What lockdep detects
> > +--------------------
> > +
> > +A deadlock actually occurs only when all operations creating problematic
> 
> Instead of 'problematic', I would use 'cyclic'.

I'd like to highlight _problematic_. Is it better to use a specific word,
like 'cycle'?

> 
> > +dependencies are run at a time. However, even if it has not happend, the
> 
> dependencies happen at run time.  However, even if they don't, the

It ends in "operations happen". Is it a right expression?

> 
> > +deadlock potentially can occur if the problematic dependencies obviously
> 
> remove obviously

Ok.

> 
> > +exist. Thus it's meaningful to detect not only an actual deadlock but
> > +also its possibility. Lockdep does the both.
> > +
> > +Whether a deadlock actually occurs or not depends on several factors,
> > +which means a deadlock may not occur even though problematic
> 
> cyclic instead of problematic
> 
> > +dependencies exist. For example, what order contexts are switched in is
> > +a factor. A deadlock will occur when contexts are switched so that all
> > +operations causing a deadlock become run simultaneously.
> 
> delete become.

Ok.

> 
> > +
> > +Lockdep tries to detect a deadlock or its possibility aggressively,
> > +though it also tries to avoid false positive detections. So lockdep is
> > +designed to consider all possible combinations of dependencies so that
> > +it can detect all potential possibilities of deadlock in advance. What
> > +lockdep tries in order to consider all possibilities are,
> > +
> > +1. Use a global dependency graph including all dependencies.
> > +
> > +   What lockdep checks is based on dependencies instead of what actually
> > +   happened. So no matter which context or call path a new dependency is
> > +   detected in, it's just referred to as a global factor.
> 
> Can you explain more what 'global factor' means?

I'm sorry. My words might be wrong. :(
I wanted to say "there's only one global data managing dependencies".

> 
> 
> > +
> > +2. Use lock classes than lock instances when checking dependencies.
> > +
> > +   What actually causes a deadlock is lock instances. However, lockdep
> > +   uses lock classes than its instances when checking dependencies since
> > +   any instance of a same lock class can be altered anytime.
> 
> I am unable to make sense of the sentence above.  Do you want to say
> the following:
> 
> However, lockdep uses lock classes than its instances when checking
> dependencies since instances from the same lock class behave in the
> same (or similar) fashion.

I wanted to say,

For example,

do_handle_something(struct data *d)
{
  lock d->a of class A
  handle data protected by d->a
  unlcok d->a
}

where d can be different between calls but it's always one of class A.

I expressed it as 'altered'.

> 
> > +
> > +So lockdep detects both an actual deadlock and its possibility. But the
> > +latter is more valuable than the former. When a deadlock actually
> > +occures, 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 does, the fisrt one is more valuable,
> > +
> > +1. Detecting and reporting deadlock possibility.
> > +2. Detecting and reporting a deadlock actually occured.
> > +
> > +
> > +How lockdep works
> > +-----------------
> > +
> > +What lockdep should do, to detect a deadlock or its possibility are,
> > +
> > +1. Detect a new dependency created.
> > +2. Keep the dependency in a global data structure esp. graph.
> > +3. Check if any of all possible chains of dependencies forms a cycle.
> > +4. Report a deadlock or its possibility if a cycle is detected.
> > +
> > +A graph used by lockdep to keep all dependencies looks like,
> > +
> > +A -> B -        -> F -> G
> > +        \      /
> > +         -> E -        -> L
> > +        /      \      /
> > +C -> D -        -> H -
> > +                      \
> > +                       -> I -> K
> > +                      /
> > +                   J -
> > +
> > +where A, B,..., L are different lock classes.
> > +
> > +Lockdep adds a dependency into graph when a new dependency is detected.
> > +For example, it adds a dependency 'A -> B' when a dependency between
> > +releasing lock A and releasing lock B, which has not been added yet, is
> > +detected. It does same thing on other dependencies, too. See 'What
> > +causes deadlock' section.
> 
> Just from the text above I am not able to understand at what point the
> dependency A-> B is added.  If I was implementing lockdep, I would
> probably track all the locks that can be acquired between acquisition
> and release of A.  For example,
> 
> acquire A
> 
> if (/* some condition */)
>     acquire B
> else
>     acquire C
> 
> release A
> 
> 
> I would add to the dependency graph that 'release A' depends on
> 'acquire B' and 'acquire C'.

It's not wrong. But each acquire B(or C) in turn depends on each release
B(or C) when it fails to acquire them right away.

Thus, 'release A depends on release B and release C'.

That's what I wanted to say.

> 
> > +
> > +NOTE: Precisely speaking, a dependency is one between releasing a lock
> > +and releasing another lock as described in 'What causes deadlock'
> > +section. However from now on, we will describe a dependency as if it's
> > +one between a lock and another lock for simplicity. Then 'A -> B' can be
> > +described as a dependency between lock A and lock B.
> > +
> > +We already checked how a problematic set of dependencies causes a
> > +deadlock in 'What causes deadlock' section. This time let's check if a
> > +deadlock or its possibility can be detected using a problematic set of
> > +dependencies. Assume that 'A -> B', 'B -> E' and 'E -> A' were added in
> > +the sequence into graph. Then the graph finally will be,
> > +
> > + -> A -> B -> E -
> > +/                \
> > +\                /
> > + ----------------
> > +
> > +where A, B and E are different lock classes.
> > +
> > +From adding three dependencies, a cycle was created which means, by
> > +definition of dependency, the situation 'lock E must be released to
> > +release lock B which in turn must be released to release lock A which in
> > +turn must be released to release lock E which in turn must be released
> > +to release B and so on infinitely' can happen.
> > +
> > +Once the situation happens, no lock can be released since any of them
> > +can never meet each precondition. It's a deadlock. Lockdep can detect a
> > +deadlock or its possibility with checking if a cycle was created after
> > +adding each dependency into graph. This is how lockdep detects a
> > +deadlock or its possibility.
> 
> I think the text here is just repeating what was said above in the
> 'What causes deadlock' section.  You probably should remove of the
> text here.

Yes. I will check it.

> 
> > +
> > +CONCLUSION
> > +
> > +Lockdep detects a deadlock or its possibility with checking if a cycle
> > +was created after adding each dependency into graph.
> > +
> > +
> > +==========
> > +Limitation
> > +==========
> > +
> > +Limit to typical lock
> > +---------------------
> > +
> > +Limiting what lockdep has to consider to only ones satisfying the
> > +following condition, the implementation of adding dependencies becomes
> > +simple while its capacity for detection becomes limited. Typical lock
> > +e.g. spin lock and mutex is the case. Let's check what pros and cons of
> > +it are, in next section.
> > +
> > +       A lock should be released within the context holding the lock.
> 
> I would rephrase the above in the following way:  Lockdep is limited
> to checking dependencies on locks that are released within the context
> that acquired them.  This makes adding dependencies simple but limits
> lockdep's capacity for detection.

It looks better to me. I'll try to rephrase it.

> 
> 
> > +
> > +CONCLUSION
> > +
> > +Limiting what lockdep has to consider to typical lock e.g. spin lock and
> > +mutex, the implmentation becomes simple while it has a limited capacity.
> 
> I would drop the conclusion altogether.  The above paragraph is too
> small to require a conclusion.

I'm not sure. I want to let one read only conclusions, who want to get only
conclusions.

> 
> > +
> > +
> > +Pros from the limitation
> > +------------------------
> > +
> > +Given the limitation, when acquiring a lock, any lock being in
> > +held_locks of the acquire context cannot be released if the lock to
> 
> What does held_locks mean here?  Is it some structure maintained by
> the Linux kernel?

Exactly. It's embedded in struct task as an array.

> 
> > +acquire was not released yet. Yes. It's the exact case to add a new
> > +dependency 'A -> B' into graph, where lock A represents each lock being
> > +in held_locks and lock B represents the lock to acquire.
> > +
> > +For example, only considering typical lock,
> > +
> > +       PROCESS 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, there's nothing in held_locks of PROCESS X thus
> > +no dependency is added. When acquiring lock B, lockdep detects and adds
> > +a new dependency 'A -> B' between lock A being in held_locks and lock B.
> > +And when acquiring lock C, lockdep also adds another dependency 'B -> C'
> > +for same reason. They are added when acquiring each lock, simply.
> > +
> > +NOTE: Even though every lock being in held_locks depends on the lock to
> > +acquire, lockdep does not add all dependencies between them because all
> > +of them can be covered by other dependencies except one dependency
> > +between the lock on top of held_locks and the lock to acquire, which
> > +must be added.
> 
> I am unable to understand the above sentence.  Can you break it into
> two more sentences?

Sorry. I will try to explain it in more detail.

> 
> > +
> > +Besides, we can expect several advantages from the limitation.
> > +
> > +1. Any lock being in held_locks cannot be released unconditionally if
> > +   the context is stuck, thus we can easily identify a dependency when
> > +   acquiring a lock.
> > +
> > +2. Considering only locks being in local held_locks of a single context
> > +   makes some races avoidable, even though it fails of course when
> > +   modifying its global dependency graph.
> > +
> > +3. To build a dependency graph, lockdep only needs to keep locks not
> > +   released yet. However relaxing the limitation, it might need to keep
> > +   even locks already released, additionally. See 'Crossrelease' section.
> > +
> > +CONCLUSION
> > +
> > +Given the limitation, the implementation becomes simple and efficient.
> > +
> > +
> > +Cons from the limitation
> > +------------------------
> > +
> > +Given the limitation, lockdep is applicable only to typical lock. For
> > +example, page lock for page access or completion for synchronization
> > +cannot play with lockdep having the limitation. However since page lock
> > +or completion also causes a deadlock, it would be better to detect a
> > +deadlock or its possibility even for them.
> > +
> > +Can we detect deadlocks below with lockdep having the limitation?
> > +
> > +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.
> 
> I think the formatting is slightly off.  The events corresponding to
> process Y should be shifted more towards right.  The vertical ordering
> would still clearly indicate the order in which the events happened.

Do you think it's better to shift it?

> 
> 
> > +
> > +No, we cannot.
> 
> I do not follow this example.  The context that acquired the mutex A
> or lock B also released it.  Should not lockdep be able to detect
> this?  I am guessing I have misunderstood which events occurred in
> which context.   Here is what I think the example is showing:
> 
> X acquired mutex A --> Y acquired lock B --> X tries to acquire B -->
> Y tries to acquire A -->  X and Y are in a deadlock since each of them
> holds a resource that the other needs.

Current lockdep is not able to deal with (un)lock_page thingy at all.

> > +Let's look at how commit works for crosslock.
> > +
> > +       RELEASE CONTEXT of a    ACQUIRE CONTEXT of a
> > +       --------------------    --------------------
> > +                               acquire a -> mark a as started
> > +
> > +       (serialized by some means e.g. barrier)
> > +
> > +       acquire D -> queue D
> > +                               acquire B -> queue B
> > +       release D
> > +                               acquire C -> add 'B -> C' and queue C
> > +       acquire E -> queue E
> > +                               acquire D -> add 'C -> D' and queue D
> > +       release E
> > +                               release D
> > +       release a -> commit a -> add 'a -> D' and 'a -> E'
> > +                               release C
> > +
> > +                               release B
> > +
> > +where a, B,..., E are different lock classes, and upper case letters
> > +represent typical lock while lower case letters represent crosslock.
> > +
> > +When acquiring crosslock a, no dependency can be added since there's
> > +nothing in the held_locks. However, crossrelease feature marks the
> > +crosslock as started, which means all locks to acquire from now are
> > +candidates which might create new dependencies later when identifying
> > +release context.
> > +
> > +When acquiring lock B, lockdep does what it originally does for typical
> > +lock and additionally queues the lock for later commit to refer to
> > +because it might be a dependent lock of the crosslock. It does same
> > +thing on lock C, D and E. And then two dependencies 'a -> D' and 'a -> E'
> > +are added when identifying the release context, at commit step.
> > +
> > +The final graph is, with crossrelease feature using commit,
> > +
> > +B -> C -
> > +        \
> > +         -> D
> > +        /
> > +     a -
> > +        \
> > +         -> E
> > +
> 
> Would not the graph also contain edges a->B and a->C, which lockdep
> would add as it usually does?

No. 'a' is a crosslock. We should not add 'a->B(or C)' dependency.
In other words, 'a' can be released without releasing B(or C), by another
context.

> Overall, I think I have generally understood what you are trying to do
> and how are doing it.  I think now I'll be able to better review the
> patches with actual.

Thanks a lot. Your comment would be helpful.

Thank you,
Byungchul

> 
> Thanks!
> Nilay

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-19  8:50               ` Peter Zijlstra
@ 2016-09-20  5:50                 ` Byungchul Park
  2016-09-20  6:26                   ` Byungchul Park
  2016-09-21  1:37                   ` Byungchul Park
  2016-09-22  2:57                 ` Byungchul Park
  1 sibling, 2 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-20  5:50 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Mon, Sep 19, 2016 at 10:50:09AM +0200, Peter Zijlstra wrote:
> On Mon, Sep 19, 2016 at 11:41:02AM +0900, Byungchul Park wrote:
> 
> > > But since these threads are independently scheduled there is no point in
> > > transferring the point in time thread A does W to thread B. There is no
> > > relation there.
> > > 
> > > B could have already executed the complete or it could not yet have
> > > started execution at all or anything in between, entirely random.
> > 
> > Of course B could have already executed the complete or it could not yet
> > have started execution at all or anything in between. But it's not entirely
> > random.
> > 
> > It might be a random point since they are independently scheduled, but it's
> > not entirely random. And it's a random point among valid points which lockdep
> > needs to consider. For example,
> > 
> > 
> > CONTEXT 1			CONTEXT 2(forked one)
> > =========			=====================
> > (a)				acquire F
> > acquire A			acquire G
> > acquire B			wait_for_completion Z
> > acquire C
> > (b)				acquire H
> > fork 2				acquire I
> > acquire D			acquire J
> > complete Z			acquire K
> > 
> 
> I'm hoping you left out the releases for brevity? Because calling fork()
> with locks held is _really_ poor form.

Exactly. Sorry. I shouldn't have omitted releases.

> 
> > I can provide countless examples with which I can say you're wrong.
> > In this case, all acquires between (a) and (b) must be ignored when
> > generating dependencies with complete operation of Z.
> 
> I still don't get the point. Why does this matter?
> 
> Sure, A-C are irrelevant in this example, but I don't see how they're
> differently irrelevant from a whole bunch of other prior state action.
> 
> 
> Earlier you said the algorithm for selecting the dependency is the first
> acquire observed in the completing thread after the
> wait_for_completion(). Is this correct?

Sorry for insufficient description.

held_locks of left context will be,

time 1: a
time 2: a, x[0]
time 3: a, x[1]
...
time n: b

Between time 1 and time (n-1), 'a' will be the first among held_locks. At
time n, 'b' will be the fist among held_locks. So 'a' and 'b' should be
connected to 'z' if we ignore IRQ context. (I will explain it soon.)

Acquire x[i] is also valid one but crossrelease doesn't take it into
account since original lockdep will cover using 'a -> x[i]'. So only
connections we need are 'z -> a' and 'z -> b'.

> 
> 
> 				W z
> 
> 	A a
> 	for (i<0;i<many;i++) {
> 	  A x[i]
> 	  R x[i]
> 	}
> 	R a
> 
> 	<IRQ>
> 	  A b
> 	  R b
> 	  C z
> 	</IRQ>

My crossrelease implementation distinguishes each IRQ from normal context
or other IRQs in different timeline, even though they might share
held_locks. So in this example, precisely speaking, there are two different
contexts. One is normal context and the other is IRQ context. So only 'A b'
is related with 'W z' in this example.

> 
> That would be 'a' in this case, but that isn't at all related. Its just
> as irrelevant as your A-C. And we can pick @many as big as needed to
> flush the prev held cyclic buffer (although I've no idea how that
> matters either).

I designed crossrelease so that x[i] is not added into ring buffer because
adding 'z -> a' is sufficient and x[i] doesn't need to be taken into
account in this case.

> 
> What we want here is to link z to b, no? That is the last, not the first

Exactly right. Only 'z -> b' must be added under considering IRQ context.
That is the first among held_locks in the IRQ context.

> acquire, it also is independent of when W happened.

If the IRQ is really random, then it can happen before W z and it can also
happen after W z. We cannot determine the time. Then we need to consider all
combination and possibility. It's a key point. We have to consider
dependencies for all possibility.

However, we don't know what synchronizes the flow. So it must be based on
what actually happened, to identify true dependencies.

> 
> At the same time, picking the last is no guarantee either, since that
> can equally miss dependencies. Suppose the IRQ handler did:
> 
> 	<IRQ>
> 	  A c
> 	  R c
> 	  A b
> 	  R b
> 	  C z
> 	</IRQ>
> 

time 1: c (in held_locks)
time 2: b (in held_locks)

So 'c' and 'b' can be the first among held_locks at each moment.
So 'z -> b' and 'z -> c' will be added.

> instead. We'd miss the z depends on c relation, and since they're
> independent lock sections, lockdep wouldn't make a b-c relation either.
> 
> 
> Clearly I'm still missing stuff...

Sorry for insufficient description. I tried to describ crossrelease in as
much detail as possible, really.

The reason why I consider only the first among valid locks in held_locks is
simple. For example,

Context 1
A a -> A b -> A crosslock -> R a -> R b

Context 2
A c -> A d -> R d -> R the crosslock -> R c

If 'A c' after 'A crosslock' is possible, then 'A crosslock' does not only
depends on 'A c' but also 'A d'. But all dependencies we need to add is only
'crosslock -> c' because 'crosslock -> d' will be covered by 'crosslock ->
c' and 'a -> b'. 'a -> b' is added by original lockdep.

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-20  5:50                 ` Byungchul Park
@ 2016-09-20  6:26                   ` Byungchul Park
  2016-09-21  1:37                   ` Byungchul Park
  1 sibling, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-20  6:26 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 20, 2016 at 02:50:38PM +0900, Byungchul Park wrote:
> On Mon, Sep 19, 2016 at 10:50:09AM +0200, Peter Zijlstra wrote:
> > On Mon, Sep 19, 2016 at 11:41:02AM +0900, Byungchul Park wrote:
> > 
> > > > But since these threads are independently scheduled there is no point in
> > > > transferring the point in time thread A does W to thread B. There is no
> > > > relation there.
> > > > 
> > > > B could have already executed the complete or it could not yet have
> > > > started execution at all or anything in between, entirely random.
> > > 
> > > Of course B could have already executed the complete or it could not yet
> > > have started execution at all or anything in between. But it's not entirely
> > > random.
> > > 
> > > It might be a random point since they are independently scheduled, but it's
> > > not entirely random. And it's a random point among valid points which lockdep
> > > needs to consider. For example,
> > > 
> > > 
> > > CONTEXT 1			CONTEXT 2(forked one)
> > > =========			=====================
> > > (a)				acquire F
> > > acquire A			acquire G
> > > acquire B			wait_for_completion Z
> > > acquire C
> > > (b)				acquire H
> > > fork 2				acquire I
> > > acquire D			acquire J
> > > complete Z			acquire K
> > > 
> > 
> > I'm hoping you left out the releases for brevity? Because calling fork()
> > with locks held is _really_ poor form.
> 
> Exactly. Sorry. I shouldn't have omitted releases.
> 
> > 
> > > I can provide countless examples with which I can say you're wrong.
> > > In this case, all acquires between (a) and (b) must be ignored when
> > > generating dependencies with complete operation of Z.
> > 
> > I still don't get the point. Why does this matter?
> > 
> > Sure, A-C are irrelevant in this example, but I don't see how they're
> > differently irrelevant from a whole bunch of other prior state action.
> > 
> > 
> > Earlier you said the algorithm for selecting the dependency is the first
> > acquire observed in the completing thread after the
> > wait_for_completion(). Is this correct?
> 
> Sorry for insufficient description.
> 
> held_locks of left context will be,
> 
> time 1: a
> time 2: a, x[0]
> time 3: a, x[1]
> ...
> time n: b
> 
> Between time 1 and time (n-1), 'a' will be the first among held_locks. At
> time n, 'b' will be the fist among held_locks. So 'a' and 'b' should be
> connected to 'z' if we ignore IRQ context. (I will explain it soon.)
> 
> Acquire x[i] is also valid one but crossrelease doesn't take it into
> account since original lockdep will cover using 'a -> x[i]'. So only
> connections we need are 'z -> a' and 'z -> b'.
> 
> > 
> > 
> > 				W z
> > 
> > 	A a
> > 	for (i<0;i<many;i++) {
> > 	  A x[i]
> > 	  R x[i]
> > 	}
> > 	R a
> > 
> > 	<IRQ>
> > 	  A b
> > 	  R b
> > 	  C z
> > 	</IRQ>
> 
> My crossrelease implementation distinguishes each IRQ from normal context
> or other IRQs in different timeline, even though they might share
> held_locks. So in this example, precisely speaking, there are two different
> contexts. One is normal context and the other is IRQ context. So only 'A b'
> is related with 'W z' in this example.
> 
> > 
> > That would be 'a' in this case, but that isn't at all related. Its just
> > as irrelevant as your A-C. And we can pick @many as big as needed to
> > flush the prev held cyclic buffer (although I've no idea how that
> > matters either).
> 
> I designed crossrelease so that x[i] is not added into ring buffer because
> adding 'z -> a' is sufficient and x[i] doesn't need to be taken into
> account in this case.
> 
> > 
> > What we want here is to link z to b, no? That is the last, not the first
> 
> Exactly right. Only 'z -> b' must be added under considering IRQ context.
> That is the first among held_locks in the IRQ context.
> 
> > acquire, it also is independent of when W happened.
> 
> If the IRQ is really random, then it can happen before W z and it can also
> happen after W z. We cannot determine the time. Then we need to consider all
> combination and possibility. It's a key point. We have to consider
> dependencies for all possibility.
> 
> However, we don't know what synchronizes the flow. So it must be based on
> what actually happened, to identify true dependencies.
> 
> > 
> > At the same time, picking the last is no guarantee either, since that
> > can equally miss dependencies. Suppose the IRQ handler did:
> > 
> > 	<IRQ>
> > 	  A c
> > 	  R c
> > 	  A b
> > 	  R b
> > 	  C z
> > 	</IRQ>
> > 
> 
> time 1: c (in held_locks)
> time 2: b (in held_locks)
> 
> So 'c' and 'b' can be the first among held_locks at each moment.
> So 'z -> b' and 'z -> c' will be added.
> 
> > instead. We'd miss the z depends on c relation, and since they're
> > independent lock sections, lockdep wouldn't make a b-c relation either.
> > 
> > 
> > Clearly I'm still missing stuff...
> 
> Sorry for insufficient description. I tried to describ crossrelease in as
> much detail as possible, really.
> 
> The reason why I consider only the first among valid locks in held_locks is
> simple. For example,
> 
> Context 1
> A a -> A b -> A crosslock -> R a -> R b

Here, '->' represents the order executing A(cquire)s and R(elease)s.

_Not_ dependency.

> 
> Context 2
> A c -> A d -> R d -> R the crosslock -> R c
> 
> If 'A c' after 'A crosslock' is possible, then 'A crosslock' does not only
> depends on 'A c' but also 'A d'. But all dependencies we need to add is only
> 'crosslock -> c' because 'crosslock -> d' will be covered by 'crosslock ->
> c' and 'a -> b'. 'a -> b' is added by original lockdep.

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-20  5:50                 ` Byungchul Park
  2016-09-20  6:26                   ` Byungchul Park
@ 2016-09-21  1:37                   ` Byungchul Park
  1 sibling, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-21  1:37 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Tue, Sep 20, 2016 at 02:50:38PM +0900, Byungchul Park wrote:
> On Mon, Sep 19, 2016 at 10:50:09AM +0200, Peter Zijlstra wrote:
> > On Mon, Sep 19, 2016 at 11:41:02AM +0900, Byungchul Park wrote:
> > 
> > > > But since these threads are independently scheduled there is no point in
> > > > transferring the point in time thread A does W to thread B. There is no
> > > > relation there.
> > > > 
> > > > B could have already executed the complete or it could not yet have
> > > > started execution at all or anything in between, entirely random.
> > > 
> > > Of course B could have already executed the complete or it could not yet
> > > have started execution at all or anything in between. But it's not entirely
> > > random.
> > > 
> > > It might be a random point since they are independently scheduled, but it's
> > > not entirely random. And it's a random point among valid points which lockdep
> > > needs to consider. For example,
> > > 
> > > 
> > > CONTEXT 1			CONTEXT 2(forked one)
> > > =========			=====================
> > > (a)				acquire F
> > > acquire A			acquire G
> > > acquire B			wait_for_completion Z
> > > acquire C
> > > (b)				acquire H
> > > fork 2				acquire I
> > > acquire D			acquire J
> > > complete Z			acquire K
> > > 
> > 
> > I'm hoping you left out the releases for brevity? Because calling fork()
> > with locks held is _really_ poor form.
> 
> Exactly. Sorry. I shouldn't have omitted releases.
> 
> > 
> > > I can provide countless examples with which I can say you're wrong.
> > > In this case, all acquires between (a) and (b) must be ignored when
> > > generating dependencies with complete operation of Z.
> > 
> > I still don't get the point. Why does this matter?
> > 
> > Sure, A-C are irrelevant in this example, but I don't see how they're
> > differently irrelevant from a whole bunch of other prior state action.
> > 
> > 
> > Earlier you said the algorithm for selecting the dependency is the first
> > acquire observed in the completing thread after the
> > wait_for_completion(). Is this correct?
> 
> Sorry for insufficient description.
> 
> held_locks of left context will be,
> 
> time 1: a
> time 2: a, x[0]
> time 3: a, x[1]
> ...
> time n: b
> 
> Between time 1 and time (n-1), 'a' will be the first among held_locks. At
> time n, 'b' will be the fist among held_locks. So 'a' and 'b' should be
> connected to 'z' if we ignore IRQ context. (I will explain it soon.)
> 
> Acquire x[i] is also valid one but crossrelease doesn't take it into
> account since original lockdep will cover using 'a -> x[i]'. So only

... since lockdep will cover 'z -> x[i]' using 'z -> a' and 'a -> x[i]' ...

> connections we need are 'z -> a' and 'z -> b'.
> 
> > 
> > 
> > 				W z
> > 
> > 	A a
> > 	for (i<0;i<many;i++) {
> > 	  A x[i]
> > 	  R x[i]
> > 	}
> > 	R a
> > 
> > 	<IRQ>
> > 	  A b
> > 	  R b
> > 	  C z
> > 	</IRQ>
> 
> My crossrelease implementation distinguishes each IRQ from normal context
> or other IRQs in different timeline, even though they might share
> held_locks. So in this example, precisely speaking, there are two different
> contexts. One is normal context and the other is IRQ context. So only 'A b'
> is related with 'W z' in this example.
> 
> > 
> > That would be 'a' in this case, but that isn't at all related. Its just
> > as irrelevant as your A-C. And we can pick @many as big as needed to
> > flush the prev held cyclic buffer (although I've no idea how that
> > matters either).
> 
> I designed crossrelease so that x[i] is not added into ring buffer because
> adding 'z -> a' is sufficient and x[i] doesn't need to be taken into
> account in this case.
> 
> > 
> > What we want here is to link z to b, no? That is the last, not the first
> 
> Exactly right. Only 'z -> b' must be added under considering IRQ context.
> That is the first among held_locks in the IRQ context.
> 
> > acquire, it also is independent of when W happened.
> 
> If the IRQ is really random, then it can happen before W z and it can also
> happen after W z. We cannot determine the time. Then we need to consider all
> combination and possibility. It's a key point. We have to consider
> dependencies for all possibility.

                       possibilities.

> 
> However, we don't know what synchronizes the flow. So it must be based on
                   ^^^
                 generally

> what actually happened, to identify true dependencies.
> 
> > 
> > At the same time, picking the last is no guarantee either, since that
> > can equally miss dependencies. Suppose the IRQ handler did:
> > 
> > 	<IRQ>
> > 	  A c
> > 	  R c
> > 	  A b
> > 	  R b
> > 	  C z
> > 	</IRQ>
> > 
> 
> time 1: c (in held_locks)
> time 2: b (in held_locks)
> 
> So 'c' and 'b' can be the first among held_locks at each moment.
                 ^^^^^^
                   are

> So 'z -> b' and 'z -> c' will be added.
> 
> > instead. We'd miss the z depends on c relation, and since they're
> > independent lock sections, lockdep wouldn't make a b-c relation either.
> > 
> > 
> > Clearly I'm still missing stuff...
> 
> Sorry for insufficient description. I tried to describ crossrelease in as
> much detail as possible, really.
> 
> The reason why I consider only the first among valid locks in held_locks is
> simple. For example,
> 
> Context 1
> A a -> A b -> A crosslock -> R a -> R b

I meant,

Context 1
=========
A a
A b
A crosslock
R a
R b

> 
> Context 2
> A c -> A d -> R d -> R the crosslock -> R c

Context 2
=========
A c
A d
R d
R the crosslock
R c

> 
> If 'A c' after 'A crosslock' is possible, then 'A crosslock' does not only
> depends on 'A c' but also 'A d'. But all dependencies we need to add is only
> 'crosslock -> c' because 'crosslock -> d' will be covered by 'crosslock ->
> c' and 'a -> b'. Here, 'a -> b' is added by original lockdep.

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

* Re: [PATCH v3 07/15] lockdep: Implement crossrelease feature
  2016-09-19  8:50               ` Peter Zijlstra
  2016-09-20  5:50                 ` Byungchul Park
@ 2016-09-22  2:57                 ` Byungchul Park
  1 sibling, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-09-22  2:57 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Byungchul Park, Ingo Molnar, tglx, Michel Lespinasse, boqun.feng,
	kirill, linux-kernel, linux-mm, iamjoonsoo.kim, akpm, npiggin

On Mon, Sep 19, 2016 at 10:50:09AM +0200, Peter Zijlstra wrote:
> Clearly I'm still missing stuff...

By the way.. do I have to explain more? Lack of explanation?

It would be the best to consider 'all valid acquires', which can occur
deadlock, but it looks impossible without parsing all code in head.

So it would be the safest to rely on 'acquires which actually happened',
even though it might be 'random acquires' among all valid acquires.

This conservative appoach is exactly same as how original lockdep is doing.
Let me explain more if you doubt it.

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

* [REVISED DOC on v3] Crossrelease Lockdep
  2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
                   ` (15 preceding siblings ...)
  2016-09-13  9:58 ` [FYI] Output of 'cat /proc/lockdep' after applying crossrelease Byungchul Park
@ 2016-11-02  5:42 ` Byungchul Park
  2016-11-03  8:18   ` Byungchul Park
  16 siblings, 1 reply; 49+ messages in thread
From: Byungchul Park @ 2016-11-02  5:42 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

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.

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

* Re: [REVISED DOC on v3] Crossrelease Lockdep
  2016-11-02  5:42 ` [REVISED DOC on v3] Crossrelease Lockdep Byungchul Park
@ 2016-11-03  8:18   ` Byungchul Park
  2016-11-08  2:54     ` Byungchul Park
  0 siblings, 1 reply; 49+ messages in thread
From: Byungchul Park @ 2016-11-03  8:18 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

Hello Peterz,

I tried to explain about what you asked me for.
I wonder if I did it exactly. But I hope so.
Please let me know if there's something more I need to add.

Thank you,
Byungchul

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

* Re: [REVISED DOC on v3] Crossrelease Lockdep
  2016-11-03  8:18   ` Byungchul Park
@ 2016-11-08  2:54     ` Byungchul Park
  0 siblings, 0 replies; 49+ messages in thread
From: Byungchul Park @ 2016-11-08  2:54 UTC (permalink / raw)
  To: peterz, mingo
  Cc: tglx, walken, boqun.feng, kirill, linux-kernel, linux-mm,
	iamjoonsoo.kim, akpm, npiggin

On Thu, Nov 03, 2016 at 05:18:13PM +0900, Byungchul Park wrote:
> Hello Peterz,
> 
> I tried to explain about what you asked me for.
> I wonder if I did it exactly. But I hope so.
> Please let me know if there's something more I need to add.

Just to be sure, are you concerning about sync and propagation delay of
memory contents including lock variables between cpus?

IMHO, it makes no difference. It's a true dependence once the dependency
is viewable by a cpu, which means anyway it actually happened. And please
remind all locks related to crosslocks are serialized via proper memory
barriers. I think this was already descibed in my document.

Is there something I missed? Please let me know.

Thank you,
Byungchul

> 
> Thank you,
> Byungchul

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

end of thread, other threads:[~2016-11-08  2:58 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-13  9:44 [PATCH v3 00/15] lockdep: Implement crossrelease feature Byungchul Park
2016-09-13  9:45 ` [PATCH v3 01/15] x86/dumpstack: Optimize save_stack_trace Byungchul Park
2016-09-13 13:18   ` Josh Poimboeuf
2016-09-13 14:54     ` Byungchul Park
2016-09-13  9:45 ` [PATCH v3 02/15] x86/dumpstack: Add save_stack_trace()_fast() Byungchul Park
2016-09-13 13:20   ` Josh Poimboeuf
2016-09-13  9:45 ` [PATCH v3 03/15] lockdep: Refactor lookup_chain_cache() Byungchul Park
2016-09-15 15:33   ` Nilay Vaish
2016-09-19  3:05     ` Byungchul Park
2016-09-19 16:36       ` Nilay Vaish
2016-09-20  2:00         ` Byungchul Park
2016-09-13  9:45 ` [PATCH v3 04/15] lockdep: Add a function building a chain between two classes Byungchul Park
2016-09-13  9:45 ` [PATCH v3 05/15] lockdep: Make check_prev_add can use a separate stack_trace Byungchul Park
2016-09-13  9:45 ` [PATCH v3 06/15] lockdep: Make save_trace can skip stack tracing of the current Byungchul Park
2016-09-13  9:45 ` [PATCH v3 07/15] lockdep: Implement crossrelease feature Byungchul Park
2016-09-13 10:05   ` Peter Zijlstra
2016-09-13 12:09     ` Peter Zijlstra
2016-09-13 15:14     ` Byungchul Park
2016-09-13 15:05   ` Peter Zijlstra
2016-09-13 17:12     ` Byungchul Park
2016-09-13 19:38       ` Peter Zijlstra
2016-09-13 21:42         ` Peter Zijlstra
2016-09-14  1:01           ` Byungchul Park
2016-09-14  2:27         ` Byungchul Park
2016-09-14  4:49           ` Byungchul Park
2016-09-14  8:11           ` Peter Zijlstra
2016-09-19  2:41             ` Byungchul Park
2016-09-19  8:50               ` Peter Zijlstra
2016-09-20  5:50                 ` Byungchul Park
2016-09-20  6:26                   ` Byungchul Park
2016-09-21  1:37                   ` Byungchul Park
2016-09-22  2:57                 ` Byungchul Park
2016-09-13  9:45 ` [PATCH v3 08/15] lockdep: Make crossrelease use save_stack_trace_fast() Byungchul Park
2016-09-13  9:45 ` [PATCH v3 09/15] lockdep: Make print_circular_bug() crosslock-aware Byungchul Park
2016-09-13  9:45 ` [PATCH v3 10/15] lockdep: Apply crossrelease to completion operation Byungchul Park
2016-09-13  9:45 ` [PATCH v3 11/15] pagemap.h: Remove trailing white space Byungchul Park
2016-09-13  9:45 ` [PATCH v3 12/15] lockdep: Apply crossrelease to PG_locked lock Byungchul Park
2016-09-13  9:45 ` [PATCH v3 13/15] lockdep: Apply lock_acquire(release) on __Set(__Clear)PageLocked Byungchul Park
2016-09-13  9:45 ` [PATCH v3 14/15] lockdep: Move data used in CONFIG_LOCKDEP_PAGELOCK from page to page_ext Byungchul Park
2016-09-13  9:45 ` [PATCH v3 15/15] lockdep: Crossrelease feature documentation Byungchul Park
2016-09-15 17:25   ` Nilay Vaish
2016-09-19  2:59     ` Byungchul Park
2016-09-16 15:47   ` Nilay Vaish
2016-09-19  3:00     ` Byungchul Park
2016-09-20  5:00     ` Byungchul Park
2016-09-13  9:58 ` [FYI] Output of 'cat /proc/lockdep' after applying crossrelease Byungchul Park
2016-11-02  5:42 ` [REVISED DOC on v3] Crossrelease Lockdep Byungchul Park
2016-11-03  8:18   ` Byungchul Park
2016-11-08  2:54     ` 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).