Skip to content

Commit d3f52f5

Browse files
paulmckrcuFrederic Weisbecker
authored andcommitted
srcu: Create an SRCU-fast-updown API
This commit creates an SRCU-fast-updown API, including DEFINE_SRCU_FAST_UPDOWN(), DEFINE_STATIC_SRCU_FAST_UPDOWN(), __init_srcu_struct_fast_updown(), init_srcu_struct_fast_updown(), srcu_read_lock_fast_updown(), srcu_read_unlock_fast_updown(), __srcu_read_lock_fast_updown(), and __srcu_read_unlock_fast_updown(). These are initially identical to their SRCU-fast counterparts, but both SRCU-fast and SRCU-fast-updown will be optimized in different directions by later commits. SRCU-fast will lack any sort of srcu_down_read() and srcu_up_read() APIs, which will enable extremely efficient NMI safety. For its part, SRCU-fast-updown will not be NMI safe, which will enable reasonably efficient implementations of srcu_down_read_fast() and srcu_up_read_fast(). This API fork happens to meet two different future use cases. * SRCU-fast will become the reimplementation basis for RCU-TASK-TRACE for consolidation. Since RCU-TASK-TRACE must be NMI safe, SRCU-fast must be as well. * SRCU-fast-updown will be needed for uretprobes code in order to get rid of the read-side memory barriers while still allowing entering the reader at task level while exiting it in a timer handler. This commit also adds rcutorture tests for the new APIs. This (annoyingly) needs to be in the same commit for bisectability. With this commit, the 0x8 value tests SRCU-fast-updown. However, most SRCU-fast testing will be via the RCU Tasks Trace wrappers. [ paulmck: Apply s/0x8/0x4/ missing change per Boqun Feng feedback. ] [ paulmck: Apply Akira Yokosawa feedback. ] Signed-off-by: Paul E. McKenney <paulmck@kernel.org> Cc: Andrii Nakryiko <andrii@kernel.org> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: <bpf@vger.kernel.org> Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
1 parent f2b7d62 commit d3f52f5

5 files changed

Lines changed: 183 additions & 16 deletions

File tree

include/linux/srcu.h

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ struct srcu_struct;
2828
int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
2929
#ifndef CONFIG_TINY_SRCU
3030
int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
31+
int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
32+
struct lock_class_key *key);
3133
#endif // #ifndef CONFIG_TINY_SRCU
3234

3335
#define init_srcu_struct(ssp) \
@@ -44,12 +46,20 @@ int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lo
4446
__init_srcu_struct_fast((ssp), #ssp, &__srcu_key); \
4547
})
4648

49+
#define init_srcu_struct_fast_updown(ssp) \
50+
({ \
51+
static struct lock_class_key __srcu_key; \
52+
\
53+
__init_srcu_struct_fast_updown((ssp), #ssp, &__srcu_key); \
54+
})
55+
4756
#define __SRCU_DEP_MAP_INIT(srcu_name) .dep_map = { .name = #srcu_name },
4857
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
4958

5059
int init_srcu_struct(struct srcu_struct *ssp);
5160
#ifndef CONFIG_TINY_SRCU
5261
int init_srcu_struct_fast(struct srcu_struct *ssp);
62+
int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
5363
#endif // #ifndef CONFIG_TINY_SRCU
5464

5565
#define __SRCU_DEP_MAP_INIT(srcu_name)
@@ -305,6 +315,46 @@ static inline struct srcu_ctr __percpu *srcu_read_lock_fast(struct srcu_struct *
305315
return retval;
306316
}
307317

318+
/**
319+
* srcu_read_lock_fast_updown - register a new reader for an SRCU-fast-updown structure.
320+
* @ssp: srcu_struct in which to register the new reader.
321+
*
322+
* Enter an SRCU read-side critical section, but for a light-weight
323+
* smp_mb()-free reader. See srcu_read_lock() for more information.
324+
* This function is compatible with srcu_down_read_fast(), but is not
325+
* NMI-safe.
326+
*
327+
* For srcu_read_lock_fast_updown() to be used on an srcu_struct
328+
* structure, that structure must have been defined using either
329+
* DEFINE_SRCU_FAST_UPDOWN() or DEFINE_STATIC_SRCU_FAST_UPDOWN() on the one
330+
* hand or initialized with init_srcu_struct_fast_updown() on the other.
331+
* Such an srcu_struct structure cannot be passed to any non-fast-updown
332+
* variant of srcu_read_{,un}lock() or srcu_{down,up}_read(). In kernels
333+
* built with CONFIG_PROVE_RCU=y, __srcu_check_read_flavor() will complain
334+
* bitterly if you ignore this * restriction.
335+
*
336+
* Grace-period auto-expediting is disabled for SRCU-fast-updown
337+
* srcu_struct structures because SRCU-fast-updown expedited grace periods
338+
* invoke synchronize_rcu_expedited(), IPIs and all. If you need expedited
339+
* SRCU-fast-updown grace periods, use synchronize_srcu_expedited().
340+
*
341+
* The srcu_read_lock_fast_updown() function can be invoked only from
342+
* those contexts where RCU is watching, that is, from contexts where
343+
* it would be legal to invoke rcu_read_lock(). Otherwise, lockdep will
344+
* complain.
345+
*/
346+
static inline struct srcu_ctr __percpu *srcu_read_lock_fast_updown(struct srcu_struct *ssp)
347+
__acquires(ssp)
348+
{
349+
struct srcu_ctr __percpu *retval;
350+
351+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_lock_fast_updown().");
352+
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST_UPDOWN);
353+
retval = __srcu_read_lock_fast_updown(ssp);
354+
rcu_try_lock_acquire(&ssp->dep_map);
355+
return retval;
356+
}
357+
308358
/*
309359
* Used by tracing, cannot be traced and cannot call lockdep.
310360
* See srcu_read_lock_fast() for more information.
@@ -335,8 +385,8 @@ static inline struct srcu_ctr __percpu *srcu_down_read_fast(struct srcu_struct *
335385
{
336386
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi());
337387
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_down_read_fast().");
338-
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST);
339-
return __srcu_read_lock_fast(ssp);
388+
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST_UPDOWN);
389+
return __srcu_read_lock_fast_updown(ssp);
340390
}
341391

342392
/**
@@ -432,6 +482,23 @@ static inline void srcu_read_unlock_fast(struct srcu_struct *ssp, struct srcu_ct
432482
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_unlock_fast().");
433483
}
434484

485+
/**
486+
* srcu_read_unlock_fast_updown - unregister a old reader from an SRCU-fast-updown structure.
487+
* @ssp: srcu_struct in which to unregister the old reader.
488+
* @scp: return value from corresponding srcu_read_lock_fast_updown().
489+
*
490+
* Exit an SRCU-fast-updown read-side critical section.
491+
*/
492+
static inline void
493+
srcu_read_unlock_fast_updown(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp) __releases(ssp)
494+
{
495+
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST_UPDOWN);
496+
srcu_lock_release(&ssp->dep_map);
497+
__srcu_read_unlock_fast_updown(ssp, scp);
498+
RCU_LOCKDEP_WARN(!rcu_is_watching(),
499+
"RCU must be watching srcu_read_unlock_fast_updown().");
500+
}
501+
435502
/*
436503
* Used by tracing, cannot be traced and cannot call lockdep.
437504
* See srcu_read_unlock_fast() for more information.
@@ -455,9 +522,9 @@ static inline void srcu_up_read_fast(struct srcu_struct *ssp, struct srcu_ctr __
455522
__releases(ssp)
456523
{
457524
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi());
458-
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST);
459-
__srcu_read_unlock_fast(ssp, scp);
460-
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_up_read_fast().");
525+
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST_UPDOWN);
526+
__srcu_read_unlock_fast_updown(ssp, scp);
527+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_up_read_fast_updown().");
461528
}
462529

463530
/**

include/linux/srcutiny.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,18 @@ void srcu_drive_gp(struct work_struct *wp);
5050
#define DEFINE_SRCU_FAST(name) DEFINE_SRCU(name)
5151
#define DEFINE_STATIC_SRCU_FAST(name) \
5252
static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name, name, name)
53+
#define DEFINE_SRCU_FAST_UPDOWN(name) DEFINE_SRCU(name)
54+
#define DEFINE_STATIC_SRCU_FAST_UPDOWN(name) \
55+
static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name, name, name)
5356

5457
// Dummy structure for srcu_notifier_head.
5558
struct srcu_usage { };
5659
#define __SRCU_USAGE_INIT(name) { }
5760
#define __init_srcu_struct_fast __init_srcu_struct
61+
#define __init_srcu_struct_fast_updown __init_srcu_struct
5862
#ifndef CONFIG_DEBUG_LOCK_ALLOC
5963
#define init_srcu_struct_fast init_srcu_struct
64+
#define init_srcu_struct_fast_updown init_srcu_struct
6065
#endif // #ifndef CONFIG_DEBUG_LOCK_ALLOC
6166

6267
void synchronize_srcu(struct srcu_struct *ssp);
@@ -100,6 +105,17 @@ static inline void __srcu_read_unlock_fast(struct srcu_struct *ssp, struct srcu_
100105
__srcu_read_unlock(ssp, __srcu_ptr_to_ctr(ssp, scp));
101106
}
102107

108+
static inline struct srcu_ctr __percpu *__srcu_read_lock_fast_updown(struct srcu_struct *ssp)
109+
{
110+
return __srcu_ctr_to_ptr(ssp, __srcu_read_lock(ssp));
111+
}
112+
113+
static inline
114+
void __srcu_read_unlock_fast_updown(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)
115+
{
116+
__srcu_read_unlock(ssp, __srcu_ptr_to_ctr(ssp, scp));
117+
}
118+
103119
static inline void synchronize_srcu_expedited(struct srcu_struct *ssp)
104120
{
105121
synchronize_srcu(ssp);

include/linux/srcutree.h

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,15 @@ struct srcu_struct {
199199
*
200200
* See include/linux/percpu-defs.h for the rules on per-CPU variables.
201201
*
202-
* DEFINE_SRCU_FAST() creates an srcu_struct and associated structures
203-
* whose readers must be of the SRCU-fast variety.
202+
* DEFINE_SRCU_FAST() and DEFINE_STATIC_SRCU_FAST create an srcu_struct
203+
* and associated structures whose readers must be of the SRCU-fast variety.
204+
* DEFINE_SRCU_FAST_UPDOWN() and DEFINE_STATIC_SRCU_FAST_UPDOWN() create
205+
* an srcu_struct and associated structures whose readers must be of the
206+
* SRCU-fast-updown variety. The key point (aside from error checking) with
207+
* both varieties is that the grace periods must use synchronize_rcu()
208+
* instead of smp_mb(), and given that the first (for example)
209+
* srcu_read_lock_fast() might race with the first synchronize_srcu(),
210+
* this different must be specified at initialization time.
204211
*/
205212
#ifdef MODULE
206213
# define __DEFINE_SRCU(name, fast, is_static) \
@@ -221,6 +228,10 @@ struct srcu_struct {
221228
#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, 0, static)
222229
#define DEFINE_SRCU_FAST(name) __DEFINE_SRCU(name, SRCU_READ_FLAVOR_FAST, /* not static */)
223230
#define DEFINE_STATIC_SRCU_FAST(name) __DEFINE_SRCU(name, SRCU_READ_FLAVOR_FAST, static)
231+
#define DEFINE_SRCU_FAST_UPDOWN(name) __DEFINE_SRCU(name, SRCU_READ_FLAVOR_FAST_UPDOWN, \
232+
/* not static */)
233+
#define DEFINE_STATIC_SRCU_FAST_UPDOWN(name) \
234+
__DEFINE_SRCU(name, SRCU_READ_FLAVOR_FAST_UPDOWN, static)
224235

225236
int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp);
226237
void synchronize_srcu_expedited(struct srcu_struct *ssp);
@@ -305,6 +316,46 @@ __srcu_read_unlock_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)
305316
atomic_long_inc(raw_cpu_ptr(&scp->srcu_unlocks)); // Z, and implicit RCU reader.
306317
}
307318

319+
/*
320+
* Counts the new reader in the appropriate per-CPU element of the
321+
* srcu_struct. Returns a pointer that must be passed to the matching
322+
* srcu_read_unlock_fast_updown(). This type of reader is compatible
323+
* with srcu_down_read_fast() and srcu_up_read_fast().
324+
*
325+
* See the __srcu_read_lock_fast() comment for more details.
326+
*/
327+
static inline
328+
struct srcu_ctr __percpu notrace *__srcu_read_lock_fast_updown(struct srcu_struct *ssp)
329+
{
330+
struct srcu_ctr __percpu *scp = READ_ONCE(ssp->srcu_ctrp);
331+
332+
if (!IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE))
333+
this_cpu_inc(scp->srcu_locks.counter); // Y, and implicit RCU reader.
334+
else
335+
atomic_long_inc(raw_cpu_ptr(&scp->srcu_locks)); // Y, and implicit RCU reader.
336+
barrier(); /* Avoid leaking the critical section. */
337+
return scp;
338+
}
339+
340+
/*
341+
* Removes the count for the old reader from the appropriate
342+
* per-CPU element of the srcu_struct. Note that this may well be a
343+
* different CPU than that which was incremented by the corresponding
344+
* srcu_read_lock_fast(), but it must be within the same task.
345+
*
346+
* Please see the __srcu_read_lock_fast() function's header comment for
347+
* information on implicit RCU readers and NMI safety.
348+
*/
349+
static inline void notrace
350+
__srcu_read_unlock_fast_updown(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)
351+
{
352+
barrier(); /* Avoid leaking the critical section. */
353+
if (!IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE))
354+
this_cpu_inc(scp->srcu_unlocks.counter); // Z, and implicit RCU reader.
355+
else
356+
atomic_long_inc(raw_cpu_ptr(&scp->srcu_unlocks)); // Z, and implicit RCU reader.
357+
}
358+
308359
void __srcu_check_read_flavor(struct srcu_struct *ssp, int read_flavor);
309360

310361
// Record SRCU-reader usage type only for CONFIG_PROVE_RCU=y kernels.

kernel/rcu/rcutorture.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,7 @@ static struct rcu_torture_ops rcu_busted_ops = {
693693

694694
DEFINE_STATIC_SRCU(srcu_ctl);
695695
DEFINE_STATIC_SRCU_FAST(srcu_ctlf);
696+
DEFINE_STATIC_SRCU_FAST_UPDOWN(srcu_ctlfud);
696697
static struct srcu_struct srcu_ctld;
697698
static struct srcu_struct *srcu_ctlp = &srcu_ctl;
698699
static struct rcu_torture_ops srcud_ops;
@@ -703,7 +704,7 @@ static void srcu_torture_init(void)
703704
if (reader_flavor & SRCU_READ_FLAVOR_FAST)
704705
srcu_ctlp = &srcu_ctlf;
705706
if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN)
706-
srcu_ctlp = &srcu_ctlf;
707+
srcu_ctlp = &srcu_ctlfud;
707708
}
708709

709710
static void srcu_get_gp_data(int *flags, unsigned long *gp_seq)
@@ -736,7 +737,7 @@ static int srcu_torture_read_lock(void)
736737
ret += idx << 2;
737738
}
738739
if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN) {
739-
scp = srcu_read_lock_fast(srcu_ctlp);
740+
scp = srcu_read_lock_fast_updown(srcu_ctlp);
740741
idx = __srcu_ptr_to_ctr(srcu_ctlp, scp);
741742
WARN_ON_ONCE(idx & ~0x1);
742743
ret += idx << 3;
@@ -767,9 +768,10 @@ static void srcu_torture_read_unlock(int idx)
767768
{
768769
WARN_ON_ONCE((reader_flavor && (idx & ~reader_flavor)) || (!reader_flavor && (idx & ~0x1)));
769770
if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN)
770-
srcu_read_unlock_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3));
771+
srcu_read_unlock_fast_updown(srcu_ctlp,
772+
__srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3));
771773
if (reader_flavor & SRCU_READ_FLAVOR_FAST)
772-
srcu_read_unlock_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 2));
774+
srcu_read_unlock_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x4) >> 2));
773775
if (reader_flavor & SRCU_READ_FLAVOR_NMI)
774776
srcu_read_unlock_nmisafe(srcu_ctlp, (idx & 0x2) >> 1);
775777
if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) || !(reader_flavor & SRCU_READ_FLAVOR_ALL))
@@ -919,7 +921,7 @@ static void srcud_torture_init(void)
919921
{
920922
rcu_sync_torture_init();
921923
if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN)
922-
WARN_ON(init_srcu_struct_fast(&srcu_ctld));
924+
WARN_ON(init_srcu_struct_fast_updown(&srcu_ctld));
923925
else if (reader_flavor & SRCU_READ_FLAVOR_FAST)
924926
WARN_ON(init_srcu_struct_fast(&srcu_ctld));
925927
else

kernel/rcu/srcutree.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,24 @@ int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lo
309309
}
310310
EXPORT_SYMBOL_GPL(__init_srcu_struct_fast);
311311

312+
int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
313+
struct lock_class_key *key)
314+
{
315+
ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_FAST_UPDOWN;
316+
return __init_srcu_struct_common(ssp, name, key);
317+
}
318+
EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);
319+
312320
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
313321

314322
/**
315323
* init_srcu_struct - initialize a sleep-RCU structure
316324
* @ssp: structure to initialize.
317325
*
318-
* Must invoke this on a given srcu_struct before passing that srcu_struct
326+
* Use this in place of DEFINE_SRCU() and DEFINE_STATIC_SRCU()
327+
* for non-static srcu_struct structures that are to be passed to
328+
* srcu_read_lock(), srcu_read_lock_nmisafe(), and friends. It is necessary
329+
* to invoke this on a given srcu_struct before passing that srcu_struct
319330
* to any other function. Each srcu_struct represents a separate domain
320331
* of SRCU protection.
321332
*/
@@ -330,9 +341,11 @@ EXPORT_SYMBOL_GPL(init_srcu_struct);
330341
* init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure
331342
* @ssp: structure to initialize.
332343
*
333-
* Must invoke this on a given srcu_struct before passing that srcu_struct
334-
* to any other function. Each srcu_struct represents a separate domain
335-
* of SRCU protection.
344+
* Use this in place of DEFINE_SRCU_FAST() and DEFINE_STATIC_SRCU_FAST()
345+
* for non-static srcu_struct structures that are to be passed to
346+
* srcu_read_lock_fast() and friends. It is necessary to invoke this on a
347+
* given srcu_struct before passing that srcu_struct to any other function.
348+
* Each srcu_struct represents a separate domain of SRCU protection.
336349
*/
337350
int init_srcu_struct_fast(struct srcu_struct *ssp)
338351
{
@@ -341,6 +354,24 @@ int init_srcu_struct_fast(struct srcu_struct *ssp)
341354
}
342355
EXPORT_SYMBOL_GPL(init_srcu_struct_fast);
343356

357+
/**
358+
* init_srcu_struct_fast_updown - initialize a fast-reader up/down sleep-RCU structure
359+
* @ssp: structure to initialize.
360+
*
361+
* Use this function in place of DEFINE_SRCU_FAST_UPDOWN() and
362+
* DEFINE_STATIC_SRCU_FAST_UPDOWN() for non-static srcu_struct
363+
* structures that are to be passed to srcu_read_lock_fast_updown(),
364+
* srcu_down_read_fast(), and friends. It is necessary to invoke this on a
365+
* given srcu_struct before passing that srcu_struct to any other function.
366+
* Each srcu_struct represents a separate domain of SRCU protection.
367+
*/
368+
int init_srcu_struct_fast_updown(struct srcu_struct *ssp)
369+
{
370+
ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_FAST_UPDOWN;
371+
return init_srcu_struct_fields(ssp, false);
372+
}
373+
EXPORT_SYMBOL_GPL(init_srcu_struct_fast_updown);
374+
344375
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
345376

346377
/*

0 commit comments

Comments
 (0)