Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Peter Zijlstra and committed by
Ingo Molnar
7531e2f3 4f3e7524

+40 -22
+18 -16
include/linux/lockdep.h
··· 211 211 u64 prev_chain_key; 212 212 unsigned long acquire_ip; 213 213 struct lockdep_map *instance; 214 + struct lockdep_map *nest_lock; 214 215 #ifdef CONFIG_LOCK_STAT 215 216 u64 waittime_stamp; 216 217 u64 holdtime_stamp; ··· 298 297 * 2: full validation 299 298 */ 300 299 extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass, 301 - int trylock, int read, int check, unsigned long ip); 300 + int trylock, int read, int check, 301 + struct lockdep_map *nest_lock, unsigned long ip); 302 302 303 303 extern void lock_release(struct lockdep_map *lock, int nested, 304 304 unsigned long ip); ··· 321 319 { 322 320 } 323 321 324 - # define lock_acquire(l, s, t, r, c, i) do { } while (0) 322 + # define lock_acquire(l, s, t, r, c, n, i) do { } while (0) 325 323 # define lock_release(l, n, i) do { } while (0) 326 324 # define lock_set_subclass(l, s, i) do { } while (0) 327 325 # define lockdep_init() do { } while (0) ··· 409 407 410 408 #ifdef CONFIG_DEBUG_LOCK_ALLOC 411 409 # ifdef CONFIG_PROVE_LOCKING 412 - # define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) 410 + # define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) 413 411 # else 414 - # define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) 412 + # define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) 415 413 # endif 416 414 # define spin_release(l, n, i) lock_release(l, n, i) 417 415 #else ··· 421 419 422 420 #ifdef CONFIG_DEBUG_LOCK_ALLOC 423 421 # ifdef CONFIG_PROVE_LOCKING 424 - # define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) 425 - # define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 2, i) 422 + # define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) 423 + # define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 2, NULL, i) 426 424 # else 427 - # define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) 428 - # define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 1, i) 425 + # define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) 426 + # define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 1, NULL, i) 429 427 # endif 430 428 # define rwlock_release(l, n, i) lock_release(l, n, i) 431 429 #else ··· 436 434 437 435 #ifdef CONFIG_DEBUG_LOCK_ALLOC 438 436 # ifdef CONFIG_PROVE_LOCKING 439 - # define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) 437 + # define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) 440 438 # else 441 - # define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) 439 + # define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) 442 440 # endif 443 441 # define mutex_release(l, n, i) lock_release(l, n, i) 444 442 #else ··· 448 446 449 447 #ifdef CONFIG_DEBUG_LOCK_ALLOC 450 448 # ifdef CONFIG_PROVE_LOCKING 451 - # define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) 452 - # define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 2, i) 449 + # define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) 450 + # define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 2, NULL, i) 453 451 # else 454 - # define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) 455 - # define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 1, i) 452 + # define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) 453 + # define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 1, NULL, i) 456 454 # endif 457 455 # define rwsem_release(l, n, i) lock_release(l, n, i) 458 456 #else ··· 463 461 464 462 #ifdef CONFIG_DEBUG_LOCK_ALLOC 465 463 # ifdef CONFIG_PROVE_LOCKING 466 - # define map_acquire(l) lock_acquire(l, 0, 0, 0, 2, _THIS_IP_) 464 + # define map_acquire(l) lock_acquire(l, 0, 0, 0, 2, NULL, _THIS_IP_) 467 465 # else 468 - # define map_acquire(l) lock_acquire(l, 0, 0, 0, 1, _THIS_IP_) 466 + # define map_acquire(l) lock_acquire(l, 0, 0, 0, 1, NULL, _THIS_IP_) 469 467 # endif 470 468 # define map_release(l) lock_release(l, 1, _THIS_IP_) 471 469 #else
+1 -1
include/linux/rcuclassic.h
··· 117 117 #ifdef CONFIG_DEBUG_LOCK_ALLOC 118 118 extern struct lockdep_map rcu_lock_map; 119 119 # define rcu_read_acquire() \ 120 - lock_acquire(&rcu_lock_map, 0, 0, 2, 1, _THIS_IP_) 120 + lock_acquire(&rcu_lock_map, 0, 0, 2, 1, NULL, _THIS_IP_) 121 121 # define rcu_read_release() lock_release(&rcu_lock_map, 1, _THIS_IP_) 122 122 #else 123 123 # define rcu_read_acquire() do { } while (0)
+21 -5
kernel/lockdep.c
··· 1372 1372 struct lockdep_map *next_instance, int read) 1373 1373 { 1374 1374 struct held_lock *prev; 1375 + struct held_lock *nest = NULL; 1375 1376 int i; 1376 1377 1377 1378 for (i = 0; i < curr->lockdep_depth; i++) { 1378 1379 prev = curr->held_locks + i; 1380 + 1381 + if (prev->instance == next->nest_lock) 1382 + nest = prev; 1383 + 1379 1384 if (hlock_class(prev) != hlock_class(next)) 1380 1385 continue; 1386 + 1381 1387 /* 1382 1388 * Allow read-after-read recursion of the same 1383 1389 * lock class (i.e. read_lock(lock)+read_lock(lock)): 1384 1390 */ 1385 1391 if ((read == 2) && prev->read) 1386 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 1401 return print_deadlock_bug(curr, prev, next); 1388 1402 } 1389 1403 return 1; ··· 2521 2507 */ 2522 2508 static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, 2523 2509 int trylock, int read, int check, int hardirqs_off, 2524 - unsigned long ip) 2510 + struct lockdep_map *nest_lock, unsigned long ip) 2525 2511 { 2526 2512 struct task_struct *curr = current; 2527 2513 struct lock_class *class = NULL; ··· 2580 2566 hlock->class_idx = class - lock_classes + 1; 2581 2567 hlock->acquire_ip = ip; 2582 2568 hlock->instance = lock; 2569 + hlock->nest_lock = nest_lock; 2583 2570 hlock->trylock = trylock; 2584 2571 hlock->read = read; 2585 2572 hlock->check = check; ··· 2732 2717 if (!__lock_acquire(hlock->instance, 2733 2718 hlock_class(hlock)->subclass, hlock->trylock, 2734 2719 hlock->read, hlock->check, hlock->hardirqs_off, 2735 - hlock->acquire_ip)) 2720 + hlock->nest_lock, hlock->acquire_ip)) 2736 2721 return 0; 2737 2722 } 2738 2723 ··· 2793 2778 if (!__lock_acquire(hlock->instance, 2794 2779 hlock_class(hlock)->subclass, hlock->trylock, 2795 2780 hlock->read, hlock->check, hlock->hardirqs_off, 2796 - hlock->acquire_ip)) 2781 + hlock->nest_lock, hlock->acquire_ip)) 2797 2782 return 0; 2798 2783 } 2799 2784 ··· 2930 2915 * and also avoid lockdep recursion: 2931 2916 */ 2932 2917 void lock_acquire(struct lockdep_map *lock, unsigned int subclass, 2933 - int trylock, int read, int check, unsigned long ip) 2918 + int trylock, int read, int check, 2919 + struct lockdep_map *nest_lock, unsigned long ip) 2934 2920 { 2935 2921 unsigned long flags; 2936 2922 ··· 2946 2930 2947 2931 current->lockdep_recursion = 1; 2948 2932 __lock_acquire(lock, subclass, trylock, read, check, 2949 - irqs_disabled_flags(flags), ip); 2933 + irqs_disabled_flags(flags), nest_lock, ip); 2950 2934 current->lockdep_recursion = 0; 2951 2935 raw_local_irq_restore(flags); 2952 2936 }