From de1ce5265b332689f4beea383e3538ad54796322 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 12 Aug 2021 14:25:38 +0200 Subject: [PATCH 084/158] lockdep/selftests: Unbalanced migrate_disable() & rcu_read_lock() The tests with unbalanced lock() + unlock() operation leave a modified preemption counter behind which is then reset to its original value after the test. The spin_lock() function on PREEMPT_RT does not include a preempt_disable() statement but migrate_disable() and read_rcu_lock(). As a consequence both counter never get back to their original value and system explodes later after the selftest. In the double-unlock case on PREEMPT_RT, the migrate_disable() and RCU code will trigger which should be avoided. These counter should not be decremented below their initial value. Save both counters and bring them back to their original value after the test. In the double-unlock case, increment both counter in advance to they become balanced after the double unlock. Signed-off-by: Sebastian Andrzej Siewior --- lib/locking-selftest.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) Index: linux-5.15.19-rt29/lib/locking-selftest.c =================================================================== --- linux-5.15.19-rt29.orig/lib/locking-selftest.c +++ linux-5.15.19-rt29/lib/locking-selftest.c @@ -712,12 +712,18 @@ GENERATE_TESTCASE(ABCDBCDA_rtmutex); #undef E +#ifdef CONFIG_PREEMPT_RT +# define RT_PREPARE_DBL_UNLOCK() { migrate_disable(); rcu_read_lock(); } +#else +# define RT_PREPARE_DBL_UNLOCK() +#endif /* * Double unlock: */ #define E() \ \ LOCK(A); \ + RT_PREPARE_DBL_UNLOCK(); \ UNLOCK(A); \ UNLOCK(A); /* fail */ @@ -1398,7 +1404,13 @@ static int unexpected_testcase_failures; static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) { - unsigned long saved_preempt_count = preempt_count(); + int saved_preempt_count = preempt_count(); +#ifdef CONFIG_PREEMPT_RT +#ifdef CONFIG_SMP + int saved_mgd_count = current->migration_disabled; +#endif + int saved_rcu_count = current->rcu_read_lock_nesting; +#endif WARN_ON(irqs_disabled()); @@ -1432,6 +1444,18 @@ static void dotest(void (*testcase_fn)(v * count, so restore it: */ preempt_count_set(saved_preempt_count); + +#ifdef CONFIG_PREEMPT_RT +#ifdef CONFIG_SMP + while (current->migration_disabled > saved_mgd_count) + migrate_enable(); +#endif + + while (current->rcu_read_lock_nesting > saved_rcu_count) + rcu_read_unlock(); + WARN_ON_ONCE(current->rcu_read_lock_nesting < saved_rcu_count); +#endif + #ifdef CONFIG_TRACE_IRQFLAGS if (softirq_count()) current->softirqs_enabled = 0;