1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <linux/export.h>
25#include <linux/mutex.h>
26#include <linux/preempt.h>
27#include <linux/rcupdate_wait.h>
28#include <linux/sched.h>
29#include <linux/delay.h>
30#include <linux/srcu.h>
31
32#include <linux/rcu_node_tree.h>
33#include "rcu_segcblist.h"
34#include "rcu.h"
35
36int rcu_scheduler_active __read_mostly;
37static LIST_HEAD(srcu_boot_list);
38static bool srcu_init_done;
39
40static int init_srcu_struct_fields(struct srcu_struct *ssp)
41{
42 ssp->srcu_lock_nesting[0] = 0;
43 ssp->srcu_lock_nesting[1] = 0;
44 init_swait_queue_head(&ssp->srcu_wq);
45 ssp->srcu_cb_head = NULL;
46 ssp->srcu_cb_tail = &ssp->srcu_cb_head;
47 ssp->srcu_gp_running = false;
48 ssp->srcu_gp_waiting = false;
49 ssp->srcu_idx = 0;
50 INIT_WORK(&ssp->srcu_work, srcu_drive_gp);
51 INIT_LIST_HEAD(&ssp->srcu_work.entry);
52 return 0;
53}
54
55#ifdef CONFIG_DEBUG_LOCK_ALLOC
56
57int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
58 struct lock_class_key *key)
59{
60
61 debug_check_no_locks_freed((void *)ssp, sizeof(*ssp));
62 lockdep_init_map(&ssp->dep_map, name, key, 0);
63 return init_srcu_struct_fields(ssp);
64}
65EXPORT_SYMBOL_GPL(__init_srcu_struct);
66
67#else
68
69
70
71
72
73
74
75
76
77int init_srcu_struct(struct srcu_struct *ssp)
78{
79 return init_srcu_struct_fields(ssp);
80}
81EXPORT_SYMBOL_GPL(init_srcu_struct);
82
83#endif
84
85
86
87
88
89
90
91
92void cleanup_srcu_struct(struct srcu_struct *ssp)
93{
94 WARN_ON(ssp->srcu_lock_nesting[0] || ssp->srcu_lock_nesting[1]);
95 flush_work(&ssp->srcu_work);
96 WARN_ON(ssp->srcu_gp_running);
97 WARN_ON(ssp->srcu_gp_waiting);
98 WARN_ON(ssp->srcu_cb_head);
99 WARN_ON(&ssp->srcu_cb_head != ssp->srcu_cb_tail);
100}
101EXPORT_SYMBOL_GPL(cleanup_srcu_struct);
102
103
104
105
106
107void __srcu_read_unlock(struct srcu_struct *ssp, int idx)
108{
109 int newval = ssp->srcu_lock_nesting[idx] - 1;
110
111 WRITE_ONCE(ssp->srcu_lock_nesting[idx], newval);
112 if (!newval && READ_ONCE(ssp->srcu_gp_waiting))
113 swake_up_one(&ssp->srcu_wq);
114}
115EXPORT_SYMBOL_GPL(__srcu_read_unlock);
116
117
118
119
120
121
122void srcu_drive_gp(struct work_struct *wp)
123{
124 int idx;
125 struct rcu_head *lh;
126 struct rcu_head *rhp;
127 struct srcu_struct *ssp;
128
129 ssp = container_of(wp, struct srcu_struct, srcu_work);
130 if (ssp->srcu_gp_running || !READ_ONCE(ssp->srcu_cb_head))
131 return;
132
133
134 WRITE_ONCE(ssp->srcu_gp_running, true);
135 local_irq_disable();
136 lh = ssp->srcu_cb_head;
137 ssp->srcu_cb_head = NULL;
138 ssp->srcu_cb_tail = &ssp->srcu_cb_head;
139 local_irq_enable();
140 idx = ssp->srcu_idx;
141 WRITE_ONCE(ssp->srcu_idx, !ssp->srcu_idx);
142 WRITE_ONCE(ssp->srcu_gp_waiting, true);
143 swait_event_exclusive(ssp->srcu_wq, !READ_ONCE(ssp->srcu_lock_nesting[idx]));
144 WRITE_ONCE(ssp->srcu_gp_waiting, false);
145
146
147 while (lh) {
148 rhp = lh;
149 lh = lh->next;
150 local_bh_disable();
151 rhp->func(rhp);
152 local_bh_enable();
153 }
154
155
156
157
158
159
160
161 WRITE_ONCE(ssp->srcu_gp_running, false);
162 if (READ_ONCE(ssp->srcu_cb_head))
163 schedule_work(&ssp->srcu_work);
164}
165EXPORT_SYMBOL_GPL(srcu_drive_gp);
166
167
168
169
170
171void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
172 rcu_callback_t func)
173{
174 unsigned long flags;
175
176 rhp->func = func;
177 rhp->next = NULL;
178 local_irq_save(flags);
179 *ssp->srcu_cb_tail = rhp;
180 ssp->srcu_cb_tail = &rhp->next;
181 local_irq_restore(flags);
182 if (!READ_ONCE(ssp->srcu_gp_running)) {
183 if (likely(srcu_init_done))
184 schedule_work(&ssp->srcu_work);
185 else if (list_empty(&ssp->srcu_work.entry))
186 list_add(&ssp->srcu_work.entry, &srcu_boot_list);
187 }
188}
189EXPORT_SYMBOL_GPL(call_srcu);
190
191
192
193
194void synchronize_srcu(struct srcu_struct *ssp)
195{
196 struct rcu_synchronize rs;
197
198 init_rcu_head_on_stack(&rs.head);
199 init_completion(&rs.completion);
200 call_srcu(ssp, &rs.head, wakeme_after_rcu);
201 wait_for_completion(&rs.completion);
202 destroy_rcu_head_on_stack(&rs.head);
203}
204EXPORT_SYMBOL_GPL(synchronize_srcu);
205
206
207void __init rcu_scheduler_starting(void)
208{
209 rcu_scheduler_active = RCU_SCHEDULER_RUNNING;
210}
211
212
213
214
215
216
217void __init srcu_init(void)
218{
219 struct srcu_struct *ssp;
220
221 srcu_init_done = true;
222 while (!list_empty(&srcu_boot_list)) {
223 ssp = list_first_entry(&srcu_boot_list,
224 struct srcu_struct, srcu_work.entry);
225 list_del_init(&ssp->srcu_work.entry);
226 schedule_work(&ssp->srcu_work);
227 }
228}
229