Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

srcu: Permit Tiny SRCU srcu_read_unlock() with interrupts disabled

The current Tiny SRCU implementation of srcu_read_unlock() awakens
the grace-period processing when exiting the outermost SRCU read-side
critical section. However, not all Linux-kernel configurations and
contexts permit swake_up_one() to be invoked while interrupts are
disabled, and this can result in indefinitely extended SRCU grace periods.
This commit therefore only invokes swake_up_one() when interrupts are
enabled, and introduces polling to the grace-period workqueue handler.

Reported-by: kernel test robot <oliver.sang@intel.com>
Reported-by: Zqiang <qiang.zhang@linux.dev>
Closes: https://lore.kernel.org/oe-lkp/202508261642.b15eefbb-lkp@intel.com
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>

authored by

Paul E. McKenney and committed by
Frederic Weisbecker
58ac42f2 3a866087

+9 -4
+9 -4
kernel/rcu/srcutiny.c
··· 106 106 newval = READ_ONCE(ssp->srcu_lock_nesting[idx]) - 1; 107 107 WRITE_ONCE(ssp->srcu_lock_nesting[idx], newval); 108 108 preempt_enable(); 109 - if (!newval && READ_ONCE(ssp->srcu_gp_waiting) && in_task()) 109 + if (!newval && READ_ONCE(ssp->srcu_gp_waiting) && in_task() && !irqs_disabled()) 110 110 swake_up_one(&ssp->srcu_wq); 111 111 } 112 112 EXPORT_SYMBOL_GPL(__srcu_read_unlock); 113 113 114 114 /* 115 115 * Workqueue handler to drive one grace period and invoke any callbacks 116 - * that become ready as a result. Single-CPU and !PREEMPTION operation 117 - * means that we get away with murder on synchronization. ;-) 116 + * that become ready as a result. Single-CPU operation and preemption 117 + * disabling mean that we get away with murder on synchronization. ;-) 118 118 */ 119 119 void srcu_drive_gp(struct work_struct *wp) 120 120 { ··· 141 141 WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1); 142 142 WRITE_ONCE(ssp->srcu_gp_waiting, true); /* srcu_read_unlock() wakes! */ 143 143 preempt_enable(); 144 - swait_event_exclusive(ssp->srcu_wq, !READ_ONCE(ssp->srcu_lock_nesting[idx])); 144 + do { 145 + // Deadlock issues prevent __srcu_read_unlock() from 146 + // doing an unconditional wakeup, so polling is required. 147 + swait_event_timeout_exclusive(ssp->srcu_wq, 148 + !READ_ONCE(ssp->srcu_lock_nesting[idx]), HZ / 10); 149 + } while (READ_ONCE(ssp->srcu_lock_nesting[idx])); 145 150 preempt_disable(); // Needed for PREEMPT_LAZY 146 151 WRITE_ONCE(ssp->srcu_gp_waiting, false); /* srcu_read_unlock() cheap. */ 147 152 WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1);