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

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>

authored by

Paul E. McKenney and committed by
Frederic Weisbecker
d3f52f53 f2b7d625

+183 -16
+72 -5
include/linux/srcu.h
··· 28 28 int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key); 29 29 #ifndef CONFIG_TINY_SRCU 30 30 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); 31 33 #endif // #ifndef CONFIG_TINY_SRCU 32 34 33 35 #define init_srcu_struct(ssp) \ ··· 46 44 __init_srcu_struct_fast((ssp), #ssp, &__srcu_key); \ 47 45 }) 48 46 47 + #define init_srcu_struct_fast_updown(ssp) \ 48 + ({ \ 49 + static struct lock_class_key __srcu_key; \ 50 + \ 51 + __init_srcu_struct_fast_updown((ssp), #ssp, &__srcu_key); \ 52 + }) 53 + 49 54 #define __SRCU_DEP_MAP_INIT(srcu_name) .dep_map = { .name = #srcu_name }, 50 55 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 51 56 52 57 int init_srcu_struct(struct srcu_struct *ssp); 53 58 #ifndef CONFIG_TINY_SRCU 54 59 int init_srcu_struct_fast(struct srcu_struct *ssp); 60 + int init_srcu_struct_fast_updown(struct srcu_struct *ssp); 55 61 #endif // #ifndef CONFIG_TINY_SRCU 56 62 57 63 #define __SRCU_DEP_MAP_INIT(srcu_name) ··· 315 305 return retval; 316 306 } 317 307 308 + /** 309 + * srcu_read_lock_fast_updown - register a new reader for an SRCU-fast-updown structure. 310 + * @ssp: srcu_struct in which to register the new reader. 311 + * 312 + * Enter an SRCU read-side critical section, but for a light-weight 313 + * smp_mb()-free reader. See srcu_read_lock() for more information. 314 + * This function is compatible with srcu_down_read_fast(), but is not 315 + * NMI-safe. 316 + * 317 + * For srcu_read_lock_fast_updown() to be used on an srcu_struct 318 + * structure, that structure must have been defined using either 319 + * DEFINE_SRCU_FAST_UPDOWN() or DEFINE_STATIC_SRCU_FAST_UPDOWN() on the one 320 + * hand or initialized with init_srcu_struct_fast_updown() on the other. 321 + * Such an srcu_struct structure cannot be passed to any non-fast-updown 322 + * variant of srcu_read_{,un}lock() or srcu_{down,up}_read(). In kernels 323 + * built with CONFIG_PROVE_RCU=y, __srcu_check_read_flavor() will complain 324 + * bitterly if you ignore this * restriction. 325 + * 326 + * Grace-period auto-expediting is disabled for SRCU-fast-updown 327 + * srcu_struct structures because SRCU-fast-updown expedited grace periods 328 + * invoke synchronize_rcu_expedited(), IPIs and all. If you need expedited 329 + * SRCU-fast-updown grace periods, use synchronize_srcu_expedited(). 330 + * 331 + * The srcu_read_lock_fast_updown() function can be invoked only from 332 + * those contexts where RCU is watching, that is, from contexts where 333 + * it would be legal to invoke rcu_read_lock(). Otherwise, lockdep will 334 + * complain. 335 + */ 336 + static inline struct srcu_ctr __percpu *srcu_read_lock_fast_updown(struct srcu_struct *ssp) 337 + __acquires(ssp) 338 + { 339 + struct srcu_ctr __percpu *retval; 340 + 341 + RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_lock_fast_updown()."); 342 + srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST_UPDOWN); 343 + retval = __srcu_read_lock_fast_updown(ssp); 344 + rcu_try_lock_acquire(&ssp->dep_map); 345 + return retval; 346 + } 347 + 318 348 /* 319 349 * Used by tracing, cannot be traced and cannot call lockdep. 320 350 * See srcu_read_lock_fast() for more information. ··· 385 335 { 386 336 WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi()); 387 337 RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_down_read_fast()."); 388 - srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST); 389 - return __srcu_read_lock_fast(ssp); 338 + srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST_UPDOWN); 339 + return __srcu_read_lock_fast_updown(ssp); 390 340 } 391 341 392 342 /** ··· 482 432 RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_unlock_fast()."); 483 433 } 484 434 435 + /** 436 + * srcu_read_unlock_fast_updown - unregister a old reader from an SRCU-fast-updown structure. 437 + * @ssp: srcu_struct in which to unregister the old reader. 438 + * @scp: return value from corresponding srcu_read_lock_fast_updown(). 439 + * 440 + * Exit an SRCU-fast-updown read-side critical section. 441 + */ 442 + static inline void 443 + srcu_read_unlock_fast_updown(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp) __releases(ssp) 444 + { 445 + srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST_UPDOWN); 446 + srcu_lock_release(&ssp->dep_map); 447 + __srcu_read_unlock_fast_updown(ssp, scp); 448 + RCU_LOCKDEP_WARN(!rcu_is_watching(), 449 + "RCU must be watching srcu_read_unlock_fast_updown()."); 450 + } 451 + 485 452 /* 486 453 * Used by tracing, cannot be traced and cannot call lockdep. 487 454 * See srcu_read_unlock_fast() for more information. ··· 522 455 __releases(ssp) 523 456 { 524 457 WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi()); 525 - srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST); 526 - __srcu_read_unlock_fast(ssp, scp); 527 - RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_up_read_fast()."); 458 + srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST_UPDOWN); 459 + __srcu_read_unlock_fast_updown(ssp, scp); 460 + RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_up_read_fast_updown()."); 528 461 } 529 462 530 463 /**
+16
include/linux/srcutiny.h
··· 50 50 #define DEFINE_SRCU_FAST(name) DEFINE_SRCU(name) 51 51 #define DEFINE_STATIC_SRCU_FAST(name) \ 52 52 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) 53 56 54 57 // Dummy structure for srcu_notifier_head. 55 58 struct srcu_usage { }; 56 59 #define __SRCU_USAGE_INIT(name) { } 57 60 #define __init_srcu_struct_fast __init_srcu_struct 61 + #define __init_srcu_struct_fast_updown __init_srcu_struct 58 62 #ifndef CONFIG_DEBUG_LOCK_ALLOC 59 63 #define init_srcu_struct_fast init_srcu_struct 64 + #define init_srcu_struct_fast_updown init_srcu_struct 60 65 #endif // #ifndef CONFIG_DEBUG_LOCK_ALLOC 61 66 62 67 void synchronize_srcu(struct srcu_struct *ssp); ··· 101 96 } 102 97 103 98 static inline void __srcu_read_unlock_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp) 99 + { 100 + __srcu_read_unlock(ssp, __srcu_ptr_to_ctr(ssp, scp)); 101 + } 102 + 103 + static inline struct srcu_ctr __percpu *__srcu_read_lock_fast_updown(struct srcu_struct *ssp) 104 + { 105 + return __srcu_ctr_to_ptr(ssp, __srcu_read_lock(ssp)); 106 + } 107 + 108 + static inline 109 + void __srcu_read_unlock_fast_updown(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp) 104 110 { 105 111 __srcu_read_unlock(ssp, __srcu_ptr_to_ctr(ssp, scp)); 106 112 }
+53 -2
include/linux/srcutree.h
··· 199 199 * 200 200 * See include/linux/percpu-defs.h for the rules on per-CPU variables. 201 201 * 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. 204 211 */ 205 212 #ifdef MODULE 206 213 # define __DEFINE_SRCU(name, fast, is_static) \ ··· 228 221 #define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, 0, static) 229 222 #define DEFINE_SRCU_FAST(name) __DEFINE_SRCU(name, SRCU_READ_FLAVOR_FAST, /* not static */) 230 223 #define DEFINE_STATIC_SRCU_FAST(name) __DEFINE_SRCU(name, SRCU_READ_FLAVOR_FAST, static) 224 + #define DEFINE_SRCU_FAST_UPDOWN(name) __DEFINE_SRCU(name, SRCU_READ_FLAVOR_FAST_UPDOWN, \ 225 + /* not static */) 226 + #define DEFINE_STATIC_SRCU_FAST_UPDOWN(name) \ 227 + __DEFINE_SRCU(name, SRCU_READ_FLAVOR_FAST_UPDOWN, static) 231 228 232 229 int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp); 233 230 void synchronize_srcu_expedited(struct srcu_struct *ssp); ··· 308 297 */ 309 298 static inline void notrace 310 299 __srcu_read_unlock_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp) 300 + { 301 + barrier(); /* Avoid leaking the critical section. */ 302 + if (!IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE)) 303 + this_cpu_inc(scp->srcu_unlocks.counter); // Z, and implicit RCU reader. 304 + else 305 + atomic_long_inc(raw_cpu_ptr(&scp->srcu_unlocks)); // Z, and implicit RCU reader. 306 + } 307 + 308 + /* 309 + * Counts the new reader in the appropriate per-CPU element of the 310 + * srcu_struct. Returns a pointer that must be passed to the matching 311 + * srcu_read_unlock_fast_updown(). This type of reader is compatible 312 + * with srcu_down_read_fast() and srcu_up_read_fast(). 313 + * 314 + * See the __srcu_read_lock_fast() comment for more details. 315 + */ 316 + static inline 317 + struct srcu_ctr __percpu notrace *__srcu_read_lock_fast_updown(struct srcu_struct *ssp) 318 + { 319 + struct srcu_ctr __percpu *scp = READ_ONCE(ssp->srcu_ctrp); 320 + 321 + if (!IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE)) 322 + this_cpu_inc(scp->srcu_locks.counter); // Y, and implicit RCU reader. 323 + else 324 + atomic_long_inc(raw_cpu_ptr(&scp->srcu_locks)); // Y, and implicit RCU reader. 325 + barrier(); /* Avoid leaking the critical section. */ 326 + return scp; 327 + } 328 + 329 + /* 330 + * Removes the count for the old reader from the appropriate 331 + * per-CPU element of the srcu_struct. Note that this may well be a 332 + * different CPU than that which was incremented by the corresponding 333 + * srcu_read_lock_fast(), but it must be within the same task. 334 + * 335 + * Please see the __srcu_read_lock_fast() function's header comment for 336 + * information on implicit RCU readers and NMI safety. 337 + */ 338 + static inline void notrace 339 + __srcu_read_unlock_fast_updown(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp) 311 340 { 312 341 barrier(); /* Avoid leaking the critical section. */ 313 342 if (!IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE))
+7 -5
kernel/rcu/rcutorture.c
··· 693 693 694 694 DEFINE_STATIC_SRCU(srcu_ctl); 695 695 DEFINE_STATIC_SRCU_FAST(srcu_ctlf); 696 + DEFINE_STATIC_SRCU_FAST_UPDOWN(srcu_ctlfud); 696 697 static struct srcu_struct srcu_ctld; 697 698 static struct srcu_struct *srcu_ctlp = &srcu_ctl; 698 699 static struct rcu_torture_ops srcud_ops; ··· 704 703 if (reader_flavor & SRCU_READ_FLAVOR_FAST) 705 704 srcu_ctlp = &srcu_ctlf; 706 705 if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN) 707 - srcu_ctlp = &srcu_ctlf; 706 + srcu_ctlp = &srcu_ctlfud; 708 707 } 709 708 710 709 static void srcu_get_gp_data(int *flags, unsigned long *gp_seq) ··· 737 736 ret += idx << 2; 738 737 } 739 738 if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN) { 740 - scp = srcu_read_lock_fast(srcu_ctlp); 739 + scp = srcu_read_lock_fast_updown(srcu_ctlp); 741 740 idx = __srcu_ptr_to_ctr(srcu_ctlp, scp); 742 741 WARN_ON_ONCE(idx & ~0x1); 743 742 ret += idx << 3; ··· 768 767 { 769 768 WARN_ON_ONCE((reader_flavor && (idx & ~reader_flavor)) || (!reader_flavor && (idx & ~0x1))); 770 769 if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN) 771 - srcu_read_unlock_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3)); 770 + srcu_read_unlock_fast_updown(srcu_ctlp, 771 + __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3)); 772 772 if (reader_flavor & SRCU_READ_FLAVOR_FAST) 773 - srcu_read_unlock_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 2)); 773 + srcu_read_unlock_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x4) >> 2)); 774 774 if (reader_flavor & SRCU_READ_FLAVOR_NMI) 775 775 srcu_read_unlock_nmisafe(srcu_ctlp, (idx & 0x2) >> 1); 776 776 if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) || !(reader_flavor & SRCU_READ_FLAVOR_ALL)) ··· 921 919 { 922 920 rcu_sync_torture_init(); 923 921 if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN) 924 - WARN_ON(init_srcu_struct_fast(&srcu_ctld)); 922 + WARN_ON(init_srcu_struct_fast_updown(&srcu_ctld)); 925 923 else if (reader_flavor & SRCU_READ_FLAVOR_FAST) 926 924 WARN_ON(init_srcu_struct_fast(&srcu_ctld)); 927 925 else
+35 -4
kernel/rcu/srcutree.c
··· 309 309 } 310 310 EXPORT_SYMBOL_GPL(__init_srcu_struct_fast); 311 311 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 + 312 320 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 313 321 314 322 /** 315 323 * init_srcu_struct - initialize a sleep-RCU structure 316 324 * @ssp: structure to initialize. 317 325 * 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 319 330 * to any other function. Each srcu_struct represents a separate domain 320 331 * of SRCU protection. 321 332 */ ··· 341 330 * init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure 342 331 * @ssp: structure to initialize. 343 332 * 344 - * Must invoke this on a given srcu_struct before passing that srcu_struct 345 - * to any other function. Each srcu_struct represents a separate domain 346 - * of SRCU protection. 333 + * Use this in place of DEFINE_SRCU_FAST() and DEFINE_STATIC_SRCU_FAST() 334 + * for non-static srcu_struct structures that are to be passed to 335 + * srcu_read_lock_fast() and friends. It is necessary to invoke this on a 336 + * given srcu_struct before passing that srcu_struct to any other function. 337 + * Each srcu_struct represents a separate domain of SRCU protection. 347 338 */ 348 339 int init_srcu_struct_fast(struct srcu_struct *ssp) 349 340 { ··· 353 340 return init_srcu_struct_fields(ssp, false); 354 341 } 355 342 EXPORT_SYMBOL_GPL(init_srcu_struct_fast); 343 + 344 + /** 345 + * init_srcu_struct_fast_updown - initialize a fast-reader up/down sleep-RCU structure 346 + * @ssp: structure to initialize. 347 + * 348 + * Use this function in place of DEFINE_SRCU_FAST_UPDOWN() and 349 + * DEFINE_STATIC_SRCU_FAST_UPDOWN() for non-static srcu_struct 350 + * structures that are to be passed to srcu_read_lock_fast_updown(), 351 + * srcu_down_read_fast(), and friends. It is necessary to invoke this on a 352 + * given srcu_struct before passing that srcu_struct to any other function. 353 + * Each srcu_struct represents a separate domain of SRCU protection. 354 + */ 355 + int init_srcu_struct_fast_updown(struct srcu_struct *ssp) 356 + { 357 + ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_FAST_UPDOWN; 358 + return init_srcu_struct_fields(ssp, false); 359 + } 360 + EXPORT_SYMBOL_GPL(init_srcu_struct_fast_updown); 356 361 357 362 #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 358 363