All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] lockdep: liblockdep: Prevent chain_key collisions
@ 2016-02-10 23:33 Alfredo Alvarez Fernandez
  2016-02-10 23:33 ` [PATCH 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Alfredo Alvarez Fernandez @ 2016-02-10 23:33 UTC (permalink / raw)
  To: mingo, peterz, sasha.levin; +Cc: linux-kernel

This patch series prevents possible collisions in the chain_key
hashing macro iterate_chain_key(key1, key2) that can lead to lockdep 
not detecting very simple deadlocks such as AA or ABBA.

The problem only affects the first allocated lock classes. That could 
explain why it was not seen while running lockdep's test suite, since
by the time the test suite runs there are already registered lock 
classes and the indexes allocated for the lock classes under test are
high enough to avoid collisions.

The patch series also extends the tools/liblockdep test suite with 
tests covering the offending cases.

I came across the problem while testing a simple AA deadlock scenario
in userspace using a pthread_mutex and tools/liblockdep. In that 
context it is fairly easy to have a clean and deterministic initial 
state where the problem can be reproduced.

The proposed solution was tested with the newly introduced tests and
also with lockdep's test suite:
 [    0.000000] Good, all 253 testcases passed! |

Alfredo Alvarez Fernandez (3):
  tools/liblockdep: add userspace version of READ_ONCE
  tools/liblockdep: add tests
  lockdep: prevent chain_key collisions

 kernel/locking/lockdep.c                    | 14 ++++------
 tools/lib/lockdep/tests/AA.c                |  8 +++---
 tools/lib/lockdep/tests/ABA.c               | 13 +++++++++
 tools/lib/lockdep/tests/ABBA_2threads.c     | 43 +++++++++++++++++++++++++++++
 tools/lib/lockdep/uinclude/linux/compiler.h |  1 +
 5 files changed, 67 insertions(+), 12 deletions(-)
 create mode 100644 tools/lib/lockdep/tests/ABA.c
 create mode 100644 tools/lib/lockdep/tests/ABBA_2threads.c

-- 
2.5.0

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

* [PATCH 1/3] tools/liblockdep: add userspace version of READ_ONCE
  2016-02-10 23:33 [PATCH 0/3] lockdep: liblockdep: Prevent chain_key collisions Alfredo Alvarez Fernandez
@ 2016-02-10 23:33 ` Alfredo Alvarez Fernandez
  2016-02-11 15:16   ` Peter Zijlstra
  2016-02-10 23:33 ` [PATCH 2/3] tools/liblockdep: add tests Alfredo Alvarez Fernandez
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 17+ messages in thread
From: Alfredo Alvarez Fernandez @ 2016-02-10 23:33 UTC (permalink / raw)
  To: mingo, peterz, sasha.levin; +Cc: linux-kernel, Alfredo Alvarez Fernandez

From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>

This was added to the kernel code in 1658d35ead5d ("list: Use
READ_ONCE() when testing for empty lists")
There's nothing special we need to do about it in userspace.

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
---
 tools/lib/lockdep/uinclude/linux/compiler.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/lockdep/uinclude/linux/compiler.h b/tools/lib/lockdep/uinclude/linux/compiler.h
index 6386dc3..fd3e56a 100644
--- a/tools/lib/lockdep/uinclude/linux/compiler.h
+++ b/tools/lib/lockdep/uinclude/linux/compiler.h
@@ -3,6 +3,7 @@
 
 #define __used		__attribute__((__unused__))
 #define unlikely
+#define READ_ONCE(x) (x)
 #define WRITE_ONCE(x, val) x=(val)
 #define RCU_INIT_POINTER(p, v) p=(v)
 
-- 
2.5.0

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

* [PATCH 2/3] tools/liblockdep: add tests
  2016-02-10 23:33 [PATCH 0/3] lockdep: liblockdep: Prevent chain_key collisions Alfredo Alvarez Fernandez
  2016-02-10 23:33 ` [PATCH 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
@ 2016-02-10 23:33 ` Alfredo Alvarez Fernandez
  2016-02-10 23:33 ` [PATCH 3/3] lockdep: prevent chain_key collisions Alfredo Alvarez Fernandez
  2016-02-16 16:38 ` [PATCH 0/3] lockdep: liblockdep: " Sasha Levin
  3 siblings, 0 replies; 17+ messages in thread
From: Alfredo Alvarez Fernandez @ 2016-02-10 23:33 UTC (permalink / raw)
  To: mingo, peterz, sasha.levin; +Cc: linux-kernel, Alfredo Alvarez Fernandez

From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>

Add test for AA and 2 threaded ABBA locking.

Rename AA.c to ABA.c since it was implementing an ABA instead of a pure
AA. Now both cases are covered.

The expected output for AA.c is that the process blocks and lockdep
reports a deadlock.

ABBA_2threads.c differs from ABBA.c in that lockdep keeps separate chains
of held locks per task. This can lead to different behaviour regarding
lock detection. The expected output for this test is that the process
blocks and lockdep reports a circular locking dependency.

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
---
 tools/lib/lockdep/tests/AA.c            |  8 +++---
 tools/lib/lockdep/tests/ABA.c           | 13 ++++++++++
 tools/lib/lockdep/tests/ABBA_2threads.c | 43 +++++++++++++++++++++++++++++++++
 3 files changed, 60 insertions(+), 4 deletions(-)
 create mode 100644 tools/lib/lockdep/tests/ABA.c
 create mode 100644 tools/lib/lockdep/tests/ABBA_2threads.c

diff --git a/tools/lib/lockdep/tests/AA.c b/tools/lib/lockdep/tests/AA.c
index 0f782ff..18211a5 100644
--- a/tools/lib/lockdep/tests/AA.c
+++ b/tools/lib/lockdep/tests/AA.c
@@ -1,13 +1,13 @@
 #include <liblockdep/mutex.h>
 
-void main(void)
+int main(void)
 {
-	pthread_mutex_t a, b;
+	pthread_mutex_t a;
 
 	pthread_mutex_init(&a, NULL);
-	pthread_mutex_init(&b, NULL);
 
 	pthread_mutex_lock(&a);
-	pthread_mutex_lock(&b);
 	pthread_mutex_lock(&a);
+
+	return 0;
 }
diff --git a/tools/lib/lockdep/tests/ABA.c b/tools/lib/lockdep/tests/ABA.c
new file mode 100644
index 0000000..0f782ff
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABA.c
@@ -0,0 +1,13 @@
+#include <liblockdep/mutex.h>
+
+void main(void)
+{
+	pthread_mutex_t a, b;
+
+	pthread_mutex_init(&a, NULL);
+	pthread_mutex_init(&b, NULL);
+
+	pthread_mutex_lock(&a);
+	pthread_mutex_lock(&b);
+	pthread_mutex_lock(&a);
+}
diff --git a/tools/lib/lockdep/tests/ABBA_2threads.c b/tools/lib/lockdep/tests/ABBA_2threads.c
new file mode 100644
index 0000000..62b759c
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABBA_2threads.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <pthread.h>
+
+pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t b = PTHREAD_MUTEX_INITIALIZER;
+pthread_barrier_t bar;
+
+void* ba_lock(void *arg) {
+	int ret, i;
+	pthread_mutex_lock(&b);
+
+	if (pthread_barrier_wait(&bar) == PTHREAD_BARRIER_SERIAL_THREAD)
+	    pthread_barrier_destroy(&bar);
+
+	pthread_mutex_lock(&a);
+
+	pthread_mutex_unlock(&a);
+	pthread_mutex_unlock(&b);
+}
+
+int main(void)
+{
+	pthread_t t;
+	pthread_barrier_init(&bar, NULL, 2);
+
+	if (pthread_create(&t, NULL, ba_lock, NULL)) {
+		fprintf(stderr, "pthread_create() failed\n");
+		return 1;
+	}
+	pthread_mutex_lock(&a);
+
+	if (pthread_barrier_wait(&bar) == PTHREAD_BARRIER_SERIAL_THREAD)
+	    pthread_barrier_destroy(&bar);
+
+	pthread_mutex_lock(&b);
+
+	pthread_mutex_unlock(&b);
+	pthread_mutex_unlock(&a);
+
+	pthread_join(t, NULL);
+
+	return 0;
+}
-- 
2.5.0

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

* [PATCH 3/3] lockdep: prevent chain_key collisions
  2016-02-10 23:33 [PATCH 0/3] lockdep: liblockdep: Prevent chain_key collisions Alfredo Alvarez Fernandez
  2016-02-10 23:33 ` [PATCH 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
  2016-02-10 23:33 ` [PATCH 2/3] tools/liblockdep: add tests Alfredo Alvarez Fernandez
@ 2016-02-10 23:33 ` Alfredo Alvarez Fernandez
  2016-02-17  8:38   ` Ingo Molnar
  2016-02-29 11:24   ` [tip:locking/core] locking/lockdep: Prevent " tip-bot for Alfredo Alvarez Fernandez
  2016-02-16 16:38 ` [PATCH 0/3] lockdep: liblockdep: " Sasha Levin
  3 siblings, 2 replies; 17+ messages in thread
From: Alfredo Alvarez Fernandez @ 2016-02-10 23:33 UTC (permalink / raw)
  To: mingo, peterz, sasha.levin; +Cc: linux-kernel, Alfredo Alvarez Fernandez

From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>

The chain_key hashing macro iterate_chain_key(key1, key2) does not
generate a new different value if both key1 and key2 are 0. In that
case the generated value is again 0. This can lead to collisions which
can result in lockdep not detecting deadlocks or circular
dependencies.

Avoid the problem by using class_idx (1-based) instead of class id
(0-based) as an input for the hashing macro 'key2' in
iterate_chain_key(key1, key2).

The use of class id created collisions in cases like the following:

1.- Consider an initial state in which no class has been acquired yet.
Under these circumstances an AA deadlock will not be detected by
lockdep:

  lock  [key1,key2]->new key  (key1=old chain_key, key2=id)
  --------------------------
  A     [0,0]->0
  A     [0,0]->0 (collision)

  The newly generated chain_key collides with the one used before and as
  a result the check for a deadlock is skipped

  A simple test using liblockdep and a pthread mutex confirms the
  problem: (omitting stack traces)

    new class 0xe15038: 0x7ffc64950f20
    acquire class [0xe15038] 0x7ffc64950f20
    acquire class [0xe15038] 0x7ffc64950f20
    hash chain already cached, key: 0000000000000000 tail class:
    [0xe15038] 0x7ffc64950f20

2.- Consider an ABBA in 2 different tasks and no class yet acquired.

  T1 [key1,key2]->new key     T2[key1,key2]->new key
  --                          --
  A [0,0]->0

                              B [0,1]->1

  B [0,1]->1  (collision)

                              A

  In this case the collision prevents lockdep from creating the new
dependency A->B. This in turn results in lockdep not detecting the
circular dependency when T2 acquires A.
---
 kernel/locking/lockdep.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 60ace56..163657b 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -2168,7 +2168,7 @@ static void check_chain_key(struct task_struct *curr)
 {
 #ifdef CONFIG_DEBUG_LOCKDEP
 	struct held_lock *hlock, *prev_hlock = NULL;
-	unsigned int i, id;
+	unsigned int i;
 	u64 chain_key = 0;
 
 	for (i = 0; i < curr->lockdep_depth; i++) {
@@ -2185,17 +2185,16 @@ static void check_chain_key(struct task_struct *curr)
 				(unsigned long long)hlock->prev_chain_key);
 			return;
 		}
-		id = hlock->class_idx - 1;
 		/*
 		 * Whoops ran out of static storage again?
 		 */
-		if (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS))
+		if (DEBUG_LOCKS_WARN_ON(hlock->class_idx > MAX_LOCKDEP_KEYS))
 			return;
 
 		if (prev_hlock && (prev_hlock->irq_context !=
 							hlock->irq_context))
 			chain_key = 0;
-		chain_key = iterate_chain_key(chain_key, id);
+		chain_key = iterate_chain_key(chain_key, hlock->class_idx);
 		prev_hlock = hlock;
 	}
 	if (chain_key != curr->curr_chain_key) {
@@ -3073,7 +3072,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	struct task_struct *curr = current;
 	struct lock_class *class = NULL;
 	struct held_lock *hlock;
-	unsigned int depth, id;
+	unsigned int depth;
 	int chain_head = 0;
 	int class_idx;
 	u64 chain_key;
@@ -3176,11 +3175,10 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	 * The 'key ID' is what is the most compact key value to drive
 	 * the hash, not class->key.
 	 */
-	id = class - lock_classes;
 	/*
 	 * Whoops, we did it again.. ran straight out of our static allocation.
 	 */
-	if (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS))
+	if (DEBUG_LOCKS_WARN_ON(class_idx > MAX_LOCKDEP_KEYS))
 		return 0;
 
 	chain_key = curr->curr_chain_key;
@@ -3198,7 +3196,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 		chain_key = 0;
 		chain_head = 1;
 	}
-	chain_key = iterate_chain_key(chain_key, id);
+	chain_key = iterate_chain_key(chain_key, class_idx);
 
 	if (nest_lock && !__lock_is_held(nest_lock))
 		return print_lock_nested_lock_not_held(curr, hlock, ip);
-- 
2.5.0

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

* Re: [PATCH 1/3] tools/liblockdep: add userspace version of READ_ONCE
  2016-02-10 23:33 ` [PATCH 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
@ 2016-02-11 15:16   ` Peter Zijlstra
  2016-02-16 16:37     ` Sasha Levin
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Zijlstra @ 2016-02-11 15:16 UTC (permalink / raw)
  To: Alfredo Alvarez Fernandez; +Cc: mingo, sasha.levin, linux-kernel

On Thu, Feb 11, 2016 at 12:33:30AM +0100, Alfredo Alvarez Fernandez wrote:
> From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
> 
> This was added to the kernel code in 1658d35ead5d ("list: Use
> READ_ONCE() when testing for empty lists")
> There's nothing special we need to do about it in userspace.
> 
> Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
> ---
>  tools/lib/lockdep/uinclude/linux/compiler.h | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/tools/lib/lockdep/uinclude/linux/compiler.h b/tools/lib/lockdep/uinclude/linux/compiler.h
> index 6386dc3..fd3e56a 100644
> --- a/tools/lib/lockdep/uinclude/linux/compiler.h
> +++ b/tools/lib/lockdep/uinclude/linux/compiler.h
> @@ -3,6 +3,7 @@
>  
>  #define __used		__attribute__((__unused__))
>  #define unlikely
> +#define READ_ONCE(x) (x)
>  #define WRITE_ONCE(x, val) x=(val)

I would argue we'd still very much want the volatile cast for both READ
and WRITE_ONCE().

Why do these things have different semantics between user and kernel
space?

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

* Re: [PATCH 1/3] tools/liblockdep: add userspace version of READ_ONCE
  2016-02-11 15:16   ` Peter Zijlstra
@ 2016-02-16 16:37     ` Sasha Levin
  0 siblings, 0 replies; 17+ messages in thread
From: Sasha Levin @ 2016-02-16 16:37 UTC (permalink / raw)
  To: Peter Zijlstra, Alfredo Alvarez Fernandez; +Cc: mingo, linux-kernel

On 02/11/2016 10:16 AM, Peter Zijlstra wrote:
> On Thu, Feb 11, 2016 at 12:33:30AM +0100, Alfredo Alvarez Fernandez wrote:
>> From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
>>
>> This was added to the kernel code in 1658d35ead5d ("list: Use
>> READ_ONCE() when testing for empty lists")
>> There's nothing special we need to do about it in userspace.
>>
>> Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
>> ---
>>  tools/lib/lockdep/uinclude/linux/compiler.h | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git a/tools/lib/lockdep/uinclude/linux/compiler.h b/tools/lib/lockdep/uinclude/linux/compiler.h
>> index 6386dc3..fd3e56a 100644
>> --- a/tools/lib/lockdep/uinclude/linux/compiler.h
>> +++ b/tools/lib/lockdep/uinclude/linux/compiler.h
>> @@ -3,6 +3,7 @@
>>  
>>  #define __used		__attribute__((__unused__))
>>  #define unlikely
>> +#define READ_ONCE(x) (x)
>>  #define WRITE_ONCE(x, val) x=(val)
> 
> I would argue we'd still very much want the volatile cast for both READ
> and WRITE_ONCE().
> 
> Why do these things have different semantics between user and kernel
> space?

You're right. I'll send a patch.


Thanks,
Sasha

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

* Re: [PATCH 0/3] lockdep: liblockdep: Prevent chain_key collisions
  2016-02-10 23:33 [PATCH 0/3] lockdep: liblockdep: Prevent chain_key collisions Alfredo Alvarez Fernandez
                   ` (2 preceding siblings ...)
  2016-02-10 23:33 ` [PATCH 3/3] lockdep: prevent chain_key collisions Alfredo Alvarez Fernandez
@ 2016-02-16 16:38 ` Sasha Levin
  2016-02-16 17:22   ` Peter Zijlstra
  3 siblings, 1 reply; 17+ messages in thread
From: Sasha Levin @ 2016-02-16 16:38 UTC (permalink / raw)
  To: Alfredo Alvarez Fernandez, mingo, peterz; +Cc: linux-kernel

On 02/10/2016 06:33 PM, Alfredo Alvarez Fernandez wrote:
> This patch series prevents possible collisions in the chain_key
> hashing macro iterate_chain_key(key1, key2) that can lead to lockdep 
> not detecting very simple deadlocks such as AA or ABBA.
> 
> The problem only affects the first allocated lock classes. That could 
> explain why it was not seen while running lockdep's test suite, since
> by the time the test suite runs there are already registered lock 
> classes and the indexes allocated for the lock classes under test are
> high enough to avoid collisions.
> 
> The patch series also extends the tools/liblockdep test suite with 
> tests covering the offending cases.
> 
> I came across the problem while testing a simple AA deadlock scenario
> in userspace using a pthread_mutex and tools/liblockdep. In that 
> context it is fairly easy to have a clean and deterministic initial 
> state where the problem can be reproduced.
> 
> The proposed solution was tested with the newly introduced tests and
> also with lockdep's test suite:
>  [    0.000000] Good, all 253 testcases passed! |

Thanks Alfredo, I'll grab the first two.

Peter/Ingo, will you take the lockdep one or do you want it to go through
my tree?


Thanks,
Sasha

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

* Re: [PATCH 0/3] lockdep: liblockdep: Prevent chain_key collisions
  2016-02-16 16:38 ` [PATCH 0/3] lockdep: liblockdep: " Sasha Levin
@ 2016-02-16 17:22   ` Peter Zijlstra
  0 siblings, 0 replies; 17+ messages in thread
From: Peter Zijlstra @ 2016-02-16 17:22 UTC (permalink / raw)
  To: Sasha Levin; +Cc: Alfredo Alvarez Fernandez, mingo, linux-kernel

On Tue, Feb 16, 2016 at 11:38:26AM -0500, Sasha Levin wrote:
> Peter/Ingo, will you take the lockdep one or do you want it to go through
> my tree?

I've got the lockdep one. Thanks!

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

* Re: [PATCH 3/3] lockdep: prevent chain_key collisions
  2016-02-10 23:33 ` [PATCH 3/3] lockdep: prevent chain_key collisions Alfredo Alvarez Fernandez
@ 2016-02-17  8:38   ` Ingo Molnar
  2016-02-19  6:48     ` [PATCH v2 0/3] lockdep: liblockdep: Prevent " Alfredo Alvarez Fernandez
  2016-02-29 11:24   ` [tip:locking/core] locking/lockdep: Prevent " tip-bot for Alfredo Alvarez Fernandez
  1 sibling, 1 reply; 17+ messages in thread
From: Ingo Molnar @ 2016-02-17  8:38 UTC (permalink / raw)
  To: Alfredo Alvarez Fernandez; +Cc: mingo, peterz, sasha.levin, linux-kernel


* Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com> wrote:

> From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
> 
> The chain_key hashing macro iterate_chain_key(key1, key2) does not
> generate a new different value if both key1 and key2 are 0. In that
> case the generated value is again 0. This can lead to collisions which
> can result in lockdep not detecting deadlocks or circular
> dependencies.
> 
> Avoid the problem by using class_idx (1-based) instead of class id
> (0-based) as an input for the hashing macro 'key2' in
> iterate_chain_key(key1, key2).
> 
> The use of class id created collisions in cases like the following:

Nice find!

> 1.- Consider an initial state in which no class has been acquired yet.
> Under these circumstances an AA deadlock will not be detected by
> lockdep:
> 
>   lock  [key1,key2]->new key  (key1=old chain_key, key2=id)
>   --------------------------
>   A     [0,0]->0
>   A     [0,0]->0 (collision)
> 
>   The newly generated chain_key collides with the one used before and as
>   a result the check for a deadlock is skipped
> 
>   A simple test using liblockdep and a pthread mutex confirms the
>   problem: (omitting stack traces)
> 
>     new class 0xe15038: 0x7ffc64950f20
>     acquire class [0xe15038] 0x7ffc64950f20
>     acquire class [0xe15038] 0x7ffc64950f20
>     hash chain already cached, key: 0000000000000000 tail class:
>     [0xe15038] 0x7ffc64950f20
> 
> 2.- Consider an ABBA in 2 different tasks and no class yet acquired.
> 
>   T1 [key1,key2]->new key     T2[key1,key2]->new key
>   --                          --
>   A [0,0]->0
> 
>                               B [0,1]->1
> 
>   B [0,1]->1  (collision)
> 
>                               A
> 
>   In this case the collision prevents lockdep from creating the new
> dependency A->B. This in turn results in lockdep not detecting the
> circular dependency when T2 acquires A.

So this is pretty fatal result - and it was not found for years!

Could you please also do a follow up fix, to treat hash collisions as hard errors 
and shut up lockdep an report the bug?

Thanks,

	Ingo

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

* [PATCH v2 0/3] lockdep: liblockdep: Prevent chain_key collisions
  2016-02-17  8:38   ` Ingo Molnar
@ 2016-02-19  6:48     ` Alfredo Alvarez Fernandez
  2016-02-19  6:48       ` [PATCH v2 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
                         ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Alfredo Alvarez Fernandez @ 2016-02-19  6:48 UTC (permalink / raw)
  To: sasha.levin, peterz, mingo; +Cc: linux-kernel

This patch series prevents possible collisions in the chain_key
hashing macro iterate_chain_key(key1, key2) that can lead to lockdep
not detecting very simple deadlocks such as AA or ABBA.

The problem only affects the first allocated lock classes. That could
explain why it was not seen while running lockdep's test suite, since
by the time the test suite runs there are already registered lock
classes and the indexes allocated for the lock classes under test are
high enough to avoid collisions.

The patch series also extends the tools/liblockdep test suite with
tests covering the offending cases.

I came across the problem while testing a simple AA deadlock scenario
in userspace using a pthread_mutex and tools/liblockdep. In that
context it is fairly easy to have a clean and deterministic initial
state where the problem can be reproduced.

The proposed solution was tested with the newly introduced tests and
also with lockdep's test suite:
 [    0.000000] Good, all 253 testcases passed! |

v2:
  - Add detection for chain_key collisions under CONFIG_DEBUG_LOCKDEP.
    When a collision is detected the problem is reported and all lock
    debugging is turned off.

    Tested using liblockdep and the added tests before and after
    applying the fix, confirming both that the code added for the
    detection correctly reports the problem and that the fix actually
    fixes it.

    Tested tweaking lockdep to generate false collisions and
    verified that the problem is reported and that lock debugging is 
    turned off.

    Also tested with lockdep's test suite after applying the patch:
    [    0.000000] Good, all 253 testcases passed! |

  - Fix code style problems in added liblockdep tests

Alfredo Alvarez Fernandez (3):
  tools/liblockdep: add userspace version of READ_ONCE
  tools/liblockdep: add tests
  lockdep: prevent and detect chain_key collisions

 kernel/locking/lockdep.c                    | 73 ++++++++++++++++++++++-------
 tools/lib/lockdep/tests/AA.c                |  8 ++--
 tools/lib/lockdep/tests/ABA.c               | 13 +++++
 tools/lib/lockdep/tests/ABBA_2threads.c     | 46 ++++++++++++++++++
 tools/lib/lockdep/uinclude/linux/compiler.h |  1 +
 5 files changed, 121 insertions(+), 20 deletions(-)
 create mode 100644 tools/lib/lockdep/tests/ABA.c
 create mode 100644 tools/lib/lockdep/tests/ABBA_2threads.c

-- 
2.5.0

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

* [PATCH v2 1/3] tools/liblockdep: add userspace version of READ_ONCE
  2016-02-19  6:48     ` [PATCH v2 0/3] lockdep: liblockdep: Prevent " Alfredo Alvarez Fernandez
@ 2016-02-19  6:48       ` Alfredo Alvarez Fernandez
  2016-02-29 11:22         ` [tip:locking/core] tools/lib/lockdep: Add userspace version of READ_ONCE() tip-bot for Alfredo Alvarez Fernandez
  2016-02-19  6:48       ` [PATCH v2 2/3] tools/liblockdep: add tests Alfredo Alvarez Fernandez
  2016-02-19  6:48       ` [PATCH v2 3/3] lockdep: prevent and detect chain_key collisions Alfredo Alvarez Fernandez
  2 siblings, 1 reply; 17+ messages in thread
From: Alfredo Alvarez Fernandez @ 2016-02-19  6:48 UTC (permalink / raw)
  To: sasha.levin, peterz, mingo; +Cc: linux-kernel, Alfredo Alvarez Fernandez

From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>

This was added to the kernel code in <1658d35ead5d> ("list: Use
READ_ONCE() when testing for empty lists")
There's nothing special we need to do about it in userspace.

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
---
 tools/lib/lockdep/uinclude/linux/compiler.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/lockdep/uinclude/linux/compiler.h b/tools/lib/lockdep/uinclude/linux/compiler.h
index 6386dc3..fd3e56a 100644
--- a/tools/lib/lockdep/uinclude/linux/compiler.h
+++ b/tools/lib/lockdep/uinclude/linux/compiler.h
@@ -3,6 +3,7 @@
 
 #define __used		__attribute__((__unused__))
 #define unlikely
+#define READ_ONCE(x) (x)
 #define WRITE_ONCE(x, val) x=(val)
 #define RCU_INIT_POINTER(p, v) p=(v)
 
-- 
2.5.0

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

* [PATCH v2 2/3] tools/liblockdep: add tests
  2016-02-19  6:48     ` [PATCH v2 0/3] lockdep: liblockdep: Prevent " Alfredo Alvarez Fernandez
  2016-02-19  6:48       ` [PATCH v2 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
@ 2016-02-19  6:48       ` Alfredo Alvarez Fernandez
  2016-02-29 11:23         ` [tip:locking/core] tools/lib/lockdep: Add tests for AA and ABBA locking tip-bot for Alfredo Alvarez Fernandez
  2016-02-19  6:48       ` [PATCH v2 3/3] lockdep: prevent and detect chain_key collisions Alfredo Alvarez Fernandez
  2 siblings, 1 reply; 17+ messages in thread
From: Alfredo Alvarez Fernandez @ 2016-02-19  6:48 UTC (permalink / raw)
  To: sasha.levin, peterz, mingo; +Cc: linux-kernel, Alfredo Alvarez Fernandez

From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>

Add test for AA and 2 threaded ABBA locking.

Rename AA.c to ABA.c since it was implementing an ABA instead of a pure
AA. Now both cases are covered.

The expected output for AA.c is that the process blocks and lockdep
reports a deadlock.

ABBA_2threads.c differs from ABBA.c in that lockdep keeps separate chains
of held locks per task. This can lead to different behaviour regarding
lock detection. The expected output for this test is that the process
blocks and lockdep reports a circular locking dependency.

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
---
Changes in v2:
  - Fix style problems in added tests

 tools/lib/lockdep/tests/AA.c            |  8 +++---
 tools/lib/lockdep/tests/ABA.c           | 13 ++++++++++
 tools/lib/lockdep/tests/ABBA_2threads.c | 46 +++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 4 deletions(-)
 create mode 100644 tools/lib/lockdep/tests/ABA.c
 create mode 100644 tools/lib/lockdep/tests/ABBA_2threads.c

diff --git a/tools/lib/lockdep/tests/AA.c b/tools/lib/lockdep/tests/AA.c
index 0f782ff..18211a5 100644
--- a/tools/lib/lockdep/tests/AA.c
+++ b/tools/lib/lockdep/tests/AA.c
@@ -1,13 +1,13 @@
 #include <liblockdep/mutex.h>
 
-void main(void)
+int main(void)
 {
-	pthread_mutex_t a, b;
+	pthread_mutex_t a;
 
 	pthread_mutex_init(&a, NULL);
-	pthread_mutex_init(&b, NULL);
 
 	pthread_mutex_lock(&a);
-	pthread_mutex_lock(&b);
 	pthread_mutex_lock(&a);
+
+	return 0;
 }
diff --git a/tools/lib/lockdep/tests/ABA.c b/tools/lib/lockdep/tests/ABA.c
new file mode 100644
index 0000000..0f782ff
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABA.c
@@ -0,0 +1,13 @@
+#include <liblockdep/mutex.h>
+
+void main(void)
+{
+	pthread_mutex_t a, b;
+
+	pthread_mutex_init(&a, NULL);
+	pthread_mutex_init(&b, NULL);
+
+	pthread_mutex_lock(&a);
+	pthread_mutex_lock(&b);
+	pthread_mutex_lock(&a);
+}
diff --git a/tools/lib/lockdep/tests/ABBA_2threads.c b/tools/lib/lockdep/tests/ABBA_2threads.c
new file mode 100644
index 0000000..cd807d7
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABBA_2threads.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <pthread.h>
+
+pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t b = PTHREAD_MUTEX_INITIALIZER;
+pthread_barrier_t bar;
+
+void *ba_lock(void *arg)
+{
+	int ret, i;
+
+	pthread_mutex_lock(&b);
+
+	if (pthread_barrier_wait(&bar) == PTHREAD_BARRIER_SERIAL_THREAD)
+		pthread_barrier_destroy(&bar);
+
+	pthread_mutex_lock(&a);
+
+	pthread_mutex_unlock(&a);
+	pthread_mutex_unlock(&b);
+}
+
+int main(void)
+{
+	pthread_t t;
+
+	pthread_barrier_init(&bar, NULL, 2);
+
+	if (pthread_create(&t, NULL, ba_lock, NULL)) {
+		fprintf(stderr, "pthread_create() failed\n");
+		return 1;
+	}
+	pthread_mutex_lock(&a);
+
+	if (pthread_barrier_wait(&bar) == PTHREAD_BARRIER_SERIAL_THREAD)
+		pthread_barrier_destroy(&bar);
+
+	pthread_mutex_lock(&b);
+
+	pthread_mutex_unlock(&b);
+	pthread_mutex_unlock(&a);
+
+	pthread_join(t, NULL);
+
+	return 0;
+}
-- 
2.5.0

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

* [PATCH v2 3/3] lockdep: prevent and detect chain_key collisions
  2016-02-19  6:48     ` [PATCH v2 0/3] lockdep: liblockdep: Prevent " Alfredo Alvarez Fernandez
  2016-02-19  6:48       ` [PATCH v2 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
  2016-02-19  6:48       ` [PATCH v2 2/3] tools/liblockdep: add tests Alfredo Alvarez Fernandez
@ 2016-02-19  6:48       ` Alfredo Alvarez Fernandez
  2016-02-29 11:24         ` [tip:locking/core] locking/lockdep: Detect " tip-bot for Ingo Molnar
  2 siblings, 1 reply; 17+ messages in thread
From: Alfredo Alvarez Fernandez @ 2016-02-19  6:48 UTC (permalink / raw)
  To: sasha.levin, peterz, mingo; +Cc: linux-kernel, Alfredo Alvarez Fernandez

From: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>

The chain_key hashing macro iterate_chain_key(key1, key2) does not
generate a new different value if both key1 and key2 are 0. In that
case the generated value is again 0. This can lead to collisions which
can result in lockdep not detecting deadlocks or circular
dependencies.

Avoid the problem by using class_idx (1-based) instead of class id
(0-based) as an input for the hashing macro 'key2' in
iterate_chain_key(key1, key2).

The use of class id created collisions in cases like the following:

1.- Consider an initial state in which no class has been acquired yet.
Under these circumstances an AA deadlock will not be detected by
lockdep:

  lock  [key1,key2]->new key  (key1=old chain_key, key2=id)
  --------------------------
  A     [0,0]->0
  A     [0,0]->0 (collision)

  The newly generated chain_key collides with the one used before and as
  a result the check for a deadlock is skipped

  A simple test using liblockdep and a pthread mutex confirms the
  problem: (omitting stack traces)

    new class 0xe15038: 0x7ffc64950f20
    acquire class [0xe15038] 0x7ffc64950f20
    acquire class [0xe15038] 0x7ffc64950f20
    hash chain already cached, key: 0000000000000000 tail class:
    [0xe15038] 0x7ffc64950f20

2.- Consider an ABBA in 2 different tasks and no class yet acquired.

  T1 [key1,key2]->new key     T2[key1,key2]->new key
  --                          --
  A [0,0]->0

                              B [0,1]->1

  B [0,1]->1  (collision)

                              A

  In this case the collision prevents lockdep from creating the new
dependency A->B. This in turn results in lockdep not detecting the
circular dependency when T2 acquires A.

Add detection for chain_key collision under CONFIG_DEBUG_LOCKDEP.
When a collision is detected the problem is reported and all lock
debugging is turned off.

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezernandez@gmail.com>
---
Changes in v2:
  - Add detection for chain_key collisions under CONFIG_DEBUG_LOCKDEP.
    When a collision is detected the problem is reported and all lock
    debugging is turned off.

    Tested using liblockdep and the added tests before and after
    applying the fix, confirming both that the code added for the
    detection correctly reports the problem and that the fix actually
    fixes it.

    Tested tweaking lockdep to generate false collisions and
    verified that the problem is reported and that lock debugging is 
    turned off.

    Also tested with lockdep's test suite after applying the patch:
    [    0.000000] Good, all 253 testcases passed! |

 kernel/locking/lockdep.c | 73 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 57 insertions(+), 16 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 60ace56..2c28298 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -2007,6 +2007,53 @@ struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i)
 }
 
 /*
+ * Returns the index of the first held_lock of the current chain
+ */
+static inline int get_first_held_lock(struct task_struct *curr,
+					struct held_lock *hlock)
+{
+	int i;
+	struct held_lock *hlock_curr;
+
+	for (i = curr->lockdep_depth - 1; i >= 0; i--) {
+		hlock_curr = curr->held_locks + i;
+		if (hlock_curr->irq_context != hlock->irq_context)
+			break;
+
+	}
+
+	return ++i;
+}
+
+/*
+ * Checks whether the chain and the current held locks are consistent
+ * in depth and also in content. If they are not it most likely means
+ * that there was a collision during the calculation of the chain_key.
+ * Returns: 0 not passed, 1 passed
+ */
+static int check_no_collision(struct task_struct *curr,
+			struct held_lock *hlock,
+			struct lock_chain *chain)
+{
+#ifdef CONFIG_DEBUG_LOCKDEP
+	int i, j, id;
+
+	i = get_first_held_lock(curr, hlock);
+
+	if (DEBUG_LOCKS_WARN_ON(chain->depth != curr->lockdep_depth - (i - 1)))
+		return 0;
+
+	for (j = 0; j < chain->depth - 1; j++, i++) {
+		id = curr->held_locks[i].class_idx - 1;
+
+		if (DEBUG_LOCKS_WARN_ON(chain_hlocks[chain->base + j] != id))
+			return 0;
+	}
+#endif
+	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.
@@ -2019,7 +2066,6 @@ static inline int lookup_chain_cache(struct task_struct *curr,
 	struct lock_class *class = hlock_class(hlock);
 	struct list_head *hash_head = chainhashentry(chain_key);
 	struct lock_chain *chain;
-	struct held_lock *hlock_curr;
 	int i, j;
 
 	/*
@@ -2037,6 +2083,9 @@ static inline int lookup_chain_cache(struct task_struct *curr,
 		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",
@@ -2074,13 +2123,7 @@ cache_hit:
 	chain = lock_chains + nr_lock_chains++;
 	chain->chain_key = chain_key;
 	chain->irq_context = hlock->irq_context;
-	/* Find the first held_lock of current chain */
-	for (i = curr->lockdep_depth - 1; i >= 0; i--) {
-		hlock_curr = curr->held_locks + i;
-		if (hlock_curr->irq_context != hlock->irq_context)
-			break;
-	}
-	i++;
+	i = get_first_held_lock(curr, hlock);
 	chain->depth = curr->lockdep_depth + 1 - i;
 	if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) {
 		chain->base = nr_chain_hlocks;
@@ -2168,7 +2211,7 @@ static void check_chain_key(struct task_struct *curr)
 {
 #ifdef CONFIG_DEBUG_LOCKDEP
 	struct held_lock *hlock, *prev_hlock = NULL;
-	unsigned int i, id;
+	unsigned int i;
 	u64 chain_key = 0;
 
 	for (i = 0; i < curr->lockdep_depth; i++) {
@@ -2185,17 +2228,16 @@ static void check_chain_key(struct task_struct *curr)
 				(unsigned long long)hlock->prev_chain_key);
 			return;
 		}
-		id = hlock->class_idx - 1;
 		/*
 		 * Whoops ran out of static storage again?
 		 */
-		if (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS))
+		if (DEBUG_LOCKS_WARN_ON(hlock->class_idx > MAX_LOCKDEP_KEYS))
 			return;
 
 		if (prev_hlock && (prev_hlock->irq_context !=
 							hlock->irq_context))
 			chain_key = 0;
-		chain_key = iterate_chain_key(chain_key, id);
+		chain_key = iterate_chain_key(chain_key, hlock->class_idx);
 		prev_hlock = hlock;
 	}
 	if (chain_key != curr->curr_chain_key) {
@@ -3073,7 +3115,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	struct task_struct *curr = current;
 	struct lock_class *class = NULL;
 	struct held_lock *hlock;
-	unsigned int depth, id;
+	unsigned int depth;
 	int chain_head = 0;
 	int class_idx;
 	u64 chain_key;
@@ -3176,11 +3218,10 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	 * The 'key ID' is what is the most compact key value to drive
 	 * the hash, not class->key.
 	 */
-	id = class - lock_classes;
 	/*
 	 * Whoops, we did it again.. ran straight out of our static allocation.
 	 */
-	if (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS))
+	if (DEBUG_LOCKS_WARN_ON(class_idx > MAX_LOCKDEP_KEYS))
 		return 0;
 
 	chain_key = curr->curr_chain_key;
@@ -3198,7 +3239,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 		chain_key = 0;
 		chain_head = 1;
 	}
-	chain_key = iterate_chain_key(chain_key, id);
+	chain_key = iterate_chain_key(chain_key, class_idx);
 
 	if (nest_lock && !__lock_is_held(nest_lock))
 		return print_lock_nested_lock_not_held(curr, hlock, ip);
-- 
2.5.0

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

* [tip:locking/core] tools/lib/lockdep: Add userspace version of READ_ONCE()
  2016-02-19  6:48       ` [PATCH v2 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
@ 2016-02-29 11:22         ` tip-bot for Alfredo Alvarez Fernandez
  0 siblings, 0 replies; 17+ messages in thread
From: tip-bot for Alfredo Alvarez Fernandez @ 2016-02-29 11:22 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: alfredoalvarezfernandez, linux-kernel, mingo, tglx, peterz, akpm,
	torvalds, hpa, paulmck, sasha.levin

Commit-ID:  9d5a23ac8e0ede14a2b23740d231727ba5be483a
Gitweb:     http://git.kernel.org/tip/9d5a23ac8e0ede14a2b23740d231727ba5be483a
Author:     Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
AuthorDate: Fri, 19 Feb 2016 07:48:51 +0100
Committer:  Ingo Molnar <mingo@kernel.org>
CommitDate: Mon, 29 Feb 2016 10:29:27 +0100

tools/lib/lockdep: Add userspace version of READ_ONCE()

This was added to the kernel code in <1658d35ead5d> ("list: Use
READ_ONCE() when testing for empty lists").

There's nothing special we need to do about it in userspace.

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1455864533-7536-2-git-send-email-alfredoalvarezernandez@gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 tools/lib/lockdep/uinclude/linux/compiler.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/lockdep/uinclude/linux/compiler.h b/tools/lib/lockdep/uinclude/linux/compiler.h
index 6386dc3..fd3e56a 100644
--- a/tools/lib/lockdep/uinclude/linux/compiler.h
+++ b/tools/lib/lockdep/uinclude/linux/compiler.h
@@ -3,6 +3,7 @@
 
 #define __used		__attribute__((__unused__))
 #define unlikely
+#define READ_ONCE(x) (x)
 #define WRITE_ONCE(x, val) x=(val)
 #define RCU_INIT_POINTER(p, v) p=(v)
 

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

* [tip:locking/core] tools/lib/lockdep: Add tests for AA and ABBA locking
  2016-02-19  6:48       ` [PATCH v2 2/3] tools/liblockdep: add tests Alfredo Alvarez Fernandez
@ 2016-02-29 11:23         ` tip-bot for Alfredo Alvarez Fernandez
  0 siblings, 0 replies; 17+ messages in thread
From: tip-bot for Alfredo Alvarez Fernandez @ 2016-02-29 11:23 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: torvalds, sasha.levin, mingo, akpm, alfredoalvarezfernandez,
	paulmck, hpa, tglx, peterz, linux-kernel

Commit-ID:  11a1ac206d0806b0db39712c3292f6006e77a7e8
Gitweb:     http://git.kernel.org/tip/11a1ac206d0806b0db39712c3292f6006e77a7e8
Author:     Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
AuthorDate: Fri, 19 Feb 2016 07:48:52 +0100
Committer:  Ingo Molnar <mingo@kernel.org>
CommitDate: Mon, 29 Feb 2016 10:29:33 +0100

tools/lib/lockdep: Add tests for AA and ABBA locking

Add test for AA and 2 threaded ABBA locking.

Rename AA.c to ABA.c since it was implementing an ABA instead of a pure
AA. Now both cases are covered.

The expected output for AA.c is that the process blocks and lockdep
reports a deadlock.

ABBA_2threads.c differs from ABBA.c in that lockdep keeps separate chains
of held locks per task. This can lead to different behaviour regarding
lock detection. The expected output for this test is that the process
blocks and lockdep reports a circular locking dependency.

These tests found a lockdep bug - fixed by the next commit.

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1455864533-7536-3-git-send-email-alfredoalvarezernandez@gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 tools/lib/lockdep/tests/AA.c            |  8 +++---
 tools/lib/lockdep/tests/{AA.c => ABA.c} |  0
 tools/lib/lockdep/tests/ABBA_2threads.c | 46 +++++++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 4 deletions(-)

diff --git a/tools/lib/lockdep/tests/AA.c b/tools/lib/lockdep/tests/AA.c
index 0f782ff..18211a5 100644
--- a/tools/lib/lockdep/tests/AA.c
+++ b/tools/lib/lockdep/tests/AA.c
@@ -1,13 +1,13 @@
 #include <liblockdep/mutex.h>
 
-void main(void)
+int main(void)
 {
-	pthread_mutex_t a, b;
+	pthread_mutex_t a;
 
 	pthread_mutex_init(&a, NULL);
-	pthread_mutex_init(&b, NULL);
 
 	pthread_mutex_lock(&a);
-	pthread_mutex_lock(&b);
 	pthread_mutex_lock(&a);
+
+	return 0;
 }
diff --git a/tools/lib/lockdep/tests/AA.c b/tools/lib/lockdep/tests/ABA.c
similarity index 100%
copy from tools/lib/lockdep/tests/AA.c
copy to tools/lib/lockdep/tests/ABA.c
diff --git a/tools/lib/lockdep/tests/ABBA_2threads.c b/tools/lib/lockdep/tests/ABBA_2threads.c
new file mode 100644
index 0000000..cd807d7
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABBA_2threads.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <pthread.h>
+
+pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t b = PTHREAD_MUTEX_INITIALIZER;
+pthread_barrier_t bar;
+
+void *ba_lock(void *arg)
+{
+	int ret, i;
+
+	pthread_mutex_lock(&b);
+
+	if (pthread_barrier_wait(&bar) == PTHREAD_BARRIER_SERIAL_THREAD)
+		pthread_barrier_destroy(&bar);
+
+	pthread_mutex_lock(&a);
+
+	pthread_mutex_unlock(&a);
+	pthread_mutex_unlock(&b);
+}
+
+int main(void)
+{
+	pthread_t t;
+
+	pthread_barrier_init(&bar, NULL, 2);
+
+	if (pthread_create(&t, NULL, ba_lock, NULL)) {
+		fprintf(stderr, "pthread_create() failed\n");
+		return 1;
+	}
+	pthread_mutex_lock(&a);
+
+	if (pthread_barrier_wait(&bar) == PTHREAD_BARRIER_SERIAL_THREAD)
+		pthread_barrier_destroy(&bar);
+
+	pthread_mutex_lock(&b);
+
+	pthread_mutex_unlock(&b);
+	pthread_mutex_unlock(&a);
+
+	pthread_join(t, NULL);
+
+	return 0;
+}

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

* [tip:locking/core] locking/lockdep: Prevent chain_key collisions
  2016-02-10 23:33 ` [PATCH 3/3] lockdep: prevent chain_key collisions Alfredo Alvarez Fernandez
  2016-02-17  8:38   ` Ingo Molnar
@ 2016-02-29 11:24   ` tip-bot for Alfredo Alvarez Fernandez
  1 sibling, 0 replies; 17+ messages in thread
From: tip-bot for Alfredo Alvarez Fernandez @ 2016-02-29 11:24 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: peterz, tglx, mingo, paulmck, hpa, linux-kernel, torvalds,
	alfredoalvarezfernandez, alfredoalvarezernandez, akpm

Commit-ID:  5f18ab5c6bdba735b31a1e7c2618f48eae6b1b5c
Gitweb:     http://git.kernel.org/tip/5f18ab5c6bdba735b31a1e7c2618f48eae6b1b5c
Author:     Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
AuthorDate: Thu, 11 Feb 2016 00:33:32 +0100
Committer:  Ingo Molnar <mingo@kernel.org>
CommitDate: Mon, 29 Feb 2016 10:32:29 +0100

locking/lockdep: Prevent chain_key collisions

The chain_key hashing macro iterate_chain_key(key1, key2) does not
generate a new different value if both key1 and key2 are 0. In that
case the generated value is again 0. This can lead to collisions which
can result in lockdep not detecting deadlocks or circular
dependencies.

Avoid the problem by using class_idx (1-based) instead of class id
(0-based) as an input for the hashing macro 'key2' in
iterate_chain_key(key1, key2).

The use of class id created collisions in cases like the following:

1.- Consider an initial state in which no class has been acquired yet.
Under these circumstances an AA deadlock will not be detected by
lockdep:

  lock  [key1,key2]->new key  (key1=old chain_key, key2=id)
  --------------------------
  A     [0,0]->0
  A     [0,0]->0 (collision)

  The newly generated chain_key collides with the one used before and as
  a result the check for a deadlock is skipped

  A simple test using liblockdep and a pthread mutex confirms the
  problem: (omitting stack traces)

    new class 0xe15038: 0x7ffc64950f20
    acquire class [0xe15038] 0x7ffc64950f20
    acquire class [0xe15038] 0x7ffc64950f20
    hash chain already cached, key: 0000000000000000 tail class:
    [0xe15038] 0x7ffc64950f20

2.- Consider an ABBA in 2 different tasks and no class yet acquired.

  T1 [key1,key2]->new key     T2[key1,key2]->new key
  --                          --
  A [0,0]->0

                              B [0,1]->1

  B [0,1]->1  (collision)

                              A

In this case the collision prevents lockdep from creating the new
dependency A->B. This in turn results in lockdep not detecting the
circular dependency when T2 acquires A.

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezernandez@gmail.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: sasha.levin@oracle.com
Link: http://lkml.kernel.org/r/1455147212-2389-4-git-send-email-alfredoalvarezernandez@gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 kernel/locking/lockdep.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 3261214..6f446eb 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -2143,7 +2143,7 @@ static void check_chain_key(struct task_struct *curr)
 {
 #ifdef CONFIG_DEBUG_LOCKDEP
 	struct held_lock *hlock, *prev_hlock = NULL;
-	unsigned int i, id;
+	unsigned int i;
 	u64 chain_key = 0;
 
 	for (i = 0; i < curr->lockdep_depth; i++) {
@@ -2160,17 +2160,16 @@ static void check_chain_key(struct task_struct *curr)
 				(unsigned long long)hlock->prev_chain_key);
 			return;
 		}
-		id = hlock->class_idx - 1;
 		/*
 		 * Whoops ran out of static storage again?
 		 */
-		if (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS))
+		if (DEBUG_LOCKS_WARN_ON(hlock->class_idx > MAX_LOCKDEP_KEYS))
 			return;
 
 		if (prev_hlock && (prev_hlock->irq_context !=
 							hlock->irq_context))
 			chain_key = 0;
-		chain_key = iterate_chain_key(chain_key, id);
+		chain_key = iterate_chain_key(chain_key, hlock->class_idx);
 		prev_hlock = hlock;
 	}
 	if (chain_key != curr->curr_chain_key) {
@@ -3048,7 +3047,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	struct task_struct *curr = current;
 	struct lock_class *class = NULL;
 	struct held_lock *hlock;
-	unsigned int depth, id;
+	unsigned int depth;
 	int chain_head = 0;
 	int class_idx;
 	u64 chain_key;
@@ -3151,11 +3150,10 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 	 * The 'key ID' is what is the most compact key value to drive
 	 * the hash, not class->key.
 	 */
-	id = class - lock_classes;
 	/*
 	 * Whoops, we did it again.. ran straight out of our static allocation.
 	 */
-	if (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS))
+	if (DEBUG_LOCKS_WARN_ON(class_idx > MAX_LOCKDEP_KEYS))
 		return 0;
 
 	chain_key = curr->curr_chain_key;
@@ -3173,7 +3171,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 		chain_key = 0;
 		chain_head = 1;
 	}
-	chain_key = iterate_chain_key(chain_key, id);
+	chain_key = iterate_chain_key(chain_key, class_idx);
 
 	if (nest_lock && !__lock_is_held(nest_lock))
 		return print_lock_nested_lock_not_held(curr, hlock, ip);

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

* [tip:locking/core] locking/lockdep: Detect chain_key collisions
  2016-02-19  6:48       ` [PATCH v2 3/3] lockdep: prevent and detect chain_key collisions Alfredo Alvarez Fernandez
@ 2016-02-29 11:24         ` tip-bot for Ingo Molnar
  0 siblings, 0 replies; 17+ messages in thread
From: tip-bot for Ingo Molnar @ 2016-02-29 11:24 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: tglx, alfredoalvarezernandez, mingo, torvalds,
	alfredoalvarezfernandez, linux-kernel, hpa, peterz, paulmck

Commit-ID:  9e4e7554e755aaad8df0e26268787438735b4b76
Gitweb:     http://git.kernel.org/tip/9e4e7554e755aaad8df0e26268787438735b4b76
Author:     Ingo Molnar <mingo@kernel.org>
AuthorDate: Mon, 29 Feb 2016 10:03:58 +0100
Committer:  Ingo Molnar <mingo@kernel.org>
CommitDate: Mon, 29 Feb 2016 10:32:29 +0100

locking/lockdep: Detect chain_key collisions

Add detection for chain_key collision under CONFIG_DEBUG_LOCKDEP.
When a collision is detected the problem is reported and all lock
debugging is turned off.

Tested using liblockdep and the added tests before and after
applying the fix, confirming both that the code added for the
detection correctly reports the problem and that the fix actually
fixes it.

Tested tweaking lockdep to generate false collisions and
verified that the problem is reported and that lock debugging is
turned off.

Also tested with lockdep's test suite after applying the patch:

    [    0.000000] Good, all 253 testcases passed! |

Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezernandez@gmail.com>
Cc: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: sasha.levin@oracle.com
Link: http://lkml.kernel.org/r/1455864533-7536-4-git-send-email-alfredoalvarezernandez@gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 kernel/locking/lockdep.c | 59 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 51 insertions(+), 8 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 6f446eb..f894a2c 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1982,6 +1982,53 @@ struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i)
 }
 
 /*
+ * Returns the index of the first held_lock of the current chain
+ */
+static inline int get_first_held_lock(struct task_struct *curr,
+					struct held_lock *hlock)
+{
+	int i;
+	struct held_lock *hlock_curr;
+
+	for (i = curr->lockdep_depth - 1; i >= 0; i--) {
+		hlock_curr = curr->held_locks + i;
+		if (hlock_curr->irq_context != hlock->irq_context)
+			break;
+
+	}
+
+	return ++i;
+}
+
+/*
+ * Checks whether the chain and the current held locks are consistent
+ * in depth and also in content. If they are not it most likely means
+ * that there was a collision during the calculation of the chain_key.
+ * Returns: 0 not passed, 1 passed
+ */
+static int check_no_collision(struct task_struct *curr,
+			struct held_lock *hlock,
+			struct lock_chain *chain)
+{
+#ifdef CONFIG_DEBUG_LOCKDEP
+	int i, j, id;
+
+	i = get_first_held_lock(curr, hlock);
+
+	if (DEBUG_LOCKS_WARN_ON(chain->depth != curr->lockdep_depth - (i - 1)))
+		return 0;
+
+	for (j = 0; j < chain->depth - 1; j++, i++) {
+		id = curr->held_locks[i].class_idx - 1;
+
+		if (DEBUG_LOCKS_WARN_ON(chain_hlocks[chain->base + j] != id))
+			return 0;
+	}
+#endif
+	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.
@@ -1994,7 +2041,6 @@ static inline int lookup_chain_cache(struct task_struct *curr,
 	struct lock_class *class = hlock_class(hlock);
 	struct hlist_head *hash_head = chainhashentry(chain_key);
 	struct lock_chain *chain;
-	struct held_lock *hlock_curr;
 	int i, j;
 
 	/*
@@ -2012,6 +2058,9 @@ static inline int lookup_chain_cache(struct task_struct *curr,
 		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",
@@ -2049,13 +2098,7 @@ cache_hit:
 	chain = lock_chains + nr_lock_chains++;
 	chain->chain_key = chain_key;
 	chain->irq_context = hlock->irq_context;
-	/* Find the first held_lock of current chain */
-	for (i = curr->lockdep_depth - 1; i >= 0; i--) {
-		hlock_curr = curr->held_locks + i;
-		if (hlock_curr->irq_context != hlock->irq_context)
-			break;
-	}
-	i++;
+	i = get_first_held_lock(curr, hlock);
 	chain->depth = curr->lockdep_depth + 1 - i;
 	if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) {
 		chain->base = nr_chain_hlocks;

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

end of thread, other threads:[~2016-02-29 11:25 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-10 23:33 [PATCH 0/3] lockdep: liblockdep: Prevent chain_key collisions Alfredo Alvarez Fernandez
2016-02-10 23:33 ` [PATCH 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
2016-02-11 15:16   ` Peter Zijlstra
2016-02-16 16:37     ` Sasha Levin
2016-02-10 23:33 ` [PATCH 2/3] tools/liblockdep: add tests Alfredo Alvarez Fernandez
2016-02-10 23:33 ` [PATCH 3/3] lockdep: prevent chain_key collisions Alfredo Alvarez Fernandez
2016-02-17  8:38   ` Ingo Molnar
2016-02-19  6:48     ` [PATCH v2 0/3] lockdep: liblockdep: Prevent " Alfredo Alvarez Fernandez
2016-02-19  6:48       ` [PATCH v2 1/3] tools/liblockdep: add userspace version of READ_ONCE Alfredo Alvarez Fernandez
2016-02-29 11:22         ` [tip:locking/core] tools/lib/lockdep: Add userspace version of READ_ONCE() tip-bot for Alfredo Alvarez Fernandez
2016-02-19  6:48       ` [PATCH v2 2/3] tools/liblockdep: add tests Alfredo Alvarez Fernandez
2016-02-29 11:23         ` [tip:locking/core] tools/lib/lockdep: Add tests for AA and ABBA locking tip-bot for Alfredo Alvarez Fernandez
2016-02-19  6:48       ` [PATCH v2 3/3] lockdep: prevent and detect chain_key collisions Alfredo Alvarez Fernandez
2016-02-29 11:24         ` [tip:locking/core] locking/lockdep: Detect " tip-bot for Ingo Molnar
2016-02-29 11:24   ` [tip:locking/core] locking/lockdep: Prevent " tip-bot for Alfredo Alvarez Fernandez
2016-02-16 16:38 ` [PATCH 0/3] lockdep: liblockdep: " Sasha Levin
2016-02-16 17:22   ` Peter Zijlstra

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.