Skip to content

Commit 45a9209

Browse files
Frederic Weisbeckeropsiff
authored andcommitted
rcu: Fix racy re-initialization of irq_work causing hangs
commit 61399e0c5410567ef60cb1cda34cca42903842e3 upstream. RCU re-initializes the deferred QS irq work everytime before attempting to queue it. However there are situations where the irq work is attempted to be queued even though it is already queued. In that case re-initializing messes-up with the irq work queue that is about to be handled. The chances for that to happen are higher when the architecture doesn't support self-IPIs and irq work are then all lazy, such as with the following sequence: 1) rcu_read_unlock() is called when IRQs are disabled and there is a grace period involving blocked tasks on the node. The irq work is then initialized and queued. 2) The related tasks are unblocked and the CPU quiescent state is reported. rdp->defer_qs_iw_pending is reset to DEFER_QS_IDLE, allowing the irq work to be requeued in the future (note the previous one hasn't fired yet). 3) A new grace period starts and the node has blocked tasks. 4) rcu_read_unlock() is called when IRQs are disabled again. The irq work is re-initialized (but it's queued! and its node is cleared) and requeued. Which means it's requeued to itself. 5) The irq work finally fires with the tick. But since it was requeued to itself, it loops and hangs. Fix this with initializing the irq work only once before the CPU boots. Fixes: b41642c87716 ("rcu: Fix rcu_read_unlock() deadloop due to IRQ work") Reported-by: kernel test robot <[email protected]> Closes: https://lore.kernel.org/oe-lkp/[email protected] Signed-off-by: Frederic Weisbecker <[email protected]> Reviewed-by: Joel Fernandes <[email protected]> Signed-off-by: Neeraj Upadhyay (AMD) <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> (cherry picked from commit 2328010117d0d347b5a8adb2a975dde4f7eddff6)
1 parent 2944a2f commit 45a9209

File tree

3 files changed

+9
-2
lines changed

3 files changed

+9
-2
lines changed

kernel/rcu/tree.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4432,6 +4432,8 @@ int rcutree_prepare_cpu(unsigned int cpu)
44324432
rdp->rcu_iw_gp_seq = rdp->gp_seq - 1;
44334433
trace_rcu_grace_period(rcu_state.name, rdp->gp_seq, TPS("cpuonl"));
44344434
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
4435+
4436+
rcu_preempt_deferred_qs_init(rdp);
44354437
rcu_spawn_one_boost_kthread(rnp);
44364438
rcu_spawn_cpu_nocb_kthread(cpu);
44374439
WRITE_ONCE(rcu_state.n_online_cpus, rcu_state.n_online_cpus + 1);

kernel/rcu/tree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ static int rcu_print_task_exp_stall(struct rcu_node *rnp);
463463
static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp);
464464
static void rcu_flavor_sched_clock_irq(int user);
465465
static void dump_blkd_tasks(struct rcu_node *rnp, int ncheck);
466+
static void rcu_preempt_deferred_qs_init(struct rcu_data *rdp);
466467
static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags);
467468
static void rcu_preempt_boost_start_gp(struct rcu_node *rnp);
468469
static bool rcu_is_callbacks_kthread(struct rcu_data *rdp);

kernel/rcu/tree_plugin.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,6 @@ static void rcu_read_unlock_special(struct task_struct *t)
687687
cpu_online(rdp->cpu)) {
688688
// Get scheduler to re-evaluate and call hooks.
689689
// If !IRQ_WORK, FQS scan will eventually IPI.
690-
rdp->defer_qs_iw =
691-
IRQ_WORK_INIT_HARD(rcu_preempt_deferred_qs_handler);
692690
rdp->defer_qs_iw_pending = DEFER_QS_PENDING;
693691
irq_work_queue_on(&rdp->defer_qs_iw, rdp->cpu);
694692
}
@@ -828,6 +826,10 @@ dump_blkd_tasks(struct rcu_node *rnp, int ncheck)
828826
}
829827
}
830828

829+
static void rcu_preempt_deferred_qs_init(struct rcu_data *rdp)
830+
{
831+
rdp->defer_qs_iw = IRQ_WORK_INIT_HARD(rcu_preempt_deferred_qs_handler);
832+
}
831833
#else /* #ifdef CONFIG_PREEMPT_RCU */
832834

833835
/*
@@ -1027,6 +1029,8 @@ dump_blkd_tasks(struct rcu_node *rnp, int ncheck)
10271029
WARN_ON_ONCE(!list_empty(&rnp->blkd_tasks));
10281030
}
10291031

1032+
static void rcu_preempt_deferred_qs_init(struct rcu_data *rdp) { }
1033+
10301034
#endif /* #else #ifdef CONFIG_PREEMPT_RCU */
10311035

10321036
/*

0 commit comments

Comments
 (0)