Skip to content

Commit d4841a5

Browse files
committed
srcu: Create and srcu_read_lock_nmisafe() and srcu_read_unlock_nmisafe()
This commit creates a pair of new srcu_read_lock_nmisafe() and srcu_read_unlock_nmisafe() functions, which allow SRCU readers in both NMI handlers and in process and IRQ context. It is bad practice to mix the existing and the new _nmisafe() primitives on the same srcu_struct structure. Use one set or the other, not both. [ paulmck: Apply kernel test robot feedback. ] Link: https://lore.kernel.org/all/20220910221947.171557773@linutronix.de/ Signed-off-by: Paul E. McKenney <paulmck@kernel.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: John Ogness <john.ogness@linutronix.de> Cc: Petr Mladek <pmladek@suse.com>
1 parent 68684b8 commit d4841a5

File tree

4 files changed

+86
-6
lines changed

4 files changed

+86
-6
lines changed

include/linux/srcu.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ void call_srcu(struct srcu_struct *ssp, struct rcu_head *head,
5959
void cleanup_srcu_struct(struct srcu_struct *ssp);
6060
int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp);
6161
void __srcu_read_unlock(struct srcu_struct *ssp, int idx) __releases(ssp);
62+
int __srcu_read_lock_nmisafe(struct srcu_struct *ssp) __acquires(ssp);
63+
void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx) __releases(ssp);
6264
void synchronize_srcu(struct srcu_struct *ssp);
6365
unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp);
6466
unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp);
@@ -166,6 +168,25 @@ static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp)
166168
return retval;
167169
}
168170

171+
/**
172+
* srcu_read_lock_nmisafe - register a new reader for an SRCU-protected structure.
173+
* @ssp: srcu_struct in which to register the new reader.
174+
*
175+
* Enter an SRCU read-side critical section, but in an NMI-safe manner.
176+
* See srcu_read_lock() for more information.
177+
*/
178+
static inline int srcu_read_lock_nmisafe(struct srcu_struct *ssp) __acquires(ssp)
179+
{
180+
int retval;
181+
182+
if (IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE))
183+
retval = __srcu_read_lock_nmisafe(ssp);
184+
else
185+
retval = __srcu_read_lock(ssp);
186+
rcu_lock_acquire(&(ssp)->dep_map);
187+
return retval;
188+
}
189+
169190
/* Used by tracing, cannot be traced and cannot invoke lockdep. */
170191
static inline notrace int
171192
srcu_read_lock_notrace(struct srcu_struct *ssp) __acquires(ssp)
@@ -191,6 +212,24 @@ static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx)
191212
__srcu_read_unlock(ssp, idx);
192213
}
193214

215+
/**
216+
* srcu_read_unlock_nmisafe - unregister a old reader from an SRCU-protected structure.
217+
* @ssp: srcu_struct in which to unregister the old reader.
218+
* @idx: return value from corresponding srcu_read_lock().
219+
*
220+
* Exit an SRCU read-side critical section, but in an NMI-safe manner.
221+
*/
222+
static inline void srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)
223+
__releases(ssp)
224+
{
225+
WARN_ON_ONCE(idx & ~0x1);
226+
rcu_lock_release(&(ssp)->dep_map);
227+
if (IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE))
228+
__srcu_read_unlock_nmisafe(ssp, idx);
229+
else
230+
__srcu_read_unlock(ssp, idx);
231+
}
232+
194233
/* Used by tracing, cannot be traced and cannot call lockdep. */
195234
static inline notrace void
196235
srcu_read_unlock_notrace(struct srcu_struct *ssp, int idx) __releases(ssp)

kernel/rcu/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ config TREE_SRCU
7272
help
7373
This option selects the full-fledged version of SRCU.
7474

75+
config NEED_SRCU_NMI_SAFE
76+
def_bool HAVE_NMI && !ARCH_HAS_NMI_SAFE_THIS_CPU_OPS && !TINY_SRCU
77+
7578
config TASKS_RCU_GENERIC
7679
def_bool TASKS_RCU || TASKS_RUDE_RCU || TASKS_TRACE_RCU
7780
select SRCU

kernel/rcu/rcutorture.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,10 +623,14 @@ static struct rcu_torture_ops rcu_busted_ops = {
623623
DEFINE_STATIC_SRCU(srcu_ctl);
624624
static struct srcu_struct srcu_ctld;
625625
static struct srcu_struct *srcu_ctlp = &srcu_ctl;
626+
static struct rcu_torture_ops srcud_ops;
626627

627628
static int srcu_torture_read_lock(void) __acquires(srcu_ctlp)
628629
{
629-
return srcu_read_lock(srcu_ctlp);
630+
if (cur_ops == &srcud_ops)
631+
return srcu_read_lock_nmisafe(srcu_ctlp);
632+
else
633+
return srcu_read_lock(srcu_ctlp);
630634
}
631635

632636
static void
@@ -650,7 +654,10 @@ srcu_read_delay(struct torture_random_state *rrsp, struct rt_read_seg *rtrsp)
650654

651655
static void srcu_torture_read_unlock(int idx) __releases(srcu_ctlp)
652656
{
653-
srcu_read_unlock(srcu_ctlp, idx);
657+
if (cur_ops == &srcud_ops)
658+
srcu_read_unlock_nmisafe(srcu_ctlp, idx);
659+
else
660+
srcu_read_unlock(srcu_ctlp, idx);
654661
}
655662

656663
static int torture_srcu_read_lock_held(void)

kernel/rcu/srcutree.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,37 @@ void __srcu_read_unlock(struct srcu_struct *ssp, int idx)
654654
}
655655
EXPORT_SYMBOL_GPL(__srcu_read_unlock);
656656

657+
/*
658+
* Counts the new reader in the appropriate per-CPU element of the
659+
* srcu_struct, but in an NMI-safe manner using RMW atomics.
660+
* Returns an index that must be passed to the matching srcu_read_unlock().
661+
*/
662+
int __srcu_read_lock_nmisafe(struct srcu_struct *ssp)
663+
{
664+
int idx;
665+
struct srcu_data *sdp = raw_cpu_ptr(ssp->sda);
666+
667+
idx = READ_ONCE(ssp->srcu_idx) & 0x1;
668+
atomic_long_inc(&sdp->srcu_lock_count[idx]);
669+
smp_mb__after_atomic(); /* B */ /* Avoid leaking the critical section. */
670+
return idx;
671+
}
672+
EXPORT_SYMBOL_GPL(__srcu_read_lock_nmisafe);
673+
674+
/*
675+
* Removes the count for the old reader from the appropriate per-CPU
676+
* element of the srcu_struct. Note that this may well be a different
677+
* CPU than that which was incremented by the corresponding srcu_read_lock().
678+
*/
679+
void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)
680+
{
681+
struct srcu_data *sdp = raw_cpu_ptr(ssp->sda);
682+
683+
smp_mb__before_atomic(); /* C */ /* Avoid leaking the critical section. */
684+
atomic_long_inc(&sdp->srcu_unlock_count[idx]);
685+
}
686+
EXPORT_SYMBOL_GPL(__srcu_read_unlock_nmisafe);
687+
657688
/*
658689
* Start an SRCU grace period.
659690
*/
@@ -1090,7 +1121,7 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp,
10901121
int ss_state;
10911122

10921123
check_init_srcu_struct(ssp);
1093-
idx = srcu_read_lock(ssp);
1124+
idx = __srcu_read_lock_nmisafe(ssp);
10941125
ss_state = smp_load_acquire(&ssp->srcu_size_state);
10951126
if (ss_state < SRCU_SIZE_WAIT_CALL)
10961127
sdp = per_cpu_ptr(ssp->sda, 0);
@@ -1123,7 +1154,7 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp,
11231154
srcu_funnel_gp_start(ssp, sdp, s, do_norm);
11241155
else if (needexp)
11251156
srcu_funnel_exp_start(ssp, sdp_mynode, s);
1126-
srcu_read_unlock(ssp, idx);
1157+
__srcu_read_unlock_nmisafe(ssp, idx);
11271158
return s;
11281159
}
11291160

@@ -1427,13 +1458,13 @@ void srcu_barrier(struct srcu_struct *ssp)
14271458
/* Initial count prevents reaching zero until all CBs are posted. */
14281459
atomic_set(&ssp->srcu_barrier_cpu_cnt, 1);
14291460

1430-
idx = srcu_read_lock(ssp);
1461+
idx = __srcu_read_lock_nmisafe(ssp);
14311462
if (smp_load_acquire(&ssp->srcu_size_state) < SRCU_SIZE_WAIT_BARRIER)
14321463
srcu_barrier_one_cpu(ssp, per_cpu_ptr(ssp->sda, 0));
14331464
else
14341465
for_each_possible_cpu(cpu)
14351466
srcu_barrier_one_cpu(ssp, per_cpu_ptr(ssp->sda, cpu));
1436-
srcu_read_unlock(ssp, idx);
1467+
__srcu_read_unlock_nmisafe(ssp, idx);
14371468

14381469
/* Remove the initial count, at which point reaching zero can happen. */
14391470
if (atomic_dec_and_test(&ssp->srcu_barrier_cpu_cnt))

0 commit comments

Comments
 (0)