Skip to content

Commit 63fea40

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/[email protected]/ Signed-off-by: Paul E. McKenney <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: John Ogness <[email protected]> Cc: Petr Mladek <[email protected]>
1 parent 68684b8 commit 63fea40

File tree

6 files changed

+98
-6
lines changed

6 files changed

+98
-6
lines changed

include/linux/srcu.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,25 @@ static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp)
166166
return retval;
167167
}
168168

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

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

include/linux/srcutiny.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,15 @@ static inline void srcu_torture_stats_print(struct srcu_struct *ssp,
9090
data_race(READ_ONCE(ssp->srcu_idx_max)));
9191
}
9292

93+
static inline int __srcu_read_lock_nmisafe(struct srcu_struct *ssp)
94+
{
95+
BUG();
96+
return 0;
97+
}
98+
99+
static inline void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)
100+
{
101+
BUG();
102+
}
103+
93104
#endif

include/linux/srcutree.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,7 @@ void synchronize_srcu_expedited(struct srcu_struct *ssp);
154154
void srcu_barrier(struct srcu_struct *ssp);
155155
void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf);
156156

157+
int __srcu_read_lock_nmisafe(struct srcu_struct *ssp) __acquires(ssp);
158+
void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx) __releases(ssp);
159+
157160
#endif

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)