diff options
| author | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2011-05-27 01:14:36 -0400 |
|---|---|---|
| committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2011-09-29 00:36:42 -0400 |
| commit | 2c42818e962e2858334bf45bfc56662b3752df34 (patch) | |
| tree | 192364123c9aeeab282c53168e51eddece9d8be4 | |
| parent | f039d1f1884b2fe9c13d28f59d8330f0b0518fc4 (diff) | |
rcu: Abstract common code for RCU grace-period-wait primitives
Pull the code that waits for an RCU grace period into a single function,
which is then called by synchronize_rcu() and friends in the case of
TREE_RCU and TREE_PREEMPT_RCU, and from rcu_barrier() and friends in
the case of TINY_RCU and TINY_PREEMPT_RCU.
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
| -rw-r--r-- | include/linux/rcupdate.h | 130 | ||||
| -rw-r--r-- | include/linux/rcutiny.h | 16 | ||||
| -rw-r--r-- | include/linux/rcutree.h | 2 | ||||
| -rw-r--r-- | kernel/rcupdate.c | 21 | ||||
| -rw-r--r-- | kernel/rcutiny.c | 28 | ||||
| -rw-r--r-- | kernel/rcutiny_plugin.h | 14 | ||||
| -rw-r--r-- | kernel/rcutree.c | 22 | ||||
| -rw-r--r-- | kernel/rcutree_plugin.h | 11 |
8 files changed, 113 insertions, 131 deletions
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 87bd390df73f..ae5327de41aa 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h | |||
| @@ -66,11 +66,73 @@ static inline void rcutorture_record_progress(unsigned long vernum) | |||
| 66 | #define ULONG_CMP_LT(a, b) (ULONG_MAX / 2 < (a) - (b)) | 66 | #define ULONG_CMP_LT(a, b) (ULONG_MAX / 2 < (a) - (b)) |
| 67 | 67 | ||
| 68 | /* Exported common interfaces */ | 68 | /* Exported common interfaces */ |
| 69 | |||
| 70 | #ifdef CONFIG_PREEMPT_RCU | ||
| 71 | |||
| 72 | /** | ||
| 73 | * call_rcu() - Queue an RCU callback for invocation after a grace period. | ||
| 74 | * @head: structure to be used for queueing the RCU updates. | ||
| 75 | * @func: actual callback function to be invoked after the grace period | ||
| 76 | * | ||
| 77 | * The callback function will be invoked some time after a full grace | ||
| 78 | * period elapses, in other words after all pre-existing RCU read-side | ||
| 79 | * critical sections have completed. However, the callback function | ||
| 80 | * might well execute concurrently with RCU read-side critical sections | ||
| 81 | * that started after call_rcu() was invoked. RCU read-side critical | ||
| 82 | * sections are delimited by rcu_read_lock() and rcu_read_unlock(), | ||
| 83 | * and may be nested. | ||
| 84 | */ | ||
| 85 | extern void call_rcu(struct rcu_head *head, | ||
| 86 | void (*func)(struct rcu_head *head)); | ||
| 87 | |||
| 88 | #else /* #ifdef CONFIG_PREEMPT_RCU */ | ||
| 89 | |||
| 90 | /* In classic RCU, call_rcu() is just call_rcu_sched(). */ | ||
| 91 | #define call_rcu call_rcu_sched | ||
| 92 | |||
| 93 | #endif /* #else #ifdef CONFIG_PREEMPT_RCU */ | ||
| 94 | |||
| 95 | /** | ||
| 96 | * call_rcu_bh() - Queue an RCU for invocation after a quicker grace period. | ||
| 97 | * @head: structure to be used for queueing the RCU updates. | ||
| 98 | * @func: actual callback function to be invoked after the grace period | ||
| 99 | * | ||
| 100 | * The callback function will be invoked some time after a full grace | ||
| 101 | * period elapses, in other words after all currently executing RCU | ||
| 102 | * read-side critical sections have completed. call_rcu_bh() assumes | ||
| 103 | * that the read-side critical sections end on completion of a softirq | ||
| 104 | * handler. This means that read-side critical sections in process | ||
| 105 | * context must not be interrupted by softirqs. This interface is to be | ||
| 106 | * used when most of the read-side critical sections are in softirq context. | ||
| 107 | * RCU read-side critical sections are delimited by : | ||
| 108 | * - rcu_read_lock() and rcu_read_unlock(), if in interrupt context. | ||
| 109 | * OR | ||
| 110 | * - rcu_read_lock_bh() and rcu_read_unlock_bh(), if in process context. | ||
| 111 | * These may be nested. | ||
| 112 | */ | ||
| 113 | extern void call_rcu_bh(struct rcu_head *head, | ||
| 114 | void (*func)(struct rcu_head *head)); | ||
| 115 | |||
| 116 | /** | ||
| 117 | * call_rcu_sched() - Queue an RCU for invocation after sched grace period. | ||
| 118 | * @head: structure to be used for queueing the RCU updates. | ||
| 119 | * @func: actual callback function to be invoked after the grace period | ||
| 120 | * | ||
| 121 | * The callback function will be invoked some time after a full grace | ||
| 122 | * period elapses, in other words after all currently executing RCU | ||
| 123 | * read-side critical sections have completed. call_rcu_sched() assumes | ||
| 124 | * that the read-side critical sections end on enabling of preemption | ||
| 125 | * or on voluntary preemption. | ||
| 126 | * RCU read-side critical sections are delimited by : | ||
| 127 | * - rcu_read_lock_sched() and rcu_read_unlock_sched(), | ||
| 128 | * OR | ||
| 129 | * anything that disables preemption. | ||
| 130 | * These may be nested. | ||
| 131 | */ | ||
| 69 | extern void call_rcu_sched(struct rcu_head *head, | 132 | extern void call_rcu_sched(struct rcu_head *head, |
| 70 | void (*func)(struct rcu_head *rcu)); | 133 | void (*func)(struct rcu_head *rcu)); |
| 134 | |||
| 71 | extern void synchronize_sched(void); | 135 | extern void synchronize_sched(void); |
| 72 | extern void rcu_barrier_bh(void); | ||
| 73 | extern void rcu_barrier_sched(void); | ||
| 74 | 136 | ||
| 75 | static inline void __rcu_read_lock_bh(void) | 137 | static inline void __rcu_read_lock_bh(void) |
| 76 | { | 138 | { |
| @@ -143,6 +205,15 @@ static inline void rcu_exit_nohz(void) | |||
| 143 | 205 | ||
| 144 | #endif /* #else #ifdef CONFIG_NO_HZ */ | 206 | #endif /* #else #ifdef CONFIG_NO_HZ */ |
| 145 | 207 | ||
| 208 | /* | ||
| 209 | * Infrastructure to implement the synchronize_() primitives in | ||
| 210 | * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. | ||
| 211 | */ | ||
| 212 | |||
| 213 | typedef void call_rcu_func_t(struct rcu_head *head, | ||
| 214 | void (*func)(struct rcu_head *head)); | ||
| 215 | void wait_rcu_gp(call_rcu_func_t crf); | ||
| 216 | |||
| 146 | #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) | 217 | #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) |
| 147 | #include <linux/rcutree.h> | 218 | #include <linux/rcutree.h> |
| 148 | #elif defined(CONFIG_TINY_RCU) || defined(CONFIG_TINY_PREEMPT_RCU) | 219 | #elif defined(CONFIG_TINY_RCU) || defined(CONFIG_TINY_PREEMPT_RCU) |
| @@ -723,61 +794,6 @@ static inline notrace void rcu_read_unlock_sched_notrace(void) | |||
| 723 | #define RCU_INIT_POINTER(p, v) \ | 794 | #define RCU_INIT_POINTER(p, v) \ |
| 724 | p = (typeof(*v) __force __rcu *)(v) | 795 | p = (typeof(*v) __force __rcu *)(v) |
| 725 | 796 | ||
| 726 | /* Infrastructure to implement the synchronize_() primitives. */ | ||
| 727 | |||
| 728 | struct rcu_synchronize { | ||
| 729 | struct rcu_head head; | ||
| 730 | struct completion completion; | ||
| 731 | }; | ||
| 732 | |||
| 733 | extern void wakeme_after_rcu(struct rcu_head *head); | ||
| 734 | |||
| 735 | #ifdef CONFIG_PREEMPT_RCU | ||
| 736 | |||
| 737 | /** | ||
| 738 | * call_rcu() - Queue an RCU callback for invocation after a grace period. | ||
| 739 | * @head: structure to be used for queueing the RCU updates. | ||
| 740 | * @func: actual callback function to be invoked after the grace period | ||
| 741 | * | ||
| 742 | * The callback function will be invoked some time after a full grace | ||
| 743 | * period elapses, in other words after all pre-existing RCU read-side | ||
| 744 | * critical sections have completed. However, the callback function | ||
| 745 | * might well execute concurrently with RCU read-side critical sections | ||
| 746 | * that started after call_rcu() was invoked. RCU read-side critical | ||
| 747 | * sections are delimited by rcu_read_lock() and rcu_read_unlock(), | ||
| 748 | * and may be nested. | ||
| 749 | */ | ||
| 750 | extern void call_rcu(struct rcu_head *head, | ||
| 751 | void (*func)(struct rcu_head *head)); | ||
| 752 | |||
| 753 | #else /* #ifdef CONFIG_PREEMPT_RCU */ | ||
| 754 | |||
| 755 | /* In classic RCU, call_rcu() is just call_rcu_sched(). */ | ||
| 756 | #define call_rcu call_rcu_sched | ||
| 757 | |||
| 758 | #endif /* #else #ifdef CONFIG_PREEMPT_RCU */ | ||
| 759 | |||
| 760 | /** | ||
| 761 | * call_rcu_bh() - Queue an RCU for invocation after a quicker grace period. | ||
| 762 | * @head: structure to be used for queueing the RCU updates. | ||
| 763 | * @func: actual callback function to be invoked after the grace period | ||
| 764 | * | ||
| 765 | * The callback function will be invoked some time after a full grace | ||
| 766 | * period elapses, in other words after all currently executing RCU | ||
| 767 | * read-side critical sections have completed. call_rcu_bh() assumes | ||
| 768 | * that the read-side critical sections end on completion of a softirq | ||
| 769 | * handler. This means that read-side critical sections in process | ||
| 770 | * context must not be interrupted by softirqs. This interface is to be | ||
| 771 | * used when most of the read-side critical sections are in softirq context. | ||
| 772 | * RCU read-side critical sections are delimited by : | ||
| 773 | * - rcu_read_lock() and rcu_read_unlock(), if in interrupt context. | ||
| 774 | * OR | ||
| 775 | * - rcu_read_lock_bh() and rcu_read_unlock_bh(), if in process context. | ||
| 776 | * These may be nested. | ||
| 777 | */ | ||
| 778 | extern void call_rcu_bh(struct rcu_head *head, | ||
| 779 | void (*func)(struct rcu_head *head)); | ||
| 780 | |||
| 781 | /* | 797 | /* |
| 782 | * debug_rcu_head_queue()/debug_rcu_head_unqueue() are used internally | 798 | * debug_rcu_head_queue()/debug_rcu_head_unqueue() are used internally |
| 783 | * by call_rcu() and rcu callback execution, and are therefore not part of the | 799 | * by call_rcu() and rcu callback execution, and are therefore not part of the |
diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 52b3e0281fd0..4eab233a00cd 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h | |||
| @@ -31,6 +31,16 @@ static inline void rcu_init(void) | |||
| 31 | { | 31 | { |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | static inline void rcu_barrier_bh(void) | ||
| 35 | { | ||
| 36 | wait_rcu_gp(call_rcu_bh); | ||
| 37 | } | ||
| 38 | |||
| 39 | static inline void rcu_barrier_sched(void) | ||
| 40 | { | ||
| 41 | wait_rcu_gp(call_rcu_sched); | ||
| 42 | } | ||
| 43 | |||
| 34 | #ifdef CONFIG_TINY_RCU | 44 | #ifdef CONFIG_TINY_RCU |
| 35 | 45 | ||
| 36 | static inline void synchronize_rcu_expedited(void) | 46 | static inline void synchronize_rcu_expedited(void) |
| @@ -45,9 +55,13 @@ static inline void rcu_barrier(void) | |||
| 45 | 55 | ||
| 46 | #else /* #ifdef CONFIG_TINY_RCU */ | 56 | #else /* #ifdef CONFIG_TINY_RCU */ |
| 47 | 57 | ||
| 48 | void rcu_barrier(void); | ||
| 49 | void synchronize_rcu_expedited(void); | 58 | void synchronize_rcu_expedited(void); |
| 50 | 59 | ||
| 60 | static inline void rcu_barrier(void) | ||
| 61 | { | ||
| 62 | wait_rcu_gp(call_rcu); | ||
| 63 | } | ||
| 64 | |||
| 51 | #endif /* #else #ifdef CONFIG_TINY_RCU */ | 65 | #endif /* #else #ifdef CONFIG_TINY_RCU */ |
| 52 | 66 | ||
| 53 | static inline void synchronize_rcu_bh(void) | 67 | static inline void synchronize_rcu_bh(void) |
diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index e65d06634dd8..67458468f1a8 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h | |||
| @@ -67,6 +67,8 @@ static inline void synchronize_rcu_bh_expedited(void) | |||
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | extern void rcu_barrier(void); | 69 | extern void rcu_barrier(void); |
| 70 | extern void rcu_barrier_bh(void); | ||
| 71 | extern void rcu_barrier_sched(void); | ||
| 70 | 72 | ||
| 71 | extern unsigned long rcutorture_testseq; | 73 | extern unsigned long rcutorture_testseq; |
| 72 | extern unsigned long rcutorture_vernum; | 74 | extern unsigned long rcutorture_vernum; |
diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index ddddb320be61..09b3b1b54e02 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c | |||
| @@ -94,11 +94,16 @@ EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held); | |||
| 94 | 94 | ||
| 95 | #endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ | 95 | #endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ |
| 96 | 96 | ||
| 97 | struct rcu_synchronize { | ||
| 98 | struct rcu_head head; | ||
| 99 | struct completion completion; | ||
| 100 | }; | ||
| 101 | |||
| 97 | /* | 102 | /* |
| 98 | * Awaken the corresponding synchronize_rcu() instance now that a | 103 | * Awaken the corresponding synchronize_rcu() instance now that a |
| 99 | * grace period has elapsed. | 104 | * grace period has elapsed. |
| 100 | */ | 105 | */ |
| 101 | void wakeme_after_rcu(struct rcu_head *head) | 106 | static void wakeme_after_rcu(struct rcu_head *head) |
| 102 | { | 107 | { |
| 103 | struct rcu_synchronize *rcu; | 108 | struct rcu_synchronize *rcu; |
| 104 | 109 | ||
| @@ -106,6 +111,20 @@ void wakeme_after_rcu(struct rcu_head *head) | |||
| 106 | complete(&rcu->completion); | 111 | complete(&rcu->completion); |
| 107 | } | 112 | } |
| 108 | 113 | ||
| 114 | void wait_rcu_gp(call_rcu_func_t crf) | ||
| 115 | { | ||
| 116 | struct rcu_synchronize rcu; | ||
| 117 | |||
| 118 | init_rcu_head_on_stack(&rcu.head); | ||
| 119 | init_completion(&rcu.completion); | ||
| 120 | /* Will wake me after RCU finished. */ | ||
| 121 | crf(&rcu.head, wakeme_after_rcu); | ||
| 122 | /* Wait for it. */ | ||
| 123 | wait_for_completion(&rcu.completion); | ||
| 124 | destroy_rcu_head_on_stack(&rcu.head); | ||
| 125 | } | ||
| 126 | EXPORT_SYMBOL_GPL(wait_rcu_gp); | ||
| 127 | |||
| 109 | #ifdef CONFIG_PROVE_RCU | 128 | #ifdef CONFIG_PROVE_RCU |
| 110 | /* | 129 | /* |
| 111 | * wrapper function to avoid #include problems. | 130 | * wrapper function to avoid #include problems. |
diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index 7bbac7d0f5ab..f544e343256a 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c | |||
| @@ -281,34 +281,6 @@ void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) | |||
| 281 | } | 281 | } |
| 282 | EXPORT_SYMBOL_GPL(call_rcu_bh); | 282 | EXPORT_SYMBOL_GPL(call_rcu_bh); |
| 283 | 283 | ||
| 284 | void rcu_barrier_bh(void) | ||
| 285 | { | ||
| 286 | struct rcu_synchronize rcu; | ||
| 287 | |||
| 288 | init_rcu_head_on_stack(&rcu.head); | ||
| 289 | init_completion(&rcu.completion); | ||
| 290 | /* Will wake me after RCU finished. */ | ||
| 291 | call_rcu_bh(&rcu.head, wakeme_after_rcu); | ||
| 292 | /* Wait for it. */ | ||
| 293 | wait_for_completion(&rcu.completion); | ||
| 294 | destroy_rcu_head_on_stack(&rcu.head); | ||
| 295 | } | ||
| 296 | EXPORT_SYMBOL_GPL(rcu_barrier_bh); | ||
| 297 | |||
| 298 | void rcu_barrier_sched(void) | ||
| 299 | { | ||
| 300 | struct rcu_synchronize rcu; | ||
| 301 | |||
| 302 | init_rcu_head_on_stack(&rcu.head); | ||
| 303 | init_completion(&rcu.completion); | ||
| 304 | /* Will wake me after RCU finished. */ | ||
| 305 | call_rcu_sched(&rcu.head, wakeme_after_rcu); | ||
| 306 | /* Wait for it. */ | ||
| 307 | wait_for_completion(&rcu.completion); | ||
| 308 | destroy_rcu_head_on_stack(&rcu.head); | ||
| 309 | } | ||
| 310 | EXPORT_SYMBOL_GPL(rcu_barrier_sched); | ||
| 311 | |||
| 312 | /* | 284 | /* |
| 313 | * Spawn the kthread that invokes RCU callbacks. | 285 | * Spawn the kthread that invokes RCU callbacks. |
| 314 | */ | 286 | */ |
diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h index f259c676195f..6b0cedb383e0 100644 --- a/kernel/rcutiny_plugin.h +++ b/kernel/rcutiny_plugin.h | |||
| @@ -697,20 +697,6 @@ void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) | |||
| 697 | } | 697 | } |
| 698 | EXPORT_SYMBOL_GPL(call_rcu); | 698 | EXPORT_SYMBOL_GPL(call_rcu); |
| 699 | 699 | ||
| 700 | void rcu_barrier(void) | ||
| 701 | { | ||
| 702 | struct rcu_synchronize rcu; | ||
| 703 | |||
| 704 | init_rcu_head_on_stack(&rcu.head); | ||
| 705 | init_completion(&rcu.completion); | ||
| 706 | /* Will wake me after RCU finished. */ | ||
| 707 | call_rcu(&rcu.head, wakeme_after_rcu); | ||
| 708 | /* Wait for it. */ | ||
| 709 | wait_for_completion(&rcu.completion); | ||
| 710 | destroy_rcu_head_on_stack(&rcu.head); | ||
| 711 | } | ||
| 712 | EXPORT_SYMBOL_GPL(rcu_barrier); | ||
| 713 | |||
| 714 | /* | 700 | /* |
| 715 | * synchronize_rcu - wait until a grace period has elapsed. | 701 | * synchronize_rcu - wait until a grace period has elapsed. |
| 716 | * | 702 | * |
diff --git a/kernel/rcutree.c b/kernel/rcutree.c index ba06207b1dd3..a7c6bce1af83 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c | |||
| @@ -1613,18 +1613,9 @@ EXPORT_SYMBOL_GPL(call_rcu_bh); | |||
| 1613 | */ | 1613 | */ |
| 1614 | void synchronize_sched(void) | 1614 | void synchronize_sched(void) |
| 1615 | { | 1615 | { |
| 1616 | struct rcu_synchronize rcu; | ||
| 1617 | |||
| 1618 | if (rcu_blocking_is_gp()) | 1616 | if (rcu_blocking_is_gp()) |
| 1619 | return; | 1617 | return; |
| 1620 | 1618 | wait_rcu_gp(call_rcu_sched); | |
| 1621 | init_rcu_head_on_stack(&rcu.head); | ||
| 1622 | init_completion(&rcu.completion); | ||
| 1623 | /* Will wake me after RCU finished. */ | ||
| 1624 | call_rcu_sched(&rcu.head, wakeme_after_rcu); | ||
| 1625 | /* Wait for it. */ | ||
| 1626 | wait_for_completion(&rcu.completion); | ||
| 1627 | destroy_rcu_head_on_stack(&rcu.head); | ||
| 1628 | } | 1619 | } |
| 1629 | EXPORT_SYMBOL_GPL(synchronize_sched); | 1620 | EXPORT_SYMBOL_GPL(synchronize_sched); |
| 1630 | 1621 | ||
| @@ -1639,18 +1630,9 @@ EXPORT_SYMBOL_GPL(synchronize_sched); | |||
| 1639 | */ | 1630 | */ |
| 1640 | void synchronize_rcu_bh(void) | 1631 | void synchronize_rcu_bh(void) |
| 1641 | { | 1632 | { |
| 1642 | struct rcu_synchronize rcu; | ||
| 1643 | |||
| 1644 | if (rcu_blocking_is_gp()) | 1633 | if (rcu_blocking_is_gp()) |
| 1645 | return; | 1634 | return; |
| 1646 | 1635 | wait_rcu_gp(call_rcu_bh); | |
| 1647 | init_rcu_head_on_stack(&rcu.head); | ||
| 1648 | init_completion(&rcu.completion); | ||
| 1649 | /* Will wake me after RCU finished. */ | ||
| 1650 | call_rcu_bh(&rcu.head, wakeme_after_rcu); | ||
| 1651 | /* Wait for it. */ | ||
| 1652 | wait_for_completion(&rcu.completion); | ||
| 1653 | destroy_rcu_head_on_stack(&rcu.head); | ||
| 1654 | } | 1636 | } |
| 1655 | EXPORT_SYMBOL_GPL(synchronize_rcu_bh); | 1637 | EXPORT_SYMBOL_GPL(synchronize_rcu_bh); |
| 1656 | 1638 | ||
diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 970329853dc5..43daa46bc6f2 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h | |||
| @@ -656,18 +656,9 @@ EXPORT_SYMBOL_GPL(call_rcu); | |||
| 656 | */ | 656 | */ |
| 657 | void synchronize_rcu(void) | 657 | void synchronize_rcu(void) |
| 658 | { | 658 | { |
| 659 | struct rcu_synchronize rcu; | ||
| 660 | |||
| 661 | if (!rcu_scheduler_active) | 659 | if (!rcu_scheduler_active) |
| 662 | return; | 660 | return; |
| 663 | 661 | wait_rcu_gp(call_rcu); | |
| 664 | init_rcu_head_on_stack(&rcu.head); | ||
| 665 | init_completion(&rcu.completion); | ||
| 666 | /* Will wake me after RCU finished. */ | ||
| 667 | call_rcu(&rcu.head, wakeme_after_rcu); | ||
| 668 | /* Wait for it. */ | ||
| 669 | wait_for_completion(&rcu.completion); | ||
| 670 | destroy_rcu_head_on_stack(&rcu.head); | ||
| 671 | } | 662 | } |
| 672 | EXPORT_SYMBOL_GPL(synchronize_rcu); | 663 | EXPORT_SYMBOL_GPL(synchronize_rcu); |
| 673 | 664 | ||
