diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2008-08-11 03:30:24 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-08-11 03:30:24 -0400 |
commit | 7531e2f34d1d551b096143f19111139f0dd84c8b (patch) | |
tree | 0a29d6703e28dc6752b9b4085594cca238595aac /kernel/lockdep.c | |
parent | 4f3e7524b2e703d9f8b02ac338153a53dd7ede66 (diff) |
lockdep: lock protection locks
On Fri, 2008-08-01 at 16:26 -0700, Linus Torvalds wrote:
> On Fri, 1 Aug 2008, David Miller wrote:
> >
> > Taking more than a few locks of the same class at once is bad
> > news and it's better to find an alternative method.
>
> It's not always wrong.
>
> If you can guarantee that anybody that takes more than one lock of a
> particular class will always take a single top-level lock _first_, then
> that's all good. You can obviously screw up and take the same lock _twice_
> (which will deadlock), but at least you cannot get into ABBA situations.
>
> So maybe the right thing to do is to just teach lockdep about "lock
> protection locks". That would have solved the multi-queue issues for
> networking too - all the actual network drivers would still have taken
> just their single queue lock, but the one case that needs to take all of
> them would have taken a separate top-level lock first.
>
> Never mind that the multi-queue locks were always taken in the same order:
> it's never wrong to just have some top-level serialization, and anybody
> who needs to take <n> locks might as well do <n+1>, because they sure as
> hell aren't going to be on _any_ fastpaths.
>
> So the simplest solution really sounds like just teaching lockdep about
> that one special case. It's not "nesting" exactly, although it's obviously
> related to it.
Do as Linus suggested. The lock protection lock is called nest_lock.
Note that we still have the MAX_LOCK_DEPTH (48) limit to consider, so anything
that spills that it still up shit creek.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/lockdep.c')
-rw-r--r-- | kernel/lockdep.c | 26 |
1 files changed, 21 insertions, 5 deletions
diff --git a/kernel/lockdep.c b/kernel/lockdep.c index d3c72ad8d09e..410c3365ad8f 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c | |||
@@ -1372,18 +1372,32 @@ check_deadlock(struct task_struct *curr, struct held_lock *next, | |||
1372 | struct lockdep_map *next_instance, int read) | 1372 | struct lockdep_map *next_instance, int read) |
1373 | { | 1373 | { |
1374 | struct held_lock *prev; | 1374 | struct held_lock *prev; |
1375 | struct held_lock *nest = NULL; | ||
1375 | int i; | 1376 | int i; |
1376 | 1377 | ||
1377 | for (i = 0; i < curr->lockdep_depth; i++) { | 1378 | for (i = 0; i < curr->lockdep_depth; i++) { |
1378 | prev = curr->held_locks + i; | 1379 | prev = curr->held_locks + i; |
1380 | |||
1381 | if (prev->instance == next->nest_lock) | ||
1382 | nest = prev; | ||
1383 | |||
1379 | if (hlock_class(prev) != hlock_class(next)) | 1384 | if (hlock_class(prev) != hlock_class(next)) |
1380 | continue; | 1385 | continue; |
1386 | |||
1381 | /* | 1387 | /* |
1382 | * Allow read-after-read recursion of the same | 1388 | * Allow read-after-read recursion of the same |
1383 | * lock class (i.e. read_lock(lock)+read_lock(lock)): | 1389 | * lock class (i.e. read_lock(lock)+read_lock(lock)): |
1384 | */ | 1390 | */ |
1385 | if ((read == 2) && prev->read) | 1391 | if ((read == 2) && prev->read) |
1386 | return 2; | 1392 | return 2; |
1393 | |||
1394 | /* | ||
1395 | * We're holding the nest_lock, which serializes this lock's | ||
1396 | * nesting behaviour. | ||
1397 | */ | ||
1398 | if (nest) | ||
1399 | return 2; | ||
1400 | |||
1387 | return print_deadlock_bug(curr, prev, next); | 1401 | return print_deadlock_bug(curr, prev, next); |
1388 | } | 1402 | } |
1389 | return 1; | 1403 | return 1; |
@@ -2507,7 +2521,7 @@ EXPORT_SYMBOL_GPL(lockdep_init_map); | |||
2507 | */ | 2521 | */ |
2508 | static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, | 2522 | static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, |
2509 | int trylock, int read, int check, int hardirqs_off, | 2523 | int trylock, int read, int check, int hardirqs_off, |
2510 | unsigned long ip) | 2524 | struct lockdep_map *nest_lock, unsigned long ip) |
2511 | { | 2525 | { |
2512 | struct task_struct *curr = current; | 2526 | struct task_struct *curr = current; |
2513 | struct lock_class *class = NULL; | 2527 | struct lock_class *class = NULL; |
@@ -2566,6 +2580,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, | |||
2566 | hlock->class_idx = class - lock_classes + 1; | 2580 | hlock->class_idx = class - lock_classes + 1; |
2567 | hlock->acquire_ip = ip; | 2581 | hlock->acquire_ip = ip; |
2568 | hlock->instance = lock; | 2582 | hlock->instance = lock; |
2583 | hlock->nest_lock = nest_lock; | ||
2569 | hlock->trylock = trylock; | 2584 | hlock->trylock = trylock; |
2570 | hlock->read = read; | 2585 | hlock->read = read; |
2571 | hlock->check = check; | 2586 | hlock->check = check; |
@@ -2717,7 +2732,7 @@ found_it: | |||
2717 | if (!__lock_acquire(hlock->instance, | 2732 | if (!__lock_acquire(hlock->instance, |
2718 | hlock_class(hlock)->subclass, hlock->trylock, | 2733 | hlock_class(hlock)->subclass, hlock->trylock, |
2719 | hlock->read, hlock->check, hlock->hardirqs_off, | 2734 | hlock->read, hlock->check, hlock->hardirqs_off, |
2720 | hlock->acquire_ip)) | 2735 | hlock->nest_lock, hlock->acquire_ip)) |
2721 | return 0; | 2736 | return 0; |
2722 | } | 2737 | } |
2723 | 2738 | ||
@@ -2778,7 +2793,7 @@ found_it: | |||
2778 | if (!__lock_acquire(hlock->instance, | 2793 | if (!__lock_acquire(hlock->instance, |
2779 | hlock_class(hlock)->subclass, hlock->trylock, | 2794 | hlock_class(hlock)->subclass, hlock->trylock, |
2780 | hlock->read, hlock->check, hlock->hardirqs_off, | 2795 | hlock->read, hlock->check, hlock->hardirqs_off, |
2781 | hlock->acquire_ip)) | 2796 | hlock->nest_lock, hlock->acquire_ip)) |
2782 | return 0; | 2797 | return 0; |
2783 | } | 2798 | } |
2784 | 2799 | ||
@@ -2915,7 +2930,8 @@ EXPORT_SYMBOL_GPL(lock_set_subclass); | |||
2915 | * and also avoid lockdep recursion: | 2930 | * and also avoid lockdep recursion: |
2916 | */ | 2931 | */ |
2917 | void lock_acquire(struct lockdep_map *lock, unsigned int subclass, | 2932 | void lock_acquire(struct lockdep_map *lock, unsigned int subclass, |
2918 | int trylock, int read, int check, unsigned long ip) | 2933 | int trylock, int read, int check, |
2934 | struct lockdep_map *nest_lock, unsigned long ip) | ||
2919 | { | 2935 | { |
2920 | unsigned long flags; | 2936 | unsigned long flags; |
2921 | 2937 | ||
@@ -2930,7 +2946,7 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass, | |||
2930 | 2946 | ||
2931 | current->lockdep_recursion = 1; | 2947 | current->lockdep_recursion = 1; |
2932 | __lock_acquire(lock, subclass, trylock, read, check, | 2948 | __lock_acquire(lock, subclass, trylock, read, check, |
2933 | irqs_disabled_flags(flags), ip); | 2949 | irqs_disabled_flags(flags), nest_lock, ip); |
2934 | current->lockdep_recursion = 0; | 2950 | current->lockdep_recursion = 0; |
2935 | raw_local_irq_restore(flags); | 2951 | raw_local_irq_restore(flags); |
2936 | } | 2952 | } |