From 71a076f4a61a6c779794ad286f356b39725edc3b Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Tue, 24 Nov 2020 12:02:09 +0100 Subject: [PATCH 01/24] kcsan: Rewrite kcsan_prandom_u32_max() without prandom_u32_state() Rewrite kcsan_prandom_u32_max() to not depend on code that might be instrumented, removing any dependency on lib/random32.c. The rewrite implements a simple linear congruential generator, that is sufficient for our purposes (for udelay() and skip_watch counter randomness). The initial motivation for this was to allow enabling KCSAN for kernel/sched (remove KCSAN_SANITIZE := n from kernel/sched/Makefile), with CONFIG_DEBUG_PREEMPT=y. Without this change, we could observe recursion: check_access() [via instrumentation] kcsan_setup_watchpoint() reset_kcsan_skip() kcsan_prandom_u32_max() get_cpu_var() preempt_disable() preempt_count_add() [in kernel/sched/core.c] check_access() [via instrumentation] Note, while this currently does not affect an unmodified kernel, it'd be good to keep a KCSAN kernel working when KCSAN_SANITIZE := n is removed from kernel/sched/Makefile to permit testing scheduler code with KCSAN if desired. Fixes: cd290ec24633 ("kcsan: Use tracing-safe version of prandom") Signed-off-by: Marco Elver Signed-off-by: Paul E. McKenney --- kernel/kcsan/core.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c index 3994a217bde76..3bf98db9c702d 100644 --- a/kernel/kcsan/core.c +++ b/kernel/kcsan/core.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -101,7 +100,7 @@ static atomic_long_t watchpoints[CONFIG_KCSAN_NUM_WATCHPOINTS + NUM_SLOTS-1]; static DEFINE_PER_CPU(long, kcsan_skip); /* For kcsan_prandom_u32_max(). */ -static DEFINE_PER_CPU(struct rnd_state, kcsan_rand_state); +static DEFINE_PER_CPU(u32, kcsan_rand_state); static __always_inline atomic_long_t *find_watchpoint(unsigned long addr, size_t size, @@ -275,20 +274,17 @@ should_watch(const volatile void *ptr, size_t size, int type, struct kcsan_ctx * } /* - * Returns a pseudo-random number in interval [0, ep_ro). See prandom_u32_max() - * for more details. - * - * The open-coded version here is using only safe primitives for all contexts - * where we can have KCSAN instrumentation. In particular, we cannot use - * prandom_u32() directly, as its tracepoint could cause recursion. + * Returns a pseudo-random number in interval [0, ep_ro). Simple linear + * congruential generator, using constants from "Numerical Recipes". */ static u32 kcsan_prandom_u32_max(u32 ep_ro) { - struct rnd_state *state = &get_cpu_var(kcsan_rand_state); - const u32 res = prandom_u32_state(state); + u32 state = this_cpu_read(kcsan_rand_state); + + state = 1664525 * state + 1013904223; + this_cpu_write(kcsan_rand_state, state); - put_cpu_var(kcsan_rand_state); - return (u32)(((u64) res * ep_ro) >> 32); + return state % ep_ro; } static inline void reset_kcsan_skip(void) @@ -639,10 +635,14 @@ static __always_inline void check_access(const volatile void *ptr, size_t size, void __init kcsan_init(void) { + int cpu; + BUG_ON(!in_task()); kcsan_debugfs_init(); - prandom_seed_full_state(&kcsan_rand_state); + + for_each_possible_cpu(cpu) + per_cpu(kcsan_rand_state, cpu) = (u32)get_cycles(); /* * We are in the init task, and no other tasks should be running; From 567a83e6872c15b2080d1d03de71868cd0ae7cea Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Tue, 24 Nov 2020 12:02:10 +0100 Subject: [PATCH 02/24] random32: Re-enable KCSAN instrumentation Re-enable KCSAN instrumentation, now that KCSAN no longer relies on code in lib/random32.c. Signed-off-by: Marco Elver Signed-off-by: Paul E. McKenney --- lib/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index afeff05fa8c57..dc09208b15e8a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,9 +27,6 @@ KASAN_SANITIZE_string.o := n CFLAGS_string.o += -fno-stack-protector endif -# Used by KCSAN while enabled, avoid recursion. -KCSAN_SANITIZE_random32.o := n - lib-y := ctype.o string.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o timerqueue.o xarray.o \ idr.o extable.o sha1.o irq_regs.o argv_split.o \ From 8881e7a774a8d14088d6c6fde8730660f74a3642 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 6 Nov 2020 09:58:01 -0800 Subject: [PATCH 03/24] tools/memory-model: Tie acquire loads to reads-from This commit explicitly makes the connection between acquire loads and the reads-from relation. It also adds an entry for happens-before, and refers to the corresponding section of explanation.txt. Reported-by: Boqun Feng Signed-off-by: Paul E. McKenney --- tools/memory-model/Documentation/glossary.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/memory-model/Documentation/glossary.txt b/tools/memory-model/Documentation/glossary.txt index 79acb75d56eaa..b2da6365be63c 100644 --- a/tools/memory-model/Documentation/glossary.txt +++ b/tools/memory-model/Documentation/glossary.txt @@ -33,10 +33,11 @@ Acquire: With respect to a lock, acquiring that lock, for example, acquire loads. When an acquire load returns the value stored by a release store - to that same variable, then all operations preceding that store - happen before any operations following that load acquire. + to that same variable, (in other words, the acquire load "reads + from" the release store), then all operations preceding that + store "happen before" any operations following that load acquire. - See also "Relaxed" and "Release". + See also "Happens-Before", "Reads-From", "Relaxed", and "Release". Coherence (co): When one CPU's store to a given variable overwrites either the value from another CPU's store or some later value, @@ -119,6 +120,11 @@ Fully Ordered: An operation such as smp_mb() that orders all of that orders all of its CPU's prior accesses, itself, and all of its CPU's subsequent accesses. +Happens-Before (hb): A relation between two accesses in which LKMM + guarantees the first access precedes the second. For more + detail, please see the "THE HAPPENS-BEFORE RELATION: hb" + section of explanation.txt. + Marked Access: An access to a variable that uses an special function or macro such as "r1 = READ_ONCE(x)" or "smp_store_release(&a, 1)". From 5c587f9b9c35850f9da3c425f98dc53ab1cde9f3 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Sat, 28 Nov 2020 08:43:45 +0900 Subject: [PATCH 04/24] tools/memory-model: Remove redundant initialization in litmus tests This is a revert of commit 1947bfcf81a9 ("tools/memory-model: Add types to litmus tests") with conflict resolutions. klitmus7 [1] is aware of default types of "int" and "int*". It accepts litmus tests for herd7 without extra type info unless non-"int" variables are referenced by an "exists", "locations", or "filter" directive. [1]: Tested with klitmus7 versions 7.49 or later. Suggested-by: Paul E. McKenney Signed-off-by: Akira Yokosawa Signed-off-by: Paul E. McKenney --- .../memory-model/litmus-tests/CoRR+poonceonce+Once.litmus | 4 +--- .../memory-model/litmus-tests/CoRW+poonceonce+Once.litmus | 4 +--- .../memory-model/litmus-tests/CoWR+poonceonce+Once.litmus | 4 +--- tools/memory-model/litmus-tests/CoWW+poonceonce.litmus | 4 +--- .../litmus-tests/IRIW+fencembonceonces+OnceOnce.litmus | 5 +---- .../litmus-tests/IRIW+poonceonces+OnceOnce.litmus | 5 +---- .../ISA2+pooncelock+pooncelock+pombonce.litmus | 7 +------ tools/memory-model/litmus-tests/ISA2+poonceonces.litmus | 6 +----- ...SA2+pooncerelease+poacquirerelease+poacquireonce.litmus | 6 +----- .../litmus-tests/LB+fencembonceonce+ctrlonceonce.litmus | 5 +---- .../litmus-tests/LB+poacquireonce+pooncerelease.litmus | 5 +---- tools/memory-model/litmus-tests/LB+poonceonces.litmus | 5 +---- .../MP+fencewmbonceonce+fencermbonceonce.litmus | 5 +---- .../litmus-tests/MP+onceassign+derefonce.litmus | 4 +--- .../litmus-tests/MP+polockmbonce+poacquiresilsil.litmus | 5 +---- .../litmus-tests/MP+polockonce+poacquiresilsil.litmus | 5 +---- tools/memory-model/litmus-tests/MP+polocks.litmus | 6 +----- tools/memory-model/litmus-tests/MP+poonceonces.litmus | 5 +---- .../litmus-tests/MP+pooncerelease+poacquireonce.litmus | 5 +---- tools/memory-model/litmus-tests/MP+porevlocks.litmus | 6 +----- tools/memory-model/litmus-tests/R+fencembonceonces.litmus | 5 +---- tools/memory-model/litmus-tests/R+poonceonces.litmus | 5 +---- .../litmus-tests/S+fencewmbonceonce+poacquireonce.litmus | 5 +---- tools/memory-model/litmus-tests/S+poonceonces.litmus | 5 +---- tools/memory-model/litmus-tests/SB+fencembonceonces.litmus | 5 +---- tools/memory-model/litmus-tests/SB+poonceonces.litmus | 5 +---- .../litmus-tests/SB+rfionceonce-poonceonces.litmus | 5 +---- .../memory-model/litmus-tests/WRC+poonceonces+Once.litmus | 5 +---- .../WRC+pooncerelease+fencermbonceonce+Once.litmus | 5 +---- .../Z6.0+pooncelock+poonceLock+pombonce.litmus | 7 +------ .../Z6.0+pooncelock+pooncelock+pombonce.litmus | 7 +------ ...0+pooncerelease+poacquirerelease+fencembonceonce.litmus | 6 +----- 32 files changed, 32 insertions(+), 134 deletions(-) diff --git a/tools/memory-model/litmus-tests/CoRR+poonceonce+Once.litmus b/tools/memory-model/litmus-tests/CoRR+poonceonce+Once.litmus index 772544f03fb5f..967f9f2a6226b 100644 --- a/tools/memory-model/litmus-tests/CoRR+poonceonce+Once.litmus +++ b/tools/memory-model/litmus-tests/CoRR+poonceonce+Once.litmus @@ -7,9 +7,7 @@ C CoRR+poonceonce+Once * reads from the same variable are ordered. *) -{ - int x; -} +{} P0(int *x) { diff --git a/tools/memory-model/litmus-tests/CoRW+poonceonce+Once.litmus b/tools/memory-model/litmus-tests/CoRW+poonceonce+Once.litmus index 5faae98f7ffb3..4635739f3974d 100644 --- a/tools/memory-model/litmus-tests/CoRW+poonceonce+Once.litmus +++ b/tools/memory-model/litmus-tests/CoRW+poonceonce+Once.litmus @@ -7,9 +7,7 @@ C CoRW+poonceonce+Once * a given variable and a later write to that same variable are ordered. *) -{ - int x; -} +{} P0(int *x) { diff --git a/tools/memory-model/litmus-tests/CoWR+poonceonce+Once.litmus b/tools/memory-model/litmus-tests/CoWR+poonceonce+Once.litmus index 77c9cc9f8dc66..bb068c92d8da2 100644 --- a/tools/memory-model/litmus-tests/CoWR+poonceonce+Once.litmus +++ b/tools/memory-model/litmus-tests/CoWR+poonceonce+Once.litmus @@ -7,9 +7,7 @@ C CoWR+poonceonce+Once * given variable and a later read from that same variable are ordered. *) -{ - int x; -} +{} P0(int *x) { diff --git a/tools/memory-model/litmus-tests/CoWW+poonceonce.litmus b/tools/memory-model/litmus-tests/CoWW+poonceonce.litmus index 85ef746f511a7..0d9f0a9587996 100644 --- a/tools/memory-model/litmus-tests/CoWW+poonceonce.litmus +++ b/tools/memory-model/litmus-tests/CoWW+poonceonce.litmus @@ -7,9 +7,7 @@ C CoWW+poonceonce * writes to the same variable are ordered. *) -{ - int x; -} +{} P0(int *x) { diff --git a/tools/memory-model/litmus-tests/IRIW+fencembonceonces+OnceOnce.litmus b/tools/memory-model/litmus-tests/IRIW+fencembonceonces+OnceOnce.litmus index 87aa900125ab2..e729d2776e89a 100644 --- a/tools/memory-model/litmus-tests/IRIW+fencembonceonces+OnceOnce.litmus +++ b/tools/memory-model/litmus-tests/IRIW+fencembonceonces+OnceOnce.litmus @@ -10,10 +10,7 @@ C IRIW+fencembonceonces+OnceOnce * process? This litmus test exercises LKMM's "propagation" rule. *) -{ - int x; - int y; -} +{} P0(int *x) { diff --git a/tools/memory-model/litmus-tests/IRIW+poonceonces+OnceOnce.litmus b/tools/memory-model/litmus-tests/IRIW+poonceonces+OnceOnce.litmus index f84022dca5551..4b54dd6a6cd94 100644 --- a/tools/memory-model/litmus-tests/IRIW+poonceonces+OnceOnce.litmus +++ b/tools/memory-model/litmus-tests/IRIW+poonceonces+OnceOnce.litmus @@ -10,10 +10,7 @@ C IRIW+poonceonces+OnceOnce * different process? *) -{ - int x; - int y; -} +{} P0(int *x) { diff --git a/tools/memory-model/litmus-tests/ISA2+pooncelock+pooncelock+pombonce.litmus b/tools/memory-model/litmus-tests/ISA2+pooncelock+pooncelock+pombonce.litmus index 398f624daa771..094d58df77896 100644 --- a/tools/memory-model/litmus-tests/ISA2+pooncelock+pooncelock+pombonce.litmus +++ b/tools/memory-model/litmus-tests/ISA2+pooncelock+pooncelock+pombonce.litmus @@ -7,12 +7,7 @@ C ISA2+pooncelock+pooncelock+pombonce * (in P0() and P1()) is visible to external process P2(). *) -{ - spinlock_t mylock; - int x; - int y; - int z; -} +{} P0(int *x, int *y, spinlock_t *mylock) { diff --git a/tools/memory-model/litmus-tests/ISA2+poonceonces.litmus b/tools/memory-model/litmus-tests/ISA2+poonceonces.litmus index 212a432ba16ba..b321aa6f4ea52 100644 --- a/tools/memory-model/litmus-tests/ISA2+poonceonces.litmus +++ b/tools/memory-model/litmus-tests/ISA2+poonceonces.litmus @@ -9,11 +9,7 @@ C ISA2+poonceonces * of the smp_load_acquire() invocations are replaced by READ_ONCE()? *) -{ - int x; - int y; - int z; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/ISA2+pooncerelease+poacquirerelease+poacquireonce.litmus b/tools/memory-model/litmus-tests/ISA2+pooncerelease+poacquirerelease+poacquireonce.litmus index 7afd85672ccde..025b0462ec9bc 100644 --- a/tools/memory-model/litmus-tests/ISA2+pooncerelease+poacquirerelease+poacquireonce.litmus +++ b/tools/memory-model/litmus-tests/ISA2+pooncerelease+poacquirerelease+poacquireonce.litmus @@ -11,11 +11,7 @@ C ISA2+pooncerelease+poacquirerelease+poacquireonce * (AKA non-rf) link, so release-acquire is all that is needed. *) -{ - int x; - int y; - int z; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/LB+fencembonceonce+ctrlonceonce.litmus b/tools/memory-model/litmus-tests/LB+fencembonceonce+ctrlonceonce.litmus index c8a93c7ee556a..4727f5aaf03b0 100644 --- a/tools/memory-model/litmus-tests/LB+fencembonceonce+ctrlonceonce.litmus +++ b/tools/memory-model/litmus-tests/LB+fencembonceonce+ctrlonceonce.litmus @@ -11,10 +11,7 @@ C LB+fencembonceonce+ctrlonceonce * another control dependency and order would still be maintained.) *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/LB+poacquireonce+pooncerelease.litmus b/tools/memory-model/litmus-tests/LB+poacquireonce+pooncerelease.litmus index 2fa029568fa1c..07b9904b0e49f 100644 --- a/tools/memory-model/litmus-tests/LB+poacquireonce+pooncerelease.litmus +++ b/tools/memory-model/litmus-tests/LB+poacquireonce+pooncerelease.litmus @@ -8,10 +8,7 @@ C LB+poacquireonce+pooncerelease * to the other? *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/LB+poonceonces.litmus b/tools/memory-model/litmus-tests/LB+poonceonces.litmus index 2107306e8625b..74c49cb3c37bf 100644 --- a/tools/memory-model/litmus-tests/LB+poonceonces.litmus +++ b/tools/memory-model/litmus-tests/LB+poonceonces.litmus @@ -7,10 +7,7 @@ C LB+poonceonces * be prevented even with no explicit ordering? *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/MP+fencewmbonceonce+fencermbonceonce.litmus b/tools/memory-model/litmus-tests/MP+fencewmbonceonce+fencermbonceonce.litmus index c5c168d929737..f8ca1229857ad 100644 --- a/tools/memory-model/litmus-tests/MP+fencewmbonceonce+fencermbonceonce.litmus +++ b/tools/memory-model/litmus-tests/MP+fencewmbonceonce+fencermbonceonce.litmus @@ -8,10 +8,7 @@ C MP+fencewmbonceonce+fencermbonceonce * is usually better to use smp_store_release() and smp_load_acquire(). *) -{ - int buf; - int flag; -} +{} P0(int *buf, int *flag) // Producer { diff --git a/tools/memory-model/litmus-tests/MP+onceassign+derefonce.litmus b/tools/memory-model/litmus-tests/MP+onceassign+derefonce.litmus index 20ff62649f1ee..d84160b9c1ae0 100644 --- a/tools/memory-model/litmus-tests/MP+onceassign+derefonce.litmus +++ b/tools/memory-model/litmus-tests/MP+onceassign+derefonce.litmus @@ -10,9 +10,7 @@ C MP+onceassign+derefonce *) { - int *p=y; - int x; - int y=0; +p=y; } P0(int *x, int **p) // Producer diff --git a/tools/memory-model/litmus-tests/MP+polockmbonce+poacquiresilsil.litmus b/tools/memory-model/litmus-tests/MP+polockmbonce+poacquiresilsil.litmus index 153917ad5dc95..ba91cc63e1487 100644 --- a/tools/memory-model/litmus-tests/MP+polockmbonce+poacquiresilsil.litmus +++ b/tools/memory-model/litmus-tests/MP+polockmbonce+poacquiresilsil.litmus @@ -10,10 +10,7 @@ C MP+polockmbonce+poacquiresilsil * executed before the lock was acquired (loosely speaking). *) -{ - spinlock_t lo; - int x; -} +{} P0(spinlock_t *lo, int *x) // Producer { diff --git a/tools/memory-model/litmus-tests/MP+polockonce+poacquiresilsil.litmus b/tools/memory-model/litmus-tests/MP+polockonce+poacquiresilsil.litmus index aad64397bb8cd..a5ea3ed8f52eb 100644 --- a/tools/memory-model/litmus-tests/MP+polockonce+poacquiresilsil.litmus +++ b/tools/memory-model/litmus-tests/MP+polockonce+poacquiresilsil.litmus @@ -10,10 +10,7 @@ C MP+polockonce+poacquiresilsil * speaking). *) -{ - spinlock_t lo; - int x; -} +{} P0(spinlock_t *lo, int *x) // Producer { diff --git a/tools/memory-model/litmus-tests/MP+polocks.litmus b/tools/memory-model/litmus-tests/MP+polocks.litmus index 21cbca6f3be4c..e6af05f70069f 100644 --- a/tools/memory-model/litmus-tests/MP+polocks.litmus +++ b/tools/memory-model/litmus-tests/MP+polocks.litmus @@ -11,11 +11,7 @@ C MP+polocks * to see all prior accesses by those other CPUs. *) -{ - spinlock_t mylock; - int buf; - int flag; -} +{} P0(int *buf, int *flag, spinlock_t *mylock) // Producer { diff --git a/tools/memory-model/litmus-tests/MP+poonceonces.litmus b/tools/memory-model/litmus-tests/MP+poonceonces.litmus index 9f9769d647c7b..ba9c99c6cf65d 100644 --- a/tools/memory-model/litmus-tests/MP+poonceonces.litmus +++ b/tools/memory-model/litmus-tests/MP+poonceonces.litmus @@ -7,10 +7,7 @@ C MP+poonceonces * no ordering at all? *) -{ - int buf; - int flag; -} +{} P0(int *buf, int *flag) // Producer { diff --git a/tools/memory-model/litmus-tests/MP+pooncerelease+poacquireonce.litmus b/tools/memory-model/litmus-tests/MP+pooncerelease+poacquireonce.litmus index cbe28e7334437..f174bfe61702c 100644 --- a/tools/memory-model/litmus-tests/MP+pooncerelease+poacquireonce.litmus +++ b/tools/memory-model/litmus-tests/MP+pooncerelease+poacquireonce.litmus @@ -8,10 +8,7 @@ C MP+pooncerelease+poacquireonce * pattern. *) -{ - int buf; - int flag; -} +{} P0(int *buf, int *flag) // Producer { diff --git a/tools/memory-model/litmus-tests/MP+porevlocks.litmus b/tools/memory-model/litmus-tests/MP+porevlocks.litmus index 012041bd4feb3..b9599141160e6 100644 --- a/tools/memory-model/litmus-tests/MP+porevlocks.litmus +++ b/tools/memory-model/litmus-tests/MP+porevlocks.litmus @@ -11,11 +11,7 @@ C MP+porevlocks * see all prior accesses by those other CPUs. *) -{ - spinlock_t mylock; - int buf; - int flag; -} +{} P0(int *buf, int *flag, spinlock_t *mylock) // Consumer { diff --git a/tools/memory-model/litmus-tests/R+fencembonceonces.litmus b/tools/memory-model/litmus-tests/R+fencembonceonces.litmus index af9463b39b4a5..222a0b850b4a5 100644 --- a/tools/memory-model/litmus-tests/R+fencembonceonces.litmus +++ b/tools/memory-model/litmus-tests/R+fencembonceonces.litmus @@ -9,10 +9,7 @@ C R+fencembonceonces * cause the resulting test to be allowed. *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/R+poonceonces.litmus b/tools/memory-model/litmus-tests/R+poonceonces.litmus index bcd5574e304ae..5386f128a131d 100644 --- a/tools/memory-model/litmus-tests/R+poonceonces.litmus +++ b/tools/memory-model/litmus-tests/R+poonceonces.litmus @@ -8,10 +8,7 @@ C R+poonceonces * store propagation delays. *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/S+fencewmbonceonce+poacquireonce.litmus b/tools/memory-model/litmus-tests/S+fencewmbonceonce+poacquireonce.litmus index c36341d1aed66..18479823cd6cc 100644 --- a/tools/memory-model/litmus-tests/S+fencewmbonceonce+poacquireonce.litmus +++ b/tools/memory-model/litmus-tests/S+fencewmbonceonce+poacquireonce.litmus @@ -7,10 +7,7 @@ C S+fencewmbonceonce+poacquireonce * store against a subsequent store? *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/S+poonceonces.litmus b/tools/memory-model/litmus-tests/S+poonceonces.litmus index 7775c23143a0c..8c9c2f81a5805 100644 --- a/tools/memory-model/litmus-tests/S+poonceonces.litmus +++ b/tools/memory-model/litmus-tests/S+poonceonces.litmus @@ -9,10 +9,7 @@ C S+poonceonces * READ_ONCE(), is ordering preserved? *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/SB+fencembonceonces.litmus b/tools/memory-model/litmus-tests/SB+fencembonceonces.litmus index 833cdfeb7c093..ed5fff18d2232 100644 --- a/tools/memory-model/litmus-tests/SB+fencembonceonces.litmus +++ b/tools/memory-model/litmus-tests/SB+fencembonceonces.litmus @@ -9,10 +9,7 @@ C SB+fencembonceonces * suffice, but not much else.) *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/SB+poonceonces.litmus b/tools/memory-model/litmus-tests/SB+poonceonces.litmus index c92211ecbfdf7..10d550730b25f 100644 --- a/tools/memory-model/litmus-tests/SB+poonceonces.litmus +++ b/tools/memory-model/litmus-tests/SB+poonceonces.litmus @@ -8,10 +8,7 @@ C SB+poonceonces * variable that the preceding process reads. *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/SB+rfionceonce-poonceonces.litmus b/tools/memory-model/litmus-tests/SB+rfionceonce-poonceonces.litmus index 84344b455eb71..04a16603660bd 100644 --- a/tools/memory-model/litmus-tests/SB+rfionceonce-poonceonces.litmus +++ b/tools/memory-model/litmus-tests/SB+rfionceonce-poonceonces.litmus @@ -6,10 +6,7 @@ C SB+rfionceonce-poonceonces * This litmus test demonstrates that LKMM is not fully multicopy atomic. *) -{ - int x; - int y; -} +{} P0(int *x, int *y) { diff --git a/tools/memory-model/litmus-tests/WRC+poonceonces+Once.litmus b/tools/memory-model/litmus-tests/WRC+poonceonces+Once.litmus index 431494708611b..6a2bc12a1af1a 100644 --- a/tools/memory-model/litmus-tests/WRC+poonceonces+Once.litmus +++ b/tools/memory-model/litmus-tests/WRC+poonceonces+Once.litmus @@ -8,10 +8,7 @@ C WRC+poonceonces+Once * test has no ordering at all. *) -{ - int x; - int y; -} +{} P0(int *x) { diff --git a/tools/memory-model/litmus-tests/WRC+pooncerelease+fencermbonceonce+Once.litmus b/tools/memory-model/litmus-tests/WRC+pooncerelease+fencermbonceonce+Once.litmus index 554999c64db58..e9947250d7de6 100644 --- a/tools/memory-model/litmus-tests/WRC+pooncerelease+fencermbonceonce+Once.litmus +++ b/tools/memory-model/litmus-tests/WRC+pooncerelease+fencermbonceonce+Once.litmus @@ -10,10 +10,7 @@ C WRC+pooncerelease+fencermbonceonce+Once * is A-cumulative in LKMM. *) -{ - int x; - int y; -} +{} P0(int *x) { diff --git a/tools/memory-model/litmus-tests/Z6.0+pooncelock+poonceLock+pombonce.litmus b/tools/memory-model/litmus-tests/Z6.0+pooncelock+poonceLock+pombonce.litmus index 265a95ffef137..415248fb66990 100644 --- a/tools/memory-model/litmus-tests/Z6.0+pooncelock+poonceLock+pombonce.litmus +++ b/tools/memory-model/litmus-tests/Z6.0+pooncelock+poonceLock+pombonce.litmus @@ -9,12 +9,7 @@ C Z6.0+pooncelock+poonceLock+pombonce * by CPUs not holding that lock. *) -{ - spinlock_t mylock; - int x; - int y; - int z; -} +{} P0(int *x, int *y, spinlock_t *mylock) { diff --git a/tools/memory-model/litmus-tests/Z6.0+pooncelock+pooncelock+pombonce.litmus b/tools/memory-model/litmus-tests/Z6.0+pooncelock+pooncelock+pombonce.litmus index 0c9aea8e80df0..10a2aa04cd078 100644 --- a/tools/memory-model/litmus-tests/Z6.0+pooncelock+pooncelock+pombonce.litmus +++ b/tools/memory-model/litmus-tests/Z6.0+pooncelock+pooncelock+pombonce.litmus @@ -8,12 +8,7 @@ C Z6.0+pooncelock+pooncelock+pombonce * seen as ordered by a third process not holding that lock. *) -{ - spinlock_t mylock; - int x; - int y; - int z; -} +{} P0(int *x, int *y, spinlock_t *mylock) { diff --git a/tools/memory-model/litmus-tests/Z6.0+pooncerelease+poacquirerelease+fencembonceonce.litmus b/tools/memory-model/litmus-tests/Z6.0+pooncerelease+poacquirerelease+fencembonceonce.litmus index 661f9aaa57914..88e70b87a683e 100644 --- a/tools/memory-model/litmus-tests/Z6.0+pooncerelease+poacquirerelease+fencembonceonce.litmus +++ b/tools/memory-model/litmus-tests/Z6.0+pooncerelease+poacquirerelease+fencembonceonce.litmus @@ -14,11 +14,7 @@ C Z6.0+pooncerelease+poacquirerelease+fencembonceonce * involving locking.) *) -{ - int x; - int y; - int z; -} +{} P0(int *x, int *y) { From 3d5c70329b910ab583673a33e3a615873c5d4115 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Sat, 28 Nov 2020 14:32:15 +0900 Subject: [PATCH 05/24] tools/memory-model: Fix typo in klitmus7 compatibility table klitmus7 of herdtools7 7.48 or earlier depends on ACCESS_ONCE(), which was removed in Linux v4.15. Fix the obvious typo in the table. Fixes: d075a78a5ab1 ("tools/memory-model/README: Expand dependency of klitmus7") Signed-off-by: Akira Yokosawa Signed-off-by: Paul E. McKenney --- tools/memory-model/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/memory-model/README b/tools/memory-model/README index 39d08d1f0443f..9a84c45504ab6 100644 --- a/tools/memory-model/README +++ b/tools/memory-model/README @@ -51,7 +51,7 @@ klitmus7 Compatibility Table ============ ========== target Linux herdtools7 ------------ ---------- - -- 4.18 7.48 -- + -- 4.14 7.48 -- 4.15 -- 4.19 7.49 -- 4.20 -- 5.5 7.54 -- 5.6 -- 7.56 -- From 9271a40d2a1429113160ccc4c16150921600bcc1 Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Tue, 8 Dec 2020 18:31:12 +0800 Subject: [PATCH 06/24] lockdep/selftest: Add wait context selftests These tests are added for two purposes: * Test the implementation of wait context checks and related annotations. * Semi-document the rules for wait context nesting when PROVE_RAW_LOCK_NESTING=y. The test cases are only avaible for PROVE_RAW_LOCK_NESTING=y, as wait context checking makes more sense for that configuration. Signed-off-by: Boqun Feng Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20201208103112.2838119-5-boqun.feng@gmail.com --- lib/locking-selftest.c | 232 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 9959ea23529e8..23376eeccf7cf 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -64,6 +64,9 @@ static DEFINE_SPINLOCK(lock_B); static DEFINE_SPINLOCK(lock_C); static DEFINE_SPINLOCK(lock_D); +static DEFINE_RAW_SPINLOCK(raw_lock_A); +static DEFINE_RAW_SPINLOCK(raw_lock_B); + static DEFINE_RWLOCK(rwlock_A); static DEFINE_RWLOCK(rwlock_B); static DEFINE_RWLOCK(rwlock_C); @@ -1306,6 +1309,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_soft_wlock) #ifdef CONFIG_DEBUG_LOCK_ALLOC # define I_SPINLOCK(x) lockdep_reset_lock(&lock_##x.dep_map) +# define I_RAW_SPINLOCK(x) lockdep_reset_lock(&raw_lock_##x.dep_map) # define I_RWLOCK(x) lockdep_reset_lock(&rwlock_##x.dep_map) # define I_MUTEX(x) lockdep_reset_lock(&mutex_##x.dep_map) # define I_RWSEM(x) lockdep_reset_lock(&rwsem_##x.dep_map) @@ -1315,6 +1319,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_soft_wlock) #endif #else # define I_SPINLOCK(x) +# define I_RAW_SPINLOCK(x) # define I_RWLOCK(x) # define I_MUTEX(x) # define I_RWSEM(x) @@ -1358,9 +1363,12 @@ static void reset_locks(void) I1(A); I1(B); I1(C); I1(D); I1(X1); I1(X2); I1(Y1); I1(Y2); I1(Z1); I1(Z2); I_WW(t); I_WW(t2); I_WW(o.base); I_WW(o2.base); I_WW(o3.base); + I_RAW_SPINLOCK(A); I_RAW_SPINLOCK(B); lockdep_reset(); I2(A); I2(B); I2(C); I2(D); init_shared_classes(); + raw_spin_lock_init(&raw_lock_A); + raw_spin_lock_init(&raw_lock_B); ww_mutex_init(&o, &ww_lockdep); ww_mutex_init(&o2, &ww_lockdep); ww_mutex_init(&o3, &ww_lockdep); memset(&t, 0, sizeof(t)); memset(&t2, 0, sizeof(t2)); @@ -2419,6 +2427,226 @@ static void fs_reclaim_tests(void) pr_cont("\n"); } +#define __guard(cleanup) __maybe_unused __attribute__((__cleanup__(cleanup))) + +static void hardirq_exit(int *_) +{ + HARDIRQ_EXIT(); +} + +#define HARDIRQ_CONTEXT(name, ...) \ + int hardirq_guard_##name __guard(hardirq_exit); \ + HARDIRQ_ENTER(); + +#define NOTTHREADED_HARDIRQ_CONTEXT(name, ...) \ + int notthreaded_hardirq_guard_##name __guard(hardirq_exit); \ + local_irq_disable(); \ + __irq_enter(); \ + WARN_ON(!in_irq()); + +static void softirq_exit(int *_) +{ + SOFTIRQ_EXIT(); +} + +#define SOFTIRQ_CONTEXT(name, ...) \ + int softirq_guard_##name __guard(softirq_exit); \ + SOFTIRQ_ENTER(); + +static void rcu_exit(int *_) +{ + rcu_read_unlock(); +} + +#define RCU_CONTEXT(name, ...) \ + int rcu_guard_##name __guard(rcu_exit); \ + rcu_read_lock(); + +static void rcu_bh_exit(int *_) +{ + rcu_read_unlock_bh(); +} + +#define RCU_BH_CONTEXT(name, ...) \ + int rcu_bh_guard_##name __guard(rcu_bh_exit); \ + rcu_read_lock_bh(); + +static void rcu_sched_exit(int *_) +{ + rcu_read_unlock_sched(); +} + +#define RCU_SCHED_CONTEXT(name, ...) \ + int rcu_sched_guard_##name __guard(rcu_sched_exit); \ + rcu_read_lock_sched(); + +static void rcu_callback_exit(int *_) +{ + rcu_lock_release(&rcu_callback_map); +} + +#define RCU_CALLBACK_CONTEXT(name, ...) \ + int rcu_callback_guard_##name __guard(rcu_callback_exit); \ + rcu_lock_acquire(&rcu_callback_map); + + +static void raw_spinlock_exit(raw_spinlock_t **lock) +{ + raw_spin_unlock(*lock); +} + +#define RAW_SPINLOCK_CONTEXT(name, lock) \ + raw_spinlock_t *raw_spinlock_guard_##name __guard(raw_spinlock_exit) = &(lock); \ + raw_spin_lock(&(lock)); + +static void spinlock_exit(spinlock_t **lock) +{ + spin_unlock(*lock); +} + +#define SPINLOCK_CONTEXT(name, lock) \ + spinlock_t *spinlock_guard_##name __guard(spinlock_exit) = &(lock); \ + spin_lock(&(lock)); + +static void mutex_exit(struct mutex **lock) +{ + mutex_unlock(*lock); +} + +#define MUTEX_CONTEXT(name, lock) \ + struct mutex *mutex_guard_##name __guard(mutex_exit) = &(lock); \ + mutex_lock(&(lock)); + +#define GENERATE_2_CONTEXT_TESTCASE(outer, outer_lock, inner, inner_lock) \ + \ +static void __maybe_unused inner##_in_##outer(void) \ +{ \ + outer##_CONTEXT(_, outer_lock); \ + { \ + inner##_CONTEXT(_, inner_lock); \ + } \ +} + +/* + * wait contexts (considering PREEMPT_RT) + * + * o: inner is allowed in outer + * x: inner is disallowed in outer + * + * \ inner | RCU | RAW_SPIN | SPIN | MUTEX + * outer \ | | | | + * ---------------+-------+----------+------+------- + * HARDIRQ | o | o | o | x + * ---------------+-------+----------+------+------- + * NOTTHREADED_IRQ| o | o | x | x + * ---------------+-------+----------+------+------- + * SOFTIRQ | o | o | o | x + * ---------------+-------+----------+------+------- + * RCU | o | o | o | x + * ---------------+-------+----------+------+------- + * RCU_BH | o | o | o | x + * ---------------+-------+----------+------+------- + * RCU_CALLBACK | o | o | o | x + * ---------------+-------+----------+------+------- + * RCU_SCHED | o | o | x | x + * ---------------+-------+----------+------+------- + * RAW_SPIN | o | o | x | x + * ---------------+-------+----------+------+------- + * SPIN | o | o | o | x + * ---------------+-------+----------+------+------- + * MUTEX | o | o | o | o + * ---------------+-------+----------+------+------- + */ + +#define GENERATE_2_CONTEXT_TESTCASE_FOR_ALL_OUTER(inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(HARDIRQ, , inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(NOTTHREADED_HARDIRQ, , inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(SOFTIRQ, , inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(RCU, , inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(RCU_BH, , inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(RCU_CALLBACK, , inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(RCU_SCHED, , inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(RAW_SPINLOCK, raw_lock_A, inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(SPINLOCK, lock_A, inner, inner_lock) \ +GENERATE_2_CONTEXT_TESTCASE(MUTEX, mutex_A, inner, inner_lock) + +GENERATE_2_CONTEXT_TESTCASE_FOR_ALL_OUTER(RCU, ) +GENERATE_2_CONTEXT_TESTCASE_FOR_ALL_OUTER(RAW_SPINLOCK, raw_lock_B) +GENERATE_2_CONTEXT_TESTCASE_FOR_ALL_OUTER(SPINLOCK, lock_B) +GENERATE_2_CONTEXT_TESTCASE_FOR_ALL_OUTER(MUTEX, mutex_B) + +/* the outer context allows all kinds of preemption */ +#define DO_CONTEXT_TESTCASE_OUTER_PREEMPTIBLE(outer) \ + dotest(RCU_in_##outer, SUCCESS, LOCKTYPE_RWLOCK); \ + dotest(RAW_SPINLOCK_in_##outer, SUCCESS, LOCKTYPE_SPIN); \ + dotest(SPINLOCK_in_##outer, SUCCESS, LOCKTYPE_SPIN); \ + dotest(MUTEX_in_##outer, SUCCESS, LOCKTYPE_MUTEX); \ + +/* + * the outer context only allows the preemption introduced by spinlock_t (which + * is a sleepable lock for PREEMPT_RT) + */ +#define DO_CONTEXT_TESTCASE_OUTER_LIMITED_PREEMPTIBLE(outer) \ + dotest(RCU_in_##outer, SUCCESS, LOCKTYPE_RWLOCK); \ + dotest(RAW_SPINLOCK_in_##outer, SUCCESS, LOCKTYPE_SPIN); \ + dotest(SPINLOCK_in_##outer, SUCCESS, LOCKTYPE_SPIN); \ + dotest(MUTEX_in_##outer, FAILURE, LOCKTYPE_MUTEX); \ + +/* the outer doesn't allows any kind of preemption */ +#define DO_CONTEXT_TESTCASE_OUTER_NOT_PREEMPTIBLE(outer) \ + dotest(RCU_in_##outer, SUCCESS, LOCKTYPE_RWLOCK); \ + dotest(RAW_SPINLOCK_in_##outer, SUCCESS, LOCKTYPE_SPIN); \ + dotest(SPINLOCK_in_##outer, FAILURE, LOCKTYPE_SPIN); \ + dotest(MUTEX_in_##outer, FAILURE, LOCKTYPE_MUTEX); \ + +static void wait_context_tests(void) +{ + printk(" --------------------------------------------------------------------------\n"); + printk(" | wait context tests |\n"); + printk(" --------------------------------------------------------------------------\n"); + printk(" | rcu | raw | spin |mutex |\n"); + printk(" --------------------------------------------------------------------------\n"); + print_testname("in hardirq context"); + DO_CONTEXT_TESTCASE_OUTER_LIMITED_PREEMPTIBLE(HARDIRQ); + pr_cont("\n"); + + print_testname("in hardirq context (not threaded)"); + DO_CONTEXT_TESTCASE_OUTER_NOT_PREEMPTIBLE(NOTTHREADED_HARDIRQ); + pr_cont("\n"); + + print_testname("in softirq context"); + DO_CONTEXT_TESTCASE_OUTER_LIMITED_PREEMPTIBLE(SOFTIRQ); + pr_cont("\n"); + + print_testname("in RCU context"); + DO_CONTEXT_TESTCASE_OUTER_LIMITED_PREEMPTIBLE(RCU); + pr_cont("\n"); + + print_testname("in RCU-bh context"); + DO_CONTEXT_TESTCASE_OUTER_LIMITED_PREEMPTIBLE(RCU_BH); + pr_cont("\n"); + + print_testname("in RCU callback context"); + DO_CONTEXT_TESTCASE_OUTER_LIMITED_PREEMPTIBLE(RCU_CALLBACK); + pr_cont("\n"); + + print_testname("in RCU-sched context"); + DO_CONTEXT_TESTCASE_OUTER_NOT_PREEMPTIBLE(RCU_SCHED); + pr_cont("\n"); + + print_testname("in RAW_SPINLOCK context"); + DO_CONTEXT_TESTCASE_OUTER_NOT_PREEMPTIBLE(RAW_SPINLOCK); + pr_cont("\n"); + + print_testname("in SPINLOCK context"); + DO_CONTEXT_TESTCASE_OUTER_LIMITED_PREEMPTIBLE(SPINLOCK); + pr_cont("\n"); + + print_testname("in MUTEX context"); + DO_CONTEXT_TESTCASE_OUTER_PREEMPTIBLE(MUTEX); + pr_cont("\n"); +} + void locking_selftest(void) { /* @@ -2542,6 +2770,10 @@ void locking_selftest(void) fs_reclaim_tests(); + /* Wait context test cases that are specific for RAW_LOCK_NESTING */ + if (IS_ENABLED(CONFIG_PROVE_RAW_LOCK_NESTING)) + wait_context_tests(); + if (unexpected_testcase_failures) { printk("-----------------------------------------------------------------\n"); debug_locks = 0; From 5831c0f71d6664c6aa7b58ba969bf645c89ecb85 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 9 Dec 2020 16:42:57 +0100 Subject: [PATCH 07/24] locking/selftests: More granular debug_locks_verbose Showing all tests all the time is tiresome. Signed-off-by: Peter Zijlstra (Intel) --- Documentation/admin-guide/kernel-parameters.txt | 11 ++++++----- lib/locking-selftest.c | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index c722ec19cd004..611a5c3034e7b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -802,13 +802,14 @@ insecure, please do not use on production kernels. debug_locks_verbose= - [KNL] verbose self-tests - Format=<0|1> + [KNL] verbose locking self-tests + Format: Print debugging info while doing the locking API self-tests. - We default to 0 (no extra messages), setting it to - 1 will print _a lot_ more information - normally - only useful to kernel developers. + Bitmask for the various LOCKTYPE_ tests. Defaults to 0 + (no extra messages), setting it to -1 (all bits set) + will print _a_lot_ more information - normally only + useful to lockdep developers. debug_objects [KNL] Enable object debugging diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 23376eeccf7cf..3306f43b00071 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -1390,6 +1390,8 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) WARN_ON(irqs_disabled()); + debug_locks_silent = !(debug_locks_verbose & lockclass_mask); + testcase_fn(); /* * Filter out expected failures: @@ -1410,7 +1412,7 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) } testcase_total++; - if (debug_locks_verbose) + if (debug_locks_verbose & lockclass_mask) pr_cont(" lockclass mask: %x, debug_locks: %d, expected: %d\n", lockclass_mask, debug_locks, expected); /* @@ -2674,7 +2676,6 @@ void locking_selftest(void) printk(" --------------------------------------------------------------------------\n"); init_shared_classes(); - debug_locks_silent = !debug_locks_verbose; lockdep_set_selftest_task(current); DO_TESTCASE_6R("A-A deadlock", AA); From dfd5e3f5fe27bda91d5cc028c86ffbb7a0614489 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 9 Dec 2020 16:06:21 +0100 Subject: [PATCH 08/24] locking/lockdep: Mark local_lock_t The local_lock_t's are special, because they cannot form IRQ inversions, make sure we can tell them apart from the rest of the locks. Signed-off-by: Peter Zijlstra (Intel) --- include/linux/local_lock_internal.h | 5 ++++- include/linux/lockdep.h | 15 ++++++++++++--- include/linux/lockdep_types.h | 18 ++++++++++++++---- kernel/locking/lockdep.c | 16 +++++++++------- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/include/linux/local_lock_internal.h b/include/linux/local_lock_internal.h index 4a8795b21d774..ded90b097e6e8 100644 --- a/include/linux/local_lock_internal.h +++ b/include/linux/local_lock_internal.h @@ -18,6 +18,7 @@ typedef struct { .dep_map = { \ .name = #lockname, \ .wait_type_inner = LD_WAIT_CONFIG, \ + .lock_type = LD_LOCK_PERCPU, \ } #else # define LL_DEP_MAP_INIT(lockname) @@ -30,7 +31,9 @@ do { \ static struct lock_class_key __key; \ \ debug_check_no_locks_freed((void *)lock, sizeof(*lock));\ - lockdep_init_map_wait(&(lock)->dep_map, #lock, &__key, 0, LD_WAIT_CONFIG);\ + lockdep_init_map_type(&(lock)->dep_map, #lock, &__key, 0, \ + LD_WAIT_CONFIG, LD_WAIT_INV, \ + LD_LOCK_PERCPU); \ } while (0) #ifdef CONFIG_DEBUG_LOCK_ALLOC diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index b9e9adec73e8b..7b7ebf2e28ec5 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -185,12 +185,19 @@ extern void lockdep_unregister_key(struct lock_class_key *key); * to lockdep: */ -extern void lockdep_init_map_waits(struct lockdep_map *lock, const char *name, - struct lock_class_key *key, int subclass, short inner, short outer); +extern void lockdep_init_map_type(struct lockdep_map *lock, const char *name, + struct lock_class_key *key, int subclass, u8 inner, u8 outer, u8 lock_type); + +static inline void +lockdep_init_map_waits(struct lockdep_map *lock, const char *name, + struct lock_class_key *key, int subclass, u8 inner, u8 outer) +{ + lockdep_init_map_type(lock, name, key, subclass, inner, LD_WAIT_INV, LD_LOCK_NORMAL); +} static inline void lockdep_init_map_wait(struct lockdep_map *lock, const char *name, - struct lock_class_key *key, int subclass, short inner) + struct lock_class_key *key, int subclass, u8 inner) { lockdep_init_map_waits(lock, name, key, subclass, inner, LD_WAIT_INV); } @@ -340,6 +347,8 @@ static inline void lockdep_set_selftest_task(struct task_struct *task) # define lock_set_class(l, n, k, s, i) do { } while (0) # define lock_set_subclass(l, s, i) do { } while (0) # define lockdep_init() do { } while (0) +# define lockdep_init_map_type(lock, name, key, sub, inner, outer, type) \ + do { (void)(name); (void)(key); } while (0) # define lockdep_init_map_waits(lock, name, key, sub, inner, outer) \ do { (void)(name); (void)(key); } while (0) # define lockdep_init_map_wait(lock, name, key, sub, inner) \ diff --git a/include/linux/lockdep_types.h b/include/linux/lockdep_types.h index 9a1fd49df17f6..2ec9ff5a7fff0 100644 --- a/include/linux/lockdep_types.h +++ b/include/linux/lockdep_types.h @@ -30,6 +30,12 @@ enum lockdep_wait_type { LD_WAIT_MAX, /* must be last */ }; +enum lockdep_lock_type { + LD_LOCK_NORMAL = 0, /* normal, catch all */ + LD_LOCK_PERCPU, /* percpu */ + LD_LOCK_MAX, +}; + #ifdef CONFIG_LOCKDEP /* @@ -119,8 +125,10 @@ struct lock_class { int name_version; const char *name; - short wait_type_inner; - short wait_type_outer; + u8 wait_type_inner; + u8 wait_type_outer; + u8 lock_type; + /* u8 hole; */ #ifdef CONFIG_LOCK_STAT unsigned long contention_point[LOCKSTAT_POINTS]; @@ -169,8 +177,10 @@ struct lockdep_map { struct lock_class_key *key; struct lock_class *class_cache[NR_LOCKDEP_CACHING_CLASSES]; const char *name; - short wait_type_outer; /* can be taken in this context */ - short wait_type_inner; /* presents this context */ + u8 wait_type_outer; /* can be taken in this context */ + u8 wait_type_inner; /* presents this context */ + u8 lock_type; + /* u8 hole; */ #ifdef CONFIG_LOCK_STAT int cpu; unsigned long ip; diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index c1418b47f625a..b061e29917005 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -1290,6 +1290,7 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) class->name_version = count_matching_names(class); class->wait_type_inner = lock->wait_type_inner; class->wait_type_outer = lock->wait_type_outer; + class->lock_type = lock->lock_type; /* * We use RCU's safe list-add method to make * parallel walking of the hash-list safe: @@ -4503,9 +4504,9 @@ print_lock_invalid_wait_context(struct task_struct *curr, */ static int check_wait_context(struct task_struct *curr, struct held_lock *next) { - short next_inner = hlock_class(next)->wait_type_inner; - short next_outer = hlock_class(next)->wait_type_outer; - short curr_inner; + u8 next_inner = hlock_class(next)->wait_type_inner; + u8 next_outer = hlock_class(next)->wait_type_outer; + u8 curr_inner; int depth; if (!curr->lockdep_depth || !next_inner || next->trylock) @@ -4528,7 +4529,7 @@ static int check_wait_context(struct task_struct *curr, struct held_lock *next) for (; depth < curr->lockdep_depth; depth++) { struct held_lock *prev = curr->held_locks + depth; - short prev_inner = hlock_class(prev)->wait_type_inner; + u8 prev_inner = hlock_class(prev)->wait_type_inner; if (prev_inner) { /* @@ -4577,9 +4578,9 @@ static inline int check_wait_context(struct task_struct *curr, /* * Initialize a lock instance's lock-class mapping info: */ -void lockdep_init_map_waits(struct lockdep_map *lock, const char *name, +void lockdep_init_map_type(struct lockdep_map *lock, const char *name, struct lock_class_key *key, int subclass, - short inner, short outer) + u8 inner, u8 outer, u8 lock_type) { int i; @@ -4602,6 +4603,7 @@ void lockdep_init_map_waits(struct lockdep_map *lock, const char *name, lock->wait_type_outer = outer; lock->wait_type_inner = inner; + lock->lock_type = lock_type; /* * No key, no joy, we need to hash something. @@ -4636,7 +4638,7 @@ void lockdep_init_map_waits(struct lockdep_map *lock, const char *name, raw_local_irq_restore(flags); } } -EXPORT_SYMBOL_GPL(lockdep_init_map_waits); +EXPORT_SYMBOL_GPL(lockdep_init_map_type); struct lock_class_key __lockdep_no_validate__; EXPORT_SYMBOL_GPL(__lockdep_no_validate__); From bc2dd71b283665f0a409d5b6fc603d5a6fdc219e Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Thu, 10 Dec 2020 11:02:40 +0100 Subject: [PATCH 09/24] locking/lockdep: Add a skip() function to __bfs() Some __bfs() walks will have additional iteration constraints (beyond the path being strong). Provide an additional function to allow terminating graph walks. Signed-off-by: Boqun Feng Signed-off-by: Peter Zijlstra (Intel) --- kernel/locking/lockdep.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index b061e29917005..f50f026772d7c 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -1672,6 +1672,7 @@ static inline struct lock_list *__bfs_next(struct lock_list *lock, int offset) static enum bfs_result __bfs(struct lock_list *source_entry, void *data, bool (*match)(struct lock_list *entry, void *data), + bool (*skip)(struct lock_list *entry, void *data), struct lock_list **target_entry, int offset) { @@ -1732,7 +1733,12 @@ static enum bfs_result __bfs(struct lock_list *source_entry, /* * Step 3: we haven't visited this and there is a strong * dependency path to this, so check with @match. + * If @skip is provide and returns true, we skip this + * lock (and any path this lock is in). */ + if (skip && skip(lock, data)) + continue; + if (match(lock, data)) { *target_entry = lock; return BFS_RMATCH; @@ -1775,9 +1781,10 @@ static inline enum bfs_result __bfs_forwards(struct lock_list *src_entry, void *data, bool (*match)(struct lock_list *entry, void *data), + bool (*skip)(struct lock_list *entry, void *data), struct lock_list **target_entry) { - return __bfs(src_entry, data, match, target_entry, + return __bfs(src_entry, data, match, skip, target_entry, offsetof(struct lock_class, locks_after)); } @@ -1786,9 +1793,10 @@ static inline enum bfs_result __bfs_backwards(struct lock_list *src_entry, void *data, bool (*match)(struct lock_list *entry, void *data), + bool (*skip)(struct lock_list *entry, void *data), struct lock_list **target_entry) { - return __bfs(src_entry, data, match, target_entry, + return __bfs(src_entry, data, match, skip, target_entry, offsetof(struct lock_class, locks_before)); } @@ -2019,7 +2027,7 @@ static unsigned long __lockdep_count_forward_deps(struct lock_list *this) unsigned long count = 0; struct lock_list *target_entry; - __bfs_forwards(this, (void *)&count, noop_count, &target_entry); + __bfs_forwards(this, (void *)&count, noop_count, NULL, &target_entry); return count; } @@ -2044,7 +2052,7 @@ static unsigned long __lockdep_count_backward_deps(struct lock_list *this) unsigned long count = 0; struct lock_list *target_entry; - __bfs_backwards(this, (void *)&count, noop_count, &target_entry); + __bfs_backwards(this, (void *)&count, noop_count, NULL, &target_entry); return count; } @@ -2072,11 +2080,12 @@ unsigned long lockdep_count_backward_deps(struct lock_class *class) static noinline enum bfs_result check_path(struct held_lock *target, struct lock_list *src_entry, bool (*match)(struct lock_list *entry, void *data), + bool (*skip)(struct lock_list *entry, void *data), struct lock_list **target_entry) { enum bfs_result ret; - ret = __bfs_forwards(src_entry, target, match, target_entry); + ret = __bfs_forwards(src_entry, target, match, skip, target_entry); if (unlikely(bfs_error(ret))) print_bfs_bug(ret); @@ -2103,7 +2112,7 @@ check_noncircular(struct held_lock *src, struct held_lock *target, debug_atomic_inc(nr_cyclic_checks); - ret = check_path(target, &src_entry, hlock_conflict, &target_entry); + ret = check_path(target, &src_entry, hlock_conflict, NULL, &target_entry); if (unlikely(ret == BFS_RMATCH)) { if (!*trace) { @@ -2152,7 +2161,7 @@ check_redundant(struct held_lock *src, struct held_lock *target) debug_atomic_inc(nr_redundant_checks); - ret = check_path(target, &src_entry, hlock_equal, &target_entry); + ret = check_path(target, &src_entry, hlock_equal, NULL, &target_entry); if (ret == BFS_RMATCH) debug_atomic_inc(nr_redundant); @@ -2246,7 +2255,7 @@ find_usage_forwards(struct lock_list *root, unsigned long usage_mask, debug_atomic_inc(nr_find_usage_forwards_checks); - result = __bfs_forwards(root, &usage_mask, usage_match, target_entry); + result = __bfs_forwards(root, &usage_mask, usage_match, NULL, target_entry); return result; } @@ -2263,7 +2272,7 @@ find_usage_backwards(struct lock_list *root, unsigned long usage_mask, debug_atomic_inc(nr_find_usage_backwards_checks); - result = __bfs_backwards(root, &usage_mask, usage_match, target_entry); + result = __bfs_backwards(root, &usage_mask, usage_match, NULL, target_entry); return result; } @@ -2628,7 +2637,7 @@ static int check_irq_usage(struct task_struct *curr, struct held_lock *prev, */ bfs_init_rootb(&this, prev); - ret = __bfs_backwards(&this, &usage_mask, usage_accumulate, NULL); + ret = __bfs_backwards(&this, &usage_mask, usage_accumulate, NULL, NULL); if (bfs_error(ret)) { print_bfs_bug(ret); return 0; From 175b1a60e8805617d74aefe17ce0d3a32eceb55c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 10 Dec 2020 11:16:34 +0100 Subject: [PATCH 10/24] locking/lockdep: Clean up check_redundant() a bit In preparation for adding an TRACE_IRQFLAGS dependent skip function to check_redundant(), move it below the TRACE_IRQFLAGS #ifdef. While there, provide a stub function to reduce #ifdef usage. Signed-off-by: Peter Zijlstra (Intel) --- kernel/locking/lockdep.c | 91 +++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index f50f026772d7c..f2ae8a65f6674 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -2130,46 +2130,6 @@ check_noncircular(struct held_lock *src, struct held_lock *target, return ret; } -#ifdef CONFIG_LOCKDEP_SMALL -/* - * Check that the dependency graph starting at can lead to - * or not. If it can, -> dependency is already - * in the graph. - * - * Return BFS_RMATCH if it does, or BFS_RMATCH if it does not, return BFS_E* if - * any error appears in the bfs search. - */ -static noinline enum bfs_result -check_redundant(struct held_lock *src, struct held_lock *target) -{ - enum bfs_result ret; - struct lock_list *target_entry; - struct lock_list src_entry; - - bfs_init_root(&src_entry, src); - /* - * Special setup for check_redundant(). - * - * To report redundant, we need to find a strong dependency path that - * is equal to or stronger than -> . So if is E, - * we need to let __bfs() only search for a path starting at a -(E*)->, - * we achieve this by setting the initial node's ->only_xr to true in - * that case. And if is S, we set initial ->only_xr to false - * because both -(S*)-> (equal) and -(E*)-> (stronger) are redundant. - */ - src_entry.only_xr = src->read == 0; - - debug_atomic_inc(nr_redundant_checks); - - ret = check_path(target, &src_entry, hlock_equal, NULL, &target_entry); - - if (ret == BFS_RMATCH) - debug_atomic_inc(nr_redundant); - - return ret; -} -#endif - #ifdef CONFIG_TRACE_IRQFLAGS /* @@ -2706,6 +2666,55 @@ static inline int check_irq_usage(struct task_struct *curr, } #endif /* CONFIG_TRACE_IRQFLAGS */ +#ifdef CONFIG_LOCKDEP_SMALL +/* + * Check that the dependency graph starting at can lead to + * or not. If it can, -> dependency is already + * in the graph. + * + * Return BFS_RMATCH if it does, or BFS_RMATCH if it does not, return BFS_E* if + * any error appears in the bfs search. + */ +static noinline enum bfs_result +check_redundant(struct held_lock *src, struct held_lock *target) +{ + enum bfs_result ret; + struct lock_list *target_entry; + struct lock_list src_entry; + + bfs_init_root(&src_entry, src); + /* + * Special setup for check_redundant(). + * + * To report redundant, we need to find a strong dependency path that + * is equal to or stronger than -> . So if is E, + * we need to let __bfs() only search for a path starting at a -(E*)->, + * we achieve this by setting the initial node's ->only_xr to true in + * that case. And if is S, we set initial ->only_xr to false + * because both -(S*)-> (equal) and -(E*)-> (stronger) are redundant. + */ + src_entry.only_xr = src->read == 0; + + debug_atomic_inc(nr_redundant_checks); + + ret = check_path(target, &src_entry, hlock_equal, NULL, &target_entry); + + if (ret == BFS_RMATCH) + debug_atomic_inc(nr_redundant); + + return ret; +} + +#else + +static inline enum bfs_result +check_redundant(struct held_lock *src, struct held_lock *target) +{ + return BFS_RNOMATCH; +} + +#endif + static void inc_chains(int irq_context) { if (irq_context & LOCK_CHAIN_HARDIRQ_CONTEXT) @@ -2926,7 +2935,6 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev, } } -#ifdef CONFIG_LOCKDEP_SMALL /* * Is the -> link redundant? */ @@ -2935,7 +2943,6 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev, return 0; else if (ret == BFS_RMATCH) return 2; -#endif if (!*trace) { *trace = save_trace(); From 5f2962401c6e195222f320d12b3a55377b2d4653 Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Thu, 10 Dec 2020 11:15:00 +0100 Subject: [PATCH 11/24] locking/lockdep: Exclude local_lock_t from IRQ inversions The purpose of local_lock_t is to abstract: preempt_disable() / local_bh_disable() / local_irq_disable(). These are the traditional means of gaining access to per-cpu data, but are fundamentally non-preemptible. local_lock_t provides a per-cpu lock, that on !PREEMPT_RT reduces to no-ops, just like regular spinlocks do on UP. This gives rise to: CPU0 CPU1 local_lock(B) spin_lock_irq(A) spin_lock(A) local_lock(B) Where lockdep then figures things will lock up; which would be true if B were any other kind of lock. However this is a false positive, no such deadlock actually exists. For !RT the above local_lock(B) is preempt_disable(), and there's obviously no deadlock; alternatively, CPU0's B != CPU1's B. For RT the argument is that since local_lock() nests inside spin_lock(), it cannot be used in hardirq context, and therefore CPU0 cannot in fact happen. Even though B is a real lock, it is a preemptible lock and any threaded-irq would simply schedule out and let the preempted task (which holds B) continue such that the task on CPU1 can make progress, after which the threaded-irq resumes and can finish. This means that we can never form an IRQ inversion on a local_lock dependency, so terminate the graph walk when looking for IRQ inversions when we encounter one. One consequence is that (for LOCKDEP_SMALL) when we look for redundant dependencies, A -> B is not redundant in the presence of A -> L -> B. Signed-off-by: Boqun Feng [peterz: Changelog] Signed-off-by: Peter Zijlstra (Intel) --- kernel/locking/lockdep.c | 57 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index f2ae8a65f6674..ad9afd8c7eb9e 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -2200,6 +2200,44 @@ static inline bool usage_match(struct lock_list *entry, void *mask) return !!((entry->class->usage_mask & LOCKF_IRQ) & *(unsigned long *)mask); } +static inline bool usage_skip(struct lock_list *entry, void *mask) +{ + /* + * Skip local_lock() for irq inversion detection. + * + * For !RT, local_lock() is not a real lock, so it won't carry any + * dependency. + * + * For RT, an irq inversion happens when we have lock A and B, and on + * some CPU we can have: + * + * lock(A); + * + * lock(B); + * + * where lock(B) cannot sleep, and we have a dependency B -> ... -> A. + * + * Now we prove local_lock() cannot exist in that dependency. First we + * have the observation for any lock chain L1 -> ... -> Ln, for any + * 1 <= i <= n, Li.inner_wait_type <= L1.inner_wait_type, otherwise + * wait context check will complain. And since B is not a sleep lock, + * therefore B.inner_wait_type >= 2, and since the inner_wait_type of + * local_lock() is 3, which is greater than 2, therefore there is no + * way the local_lock() exists in the dependency B -> ... -> A. + * + * As a result, we will skip local_lock(), when we search for irq + * inversion bugs. + */ + if (entry->class->lock_type == LD_LOCK_PERCPU) { + if (DEBUG_LOCKS_WARN_ON(entry->class->wait_type_inner < LD_WAIT_CONFIG)) + return false; + + return true; + } + + return false; +} + /* * Find a node in the forwards-direction dependency sub-graph starting * at @root->class that matches @bit. @@ -2215,7 +2253,7 @@ find_usage_forwards(struct lock_list *root, unsigned long usage_mask, debug_atomic_inc(nr_find_usage_forwards_checks); - result = __bfs_forwards(root, &usage_mask, usage_match, NULL, target_entry); + result = __bfs_forwards(root, &usage_mask, usage_match, usage_skip, target_entry); return result; } @@ -2232,7 +2270,7 @@ find_usage_backwards(struct lock_list *root, unsigned long usage_mask, debug_atomic_inc(nr_find_usage_backwards_checks); - result = __bfs_backwards(root, &usage_mask, usage_match, NULL, target_entry); + result = __bfs_backwards(root, &usage_mask, usage_match, usage_skip, target_entry); return result; } @@ -2597,7 +2635,7 @@ static int check_irq_usage(struct task_struct *curr, struct held_lock *prev, */ bfs_init_rootb(&this, prev); - ret = __bfs_backwards(&this, &usage_mask, usage_accumulate, NULL, NULL); + ret = __bfs_backwards(&this, &usage_mask, usage_accumulate, usage_skip, NULL); if (bfs_error(ret)) { print_bfs_bug(ret); return 0; @@ -2664,6 +2702,12 @@ static inline int check_irq_usage(struct task_struct *curr, { return 1; } + +static inline bool usage_skip(struct lock_list *entry, void *mask) +{ + return false; +} + #endif /* CONFIG_TRACE_IRQFLAGS */ #ifdef CONFIG_LOCKDEP_SMALL @@ -2697,7 +2741,12 @@ check_redundant(struct held_lock *src, struct held_lock *target) debug_atomic_inc(nr_redundant_checks); - ret = check_path(target, &src_entry, hlock_equal, NULL, &target_entry); + /* + * Note: we skip local_lock() for redundant check, because as the + * comment in usage_skip(), A -> local_lock() -> B and A -> B are not + * the same. + */ + ret = check_path(target, &src_entry, hlock_equal, usage_skip, &target_entry); if (ret == BFS_RMATCH) debug_atomic_inc(nr_redundant); From 7e923e6a3ceb877497dd9ee70d71fa33b94f332b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 9 Dec 2020 16:06:06 +0100 Subject: [PATCH 12/24] locking/selftests: Add local_lock inversion tests Test the local_lock_t inversion scenarios. Signed-off-by: Peter Zijlstra (Intel) --- lib/locking-selftest.c | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 3306f43b00071..2d85abac17448 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -24,6 +24,7 @@ #include #include #include +#include /* * Change this to 1 if you want to see the failure printouts: @@ -51,6 +52,7 @@ __setup("debug_locks_verbose=", setup_debug_locks_verbose); #define LOCKTYPE_RWSEM 0x8 #define LOCKTYPE_WW 0x10 #define LOCKTYPE_RTMUTEX 0x20 +#define LOCKTYPE_LL 0x40 static struct ww_acquire_ctx t, t2; static struct ww_mutex o, o2, o3; @@ -136,6 +138,8 @@ static DEFINE_RT_MUTEX(rtmutex_Z2); #endif +static local_lock_t local_A = INIT_LOCAL_LOCK(local_A); + /* * non-inlined runtime initializers, to let separate locks share * the same lock-class: @@ -1314,6 +1318,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_soft_wlock) # define I_MUTEX(x) lockdep_reset_lock(&mutex_##x.dep_map) # define I_RWSEM(x) lockdep_reset_lock(&rwsem_##x.dep_map) # define I_WW(x) lockdep_reset_lock(&x.dep_map) +# define I_LOCAL_LOCK(x) lockdep_reset_lock(&local_##x.dep_map) #ifdef CONFIG_RT_MUTEXES # define I_RTMUTEX(x) lockdep_reset_lock(&rtmutex_##x.dep_map) #endif @@ -1324,6 +1329,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_soft_wlock) # define I_MUTEX(x) # define I_RWSEM(x) # define I_WW(x) +# define I_LOCAL_LOCK(x) #endif #ifndef I_RTMUTEX @@ -1364,11 +1370,15 @@ static void reset_locks(void) I1(X1); I1(X2); I1(Y1); I1(Y2); I1(Z1); I1(Z2); I_WW(t); I_WW(t2); I_WW(o.base); I_WW(o2.base); I_WW(o3.base); I_RAW_SPINLOCK(A); I_RAW_SPINLOCK(B); + I_LOCAL_LOCK(A); + lockdep_reset(); + I2(A); I2(B); I2(C); I2(D); init_shared_classes(); raw_spin_lock_init(&raw_lock_A); raw_spin_lock_init(&raw_lock_B); + local_lock_init(&local_A); ww_mutex_init(&o, &ww_lockdep); ww_mutex_init(&o2, &ww_lockdep); ww_mutex_init(&o3, &ww_lockdep); memset(&t, 0, sizeof(t)); memset(&t2, 0, sizeof(t2)); @@ -2649,6 +2659,91 @@ static void wait_context_tests(void) pr_cont("\n"); } +static void local_lock_2(void) +{ + local_lock_acquire(&local_A); /* IRQ-ON */ + local_lock_release(&local_A); + + HARDIRQ_ENTER(); + spin_lock(&lock_A); /* IN-IRQ */ + spin_unlock(&lock_A); + HARDIRQ_EXIT() + + HARDIRQ_DISABLE(); + spin_lock(&lock_A); + local_lock_acquire(&local_A); /* IN-IRQ <-> IRQ-ON cycle, false */ + local_lock_release(&local_A); + spin_unlock(&lock_A); + HARDIRQ_ENABLE(); +} + +static void local_lock_3A(void) +{ + local_lock_acquire(&local_A); /* IRQ-ON */ + spin_lock(&lock_B); /* IRQ-ON */ + spin_unlock(&lock_B); + local_lock_release(&local_A); + + HARDIRQ_ENTER(); + spin_lock(&lock_A); /* IN-IRQ */ + spin_unlock(&lock_A); + HARDIRQ_EXIT() + + HARDIRQ_DISABLE(); + spin_lock(&lock_A); + local_lock_acquire(&local_A); /* IN-IRQ <-> IRQ-ON cycle only if we count local_lock(), false */ + local_lock_release(&local_A); + spin_unlock(&lock_A); + HARDIRQ_ENABLE(); +} + +static void local_lock_3B(void) +{ + local_lock_acquire(&local_A); /* IRQ-ON */ + spin_lock(&lock_B); /* IRQ-ON */ + spin_unlock(&lock_B); + local_lock_release(&local_A); + + HARDIRQ_ENTER(); + spin_lock(&lock_A); /* IN-IRQ */ + spin_unlock(&lock_A); + HARDIRQ_EXIT() + + HARDIRQ_DISABLE(); + spin_lock(&lock_A); + local_lock_acquire(&local_A); /* IN-IRQ <-> IRQ-ON cycle only if we count local_lock(), false */ + local_lock_release(&local_A); + spin_unlock(&lock_A); + HARDIRQ_ENABLE(); + + HARDIRQ_DISABLE(); + spin_lock(&lock_A); + spin_lock(&lock_B); /* IN-IRQ <-> IRQ-ON cycle, true */ + spin_unlock(&lock_B); + spin_unlock(&lock_A); + HARDIRQ_DISABLE(); + +} + +static void local_lock_tests(void) +{ + printk(" --------------------------------------------------------------------------\n"); + printk(" | local_lock tests |\n"); + printk(" ---------------------\n"); + + print_testname("local_lock inversion 2"); + dotest(local_lock_2, SUCCESS, LOCKTYPE_LL); + pr_cont("\n"); + + print_testname("local_lock inversion 3A"); + dotest(local_lock_3A, SUCCESS, LOCKTYPE_LL); + pr_cont("\n"); + + print_testname("local_lock inversion 3B"); + dotest(local_lock_3B, FAILURE, LOCKTYPE_LL); + pr_cont("\n"); +} + void locking_selftest(void) { /* @@ -2775,6 +2870,8 @@ void locking_selftest(void) if (IS_ENABLED(CONFIG_PROVE_RAW_LOCK_NESTING)) wait_context_tests(); + local_lock_tests(); + if (unexpected_testcase_failures) { printk("-----------------------------------------------------------------\n"); debug_locks = 0; From c7539258146844ebd8795c31275c720ded61bb84 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 17 Dec 2020 10:32:16 +0100 Subject: [PATCH 13/24] locking: Add Reviewers Spread the love.. Signed-off-by: Peter Zijlstra (Intel) Acked-by: Will Deacon Acked-by: Waiman Long Acked-by: Boqun Feng --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6eff4f720c721..de903d10ace3f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10373,6 +10373,8 @@ LOCKING PRIMITIVES M: Peter Zijlstra M: Ingo Molnar M: Will Deacon +R: Waiman Long +R: Boqun Feng (LOCKDEP) L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core From 2f0df49c89acaa58571d509830bc481250699885 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 11 Dec 2020 16:37:54 -0500 Subject: [PATCH 14/24] jump_label: Do not profile branch annotations While running my branch profiler that checks for incorrect "likely" and "unlikely"s around the kernel, there's a large number of them that are incorrect due to being "static_branches". As static_branches are rather special, as they are likely or unlikely for other reasons than normal annotations are used for, there's no reason to have them be profiled. Expose the "unlikely_notrace" and "likely_notrace" so that the static_branch can use them, and have them be ignored by the branch profilers. Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20201211163754.585174b9@gandalf.local.home --- include/linux/compiler.h | 2 ++ include/linux/jump_label.h | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/linux/compiler.h b/include/linux/compiler.h index b8fe0c23cfffb..df5b405e63051 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -76,6 +76,8 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, #else # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0) +# define likely_notrace(x) likely(x) +# define unlikely_notrace(x) unlikely(x) #endif /* Optimization barrier */ diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index 32809624d422e..d92691262f51a 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -261,14 +261,14 @@ static __always_inline void jump_label_init(void) static __always_inline bool static_key_false(struct static_key *key) { - if (unlikely(static_key_count(key) > 0)) + if (unlikely_notrace(static_key_count(key) > 0)) return true; return false; } static __always_inline bool static_key_true(struct static_key *key) { - if (likely(static_key_count(key) > 0)) + if (likely_notrace(static_key_count(key) > 0)) return true; return false; } @@ -460,7 +460,7 @@ extern bool ____wrong_branch_error(void); branch = !arch_static_branch_jump(&(x)->key, true); \ else \ branch = ____wrong_branch_error(); \ - likely(branch); \ + likely_notrace(branch); \ }) #define static_branch_unlikely(x) \ @@ -472,13 +472,13 @@ extern bool ____wrong_branch_error(void); branch = arch_static_branch(&(x)->key, false); \ else \ branch = ____wrong_branch_error(); \ - unlikely(branch); \ + unlikely_notrace(branch); \ }) #else /* !CONFIG_JUMP_LABEL */ -#define static_branch_likely(x) likely(static_key_enabled(&(x)->key)) -#define static_branch_unlikely(x) unlikely(static_key_enabled(&(x)->key)) +#define static_branch_likely(x) likely_notrace(static_key_enabled(&(x)->key)) +#define static_branch_unlikely(x) unlikely_notrace(static_key_enabled(&(x)->key)) #endif /* CONFIG_JUMP_LABEL */ From 997acaf6b4b59c6a9c259740312a69ea549cc684 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 11 Jan 2021 15:37:07 +0000 Subject: [PATCH 15/24] lockdep: report broken irq restoration We generally expect local_irq_save() and local_irq_restore() to be paired and sanely nested, and so local_irq_restore() expects to be called with irqs disabled. Thus, within local_irq_restore() we only trace irq flag changes when unmasking irqs. This means that a sequence such as: | local_irq_disable(); | local_irq_save(flags); | local_irq_enable(); | local_irq_restore(flags); ... is liable to break things, as the local_irq_restore() would mask irqs without tracing this change. Similar problems may exist for architectures whose arch_irq_restore() function depends on being called with irqs disabled. We don't consider such sequences to be a good idea, so let's define those as forbidden, and add tooling to detect such broken cases. This patch adds debug code to WARN() when raw_local_irq_restore() is called with irqs enabled. As raw_local_irq_restore() is expected to pair with raw_local_irq_save(), it should never be called with irqs enabled. To avoid the possibility of circular header dependencies between irqflags.h and bug.h, the warning is handled in a separate C file. The new code is all conditional on a new CONFIG_DEBUG_IRQFLAGS symbol which is independent of CONFIG_TRACE_IRQFLAGS. As noted above such cases will confuse lockdep, so CONFIG_DEBUG_LOCKDEP now selects CONFIG_DEBUG_IRQFLAGS. Signed-off-by: Mark Rutland Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20210111153707.10071-1-mark.rutland@arm.com --- include/linux/irqflags.h | 12 ++++++++++++ kernel/locking/Makefile | 1 + kernel/locking/irqflag-debug.c | 11 +++++++++++ lib/Kconfig.debug | 8 ++++++++ 4 files changed, 32 insertions(+) create mode 100644 kernel/locking/irqflag-debug.c diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h index 8de0e1373de70..600c10da321a7 100644 --- a/include/linux/irqflags.h +++ b/include/linux/irqflags.h @@ -149,6 +149,17 @@ do { \ # define start_critical_timings() do { } while (0) #endif +#ifdef CONFIG_DEBUG_IRQFLAGS +extern void warn_bogus_irq_restore(void); +#define raw_check_bogus_irq_restore() \ + do { \ + if (unlikely(!arch_irqs_disabled())) \ + warn_bogus_irq_restore(); \ + } while (0) +#else +#define raw_check_bogus_irq_restore() do { } while (0) +#endif + /* * Wrap the arch provided IRQ routines to provide appropriate checks. */ @@ -162,6 +173,7 @@ do { \ #define raw_local_irq_restore(flags) \ do { \ typecheck(unsigned long, flags); \ + raw_check_bogus_irq_restore(); \ arch_local_irq_restore(flags); \ } while (0) #define raw_local_save_flags(flags) \ diff --git a/kernel/locking/Makefile b/kernel/locking/Makefile index 6d11cfb9b41f2..8838f1d7c4a2e 100644 --- a/kernel/locking/Makefile +++ b/kernel/locking/Makefile @@ -15,6 +15,7 @@ CFLAGS_REMOVE_mutex-debug.o = $(CC_FLAGS_FTRACE) CFLAGS_REMOVE_rtmutex-debug.o = $(CC_FLAGS_FTRACE) endif +obj-$(CONFIG_DEBUG_IRQFLAGS) += irqflag-debug.o obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o obj-$(CONFIG_LOCKDEP) += lockdep.o ifeq ($(CONFIG_PROC_FS),y) diff --git a/kernel/locking/irqflag-debug.c b/kernel/locking/irqflag-debug.c new file mode 100644 index 0000000000000..9603d207a4ca9 --- /dev/null +++ b/kernel/locking/irqflag-debug.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include + +void warn_bogus_irq_restore(void) +{ + WARN_ONCE(1, "raw_local_irq_restore() called with IRQs enabled\n"); +} +EXPORT_SYMBOL(warn_bogus_irq_restore); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e6e58b26e8881..78eadf615df8e 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1343,6 +1343,7 @@ config LOCKDEP_SMALL config DEBUG_LOCKDEP bool "Lock dependency engine debugging" depends on DEBUG_KERNEL && LOCKDEP + select DEBUG_IRQFLAGS help If you say Y here, the lock dependency engine will do additional runtime checks to debug itself, at the price @@ -1431,6 +1432,13 @@ config TRACE_IRQFLAGS_NMI depends on TRACE_IRQFLAGS depends on TRACE_IRQFLAGS_NMI_SUPPORT +config DEBUG_IRQFLAGS + bool "Debug IRQ flag manipulation" + help + Enables checks for potentially unsafe enabling or disabling of + interrupts, such as calling raw_local_irq_restore() when interrupts + are enabled. + config STACKTRACE bool "Stack backtrace support" depends on STACKTRACE_SUPPORT From 1ce53e2c2ac069e7b3c400a427002a70deb4a916 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sat, 28 Nov 2020 13:39:46 +0100 Subject: [PATCH 16/24] futex: Change utime parameter to be 'const ... *' futex(2) says that 'utime' is a pointer to 'const'. The implementation doesn't use 'const'; however, it _never_ modifies the contents of utime. - futex() either uses 'utime' as a pointer to struct or as a 'u32'. - In case it's used as a 'u32', it makes a copy of it, and of course it is not dereferenced. - In case it's used as a 'struct __kernel_timespec __user *', the pointer is not dereferenced inside the futex() definition, and it is only passed to a function: get_timespec64(), which accepts a 'const struct __kernel_timespec __user *'. [ tglx: Make the same change to the compat syscall and fixup the prototypes. ] Signed-off-by: Alejandro Colomar Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20201128123945.4592-1-alx.manpages@gmail.com --- include/linux/syscalls.h | 8 ++++---- kernel/futex.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index f3929aff39cf2..5cb74edd9a4fa 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -583,11 +583,11 @@ asmlinkage long sys_unshare(unsigned long unshare_flags); /* kernel/futex.c */ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, - struct __kernel_timespec __user *utime, u32 __user *uaddr2, - u32 val3); + const struct __kernel_timespec __user *utime, + u32 __user *uaddr2, u32 val3); asmlinkage long sys_futex_time32(u32 __user *uaddr, int op, u32 val, - struct old_timespec32 __user *utime, u32 __user *uaddr2, - u32 val3); + const struct old_timespec32 __user *utime, + u32 __user *uaddr2, u32 val3); asmlinkage long sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr, size_t __user *len_ptr); diff --git a/kernel/futex.c b/kernel/futex.c index c47d1015d7591..d0775aab8da96 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -3790,8 +3790,8 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val, - struct __kernel_timespec __user *, utime, u32 __user *, uaddr2, - u32, val3) + const struct __kernel_timespec __user *, utime, + u32 __user *, uaddr2, u32, val3) { struct timespec64 ts; ktime_t t, *tp = NULL; @@ -3986,7 +3986,7 @@ COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid, #ifdef CONFIG_COMPAT_32BIT_TIME SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val, - struct old_timespec32 __user *, utime, u32 __user *, uaddr2, + const struct old_timespec32 __user *, utime, u32 __user *, uaddr2, u32, val3) { struct timespec64 ts; From 0f9438503ea1312ef49be4d9762e0f0006546364 Mon Sep 17 00:00:00 2001 From: Jangwoong Kim <6812skiii@gmail.com> Date: Wed, 30 Dec 2020 21:29:53 +0900 Subject: [PATCH 17/24] futex: Remove unneeded gotos Get rid of gotos that do not contain any cleanup. These were not removed in commit 9180bd467f9a ("futex: Remove put_futex_key()"). Signed-off-by: Jangwoong Kim <6812skiii@gmail.com> Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20201230122953.10473-1-6812skiii@gmail.com --- kernel/futex.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index d0775aab8da96..f3570a276990f 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -3024,7 +3024,7 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) * Success, we're done! No tricky corner cases. */ if (!ret) - goto out_putkey; + return ret; /* * The atomic access to the futex value generated a * pagefault, so retry the user-access and the wakeup: @@ -3041,7 +3041,7 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) * wake_futex_pi has detected invalid state. Tell user * space. */ - goto out_putkey; + return ret; } /* @@ -3062,7 +3062,7 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) default: WARN_ON_ONCE(1); - goto out_putkey; + return ret; } } @@ -3073,7 +3073,6 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) out_unlock: spin_unlock(&hb->lock); -out_putkey: return ret; pi_retry: From bf594bf400016a1ac58c753bcc0393a39c36f669 Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Fri, 13 Nov 2020 16:58:11 +0800 Subject: [PATCH 18/24] locking/rtmutex: Add missing kernel-doc markup To fix the following issues: kernel/locking/rtmutex.c:1612: warning: Function parameter or member 'lock' not described in '__rt_mutex_futex_unlock' kernel/locking/rtmutex.c:1612: warning: Function parameter or member 'wake_q' not described in '__rt_mutex_futex_unlock' kernel/locking/rtmutex.c:1675: warning: Function parameter or member 'name' not described in '__rt_mutex_init' kernel/locking/rtmutex.c:1675: warning: Function parameter or member 'key' not described in '__rt_mutex_init' [ tglx: Change rt lock to rt_mutex for consistency sake ] Signed-off-by: Alex Shi Signed-off-by: Thomas Gleixner Acked-by: Will Deacon Link: https://lore.kernel.org/r/1605257895-5536-2-git-send-email-alex.shi@linux.alibaba.com --- kernel/locking/rtmutex.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index cfdd5b93264d7..a201e5ef9374d 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -1604,8 +1604,11 @@ void __sched rt_mutex_unlock(struct rt_mutex *lock) EXPORT_SYMBOL_GPL(rt_mutex_unlock); /** - * Futex variant, that since futex variants do not use the fast-path, can be - * simple and will not need to retry. + * __rt_mutex_futex_unlock - Futex variant, that since futex variants + * do not use the fast-path, can be simple and will not need to retry. + * + * @lock: The rt_mutex to be unlocked + * @wake_q: The wake queue head from which to get the next lock waiter */ bool __sched __rt_mutex_futex_unlock(struct rt_mutex *lock, struct wake_q_head *wake_q) @@ -1662,13 +1665,15 @@ void rt_mutex_destroy(struct rt_mutex *lock) EXPORT_SYMBOL_GPL(rt_mutex_destroy); /** - * __rt_mutex_init - initialize the rt lock + * __rt_mutex_init - initialize the rt_mutex * - * @lock: the rt lock to be initialized + * @lock: The rt_mutex to be initialized + * @name: The lock name used for debugging + * @key: The lock class key used for debugging * - * Initialize the rt lock to unlocked state. + * Initialize the rt_mutex to unlocked state. * - * Initializing of a locked rt lock is not allowed + * Initializing of a locked rt_mutex is not allowed */ void __rt_mutex_init(struct rt_mutex *lock, const char *name, struct lock_class_key *key) From 442187f3c2de40bab13b8f9751b37925bede73b0 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 26 Jan 2021 12:17:21 +0200 Subject: [PATCH 19/24] locking/rwsem: Remove empty rwsem.h This is a leftover from 7f26482a872c ("locking/percpu-rwsem: Remove the embedded rwsem") Signed-off-by: Nikolay Borisov Signed-off-by: Peter Zijlstra (Intel) Acked-by: Will Deacon Link: https://lkml.kernel.org/r/20210126101721.976027-1-nborisov@suse.com --- kernel/locking/rwsem.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 kernel/locking/rwsem.h diff --git a/kernel/locking/rwsem.h b/kernel/locking/rwsem.h deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 7f82e631d236cafd28518b998c6d4d8dc2ef68f6 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 1 Feb 2021 11:55:38 +0100 Subject: [PATCH 20/24] locking/lockdep: Avoid unmatched unlock Commit f6f48e180404 ("lockdep: Teach lockdep about "USED" <- "IN-NMI" inversions") overlooked that print_usage_bug() releases the graph_lock and called it without the graph lock held. Fixes: f6f48e180404 ("lockdep: Teach lockdep about "USED" <- "IN-NMI" inversions") Reported-by: Dmitry Vyukov Signed-off-by: Peter Zijlstra (Intel) Acked-by: Waiman Long Link: https://lkml.kernel.org/r/YBfkuyIfB1+VRxXP@hirez.programming.kicks-ass.net --- kernel/locking/lockdep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index ad9afd8c7eb9e..5104db0e23e44 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -3773,7 +3773,7 @@ static void print_usage_bug(struct task_struct *curr, struct held_lock *this, enum lock_usage_bit prev_bit, enum lock_usage_bit new_bit) { - if (!debug_locks_off_graph_unlock() || debug_locks_silent) + if (!debug_locks_off() || debug_locks_silent) return; pr_warn("\n"); @@ -3814,6 +3814,7 @@ valid_state(struct task_struct *curr, struct held_lock *this, enum lock_usage_bit new_bit, enum lock_usage_bit bad_bit) { if (unlikely(hlock_class(this)->usage_mask & (1 << bad_bit))) { + graph_unlock(); print_usage_bug(curr, this, bad_bit, new_bit); return 0; } From c8cc7e853192d520ab6a5957f5081034103587ae Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 9 Feb 2021 09:30:03 +0100 Subject: [PATCH 21/24] lockdep: Noinstr annotate warn_bogus_irq_restore() vmlinux.o: warning: objtool: lock_is_held_type()+0x107: call to warn_bogus_irq_restore() leaves .noinstr.text section As per the general rule that WARNs are allowed to violate noinstr to get out, annotate it away. Fixes: 997acaf6b4b5 ("lockdep: report broken irq restoration") Reported-by: Randy Dunlap Signed-off-by: Peter Zijlstra (Intel) Acked-by: Mark Rutland Acked-by: Randy Dunlap # build-tested Link: https://lkml.kernel.org/r/YCKyYg53mMp4E7YI@hirez.programming.kicks-ass.net --- kernel/locking/irqflag-debug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/locking/irqflag-debug.c b/kernel/locking/irqflag-debug.c index 9603d207a4ca9..810b50344d358 100644 --- a/kernel/locking/irqflag-debug.c +++ b/kernel/locking/irqflag-debug.c @@ -4,8 +4,10 @@ #include #include -void warn_bogus_irq_restore(void) +noinstr void warn_bogus_irq_restore(void) { + instrumentation_begin(); WARN_ONCE(1, "raw_local_irq_restore() called with IRQs enabled\n"); + instrumentation_end(); } EXPORT_SYMBOL(warn_bogus_irq_restore); From b38085ba60246fccc2f49d2ac162528dedbc4e71 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 10 Feb 2021 14:24:16 +0100 Subject: [PATCH 22/24] s390: Use arch_local_irq_{save,restore}() in early boot code Commit 997acaf6b4b5 ("lockdep: report broken irq restoration") makes compiling s390 fail because the irq enable/disable functions are now no longer fully contained in header files. Fixes: 997acaf6b4b5 ("lockdep: report broken irq restoration") Signed-off-by: Sven Schnelle Signed-off-by: Peter Zijlstra (Intel) --- drivers/s390/char/sclp_early_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c index ec9f8ad5341c5..b7329af076a0f 100644 --- a/drivers/s390/char/sclp_early_core.c +++ b/drivers/s390/char/sclp_early_core.c @@ -66,13 +66,13 @@ int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb) unsigned long flags; int rc; - raw_local_irq_save(flags); + flags = arch_local_irq_save(); rc = sclp_service_call(cmd, sccb); if (rc) goto out; sclp_early_wait_irq(); out: - raw_local_irq_restore(flags); + arch_local_irq_restore(flags); return rc; } From 0f319d49a4167e402b01b2b56639386f0b6846ba Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 10 Feb 2021 09:52:47 +0100 Subject: [PATCH 23/24] locking/mutex: Kill mutex_trylock_recursive() There are not users of mutex_trylock_recursive() in tree as of v5.11-rc7. Remove it. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20210210085248.219210-2-bigeasy@linutronix.de --- include/linux/mutex.h | 25 ------------------------- kernel/locking/mutex.c | 10 ---------- 2 files changed, 35 deletions(-) diff --git a/include/linux/mutex.h b/include/linux/mutex.h index dcd185cbfe793..0cd631a197276 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -199,29 +199,4 @@ extern void mutex_unlock(struct mutex *lock); extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); -/* - * These values are chosen such that FAIL and SUCCESS match the - * values of the regular mutex_trylock(). - */ -enum mutex_trylock_recursive_enum { - MUTEX_TRYLOCK_FAILED = 0, - MUTEX_TRYLOCK_SUCCESS = 1, - MUTEX_TRYLOCK_RECURSIVE, -}; - -/** - * mutex_trylock_recursive - trylock variant that allows recursive locking - * @lock: mutex to be locked - * - * This function should not be used, _ever_. It is purely for hysterical GEM - * raisins, and once those are gone this will be removed. - * - * Returns: - * - MUTEX_TRYLOCK_FAILED - trylock failed, - * - MUTEX_TRYLOCK_SUCCESS - lock acquired, - * - MUTEX_TRYLOCK_RECURSIVE - we already owned the lock. - */ -extern /* __deprecated */ __must_check enum mutex_trylock_recursive_enum -mutex_trylock_recursive(struct mutex *lock); - #endif /* __LINUX_MUTEX_H */ diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index 5352ce50a97e3..adb9350907688 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -86,16 +86,6 @@ bool mutex_is_locked(struct mutex *lock) } EXPORT_SYMBOL(mutex_is_locked); -__must_check enum mutex_trylock_recursive_enum -mutex_trylock_recursive(struct mutex *lock) -{ - if (unlikely(__mutex_owner(lock) == current)) - return MUTEX_TRYLOCK_RECURSIVE; - - return mutex_trylock(lock); -} -EXPORT_SYMBOL(mutex_trylock_recursive); - static inline unsigned long __owner_flags(unsigned long owner) { return owner & MUTEX_FLAGS; From 6c80408a8a0360fa9223b8c21c0ab8ef42e88bfe Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 10 Feb 2021 09:52:48 +0100 Subject: [PATCH 24/24] checkpatch: Don't check for mutex_trylock_recursive() mutex_trylock_recursive() has been removed from the tree, there is no need to check for it. Remove traces of mutex_trylock_recursive()'s existence. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20210210085248.219210-3-bigeasy@linutronix.de --- scripts/checkpatch.pl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 92e888ed939f9..15f7f4fa6b991 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -7069,12 +7069,6 @@ sub process { } } -# check for mutex_trylock_recursive usage - if ($line =~ /mutex_trylock_recursive/) { - ERROR("LOCKING", - "recursive locking is bad, do not use this ever.\n" . $herecurr); - } - # check for lockdep_set_novalidate_class if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || $line =~ /__lockdep_no_validate__\s*\)/ ) {