locking/selftest: Add test cases for queued_read_lock()

Add two self test cases for the following case:

	P0:			P1:			P2:

				<in irq handler>
	spin_lock_irq(&slock)	read_lock(&rwlock)
							write_lock_irq(&rwlock)
	read_lock(&rwlock)	spin_lock(&slock)

, which is a deadlock, as the read_lock() on P0 cannot get the lock
because of the fairness.

	P0:			P1:			P2:

	<in irq handler>
	spin_lock(&slock)	read_lock(&rwlock)
							write_lock(&rwlock)
	read_lock(&rwlock)	spin_lock_irq(&slock)

, which is not a deadlock, as the read_lock() on P0 can get the lock
because it could use the unfair fastpass.

Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20200807074238.1632519-19-boqun.feng@gmail.com
This commit is contained in:
Boqun Feng 2020-08-07 15:42:37 +08:00 committed by Peter Zijlstra
parent 108dc42ed3
commit ad56450db8
1 changed files with 104 additions and 0 deletions

View File

@ -2201,6 +2201,108 @@ static void ww_tests(void)
pr_cont("\n");
}
/*
* <in hardirq handler>
* read_lock(&A);
* <hardirq disable>
* spin_lock(&B);
* spin_lock(&B);
* read_lock(&A);
*
* is a deadlock.
*/
static void queued_read_lock_hardirq_RE_Er(void)
{
HARDIRQ_ENTER();
read_lock(&rwlock_A);
LOCK(B);
UNLOCK(B);
read_unlock(&rwlock_A);
HARDIRQ_EXIT();
HARDIRQ_DISABLE();
LOCK(B);
read_lock(&rwlock_A);
read_unlock(&rwlock_A);
UNLOCK(B);
HARDIRQ_ENABLE();
}
/*
* <in hardirq handler>
* spin_lock(&B);
* <hardirq disable>
* read_lock(&A);
* read_lock(&A);
* spin_lock(&B);
*
* is not a deadlock.
*/
static void queued_read_lock_hardirq_ER_rE(void)
{
HARDIRQ_ENTER();
LOCK(B);
read_lock(&rwlock_A);
read_unlock(&rwlock_A);
UNLOCK(B);
HARDIRQ_EXIT();
HARDIRQ_DISABLE();
read_lock(&rwlock_A);
LOCK(B);
UNLOCK(B);
read_unlock(&rwlock_A);
HARDIRQ_ENABLE();
}
/*
* <hardirq disable>
* spin_lock(&B);
* read_lock(&A);
* <in hardirq handler>
* spin_lock(&B);
* read_lock(&A);
*
* is a deadlock. Because the two read_lock()s are both non-recursive readers.
*/
static void queued_read_lock_hardirq_inversion(void)
{
HARDIRQ_ENTER();
LOCK(B);
UNLOCK(B);
HARDIRQ_EXIT();
HARDIRQ_DISABLE();
LOCK(B);
read_lock(&rwlock_A);
read_unlock(&rwlock_A);
UNLOCK(B);
HARDIRQ_ENABLE();
read_lock(&rwlock_A);
read_unlock(&rwlock_A);
}
static void queued_read_lock_tests(void)
{
printk(" --------------------------------------------------------------------------\n");
printk(" | queued read lock tests |\n");
printk(" ---------------------------\n");
print_testname("hardirq read-lock/lock-read");
dotest(queued_read_lock_hardirq_RE_Er, FAILURE, LOCKTYPE_RWLOCK);
pr_cont("\n");
print_testname("hardirq lock-read/read-lock");
dotest(queued_read_lock_hardirq_ER_rE, SUCCESS, LOCKTYPE_RWLOCK);
pr_cont("\n");
print_testname("hardirq inversion");
dotest(queued_read_lock_hardirq_inversion, FAILURE, LOCKTYPE_RWLOCK);
pr_cont("\n");
}
void locking_selftest(void)
{
/*
@ -2318,6 +2420,8 @@ void locking_selftest(void)
/*
* queued_read_lock() specific test cases can be put here
*/
if (IS_ENABLED(CONFIG_QUEUED_RWLOCKS))
queued_read_lock_tests();
if (unexpected_testcase_failures) {
printk("-----------------------------------------------------------------\n");