Add testtype 7 to verify that lockdep detects an IRQ-context
mismatch when an atomic SRCU read-side critical section is held
with IRQs enabled on one CPU and synchronize_srcu_atomic() is
called from an IPI handler on another CPU.

This covers the cross-CPU case that cannot be detected by
checking the current task's held locks.

Co-developed-by: Zqiang <[email protected]>
Signed-off-by: Zqiang <[email protected]>
Signed-off-by: Kunwu Chan <[email protected]>
---
 kernel/rcu/rcutorture.c                       | 27 +++++++++++++-
 .../selftests/rcutorture/bin/srcu_lockdep.sh  | 36 +++++++++++++++++++
 2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index ac9c726cb391..6f5a1d635f94 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -4695,6 +4695,8 @@ DEFINE_STATIC_SRCU_ATOMIC(srcu7_atomic);
 DEFINE_STATIC_SRCU_ATOMIC(srcu8_atomic);
 DEFINE_STATIC_SRCU_ATOMIC(srcu9_atomic);
 
+DEFINE_STATIC_SRCU_ATOMIC(srcu_irq);
+
 static int srcu_lockdep_next(const char *f, const char *fl, const char *fs, 
const char *fu, int i,
                             int cyclelen, int deadlock)
 {
@@ -4709,6 +4711,11 @@ static int srcu_lockdep_next(const char *f, const char 
*fl, const char *fs, cons
        return j;
 }
 
+static void srcu_sync_irq(void *unused)
+{
+       synchronize_srcu_atomic(&srcu_irq);
+}
+
 // Test lockdep on SRCU-based deadlock scenarios.
 static void rcu_torture_init_srcu_lockdep(void)
 {
@@ -4892,13 +4899,31 @@ static void rcu_torture_init_srcu_lockdep(void)
                return;
        }
 
+       if (testtype == 7) {
+               int cpu;
+
+               for (i = 0; i < cyclelen; i++) {
+                       idx = srcu_read_lock_atomic(&srcu_irq);
+                       cpu = cpumask_any_but(cpu_online_mask,
+                                             smp_processor_id());
+                       if (cpu < nr_cpu_ids) {
+                               pr_info("%s: CPU%d sending IPI to CPU%d\n",
+                                       __func__, smp_processor_id(), cpu);
+                               smp_call_function_single(cpu, srcu_sync_irq,
+                                       NULL, 1);
+                       }
+                       srcu_read_unlock_atomic(&srcu_irq, idx);
+               }
+               return;
+       }
+
 err_out:
        pr_info("%s: test_srcu_lockdep = %05d does nothing.\n", __func__, 
test_srcu_lockdep);
        pr_info("%s: test_srcu_lockdep = DNNL.\n", __func__);
        pr_info("%s: D: Deadlock if nonzero.\n", __func__);
        pr_info("%s: NN: Test number, 0=SRCU, 1=SRCU/mutex, 2=SRCU/rwsem, 
3=SRCU/Tasks Trace RCU, 4=SRCU_ATOMIC, ",
                __func__);
-       pr_cont("5=SRCU_ATOMIC/raw_spinlock, 6=synchronize_srcu_atomic inside 
rcu_read_lock.\n");
+       pr_cont("5=SRCU_ATOMIC/raw_spinlock, 6=synchronize_srcu_atomic inside 
rcu_read_lock, 7=atomic SRCU cross-CPU IRQ context mismatch.\n");
        pr_info("%s: L: Cycle length.\n", __func__);
        if (!IS_ENABLED(CONFIG_TASKS_TRACE_RCU))
                pr_info("%s: NN=3 disallowed because kernel is built with 
CONFIG_TASKS_TRACE_RCU=n\n", __func__);
diff --git a/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh 
b/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
index 3acaffe52c78..c29a39f7d891 100755
--- a/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
+++ b/tools/testing/selftests/rcutorture/bin/srcu_lockdep.sh
@@ -115,6 +115,42 @@ do
        fi
 done
 
+# Verify that synchronize_srcu_atomic() detects IRQ context mismatch
+# when SRCU reader previously ran with IRQs enabled.
+for c in 1 2 3
+do
+       err=
+       val=$((1000+7*10+c))
+       tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration 5s \
+               --configs "SRCU-P" \
+               --kconfig "CONFIG_FORCE_NEED_SRCU_NMI_SAFE=y" \
+               --bootargs "rcutorture.test_srcu_lockdep=$val" \
+               --trust-make --datestamp "$ds/$val" > "$T/kvm.sh.out" 2>&1
+       ret=$?
+       mv "$T/kvm.sh.out" "$RCUTORTURE/res/$ds/$val"
+       if ! grep -q '^CONFIG_PROVE_LOCKING=y' .config
+       then
+               echo "rcu_torture_init_srcu_lockdep:Error: 
CONFIG_PROVE_LOCKING" \
+                               "disabled in rcutorture SRCU-P scenario"
+               err=1
+       fi
+       if test "$ret" -eq 0
+       then
+               err=1
+               echo -n Missing lockdep warning for > 
"$RCUTORTURE/res/$ds/$val/kvm.sh.err"
+       elif ! grep -q "inconsistent {HARDIRQ-ON-R}" 
"$RCUTORTURE/res/$ds/$val/SRCU-P/console.log"
+       then
+               err=1
+               echo -n Missing lockdep warning for > 
"$RCUTORTURE/res/$ds/$val/kvm.sh.err"
+       fi
+       if test -n "$err"
+       then
+               grep "rcu_torture_init_srcu_lockdep: test_srcu_lockdep = " 
"$RCUTORTURE/res/$ds/$val/SRCU-P/console.log" | sed -e 
's/^.*rcu_torture_init_srcu_lockdep://' >> "$RCUTORTURE/res/$ds/$val/kvm.sh.err"
+               cat "$RCUTORTURE/res/$ds/$val/kvm.sh.err"
+               nerrs=$((nerrs+1))
+       fi
+done
+
 # Test lockdep-enabled testing of mixed SRCU readers.
 for val in 0x1 0xf
 do
-- 
2.43.0


Reply via email to