diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 1fd82ff99c658bba23402640aa73bb3497bf2779..c5335df2372f31d478a45322ca99c707705ac74b 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -97,8 +97,6 @@ struct lock_class { * Generation counter, when doing certain classes of graph walking, * to ensure that we check one node only once: */ - unsigned int version; - int name_version; const char *name; diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index ef27f98714c09864822937b34921cbd8edca3035..95932333a48b13cbdd8d0a86abba9fc32745b3fc 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -138,6 +138,9 @@ static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES]; * get freed - this significantly simplifies the debugging code. */ unsigned long nr_lock_classes; +#ifndef CONFIG_DEBUG_LOCKDEP +static +#endif struct lock_class lock_classes[MAX_LOCKDEP_KEYS]; static inline struct lock_class *hlock_class(struct held_lock *hlock) @@ -626,7 +629,8 @@ static int static_obj(void *obj) /* * To make lock name printouts unique, we calculate a unique - * class->name_version generation counter: + * class->name_version generation counter. The caller must hold the graph + * lock. */ static int count_matching_names(struct lock_class *new_class) { @@ -636,7 +640,7 @@ static int count_matching_names(struct lock_class *new_class) if (!new_class->name) return 0; - list_for_each_entry_rcu(class, &all_lock_classes, lock_entry) { + list_for_each_entry(class, &all_lock_classes, lock_entry) { if (new_class->key - new_class->subclass == class->key) return class->name_version; if (class->name && !strcmp(class->name, new_class->name)) @@ -789,7 +793,6 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) class->key = key; class->name = lock->name; class->subclass = subclass; - INIT_LIST_HEAD(&class->lock_entry); INIT_LIST_HEAD(&class->locks_before); INIT_LIST_HEAD(&class->locks_after); class->name_version = count_matching_names(class); @@ -801,7 +804,7 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) /* * Add it to the global list of classes: */ - list_add_tail_rcu(&class->lock_entry, &all_lock_classes); + list_add_tail(&class->lock_entry, &all_lock_classes); if (verbose(class)) { graph_unlock(); @@ -3088,7 +3091,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this, /* * Initialize a lock instance's lock-class mapping info: */ -static void __lockdep_init_map(struct lockdep_map *lock, const char *name, +void lockdep_init_map(struct lockdep_map *lock, const char *name, struct lock_class_key *key, int subclass) { int i; @@ -3144,12 +3147,6 @@ static 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) -{ - __lockdep_init_map(lock, name, key, subclass); -} EXPORT_SYMBOL_GPL(lockdep_init_map); struct lock_class_key __lockdep_no_validate__; @@ -4126,6 +4123,9 @@ void lockdep_reset(void) raw_local_irq_restore(flags); } +/* + * Remove all references to a lock class. The caller must hold the graph lock. + */ static void zap_class(struct lock_class *class) { int i; @@ -4142,7 +4142,7 @@ static void zap_class(struct lock_class *class) * Unhash the class and remove it from the all_lock_classes list: */ hlist_del_rcu(&class->hash_entry); - list_del_rcu(&class->lock_entry); + list_del(&class->lock_entry); RCU_INIT_POINTER(class->key, NULL); RCU_INIT_POINTER(class->name, NULL); @@ -4204,15 +4204,36 @@ void lockdep_free_key_range(void *start, unsigned long size) */ } -void lockdep_reset_lock(struct lockdep_map *lock) +/* + * Check whether any element of the @lock->class_cache[] array refers to a + * registered lock class. The caller must hold either the graph lock or the + * RCU read lock. + */ +static bool lock_class_cache_is_registered(struct lockdep_map *lock) { struct lock_class *class; struct hlist_head *head; - unsigned long flags; int i, j; - int locked; + + for (i = 0; i < CLASSHASH_SIZE; i++) { + head = classhash_table + i; + hlist_for_each_entry_rcu(class, head, hash_entry) { + for (j = 0; j < NR_LOCKDEP_CACHING_CLASSES; j++) + if (lock->class_cache[j] == class) + return true; + } + } + return false; +} + +void lockdep_reset_lock(struct lockdep_map *lock) +{ + struct lock_class *class; + unsigned long flags; + int j, locked; raw_local_irq_save(flags); + locked = graph_lock(); /* * Remove all classes this lock might have: @@ -4229,25 +4250,14 @@ void lockdep_reset_lock(struct lockdep_map *lock) * Debug check: in the end all mapped classes should * be gone. */ - locked = graph_lock(); - for (i = 0; i < CLASSHASH_SIZE; i++) { - head = classhash_table + i; - hlist_for_each_entry_rcu(class, head, hash_entry) { - int match = 0; - - for (j = 0; j < NR_LOCKDEP_CACHING_CLASSES; j++) - match |= class == lock->class_cache[j]; - - if (unlikely(match)) { - if (debug_locks_off_graph_unlock()) { - /* - * We all just reset everything, how did it match? - */ - WARN_ON(1); - } - goto out_restore; - } + if (unlikely(lock_class_cache_is_registered(lock))) { + if (debug_locks_off_graph_unlock()) { + /* + * We all just reset everything, how did it match? + */ + WARN_ON(1); } + goto out_restore; } if (locked) graph_unlock(); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index ac855b2f47746efa80ed91081442626d15169568..db514993565b2274b0b02466dd7ffc03b7e66140 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9533,9 +9533,7 @@ static bool nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle) return false; } - /* - * barrier, pairs with nohz_balance_enter_idle(), ensures ... - */ + /* could be _relaxed() */ flags = atomic_fetch_andnot(NOHZ_KICK_MASK, nohz_flags(this_cpu)); if (!(flags & NOHZ_KICK_MASK)) return false; diff --git a/tools/lib/lockdep/include/liblockdep/common.h b/tools/lib/lockdep/include/liblockdep/common.h index 8862da80995a8f2cc1419ce3c839ab43195ad884..d640a9761f09c87894bbc9fac3f6e24935c523e9 100644 --- a/tools/lib/lockdep/include/liblockdep/common.h +++ b/tools/lib/lockdep/include/liblockdep/common.h @@ -44,6 +44,7 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass, struct lockdep_map *nest_lock, unsigned long ip); void lock_release(struct lockdep_map *lock, int nested, unsigned long ip); +void lockdep_reset_lock(struct lockdep_map *lock); extern void debug_check_no_locks_freed(const void *from, unsigned long len); #define STATIC_LOCKDEP_MAP_INIT(_name, _key) \ diff --git a/tools/lib/lockdep/include/liblockdep/mutex.h b/tools/lib/lockdep/include/liblockdep/mutex.h index a80ac39f966ed3d6c01f3b335494120e0fde999e..2073d4e1f2f08fc8966a9a57fa31a64d7430e823 100644 --- a/tools/lib/lockdep/include/liblockdep/mutex.h +++ b/tools/lib/lockdep/include/liblockdep/mutex.h @@ -54,6 +54,7 @@ static inline int liblockdep_pthread_mutex_trylock(liblockdep_pthread_mutex_t *l static inline int liblockdep_pthread_mutex_destroy(liblockdep_pthread_mutex_t *lock) { + lockdep_reset_lock(&lock->dep_map); return pthread_mutex_destroy(&lock->mutex); } diff --git a/tools/lib/lockdep/include/liblockdep/rwlock.h b/tools/lib/lockdep/include/liblockdep/rwlock.h index a96c3bf0fef121a19fce62b22946e5eb9f39120f..365762e3a1ea4627c8445cf6e9e6acf8ab678caf 100644 --- a/tools/lib/lockdep/include/liblockdep/rwlock.h +++ b/tools/lib/lockdep/include/liblockdep/rwlock.h @@ -60,10 +60,10 @@ static inline int liblockdep_pthread_rwlock_tryrdlock(liblockdep_pthread_rwlock_ return pthread_rwlock_tryrdlock(&lock->rwlock) == 0 ? 1 : 0; } -static inline int liblockdep_pthread_rwlock_trywlock(liblockdep_pthread_rwlock_t *lock) +static inline int liblockdep_pthread_rwlock_trywrlock(liblockdep_pthread_rwlock_t *lock) { lock_acquire(&lock->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_); - return pthread_rwlock_trywlock(&lock->rwlock) == 0 ? 1 : 0; + return pthread_rwlock_trywrlock(&lock->rwlock) == 0 ? 1 : 0; } static inline int liblockdep_rwlock_destroy(liblockdep_pthread_rwlock_t *lock) @@ -79,7 +79,7 @@ static inline int liblockdep_rwlock_destroy(liblockdep_pthread_rwlock_t *lock) #define pthread_rwlock_unlock liblockdep_pthread_rwlock_unlock #define pthread_rwlock_wrlock liblockdep_pthread_rwlock_wrlock #define pthread_rwlock_tryrdlock liblockdep_pthread_rwlock_tryrdlock -#define pthread_rwlock_trywlock liblockdep_pthread_rwlock_trywlock +#define pthread_rwlock_trywrlock liblockdep_pthread_rwlock_trywrlock #define pthread_rwlock_destroy liblockdep_rwlock_destroy #endif diff --git a/tools/lib/lockdep/lockdep.c b/tools/lib/lockdep/lockdep.c index 6002fcf2f9bc3e798422f2b579d6d86ad61f0d61..348a9d0fb766a450e1d5c96d56c435534f1ecb05 100644 --- a/tools/lib/lockdep/lockdep.c +++ b/tools/lib/lockdep/lockdep.c @@ -15,6 +15,11 @@ u32 prandom_u32(void) abort(); } +void print_irqtrace_events(struct task_struct *curr) +{ + abort(); +} + static struct new_utsname *init_utsname(void) { static struct new_utsname n = (struct new_utsname) { diff --git a/tools/lib/lockdep/run_tests.sh b/tools/lib/lockdep/run_tests.sh index 2e570a188f1675588a92096e4673807008ecdd38..c8fbd0306960fb1956c46b58d1d06dba7203e34d 100755 --- a/tools/lib/lockdep/run_tests.sh +++ b/tools/lib/lockdep/run_tests.sh @@ -1,32 +1,47 @@ #! /bin/bash # SPDX-License-Identifier: GPL-2.0 -make &> /dev/null +if ! make >/dev/null; then + echo "Building liblockdep failed." + echo "FAILED!" + exit 1 +fi -for i in `ls tests/*.c`; do +find tests -name '*.c' | sort | while read -r i; do testname=$(basename "$i" .c) - gcc -o tests/$testname -pthread $i liblockdep.a -Iinclude -D__USE_LIBLOCKDEP &> /dev/null echo -ne "$testname... " - if [ $(timeout 1 ./tests/$testname 2>&1 | wc -l) -gt 0 ]; then + if gcc -o "tests/$testname" -pthread "$i" liblockdep.a -Iinclude -D__USE_LIBLOCKDEP && + timeout 1 "tests/$testname" 2>&1 | "tests/${testname}.sh"; then echo "PASSED!" else echo "FAILED!" fi - if [ -f "tests/$testname" ]; then - rm tests/$testname - fi + rm -f "tests/$testname" done -for i in `ls tests/*.c`; do +find tests -name '*.c' | sort | while read -r i; do testname=$(basename "$i" .c) - gcc -o tests/$testname -pthread -Iinclude $i &> /dev/null echo -ne "(PRELOAD) $testname... " - if [ $(timeout 1 ./lockdep ./tests/$testname 2>&1 | wc -l) -gt 0 ]; then + if gcc -o "tests/$testname" -pthread -Iinclude "$i" && + timeout 1 ./lockdep "tests/$testname" 2>&1 | + "tests/${testname}.sh"; then echo "PASSED!" else echo "FAILED!" fi - if [ -f "tests/$testname" ]; then - rm tests/$testname + rm -f "tests/$testname" +done + +find tests -name '*.c' | sort | while read -r i; do + testname=$(basename "$i" .c) + echo -ne "(PRELOAD + Valgrind) $testname... " + if gcc -o "tests/$testname" -pthread -Iinclude "$i" && + { timeout 10 valgrind --read-var-info=yes ./lockdep "./tests/$testname" >& "tests/${testname}.vg.out"; true; } && + "tests/${testname}.sh" < "tests/${testname}.vg.out" && + ! grep -Eq '(^==[0-9]*== (Invalid |Uninitialised ))|Mismatched free|Source and destination overlap| UME ' "tests/${testname}.vg.out"; then + echo "PASSED!" + else + echo "FAILED!" fi + rm -f "tests/$testname" done diff --git a/tools/lib/lockdep/tests/AA.sh b/tools/lib/lockdep/tests/AA.sh new file mode 100644 index 0000000000000000000000000000000000000000..f39b32865074120d9e34093a58bc14a54cc9473e --- /dev/null +++ b/tools/lib/lockdep/tests/AA.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible recursive locking detected' diff --git a/tools/lib/lockdep/tests/ABA.sh b/tools/lib/lockdep/tests/ABA.sh new file mode 100644 index 0000000000000000000000000000000000000000..f39b32865074120d9e34093a58bc14a54cc9473e --- /dev/null +++ b/tools/lib/lockdep/tests/ABA.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible recursive locking detected' diff --git a/tools/lib/lockdep/tests/ABBA.c b/tools/lib/lockdep/tests/ABBA.c index 1460afd33d712c2469982190ea7a08019b3c0295..623313f5472095b302f6a0f3ce787f127ec65ab6 100644 --- a/tools/lib/lockdep/tests/ABBA.c +++ b/tools/lib/lockdep/tests/ABBA.c @@ -11,4 +11,7 @@ void main(void) LOCK_UNLOCK_2(a, b); LOCK_UNLOCK_2(b, a); + + pthread_mutex_destroy(&b); + pthread_mutex_destroy(&a); } diff --git a/tools/lib/lockdep/tests/ABBA.sh b/tools/lib/lockdep/tests/ABBA.sh new file mode 100644 index 0000000000000000000000000000000000000000..fc31c607a5a8bf1c971a651bd14b055000250e3e --- /dev/null +++ b/tools/lib/lockdep/tests/ABBA.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible circular locking dependency detected' diff --git a/tools/lib/lockdep/tests/ABBA_2threads.sh b/tools/lib/lockdep/tests/ABBA_2threads.sh new file mode 100644 index 0000000000000000000000000000000000000000..fc31c607a5a8bf1c971a651bd14b055000250e3e --- /dev/null +++ b/tools/lib/lockdep/tests/ABBA_2threads.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible circular locking dependency detected' diff --git a/tools/lib/lockdep/tests/ABBCCA.c b/tools/lib/lockdep/tests/ABBCCA.c index a54c1b2af118242a12345f0d3dc5a205bf36e505..48446129d49662d004c48c6b7cade613b9d4317c 100644 --- a/tools/lib/lockdep/tests/ABBCCA.c +++ b/tools/lib/lockdep/tests/ABBCCA.c @@ -13,4 +13,8 @@ void main(void) LOCK_UNLOCK_2(a, b); LOCK_UNLOCK_2(b, c); LOCK_UNLOCK_2(c, a); + + pthread_mutex_destroy(&c); + pthread_mutex_destroy(&b); + pthread_mutex_destroy(&a); } diff --git a/tools/lib/lockdep/tests/ABBCCA.sh b/tools/lib/lockdep/tests/ABBCCA.sh new file mode 100644 index 0000000000000000000000000000000000000000..fc31c607a5a8bf1c971a651bd14b055000250e3e --- /dev/null +++ b/tools/lib/lockdep/tests/ABBCCA.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible circular locking dependency detected' diff --git a/tools/lib/lockdep/tests/ABBCCDDA.c b/tools/lib/lockdep/tests/ABBCCDDA.c index aa5d194e88694ec95f5451083f62ed71ff842b00..3570bf7b38042fe3a96d8787cbe9d34b84fb73cb 100644 --- a/tools/lib/lockdep/tests/ABBCCDDA.c +++ b/tools/lib/lockdep/tests/ABBCCDDA.c @@ -15,4 +15,9 @@ void main(void) LOCK_UNLOCK_2(b, c); LOCK_UNLOCK_2(c, d); LOCK_UNLOCK_2(d, a); + + pthread_mutex_destroy(&d); + pthread_mutex_destroy(&c); + pthread_mutex_destroy(&b); + pthread_mutex_destroy(&a); } diff --git a/tools/lib/lockdep/tests/ABBCCDDA.sh b/tools/lib/lockdep/tests/ABBCCDDA.sh new file mode 100644 index 0000000000000000000000000000000000000000..fc31c607a5a8bf1c971a651bd14b055000250e3e --- /dev/null +++ b/tools/lib/lockdep/tests/ABBCCDDA.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible circular locking dependency detected' diff --git a/tools/lib/lockdep/tests/ABCABC.c b/tools/lib/lockdep/tests/ABCABC.c index b54a08e60416f087a5fa60a0475d2bd6cdf70f6a..a1c4659894cd6aee10e3b958686c2a8fa00b8e81 100644 --- a/tools/lib/lockdep/tests/ABCABC.c +++ b/tools/lib/lockdep/tests/ABCABC.c @@ -13,4 +13,8 @@ void main(void) LOCK_UNLOCK_2(a, b); LOCK_UNLOCK_2(c, a); LOCK_UNLOCK_2(b, c); + + pthread_mutex_destroy(&c); + pthread_mutex_destroy(&b); + pthread_mutex_destroy(&a); } diff --git a/tools/lib/lockdep/tests/ABCABC.sh b/tools/lib/lockdep/tests/ABCABC.sh new file mode 100644 index 0000000000000000000000000000000000000000..fc31c607a5a8bf1c971a651bd14b055000250e3e --- /dev/null +++ b/tools/lib/lockdep/tests/ABCABC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible circular locking dependency detected' diff --git a/tools/lib/lockdep/tests/ABCDBCDA.c b/tools/lib/lockdep/tests/ABCDBCDA.c index a56742250d86f10eedd065dc6b56b056197931a0..335af1c90ab53827fcb29f5e7a4ff9e83a5facf7 100644 --- a/tools/lib/lockdep/tests/ABCDBCDA.c +++ b/tools/lib/lockdep/tests/ABCDBCDA.c @@ -15,4 +15,9 @@ void main(void) LOCK_UNLOCK_2(c, d); LOCK_UNLOCK_2(b, c); LOCK_UNLOCK_2(d, a); + + pthread_mutex_destroy(&d); + pthread_mutex_destroy(&c); + pthread_mutex_destroy(&b); + pthread_mutex_destroy(&a); } diff --git a/tools/lib/lockdep/tests/ABCDBCDA.sh b/tools/lib/lockdep/tests/ABCDBCDA.sh new file mode 100644 index 0000000000000000000000000000000000000000..fc31c607a5a8bf1c971a651bd14b055000250e3e --- /dev/null +++ b/tools/lib/lockdep/tests/ABCDBCDA.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible circular locking dependency detected' diff --git a/tools/lib/lockdep/tests/ABCDBDDA.c b/tools/lib/lockdep/tests/ABCDBDDA.c index 238a3353f3c353e9df9f40182fa20a432386bbdb..3c59728630493fc589834ab6714f25f76d3fc199 100644 --- a/tools/lib/lockdep/tests/ABCDBDDA.c +++ b/tools/lib/lockdep/tests/ABCDBDDA.c @@ -15,4 +15,9 @@ void main(void) LOCK_UNLOCK_2(c, d); LOCK_UNLOCK_2(b, d); LOCK_UNLOCK_2(d, a); + + pthread_mutex_destroy(&d); + pthread_mutex_destroy(&c); + pthread_mutex_destroy(&b); + pthread_mutex_destroy(&a); } diff --git a/tools/lib/lockdep/tests/ABCDBDDA.sh b/tools/lib/lockdep/tests/ABCDBDDA.sh new file mode 100644 index 0000000000000000000000000000000000000000..fc31c607a5a8bf1c971a651bd14b055000250e3e --- /dev/null +++ b/tools/lib/lockdep/tests/ABCDBDDA.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible circular locking dependency detected' diff --git a/tools/lib/lockdep/tests/WW.sh b/tools/lib/lockdep/tests/WW.sh new file mode 100644 index 0000000000000000000000000000000000000000..f39b32865074120d9e34093a58bc14a54cc9473e --- /dev/null +++ b/tools/lib/lockdep/tests/WW.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: possible recursive locking detected' diff --git a/tools/lib/lockdep/tests/unlock_balance.c b/tools/lib/lockdep/tests/unlock_balance.c index 34cf32f689de00723cb0c7c55ccc29a77454c010..dba25064b50a83d4b0e5a8b1d6140b0c81c22817 100644 --- a/tools/lib/lockdep/tests/unlock_balance.c +++ b/tools/lib/lockdep/tests/unlock_balance.c @@ -10,4 +10,6 @@ void main(void) pthread_mutex_lock(&a); pthread_mutex_unlock(&a); pthread_mutex_unlock(&a); + + pthread_mutex_destroy(&a); } diff --git a/tools/lib/lockdep/tests/unlock_balance.sh b/tools/lib/lockdep/tests/unlock_balance.sh new file mode 100644 index 0000000000000000000000000000000000000000..c6e3952303fe3816429fc51c602dd1f44683ba59 --- /dev/null +++ b/tools/lib/lockdep/tests/unlock_balance.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep -q 'WARNING: bad unlock balance detected'