Skip to content

Commit 71921a9

Browse files
crwood-rhpaulmckrcu
authored andcommitted
rcutorture: Avoid problematic critical section nesting on PREEMPT_RT
rcutorture is generating some nesting scenarios that are not compatible on PREEMPT_RT. For example: preempt_disable(); rcu_read_lock_bh(); preempt_enable(); rcu_read_unlock_bh(); The problem here is that on PREEMPT_RT the bottom halves have to be disabled and enabled in preemptible context. Reorder locking: start with BH locking and continue with then with disabling preemption or interrupts. In the unlocking do it reverse by first enabling interrupts and preemption and BH at the very end. Ensure that on PREEMPT_RT BH locking remains unchanged if in non-preemptible context. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/20210819182035.GF4126399@paulmck-ThinkPad-P17-Gen-1 Signed-off-by: Scott Wood <[email protected]> [bigeasy: Drop ATOM_BH, make it only about changing BH in atomic context. Allow enabling RCU in IRQ-off section. Reword commit message.] Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]>
1 parent fd13fe1 commit 71921a9

File tree

1 file changed

+36
-12
lines changed

1 file changed

+36
-12
lines changed

kernel/rcu/rcutorture.c

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,28 +1432,34 @@ static void rcutorture_one_extend(int *readstate, int newstate,
14321432
/* First, put new protection in place to avoid critical-section gap. */
14331433
if (statesnew & RCUTORTURE_RDR_BH)
14341434
local_bh_disable();
1435+
if (statesnew & RCUTORTURE_RDR_RBH)
1436+
rcu_read_lock_bh();
14351437
if (statesnew & RCUTORTURE_RDR_IRQ)
14361438
local_irq_disable();
14371439
if (statesnew & RCUTORTURE_RDR_PREEMPT)
14381440
preempt_disable();
1439-
if (statesnew & RCUTORTURE_RDR_RBH)
1440-
rcu_read_lock_bh();
14411441
if (statesnew & RCUTORTURE_RDR_SCHED)
14421442
rcu_read_lock_sched();
14431443
if (statesnew & RCUTORTURE_RDR_RCU)
14441444
idxnew = cur_ops->readlock() << RCUTORTURE_RDR_SHIFT;
14451445

1446-
/* Next, remove old protection, irq first due to bh conflict. */
1446+
/*
1447+
* Next, remove old protection, in decreasing order of strength
1448+
* to avoid unlock paths that aren't safe in the stronger
1449+
* context. Namely: BH can not be enabled with disabled interrupts.
1450+
* Additionally PREEMPT_RT requires that BH is enabled in preemptible
1451+
* context.
1452+
*/
14471453
if (statesold & RCUTORTURE_RDR_IRQ)
14481454
local_irq_enable();
1449-
if (statesold & RCUTORTURE_RDR_BH)
1450-
local_bh_enable();
14511455
if (statesold & RCUTORTURE_RDR_PREEMPT)
14521456
preempt_enable();
1453-
if (statesold & RCUTORTURE_RDR_RBH)
1454-
rcu_read_unlock_bh();
14551457
if (statesold & RCUTORTURE_RDR_SCHED)
14561458
rcu_read_unlock_sched();
1459+
if (statesold & RCUTORTURE_RDR_BH)
1460+
local_bh_enable();
1461+
if (statesold & RCUTORTURE_RDR_RBH)
1462+
rcu_read_unlock_bh();
14571463
if (statesold & RCUTORTURE_RDR_RCU) {
14581464
bool lockit = !statesnew && !(torture_random(trsp) & 0xffff);
14591465

@@ -1496,18 +1502,36 @@ rcutorture_extend_mask(int oldmask, struct torture_random_state *trsp)
14961502
int mask = rcutorture_extend_mask_max();
14971503
unsigned long randmask1 = torture_random(trsp) >> 8;
14981504
unsigned long randmask2 = randmask1 >> 3;
1505+
unsigned long preempts = RCUTORTURE_RDR_PREEMPT | RCUTORTURE_RDR_SCHED;
1506+
unsigned long preempts_irq = preempts | RCUTORTURE_RDR_IRQ;
1507+
unsigned long bhs = RCUTORTURE_RDR_BH | RCUTORTURE_RDR_RBH;
14991508

15001509
WARN_ON_ONCE(mask >> RCUTORTURE_RDR_SHIFT);
15011510
/* Mostly only one bit (need preemption!), sometimes lots of bits. */
15021511
if (!(randmask1 & 0x7))
15031512
mask = mask & randmask2;
15041513
else
15051514
mask = mask & (1 << (randmask2 % RCUTORTURE_RDR_NBITS));
1506-
/* Can't enable bh w/irq disabled. */
1507-
if ((mask & RCUTORTURE_RDR_IRQ) &&
1508-
((!(mask & RCUTORTURE_RDR_BH) && (oldmask & RCUTORTURE_RDR_BH)) ||
1509-
(!(mask & RCUTORTURE_RDR_RBH) && (oldmask & RCUTORTURE_RDR_RBH))))
1510-
mask |= RCUTORTURE_RDR_BH | RCUTORTURE_RDR_RBH;
1515+
1516+
/*
1517+
* Can't enable bh w/irq disabled.
1518+
*/
1519+
if (mask & RCUTORTURE_RDR_IRQ)
1520+
mask |= oldmask & bhs;
1521+
1522+
/*
1523+
* Ideally these sequences would be detected in debug builds
1524+
* (regardless of RT), but until then don't stop testing
1525+
* them on non-RT.
1526+
*/
1527+
if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
1528+
/* Can't modify BH in atomic context */
1529+
if (oldmask & preempts_irq)
1530+
mask &= ~bhs;
1531+
if ((oldmask | mask) & preempts_irq)
1532+
mask |= oldmask & bhs;
1533+
}
1534+
15111535
return mask ?: RCUTORTURE_RDR_RCU;
15121536
}
15131537

0 commit comments

Comments
 (0)