aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2017-04-24 19:02:09 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2017-04-26 19:32:16 -0400
commit1e9a038b7fe9a8c10ef1238f4e695d5fbe0dd594 (patch)
treed26c6a12f4a9d34fbb55a1169dabeff447810cc5
parent7f6733c3c648ddd6cf459c1b80ad388a95452955 (diff)
srcu: Expedited grace periods with reduced memory contention
Commit f60d231a87c5 ("srcu: Crude control of expedited grace periods") introduced a per-srcu_struct atomic counter to track outstanding requests for grace periods. This works, but represents a memory-contention bottleneck. This commit therefore uses the srcu_node combining tree to remove this bottleneck. This commit adds new ->srcu_gp_seq_needed_exp fields to the srcu_data, srcu_node, and srcu_struct structures, which track the farthest-in-the-future grace period that must be expedited, which in turn requires that all nearer-term grace periods also be expedited. Requests for expediting start with the srcu_data structure, run up through the srcu_node tree, and end at the srcu_struct structure. Note that it may be necessary to expedite a grace period that just now started, and this is handled by a new srcu_funnel_exp_start() function, which is invoked when the grace period itself is already in its way, but when that grace period was not marked as expedited. A new srcu_get_delay() function returns zero if there is at least one expedited SRCU grace period in flight, or SRCU_INTERVAL otherwise. This function is used to calculate delays: Normal grace periods are allowed to extend in order to cover more requests with a given grace-period computation, which decreases per-request overhead. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Tested-by: Mike Galbraith <efault@gmx.de>
-rw-r--r--include/linux/srcutree.h4
-rw-r--r--kernel/rcu/srcutree.c135
2 files changed, 98 insertions, 41 deletions
diff --git a/include/linux/srcutree.h b/include/linux/srcutree.h
index 3865717df124..86df48d3e97b 100644
--- a/include/linux/srcutree.h
+++ b/include/linux/srcutree.h
@@ -43,6 +43,7 @@ struct srcu_data {
43 spinlock_t lock ____cacheline_internodealigned_in_smp; 43 spinlock_t lock ____cacheline_internodealigned_in_smp;
44 struct rcu_segcblist srcu_cblist; /* List of callbacks.*/ 44 struct rcu_segcblist srcu_cblist; /* List of callbacks.*/
45 unsigned long srcu_gp_seq_needed; /* Furthest future GP needed. */ 45 unsigned long srcu_gp_seq_needed; /* Furthest future GP needed. */
46 unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */
46 bool srcu_cblist_invoking; /* Invoking these CBs? */ 47 bool srcu_cblist_invoking; /* Invoking these CBs? */
47 struct delayed_work work; /* Context for CB invoking. */ 48 struct delayed_work work; /* Context for CB invoking. */
48 struct rcu_head srcu_barrier_head; /* For srcu_barrier() use. */ 49 struct rcu_head srcu_barrier_head; /* For srcu_barrier() use. */
@@ -63,6 +64,7 @@ struct srcu_node {
63 /* is > ->srcu_gq_seq. */ 64 /* is > ->srcu_gq_seq. */
64 unsigned long srcu_data_have_cbs[4]; /* Which srcu_data structs */ 65 unsigned long srcu_data_have_cbs[4]; /* Which srcu_data structs */
65 /* have CBs for given GP? */ 66 /* have CBs for given GP? */
67 unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */
66 struct srcu_node *srcu_parent; /* Next up in tree. */ 68 struct srcu_node *srcu_parent; /* Next up in tree. */
67 int grplo; /* Least CPU for node. */ 69 int grplo; /* Least CPU for node. */
68 int grphi; /* Biggest CPU for node. */ 70 int grphi; /* Biggest CPU for node. */
@@ -81,7 +83,7 @@ struct srcu_struct {
81 unsigned int srcu_idx; /* Current rdr array element. */ 83 unsigned int srcu_idx; /* Current rdr array element. */
82 unsigned long srcu_gp_seq; /* Grace-period seq #. */ 84 unsigned long srcu_gp_seq; /* Grace-period seq #. */
83 unsigned long srcu_gp_seq_needed; /* Latest gp_seq needed. */ 85 unsigned long srcu_gp_seq_needed; /* Latest gp_seq needed. */
84 atomic_t srcu_exp_cnt; /* # ongoing expedited GPs. */ 86 unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */
85 struct srcu_data __percpu *sda; /* Per-CPU srcu_data array. */ 87 struct srcu_data __percpu *sda; /* Per-CPU srcu_data array. */
86 unsigned long srcu_barrier_seq; /* srcu_barrier seq #. */ 88 unsigned long srcu_barrier_seq; /* srcu_barrier seq #. */
87 struct mutex srcu_barrier_mutex; /* Serialize barrier ops. */ 89 struct mutex srcu_barrier_mutex; /* Serialize barrier ops. */
diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
index 72b6cce5f591..4b98e6f45166 100644
--- a/kernel/rcu/srcutree.c
+++ b/kernel/rcu/srcutree.c
@@ -72,6 +72,7 @@ static void init_srcu_struct_nodes(struct srcu_struct *sp, bool is_static)
72 snp->srcu_have_cbs[i] = 0; 72 snp->srcu_have_cbs[i] = 0;
73 snp->srcu_data_have_cbs[i] = 0; 73 snp->srcu_data_have_cbs[i] = 0;
74 } 74 }
75 snp->srcu_gp_seq_needed_exp = 0;
75 snp->grplo = -1; 76 snp->grplo = -1;
76 snp->grphi = -1; 77 snp->grphi = -1;
77 if (snp == &sp->node[0]) { 78 if (snp == &sp->node[0]) {
@@ -102,6 +103,7 @@ static void init_srcu_struct_nodes(struct srcu_struct *sp, bool is_static)
102 rcu_segcblist_init(&sdp->srcu_cblist); 103 rcu_segcblist_init(&sdp->srcu_cblist);
103 sdp->srcu_cblist_invoking = false; 104 sdp->srcu_cblist_invoking = false;
104 sdp->srcu_gp_seq_needed = sp->srcu_gp_seq; 105 sdp->srcu_gp_seq_needed = sp->srcu_gp_seq;
106 sdp->srcu_gp_seq_needed_exp = sp->srcu_gp_seq;
105 sdp->mynode = &snp_first[cpu / levelspread[level]]; 107 sdp->mynode = &snp_first[cpu / levelspread[level]];
106 for (snp = sdp->mynode; snp != NULL; snp = snp->srcu_parent) { 108 for (snp = sdp->mynode; snp != NULL; snp = snp->srcu_parent) {
107 if (snp->grplo < 0) 109 if (snp->grplo < 0)
@@ -135,7 +137,6 @@ static int init_srcu_struct_fields(struct srcu_struct *sp, bool is_static)
135 mutex_init(&sp->srcu_gp_mutex); 137 mutex_init(&sp->srcu_gp_mutex);
136 sp->srcu_idx = 0; 138 sp->srcu_idx = 0;
137 sp->srcu_gp_seq = 0; 139 sp->srcu_gp_seq = 0;
138 atomic_set(&sp->srcu_exp_cnt, 0);
139 sp->srcu_barrier_seq = 0; 140 sp->srcu_barrier_seq = 0;
140 mutex_init(&sp->srcu_barrier_mutex); 141 mutex_init(&sp->srcu_barrier_mutex);
141 atomic_set(&sp->srcu_barrier_cpu_cnt, 0); 142 atomic_set(&sp->srcu_barrier_cpu_cnt, 0);
@@ -143,6 +144,7 @@ static int init_srcu_struct_fields(struct srcu_struct *sp, bool is_static)
143 if (!is_static) 144 if (!is_static)
144 sp->sda = alloc_percpu(struct srcu_data); 145 sp->sda = alloc_percpu(struct srcu_data);
145 init_srcu_struct_nodes(sp, is_static); 146 init_srcu_struct_nodes(sp, is_static);
147 sp->srcu_gp_seq_needed_exp = 0;
146 smp_store_release(&sp->srcu_gp_seq_needed, 0); /* Init done. */ 148 smp_store_release(&sp->srcu_gp_seq_needed, 0); /* Init done. */
147 return sp->sda ? 0 : -ENOMEM; 149 return sp->sda ? 0 : -ENOMEM;
148} 150}
@@ -307,6 +309,18 @@ static bool srcu_readers_active(struct srcu_struct *sp)
307 309
308#define SRCU_INTERVAL 1 310#define SRCU_INTERVAL 1
309 311
312/*
313 * Return grace-period delay, zero if there are expedited grace
314 * periods pending, SRCU_INTERVAL otherwise.
315 */
316static unsigned long srcu_get_delay(struct srcu_struct *sp)
317{
318 if (ULONG_CMP_LT(READ_ONCE(sp->srcu_gp_seq),
319 READ_ONCE(sp->srcu_gp_seq_needed_exp)))
320 return 0;
321 return SRCU_INTERVAL;
322}
323
310/** 324/**
311 * cleanup_srcu_struct - deconstruct a sleep-RCU structure 325 * cleanup_srcu_struct - deconstruct a sleep-RCU structure
312 * @sp: structure to clean up. 326 * @sp: structure to clean up.
@@ -318,7 +332,8 @@ void cleanup_srcu_struct(struct srcu_struct *sp)
318{ 332{
319 int cpu; 333 int cpu;
320 334
321 WARN_ON_ONCE(atomic_read(&sp->srcu_exp_cnt)); 335 if (WARN_ON(!srcu_get_delay(sp)))
336 return; /* Leakage unless caller handles error. */
322 if (WARN_ON(srcu_readers_active(sp))) 337 if (WARN_ON(srcu_readers_active(sp)))
323 return; /* Leakage unless caller handles error. */ 338 return; /* Leakage unless caller handles error. */
324 flush_delayed_work(&sp->work); 339 flush_delayed_work(&sp->work);
@@ -444,15 +459,14 @@ static void srcu_schedule_cbs_sdp(struct srcu_data *sdp, unsigned long delay)
444 * schedule this invocation on the corresponding CPUs. 459 * schedule this invocation on the corresponding CPUs.
445 */ 460 */
446static void srcu_schedule_cbs_snp(struct srcu_struct *sp, struct srcu_node *snp, 461static void srcu_schedule_cbs_snp(struct srcu_struct *sp, struct srcu_node *snp,
447 unsigned long mask) 462 unsigned long mask, unsigned long delay)
448{ 463{
449 int cpu; 464 int cpu;
450 465
451 for (cpu = snp->grplo; cpu <= snp->grphi; cpu++) { 466 for (cpu = snp->grplo; cpu <= snp->grphi; cpu++) {
452 if (!(mask & (1 << (cpu - snp->grplo)))) 467 if (!(mask & (1 << (cpu - snp->grplo))))
453 continue; 468 continue;
454 srcu_schedule_cbs_sdp(per_cpu_ptr(sp->sda, cpu), 469 srcu_schedule_cbs_sdp(per_cpu_ptr(sp->sda, cpu), delay);
455 atomic_read(&sp->srcu_exp_cnt) ? 0 : SRCU_INTERVAL);
456 } 470 }
457} 471}
458 472
@@ -467,6 +481,7 @@ static void srcu_schedule_cbs_snp(struct srcu_struct *sp, struct srcu_node *snp,
467 */ 481 */
468static void srcu_gp_end(struct srcu_struct *sp) 482static void srcu_gp_end(struct srcu_struct *sp)
469{ 483{
484 unsigned long cbdelay;
470 bool cbs; 485 bool cbs;
471 unsigned long gpseq; 486 unsigned long gpseq;
472 int idx; 487 int idx;
@@ -481,8 +496,11 @@ static void srcu_gp_end(struct srcu_struct *sp)
481 spin_lock_irq(&sp->gp_lock); 496 spin_lock_irq(&sp->gp_lock);
482 idx = rcu_seq_state(sp->srcu_gp_seq); 497 idx = rcu_seq_state(sp->srcu_gp_seq);
483 WARN_ON_ONCE(idx != SRCU_STATE_SCAN2); 498 WARN_ON_ONCE(idx != SRCU_STATE_SCAN2);
499 cbdelay = srcu_get_delay(sp);
484 rcu_seq_end(&sp->srcu_gp_seq); 500 rcu_seq_end(&sp->srcu_gp_seq);
485 gpseq = rcu_seq_current(&sp->srcu_gp_seq); 501 gpseq = rcu_seq_current(&sp->srcu_gp_seq);
502 if (ULONG_CMP_LT(sp->srcu_gp_seq_needed_exp, gpseq))
503 sp->srcu_gp_seq_needed_exp = gpseq;
486 spin_unlock_irq(&sp->gp_lock); 504 spin_unlock_irq(&sp->gp_lock);
487 mutex_unlock(&sp->srcu_gp_mutex); 505 mutex_unlock(&sp->srcu_gp_mutex);
488 /* A new grace period can start at this point. But only one. */ 506 /* A new grace period can start at this point. But only one. */
@@ -497,12 +515,14 @@ static void srcu_gp_end(struct srcu_struct *sp)
497 cbs = snp->srcu_have_cbs[idx] == gpseq; 515 cbs = snp->srcu_have_cbs[idx] == gpseq;
498 snp->srcu_have_cbs[idx] = gpseq; 516 snp->srcu_have_cbs[idx] = gpseq;
499 rcu_seq_set_state(&snp->srcu_have_cbs[idx], 1); 517 rcu_seq_set_state(&snp->srcu_have_cbs[idx], 1);
518 if (ULONG_CMP_LT(snp->srcu_gp_seq_needed_exp, gpseq))
519 snp->srcu_gp_seq_needed_exp = gpseq;
500 mask = snp->srcu_data_have_cbs[idx]; 520 mask = snp->srcu_data_have_cbs[idx];
501 snp->srcu_data_have_cbs[idx] = 0; 521 snp->srcu_data_have_cbs[idx] = 0;
502 spin_unlock_irq(&snp->lock); 522 spin_unlock_irq(&snp->lock);
503 if (cbs) { 523 if (cbs) {
504 smp_mb(); /* GP end before CB invocation. */ 524 smp_mb(); /* GP end before CB invocation. */
505 srcu_schedule_cbs_snp(sp, snp, mask); 525 srcu_schedule_cbs_snp(sp, snp, mask, cbdelay);
506 } 526 }
507 } 527 }
508 528
@@ -517,25 +537,52 @@ static void srcu_gp_end(struct srcu_struct *sp)
517 srcu_gp_start(sp); 537 srcu_gp_start(sp);
518 spin_unlock_irq(&sp->gp_lock); 538 spin_unlock_irq(&sp->gp_lock);
519 /* Throttle expedited grace periods: Should be rare! */ 539 /* Throttle expedited grace periods: Should be rare! */
520 srcu_reschedule(sp, atomic_read(&sp->srcu_exp_cnt) && 540 srcu_reschedule(sp, rcu_seq_ctr(gpseq) & 0x3ff
521 rcu_seq_ctr(gpseq) & 0xf 541 ? 0 : SRCU_INTERVAL);
522 ? 0
523 : SRCU_INTERVAL);
524 } else { 542 } else {
525 spin_unlock_irq(&sp->gp_lock); 543 spin_unlock_irq(&sp->gp_lock);
526 } 544 }
527} 545}
528 546
529/* 547/*
548 * Funnel-locking scheme to scalably mediate many concurrent expedited
549 * grace-period requests. This function is invoked for the first known
550 * expedited request for a grace period that has already been requested,
551 * but without expediting. To start a completely new grace period,
552 * whether expedited or not, use srcu_funnel_gp_start() instead.
553 */
554static void srcu_funnel_exp_start(struct srcu_struct *sp, struct srcu_node *snp,
555 unsigned long s)
556{
557 unsigned long flags;
558
559 for (; snp != NULL; snp = snp->srcu_parent) {
560 if (rcu_seq_done(&sp->srcu_gp_seq, s) ||
561 ULONG_CMP_GE(READ_ONCE(snp->srcu_gp_seq_needed_exp), s))
562 return;
563 spin_lock_irqsave(&snp->lock, flags);
564 if (ULONG_CMP_GE(snp->srcu_gp_seq_needed_exp, s)) {
565 spin_unlock_irqrestore(&snp->lock, flags);
566 return;
567 }
568 WRITE_ONCE(snp->srcu_gp_seq_needed_exp, s);
569 spin_unlock_irqrestore(&snp->lock, flags);
570 }
571 spin_lock_irqsave(&sp->gp_lock, flags);
572 if (!ULONG_CMP_LT(sp->srcu_gp_seq_needed_exp, s))
573 sp->srcu_gp_seq_needed_exp = s;
574 spin_unlock_irqrestore(&sp->gp_lock, flags);
575}
576
577/*
530 * Funnel-locking scheme to scalably mediate many concurrent grace-period 578 * Funnel-locking scheme to scalably mediate many concurrent grace-period
531 * requests. The winner has to do the work of actually starting grace 579 * requests. The winner has to do the work of actually starting grace
532 * period s. Losers must either ensure that their desired grace-period 580 * period s. Losers must either ensure that their desired grace-period
533 * number is recorded on at least their leaf srcu_node structure, or they 581 * number is recorded on at least their leaf srcu_node structure, or they
534 * must take steps to invoke their own callbacks. 582 * must take steps to invoke their own callbacks.
535 */ 583 */
536static void srcu_funnel_gp_start(struct srcu_struct *sp, 584static void srcu_funnel_gp_start(struct srcu_struct *sp, struct srcu_data *sdp,
537 struct srcu_data *sdp, 585 unsigned long s, bool do_norm)
538 unsigned long s)
539{ 586{
540 unsigned long flags; 587 unsigned long flags;
541 int idx = rcu_seq_ctr(s) % ARRAY_SIZE(sdp->mynode->srcu_have_cbs); 588 int idx = rcu_seq_ctr(s) % ARRAY_SIZE(sdp->mynode->srcu_have_cbs);
@@ -554,13 +601,20 @@ static void srcu_funnel_gp_start(struct srcu_struct *sp,
554 spin_unlock_irqrestore(&snp->lock, flags); 601 spin_unlock_irqrestore(&snp->lock, flags);
555 if (snp == sdp->mynode && snp_seq != s) { 602 if (snp == sdp->mynode && snp_seq != s) {
556 smp_mb(); /* CBs after GP! */ 603 smp_mb(); /* CBs after GP! */
557 srcu_schedule_cbs_sdp(sdp, 0); 604 srcu_schedule_cbs_sdp(sdp, do_norm
605 ? SRCU_INTERVAL
606 : 0);
607 return;
558 } 608 }
609 if (!do_norm)
610 srcu_funnel_exp_start(sp, snp, s);
559 return; 611 return;
560 } 612 }
561 snp->srcu_have_cbs[idx] = s; 613 snp->srcu_have_cbs[idx] = s;
562 if (snp == sdp->mynode) 614 if (snp == sdp->mynode)
563 snp->srcu_data_have_cbs[idx] |= sdp->grpmask; 615 snp->srcu_data_have_cbs[idx] |= sdp->grpmask;
616 if (!do_norm && ULONG_CMP_LT(snp->srcu_gp_seq_needed_exp, s))
617 snp->srcu_gp_seq_needed_exp = s;
564 spin_unlock_irqrestore(&snp->lock, flags); 618 spin_unlock_irqrestore(&snp->lock, flags);
565 } 619 }
566 620
@@ -573,6 +627,8 @@ static void srcu_funnel_gp_start(struct srcu_struct *sp,
573 */ 627 */
574 smp_store_release(&sp->srcu_gp_seq_needed, s); /*^^^*/ 628 smp_store_release(&sp->srcu_gp_seq_needed, s); /*^^^*/
575 } 629 }
630 if (!do_norm && ULONG_CMP_LT(sp->srcu_gp_seq_needed_exp, s))
631 sp->srcu_gp_seq_needed_exp = s;
576 632
577 /* If grace period not already done and none in progress, start it. */ 633 /* If grace period not already done and none in progress, start it. */
578 if (!rcu_seq_done(&sp->srcu_gp_seq, s) && 634 if (!rcu_seq_done(&sp->srcu_gp_seq, s) &&
@@ -580,9 +636,7 @@ static void srcu_funnel_gp_start(struct srcu_struct *sp,
580 WARN_ON_ONCE(ULONG_CMP_GE(sp->srcu_gp_seq, sp->srcu_gp_seq_needed)); 636 WARN_ON_ONCE(ULONG_CMP_GE(sp->srcu_gp_seq, sp->srcu_gp_seq_needed));
581 srcu_gp_start(sp); 637 srcu_gp_start(sp);
582 queue_delayed_work(system_power_efficient_wq, &sp->work, 638 queue_delayed_work(system_power_efficient_wq, &sp->work,
583 atomic_read(&sp->srcu_exp_cnt) 639 srcu_get_delay(sp));
584 ? 0
585 : SRCU_INTERVAL);
586 } 640 }
587 spin_unlock_irqrestore(&sp->gp_lock, flags); 641 spin_unlock_irqrestore(&sp->gp_lock, flags);
588} 642}
@@ -597,7 +651,7 @@ static bool try_check_zero(struct srcu_struct *sp, int idx, int trycount)
597 for (;;) { 651 for (;;) {
598 if (srcu_readers_active_idx_check(sp, idx)) 652 if (srcu_readers_active_idx_check(sp, idx))
599 return true; 653 return true;
600 if (--trycount + !!atomic_read(&sp->srcu_exp_cnt) <= 0) 654 if (--trycount + !srcu_get_delay(sp) <= 0)
601 return false; 655 return false;
602 udelay(SRCU_RETRY_CHECK_DELAY); 656 udelay(SRCU_RETRY_CHECK_DELAY);
603 } 657 }
@@ -650,10 +704,11 @@ static void srcu_flip(struct srcu_struct *sp)
650 * srcu_read_lock(), and srcu_read_unlock() that are all passed the same 704 * srcu_read_lock(), and srcu_read_unlock() that are all passed the same
651 * srcu_struct structure. 705 * srcu_struct structure.
652 */ 706 */
653void call_srcu(struct srcu_struct *sp, struct rcu_head *rhp, 707void __call_srcu(struct srcu_struct *sp, struct rcu_head *rhp,
654 rcu_callback_t func) 708 rcu_callback_t func, bool do_norm)
655{ 709{
656 unsigned long flags; 710 unsigned long flags;
711 bool needexp = false;
657 bool needgp = false; 712 bool needgp = false;
658 unsigned long s; 713 unsigned long s;
659 struct srcu_data *sdp; 714 struct srcu_data *sdp;
@@ -672,16 +727,28 @@ void call_srcu(struct srcu_struct *sp, struct rcu_head *rhp,
672 sdp->srcu_gp_seq_needed = s; 727 sdp->srcu_gp_seq_needed = s;
673 needgp = true; 728 needgp = true;
674 } 729 }
730 if (!do_norm && ULONG_CMP_LT(sdp->srcu_gp_seq_needed_exp, s)) {
731 sdp->srcu_gp_seq_needed_exp = s;
732 needexp = true;
733 }
675 spin_unlock_irqrestore(&sdp->lock, flags); 734 spin_unlock_irqrestore(&sdp->lock, flags);
676 if (needgp) 735 if (needgp)
677 srcu_funnel_gp_start(sp, sdp, s); 736 srcu_funnel_gp_start(sp, sdp, s, do_norm);
737 else if (needexp)
738 srcu_funnel_exp_start(sp, sdp->mynode, s);
739}
740
741void call_srcu(struct srcu_struct *sp, struct rcu_head *rhp,
742 rcu_callback_t func)
743{
744 __call_srcu(sp, rhp, func, true);
678} 745}
679EXPORT_SYMBOL_GPL(call_srcu); 746EXPORT_SYMBOL_GPL(call_srcu);
680 747
681/* 748/*
682 * Helper function for synchronize_srcu() and synchronize_srcu_expedited(). 749 * Helper function for synchronize_srcu() and synchronize_srcu_expedited().
683 */ 750 */
684static void __synchronize_srcu(struct srcu_struct *sp) 751static void __synchronize_srcu(struct srcu_struct *sp, bool do_norm)
685{ 752{
686 struct rcu_synchronize rcu; 753 struct rcu_synchronize rcu;
687 754
@@ -697,7 +764,7 @@ static void __synchronize_srcu(struct srcu_struct *sp)
697 check_init_srcu_struct(sp); 764 check_init_srcu_struct(sp);
698 init_completion(&rcu.completion); 765 init_completion(&rcu.completion);
699 init_rcu_head_on_stack(&rcu.head); 766 init_rcu_head_on_stack(&rcu.head);
700 call_srcu(sp, &rcu.head, wakeme_after_rcu); 767 __call_srcu(sp, &rcu.head, wakeme_after_rcu, do_norm);
701 wait_for_completion(&rcu.completion); 768 wait_for_completion(&rcu.completion);
702 destroy_rcu_head_on_stack(&rcu.head); 769 destroy_rcu_head_on_stack(&rcu.head);
703} 770}
@@ -714,18 +781,7 @@ static void __synchronize_srcu(struct srcu_struct *sp)
714 */ 781 */
715void synchronize_srcu_expedited(struct srcu_struct *sp) 782void synchronize_srcu_expedited(struct srcu_struct *sp)
716{ 783{
717 bool do_norm = rcu_gp_is_normal(); 784 __synchronize_srcu(sp, rcu_gp_is_normal());
718
719 check_init_srcu_struct(sp);
720 if (!do_norm) {
721 atomic_inc(&sp->srcu_exp_cnt);
722 smp_mb__after_atomic(); /* increment before GP. */
723 }
724 __synchronize_srcu(sp);
725 if (!do_norm) {
726 smp_mb__before_atomic(); /* GP before decrement. */
727 WARN_ON_ONCE(atomic_dec_return(&sp->srcu_exp_cnt) < 0);
728 }
729} 785}
730EXPORT_SYMBOL_GPL(synchronize_srcu_expedited); 786EXPORT_SYMBOL_GPL(synchronize_srcu_expedited);
731 787
@@ -773,7 +829,7 @@ void synchronize_srcu(struct srcu_struct *sp)
773 if (rcu_gp_is_expedited()) 829 if (rcu_gp_is_expedited())
774 synchronize_srcu_expedited(sp); 830 synchronize_srcu_expedited(sp);
775 else 831 else
776 __synchronize_srcu(sp); 832 __synchronize_srcu(sp, true);
777} 833}
778EXPORT_SYMBOL_GPL(synchronize_srcu); 834EXPORT_SYMBOL_GPL(synchronize_srcu);
779 835
@@ -1008,14 +1064,13 @@ void process_srcu(struct work_struct *work)
1008 sp = container_of(work, struct srcu_struct, work.work); 1064 sp = container_of(work, struct srcu_struct, work.work);
1009 1065
1010 srcu_advance_state(sp); 1066 srcu_advance_state(sp);
1011 srcu_reschedule(sp, atomic_read(&sp->srcu_exp_cnt) ? 0 : SRCU_INTERVAL); 1067 srcu_reschedule(sp, srcu_get_delay(sp));
1012} 1068}
1013EXPORT_SYMBOL_GPL(process_srcu); 1069EXPORT_SYMBOL_GPL(process_srcu);
1014 1070
1015void srcutorture_get_gp_data(enum rcutorture_type test_type, 1071void srcutorture_get_gp_data(enum rcutorture_type test_type,
1016 struct srcu_struct *sp, int *flags, 1072 struct srcu_struct *sp, int *flags,
1017 unsigned long *gpnum, 1073 unsigned long *gpnum, unsigned long *completed)
1018 unsigned long *completed)
1019{ 1074{
1020 if (test_type != SRCU_FLAVOR) 1075 if (test_type != SRCU_FLAVOR)
1021 return; 1076 return;