Skip to content

Commit 6364dd8

Browse files
paulmckrcuFrederic Weisbecker
authored andcommitted
srcu: Add srcu_read_lock_lite() and srcu_read_unlock_lite()
This patch adds srcu_read_lock_lite() and srcu_read_unlock_lite(), which dispense with the read-side smp_mb() but also are restricted to code regions that RCU is watching. If a given srcu_struct structure uses srcu_read_lock_lite() and srcu_read_unlock_lite(), it is not permitted to use any other SRCU read-side marker, before, during, or after. Another price of light-weight readers is heavier weight grace periods. Such readers mean that SRCU grace periods on srcu_struct structures used by light-weight readers will incur at least two calls to synchronize_rcu(). In addition, normal SRCU grace periods for light-weight-reader srcu_struct structures never auto-expedite. Note that expedited SRCU grace periods for light-weight-reader srcu_struct structures still invoke synchronize_rcu(), not synchronize_srcu_expedited(). Something about wishing to keep the IPIs down to a dull roar. The srcu_read_lock_lite() and srcu_read_unlock_lite() functions may not (repeat, *not*) be used from NMI handlers, but if this is needed, an additional flavor of SRCU reader can be added by some future commit. [ paulmck: Apply Alexei Starovoitov expediting feedback. ] [ paulmck: Apply kernel test robot feedback. ] Signed-off-by: Paul E. McKenney <[email protected]> Tested-by: kernel test robot <[email protected]> Cc: Alexei Starovoitov <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Kent Overstreet <[email protected]> Cc: <[email protected]> Reviewed-by: Neeraj Upadhyay <[email protected]> Signed-off-by: Frederic Weisbecker <[email protected]>
1 parent 05829be commit 6364dd8

File tree

3 files changed

+122
-12
lines changed

3 files changed

+122
-12
lines changed

include/linux/srcu.h

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ void call_srcu(struct srcu_struct *ssp, struct rcu_head *head,
5656
void cleanup_srcu_struct(struct srcu_struct *ssp);
5757
int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp);
5858
void __srcu_read_unlock(struct srcu_struct *ssp, int idx) __releases(ssp);
59+
#ifdef CONFIG_TINY_SRCU
60+
#define __srcu_read_lock_lite __srcu_read_lock
61+
#define __srcu_read_unlock_lite __srcu_read_unlock
62+
#else // #ifdef CONFIG_TINY_SRCU
63+
int __srcu_read_lock_lite(struct srcu_struct *ssp) __acquires(ssp);
64+
void __srcu_read_unlock_lite(struct srcu_struct *ssp, int idx) __releases(ssp);
65+
#endif // #else // #ifdef CONFIG_TINY_SRCU
5966
void synchronize_srcu(struct srcu_struct *ssp);
6067

6168
#define SRCU_GET_STATE_COMPLETED 0x1
@@ -179,7 +186,7 @@ static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
179186
#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_TREE_SRCU)
180187
void srcu_check_read_flavor(struct srcu_struct *ssp, int read_flavor);
181188
#else
182-
static inline void srcu_check_read_flavor(struct srcu_struct *ssp, int read_flavor) { }
189+
#define srcu_check_read_flavor(ssp, read_flavor) do { } while (0)
183190
#endif
184191

185192

@@ -249,6 +256,32 @@ static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp)
249256
return retval;
250257
}
251258

259+
/**
260+
* srcu_read_lock_lite - register a new reader for an SRCU-protected structure.
261+
* @ssp: srcu_struct in which to register the new reader.
262+
*
263+
* Enter an SRCU read-side critical section, but for a light-weight
264+
* smp_mb()-free reader. See srcu_read_lock() for more information.
265+
*
266+
* If srcu_read_lock_lite() is ever used on an srcu_struct structure,
267+
* then none of the other flavors may be used, whether before, during,
268+
* or after. Note that grace-period auto-expediting is disabled for _lite
269+
* srcu_struct structures because auto-expedited grace periods invoke
270+
* synchronize_rcu_expedited(), IPIs and all.
271+
*
272+
* Note that srcu_read_lock_lite() can be invoked only from those contexts
273+
* where RCU is watching. Otherwise, lockdep will complain.
274+
*/
275+
static inline int srcu_read_lock_lite(struct srcu_struct *ssp) __acquires(ssp)
276+
{
277+
int retval;
278+
279+
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_LITE);
280+
retval = __srcu_read_lock_lite(ssp);
281+
rcu_try_lock_acquire(&ssp->dep_map);
282+
return retval;
283+
}
284+
252285
/**
253286
* srcu_read_lock_nmisafe - register a new reader for an SRCU-protected structure.
254287
* @ssp: srcu_struct in which to register the new reader.
@@ -325,6 +358,22 @@ static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx)
325358
__srcu_read_unlock(ssp, idx);
326359
}
327360

361+
/**
362+
* srcu_read_unlock_lite - unregister a old reader from an SRCU-protected structure.
363+
* @ssp: srcu_struct in which to unregister the old reader.
364+
* @idx: return value from corresponding srcu_read_lock().
365+
*
366+
* Exit a light-weight SRCU read-side critical section.
367+
*/
368+
static inline void srcu_read_unlock_lite(struct srcu_struct *ssp, int idx)
369+
__releases(ssp)
370+
{
371+
WARN_ON_ONCE(idx & ~0x1);
372+
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_LITE);
373+
srcu_lock_release(&ssp->dep_map);
374+
__srcu_read_unlock(ssp, idx);
375+
}
376+
328377
/**
329378
* srcu_read_unlock_nmisafe - unregister a old reader from an SRCU-protected structure.
330379
* @ssp: srcu_struct in which to unregister the old reader.

include/linux/srcutree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ struct srcu_data {
4646
/* Values for ->srcu_reader_flavor. */
4747
#define SRCU_READ_FLAVOR_NORMAL 0x1 // srcu_read_lock().
4848
#define SRCU_READ_FLAVOR_NMI 0x2 // srcu_read_lock_nmisafe().
49+
#define SRCU_READ_FLAVOR_LITE 0x4 // srcu_read_lock_lite().
4950

5051
/*
5152
* Node in SRCU combining tree, similar in function to rcu_data.

kernel/rcu/srcutree.c

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -429,20 +429,29 @@ static bool srcu_gp_is_expedited(struct srcu_struct *ssp)
429429
}
430430

431431
/*
432-
* Returns approximate total of the readers' ->srcu_lock_count[] values
433-
* for the rank of per-CPU counters specified by idx.
432+
* Computes approximate total of the readers' ->srcu_lock_count[] values
433+
* for the rank of per-CPU counters specified by idx, and returns true if
434+
* the caller did the proper barrier (gp), and if the count of the locks
435+
* matches that of the unlocks passed in.
434436
*/
435-
static unsigned long srcu_readers_lock_idx(struct srcu_struct *ssp, int idx)
437+
static bool srcu_readers_lock_idx(struct srcu_struct *ssp, int idx, bool gp, unsigned long unlocks)
436438
{
437439
int cpu;
440+
unsigned long mask = 0;
438441
unsigned long sum = 0;
439442

440443
for_each_possible_cpu(cpu) {
441444
struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
442445

443446
sum += atomic_long_read(&sdp->srcu_lock_count[idx]);
447+
if (IS_ENABLED(CONFIG_PROVE_RCU))
448+
mask = mask | READ_ONCE(sdp->srcu_reader_flavor);
444449
}
445-
return sum;
450+
WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && (mask & (mask - 1)),
451+
"Mixed reader flavors for srcu_struct at %ps.\n", ssp);
452+
if (mask & SRCU_READ_FLAVOR_LITE && !gp)
453+
return false;
454+
return sum == unlocks;
446455
}
447456

448457
/*
@@ -473,6 +482,7 @@ static unsigned long srcu_readers_unlock_idx(struct srcu_struct *ssp, int idx)
473482
*/
474483
static bool srcu_readers_active_idx_check(struct srcu_struct *ssp, int idx)
475484
{
485+
bool did_gp = !!(raw_cpu_read(ssp->sda->srcu_reader_flavor) & SRCU_READ_FLAVOR_LITE);
476486
unsigned long unlocks;
477487

478488
unlocks = srcu_readers_unlock_idx(ssp, idx);
@@ -482,13 +492,16 @@ static bool srcu_readers_active_idx_check(struct srcu_struct *ssp, int idx)
482492
* unlock is counted. Needs to be a smp_mb() as the read side may
483493
* contain a read from a variable that is written to before the
484494
* synchronize_srcu() in the write side. In this case smp_mb()s
485-
* A and B act like the store buffering pattern.
495+
* A and B (or X and Y) act like the store buffering pattern.
486496
*
487-
* This smp_mb() also pairs with smp_mb() C to prevent accesses
488-
* after the synchronize_srcu() from being executed before the
489-
* grace period ends.
497+
* This smp_mb() also pairs with smp_mb() C (or, in the case of X,
498+
* Z) to prevent accesses after the synchronize_srcu() from being
499+
* executed before the grace period ends.
490500
*/
491-
smp_mb(); /* A */
501+
if (!did_gp)
502+
smp_mb(); /* A */
503+
else
504+
synchronize_rcu(); /* X */
492505

493506
/*
494507
* If the locks are the same as the unlocks, then there must have
@@ -546,7 +559,7 @@ static bool srcu_readers_active_idx_check(struct srcu_struct *ssp, int idx)
546559
* which are unlikely to be configured with an address space fully
547560
* populated with memory, at least not anytime soon.
548561
*/
549-
return srcu_readers_lock_idx(ssp, idx) == unlocks;
562+
return srcu_readers_lock_idx(ssp, idx, did_gp, unlocks);
550563
}
551564

552565
/**
@@ -750,6 +763,47 @@ void __srcu_read_unlock(struct srcu_struct *ssp, int idx)
750763
}
751764
EXPORT_SYMBOL_GPL(__srcu_read_unlock);
752765

766+
/*
767+
* Counts the new reader in the appropriate per-CPU element of the
768+
* srcu_struct. Returns an index that must be passed to the matching
769+
* srcu_read_unlock_lite().
770+
*
771+
* Note that this_cpu_inc() is an RCU read-side critical section either
772+
* because it disables interrupts, because it is a single instruction,
773+
* or because it is a read-modify-write atomic operation, depending on
774+
* the whims of the architecture.
775+
*/
776+
int __srcu_read_lock_lite(struct srcu_struct *ssp)
777+
{
778+
int idx;
779+
780+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_lock_lite().");
781+
idx = READ_ONCE(ssp->srcu_idx) & 0x1;
782+
this_cpu_inc(ssp->sda->srcu_lock_count[idx].counter); /* Y */
783+
barrier(); /* Avoid leaking the critical section. */
784+
return idx;
785+
}
786+
EXPORT_SYMBOL_GPL(__srcu_read_lock_lite);
787+
788+
/*
789+
* Removes the count for the old reader from the appropriate
790+
* per-CPU element of the srcu_struct. Note that this may well be a
791+
* different CPU than that which was incremented by the corresponding
792+
* srcu_read_lock_lite(), but it must be within the same task.
793+
*
794+
* Note that this_cpu_inc() is an RCU read-side critical section either
795+
* because it disables interrupts, because it is a single instruction,
796+
* or because it is a read-modify-write atomic operation, depending on
797+
* the whims of the architecture.
798+
*/
799+
void __srcu_read_unlock_lite(struct srcu_struct *ssp, int idx)
800+
{
801+
barrier(); /* Avoid leaking the critical section. */
802+
this_cpu_inc(ssp->sda->srcu_unlock_count[idx].counter); /* Z */
803+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_unlock_lite().");
804+
}
805+
EXPORT_SYMBOL_GPL(__srcu_read_unlock_lite);
806+
753807
#ifdef CONFIG_NEED_SRCU_NMI_SAFE
754808

755809
/*
@@ -1134,6 +1188,8 @@ static void srcu_flip(struct srcu_struct *ssp)
11341188
* it stays until either (1) Compilers learn about this sort of
11351189
* control dependency or (2) Some production workload running on
11361190
* a production system is unduly delayed by this slowpath smp_mb().
1191+
* Except for _lite() readers, where it is inoperative, which
1192+
* means that it is a good thing that it is redundant.
11371193
*/
11381194
smp_mb(); /* E */ /* Pairs with B and C. */
11391195

@@ -1152,7 +1208,8 @@ static void srcu_flip(struct srcu_struct *ssp)
11521208

11531209
/*
11541210
* If SRCU is likely idle, in other words, the next SRCU grace period
1155-
* should be expedited, return true, otherwise return false.
1211+
* should be expedited, return true, otherwise return false. Except that
1212+
* in the presence of _lite() readers, always return false.
11561213
*
11571214
* Note that it is OK for several current from-idle requests for a new
11581215
* grace period from idle to specify expediting because they will all end
@@ -1181,6 +1238,9 @@ static bool srcu_should_expedite(struct srcu_struct *ssp)
11811238
unsigned long tlast;
11821239

11831240
check_init_srcu_struct(ssp);
1241+
/* If _lite() readers, don't do unsolicited expediting. */
1242+
if (this_cpu_read(ssp->sda->srcu_reader_flavor) & SRCU_READ_FLAVOR_LITE)
1243+
return false;
11841244
/* If the local srcu_data structure has callbacks, not idle. */
11851245
sdp = raw_cpu_ptr(ssp->sda);
11861246
spin_lock_irqsave_rcu_node(sdp, flags);

0 commit comments

Comments
 (0)