diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index ed204b3f4b84..9fa0987c203b 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -701,10 +701,13 @@ void cleanup_srcu_struct(struct srcu_struct *ssp) for_each_possible_cpu(cpu) { struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu); - // Call srcu_barrier() before this cleanup_srcu_struct() - // to avoid triggering this WARN_ON(). - if (WARN_ON(timer_delete_sync(&sdp->delay_work) && - rcu_segcblist_n_cbs(&sdp->srcu_cblist)) && + // Deleting a pending delay timer that still has callbacks + // behind it is not an error: queueing the work here and + // flushing it below invokes them. A caller that really did + // forget srcu_barrier() is caught by the WARN_ON() after the + // flush. + if (timer_delete_sync(&sdp->delay_work) && + rcu_segcblist_n_cbs(&sdp->srcu_cblist) && rcu_cpu_beenfullyonline(sdp->cpu)) queue_work_on(sdp->cpu, rcu_gp_wq, &sdp->work); flush_work(&sdp->work);