aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2017-03-24 16:46:33 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2017-04-18 14:38:22 -0400
commitf60d231a87c5c9f23f10e69996f396d46f5bf901 (patch)
tree04076adf019c43dbf7f31ad435aed8361a9a46b0
parent80a7956fe36c2ee40c6ff12c77926d267802b7c8 (diff)
srcu: Crude control of expedited grace periods
SRCU's implementation of expedited grace periods has always assumed that the SRCU instance is idle when the expedited request arrives. This commit improves this a bit by maintaining a count of the number of outstanding expedited requests, thus allowing prior non-expedited grace periods accommodate these requests by shifting to expedited mode. However, any non-expedited wait already in progress will still wait for the full duration. Improved control of expedited grace periods is planned, but one step at a time. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-rw-r--r--include/linux/srcu.h1
-rw-r--r--kernel/rcu/srcu.c84
2 files changed, 47 insertions, 38 deletions
diff --git a/include/linux/srcu.h b/include/linux/srcu.h
index e7dbc01b61a1..73a1b6296224 100644
--- a/include/linux/srcu.h
+++ b/include/linux/srcu.h
@@ -42,6 +42,7 @@ struct srcu_array {
42struct srcu_struct { 42struct srcu_struct {
43 unsigned long completed; 43 unsigned long completed;
44 unsigned long srcu_gp_seq; 44 unsigned long srcu_gp_seq;
45 atomic_t srcu_exp_cnt;
45 struct srcu_array __percpu *per_cpu_ref; 46 struct srcu_array __percpu *per_cpu_ref;
46 spinlock_t queue_lock; /* protect ->srcu_cblist */ 47 spinlock_t queue_lock; /* protect ->srcu_cblist */
47 struct rcu_segcblist srcu_cblist; 48 struct rcu_segcblist srcu_cblist;
diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c
index 90ffea31b188..3cfcc59bddf3 100644
--- a/kernel/rcu/srcu.c
+++ b/kernel/rcu/srcu.c
@@ -43,6 +43,7 @@ static int init_srcu_struct_fields(struct srcu_struct *sp)
43{ 43{
44 sp->completed = 0; 44 sp->completed = 0;
45 sp->srcu_gp_seq = 0; 45 sp->srcu_gp_seq = 0;
46 atomic_set(&sp->srcu_exp_cnt, 0);
46 spin_lock_init(&sp->queue_lock); 47 spin_lock_init(&sp->queue_lock);
47 rcu_segcblist_init(&sp->srcu_cblist); 48 rcu_segcblist_init(&sp->srcu_cblist);
48 INIT_DELAYED_WORK(&sp->work, process_srcu); 49 INIT_DELAYED_WORK(&sp->work, process_srcu);
@@ -179,7 +180,6 @@ static bool srcu_readers_active(struct srcu_struct *sp)
179 return sum; 180 return sum;
180} 181}
181 182
182#define SRCU_CALLBACK_BATCH 10
183#define SRCU_INTERVAL 1 183#define SRCU_INTERVAL 1
184 184
185/** 185/**
@@ -197,6 +197,7 @@ static bool srcu_readers_active(struct srcu_struct *sp)
197 */ 197 */
198void cleanup_srcu_struct(struct srcu_struct *sp) 198void cleanup_srcu_struct(struct srcu_struct *sp)
199{ 199{
200 WARN_ON_ONCE(atomic_read(&sp->srcu_exp_cnt));
200 if (WARN_ON(srcu_readers_active(sp))) 201 if (WARN_ON(srcu_readers_active(sp)))
201 return; /* Leakage unless caller handles error. */ 202 return; /* Leakage unless caller handles error. */
202 if (WARN_ON(!rcu_segcblist_empty(&sp->srcu_cblist))) 203 if (WARN_ON(!rcu_segcblist_empty(&sp->srcu_cblist)))
@@ -244,13 +245,10 @@ EXPORT_SYMBOL_GPL(__srcu_read_unlock);
244 * We use an adaptive strategy for synchronize_srcu() and especially for 245 * We use an adaptive strategy for synchronize_srcu() and especially for
245 * synchronize_srcu_expedited(). We spin for a fixed time period 246 * synchronize_srcu_expedited(). We spin for a fixed time period
246 * (defined below) to allow SRCU readers to exit their read-side critical 247 * (defined below) to allow SRCU readers to exit their read-side critical
247 * sections. If there are still some readers after 10 microseconds, 248 * sections. If there are still some readers after a few microseconds,
248 * we repeatedly block for 1-millisecond time periods. This approach 249 * we repeatedly block for 1-millisecond time periods.
249 * has done well in testing, so there is no need for a config parameter.
250 */ 250 */
251#define SRCU_RETRY_CHECK_DELAY 5 251#define SRCU_RETRY_CHECK_DELAY 5
252#define SYNCHRONIZE_SRCU_TRYCOUNT 2
253#define SYNCHRONIZE_SRCU_EXP_TRYCOUNT 12
254 252
255/* 253/*
256 * Start an SRCU grace period. 254 * Start an SRCU grace period.
@@ -267,16 +265,16 @@ static void srcu_gp_start(struct srcu_struct *sp)
267} 265}
268 266
269/* 267/*
270 * Wait until all readers counted by array index idx complete, but loop 268 * Wait until all readers counted by array index idx complete, but
271 * a maximum of trycount times. The caller must ensure that ->completed 269 * loop an additional time if there is an expedited grace period pending.
272 * is not changed while checking. 270 * The caller must ensure that ->completed is not changed while checking.
273 */ 271 */
274static bool try_check_zero(struct srcu_struct *sp, int idx, int trycount) 272static bool try_check_zero(struct srcu_struct *sp, int idx, int trycount)
275{ 273{
276 for (;;) { 274 for (;;) {
277 if (srcu_readers_active_idx_check(sp, idx)) 275 if (srcu_readers_active_idx_check(sp, idx))
278 return true; 276 return true;
279 if (--trycount <= 0) 277 if (--trycount + !!atomic_read(&sp->srcu_exp_cnt) <= 0)
280 return false; 278 return false;
281 udelay(SRCU_RETRY_CHECK_DELAY); 279 udelay(SRCU_RETRY_CHECK_DELAY);
282 } 280 }
@@ -364,7 +362,7 @@ static void srcu_reschedule(struct srcu_struct *sp, unsigned long delay);
364/* 362/*
365 * Helper function for synchronize_srcu() and synchronize_srcu_expedited(). 363 * Helper function for synchronize_srcu() and synchronize_srcu_expedited().
366 */ 364 */
367static void __synchronize_srcu(struct srcu_struct *sp, int trycount) 365static void __synchronize_srcu(struct srcu_struct *sp)
368{ 366{
369 struct rcu_synchronize rcu; 367 struct rcu_synchronize rcu;
370 struct rcu_head *head = &rcu.head; 368 struct rcu_head *head = &rcu.head;
@@ -401,6 +399,32 @@ static void __synchronize_srcu(struct srcu_struct *sp, int trycount)
401} 399}
402 400
403/** 401/**
402 * synchronize_srcu_expedited - Brute-force SRCU grace period
403 * @sp: srcu_struct with which to synchronize.
404 *
405 * Wait for an SRCU grace period to elapse, but be more aggressive about
406 * spinning rather than blocking when waiting.
407 *
408 * Note that synchronize_srcu_expedited() has the same deadlock and
409 * memory-ordering properties as does synchronize_srcu().
410 */
411void synchronize_srcu_expedited(struct srcu_struct *sp)
412{
413 bool do_norm = rcu_gp_is_normal();
414
415 if (!do_norm) {
416 atomic_inc(&sp->srcu_exp_cnt);
417 smp_mb__after_atomic(); /* increment before GP. */
418 }
419 __synchronize_srcu(sp);
420 if (!do_norm) {
421 smp_mb__before_atomic(); /* GP before decrement. */
422 atomic_dec(&sp->srcu_exp_cnt);
423 }
424}
425EXPORT_SYMBOL_GPL(synchronize_srcu_expedited);
426
427/**
404 * synchronize_srcu - wait for prior SRCU read-side critical-section completion 428 * synchronize_srcu - wait for prior SRCU read-side critical-section completion
405 * @sp: srcu_struct with which to synchronize. 429 * @sp: srcu_struct with which to synchronize.
406 * 430 *
@@ -441,29 +465,14 @@ static void __synchronize_srcu(struct srcu_struct *sp, int trycount)
441 */ 465 */
442void synchronize_srcu(struct srcu_struct *sp) 466void synchronize_srcu(struct srcu_struct *sp)
443{ 467{
444 __synchronize_srcu(sp, (rcu_gp_is_expedited() && !rcu_gp_is_normal()) 468 if (rcu_gp_is_expedited())
445 ? SYNCHRONIZE_SRCU_EXP_TRYCOUNT 469 synchronize_srcu_expedited(sp);
446 : SYNCHRONIZE_SRCU_TRYCOUNT); 470 else
471 __synchronize_srcu(sp);
447} 472}
448EXPORT_SYMBOL_GPL(synchronize_srcu); 473EXPORT_SYMBOL_GPL(synchronize_srcu);
449 474
450/** 475/**
451 * synchronize_srcu_expedited - Brute-force SRCU grace period
452 * @sp: srcu_struct with which to synchronize.
453 *
454 * Wait for an SRCU grace period to elapse, but be more aggressive about
455 * spinning rather than blocking when waiting.
456 *
457 * Note that synchronize_srcu_expedited() has the same deadlock and
458 * memory-ordering properties as does synchronize_srcu().
459 */
460void synchronize_srcu_expedited(struct srcu_struct *sp)
461{
462 __synchronize_srcu(sp, SYNCHRONIZE_SRCU_EXP_TRYCOUNT);
463}
464EXPORT_SYMBOL_GPL(synchronize_srcu_expedited);
465
466/**
467 * srcu_barrier - Wait until all in-flight call_srcu() callbacks complete. 476 * srcu_barrier - Wait until all in-flight call_srcu() callbacks complete.
468 * @sp: srcu_struct on which to wait for in-flight callbacks. 477 * @sp: srcu_struct on which to wait for in-flight callbacks.
469 */ 478 */
@@ -490,7 +499,7 @@ EXPORT_SYMBOL_GPL(srcu_batches_completed);
490 * Core SRCU state machine. Advance callbacks from ->batch_check0 to 499 * Core SRCU state machine. Advance callbacks from ->batch_check0 to
491 * ->batch_check1 and then to ->batch_done as readers drain. 500 * ->batch_check1 and then to ->batch_done as readers drain.
492 */ 501 */
493static void srcu_advance_batches(struct srcu_struct *sp, int trycount) 502static void srcu_advance_batches(struct srcu_struct *sp)
494{ 503{
495 int idx; 504 int idx;
496 505
@@ -521,8 +530,8 @@ static void srcu_advance_batches(struct srcu_struct *sp, int trycount)
521 530
522 if (rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)) == SRCU_STATE_SCAN1) { 531 if (rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)) == SRCU_STATE_SCAN1) {
523 idx = 1 ^ (sp->completed & 1); 532 idx = 1 ^ (sp->completed & 1);
524 if (!try_check_zero(sp, idx, trycount)) 533 if (!try_check_zero(sp, idx, 1))
525 return; /* readers present, retry after SRCU_INTERVAL */ 534 return; /* readers present, retry later. */
526 srcu_flip(sp); 535 srcu_flip(sp);
527 rcu_seq_set_state(&sp->srcu_gp_seq, SRCU_STATE_SCAN2); 536 rcu_seq_set_state(&sp->srcu_gp_seq, SRCU_STATE_SCAN2);
528 } 537 }
@@ -534,9 +543,8 @@ static void srcu_advance_batches(struct srcu_struct *sp, int trycount)
534 * so check at least twice in quick succession after a flip. 543 * so check at least twice in quick succession after a flip.
535 */ 544 */
536 idx = 1 ^ (sp->completed & 1); 545 idx = 1 ^ (sp->completed & 1);
537 trycount = trycount < 2 ? 2 : trycount; 546 if (!try_check_zero(sp, idx, 2))
538 if (!try_check_zero(sp, idx, trycount)) 547 return; /* readers present, retry after later. */
539 return; /* readers present, retry after SRCU_INTERVAL */
540 srcu_gp_end(sp); 548 srcu_gp_end(sp);
541 } 549 }
542} 550}
@@ -602,8 +610,8 @@ void process_srcu(struct work_struct *work)
602 610
603 sp = container_of(work, struct srcu_struct, work.work); 611 sp = container_of(work, struct srcu_struct, work.work);
604 612
605 srcu_advance_batches(sp, 1); 613 srcu_advance_batches(sp);
606 srcu_invoke_callbacks(sp); 614 srcu_invoke_callbacks(sp);
607 srcu_reschedule(sp, SRCU_INTERVAL); 615 srcu_reschedule(sp, atomic_read(&sp->srcu_exp_cnt) ? 0 : SRCU_INTERVAL);
608} 616}
609EXPORT_SYMBOL_GPL(process_srcu); 617EXPORT_SYMBOL_GPL(process_srcu);