1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include <linux/moduleparam.h>
26#include <linux/completion.h>
27#include <linux/interrupt.h>
28#include <linux/notifier.h>
29#include <linux/rcupdate.h>
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/mutex.h>
33#include <linux/sched.h>
34#include <linux/types.h>
35#include <linux/init.h>
36#include <linux/time.h>
37#include <linux/cpu.h>
38#include <linux/prefetch.h>
39
40
41static struct task_struct *rcu_kthread_task;
42static DECLARE_WAIT_QUEUE_HEAD(rcu_kthread_wq);
43static unsigned long have_rcu_kthread_work;
44
45
46struct rcu_ctrlblk;
47static void invoke_rcu_kthread(void);
48static void rcu_process_callbacks(struct rcu_ctrlblk *rcp);
49static int rcu_kthread(void *arg);
50static void __call_rcu(struct rcu_head *head,
51 void (*func)(struct rcu_head *rcu),
52 struct rcu_ctrlblk *rcp);
53
54#include "rcutiny_plugin.h"
55
56#ifdef CONFIG_NO_HZ
57
58static long rcu_dynticks_nesting = 1;
59
60
61
62
63
64
65void rcu_enter_nohz(void)
66{
67 if (--rcu_dynticks_nesting == 0)
68 rcu_sched_qs(0);
69}
70
71
72
73
74
75void rcu_exit_nohz(void)
76{
77 rcu_dynticks_nesting++;
78}
79
80#endif
81
82
83
84
85
86
87static int rcu_qsctr_help(struct rcu_ctrlblk *rcp)
88{
89 if (rcp->rcucblist != NULL &&
90 rcp->donetail != rcp->curtail) {
91 rcp->donetail = rcp->curtail;
92 return 1;
93 }
94
95 return 0;
96}
97
98
99
100
101
102static void invoke_rcu_kthread(void)
103{
104 have_rcu_kthread_work = 1;
105 wake_up(&rcu_kthread_wq);
106}
107
108
109
110
111
112
113void rcu_sched_qs(int cpu)
114{
115 unsigned long flags;
116
117 local_irq_save(flags);
118 if (rcu_qsctr_help(&rcu_sched_ctrlblk) +
119 rcu_qsctr_help(&rcu_bh_ctrlblk))
120 invoke_rcu_kthread();
121 local_irq_restore(flags);
122}
123
124
125
126
127void rcu_bh_qs(int cpu)
128{
129 unsigned long flags;
130
131 local_irq_save(flags);
132 if (rcu_qsctr_help(&rcu_bh_ctrlblk))
133 invoke_rcu_kthread();
134 local_irq_restore(flags);
135}
136
137
138
139
140
141void rcu_check_callbacks(int cpu, int user)
142{
143 if (user ||
144 (idle_cpu(cpu) &&
145 !in_softirq() &&
146 hardirq_count() <= (1 << HARDIRQ_SHIFT)))
147 rcu_sched_qs(cpu);
148 else if (!in_softirq())
149 rcu_bh_qs(cpu);
150 rcu_preempt_check_callbacks();
151}
152
153
154
155
156
157static void rcu_process_callbacks(struct rcu_ctrlblk *rcp)
158{
159 struct rcu_head *next, *list;
160 unsigned long flags;
161 RCU_TRACE(int cb_count = 0);
162
163
164 if (&rcp->rcucblist == rcp->donetail)
165 return;
166
167
168 local_irq_save(flags);
169 list = rcp->rcucblist;
170 rcp->rcucblist = *rcp->donetail;
171 *rcp->donetail = NULL;
172 if (rcp->curtail == rcp->donetail)
173 rcp->curtail = &rcp->rcucblist;
174 rcu_preempt_remove_callbacks(rcp);
175 rcp->donetail = &rcp->rcucblist;
176 local_irq_restore(flags);
177
178
179 while (list) {
180 next = list->next;
181 prefetch(next);
182 debug_rcu_head_unqueue(list);
183 local_bh_disable();
184 __rcu_reclaim(list);
185 local_bh_enable();
186 list = next;
187 RCU_TRACE(cb_count++);
188 }
189 RCU_TRACE(rcu_trace_sub_qlen(rcp, cb_count));
190}
191
192
193
194
195
196
197
198
199static int rcu_kthread(void *arg)
200{
201 unsigned long work;
202 unsigned long morework;
203 unsigned long flags;
204
205 for (;;) {
206 wait_event_interruptible(rcu_kthread_wq,
207 have_rcu_kthread_work != 0);
208 morework = rcu_boost();
209 local_irq_save(flags);
210 work = have_rcu_kthread_work;
211 have_rcu_kthread_work = morework;
212 local_irq_restore(flags);
213 if (work) {
214 rcu_process_callbacks(&rcu_sched_ctrlblk);
215 rcu_process_callbacks(&rcu_bh_ctrlblk);
216 rcu_preempt_process_callbacks();
217 }
218 schedule_timeout_interruptible(1);
219 }
220
221 return 0;
222}
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237void synchronize_sched(void)
238{
239 cond_resched();
240}
241EXPORT_SYMBOL_GPL(synchronize_sched);
242
243
244
245
246static void __call_rcu(struct rcu_head *head,
247 void (*func)(struct rcu_head *rcu),
248 struct rcu_ctrlblk *rcp)
249{
250 unsigned long flags;
251
252 debug_rcu_head_queue(head);
253 head->func = func;
254 head->next = NULL;
255
256 local_irq_save(flags);
257 *rcp->curtail = head;
258 rcp->curtail = &head->next;
259 RCU_TRACE(rcp->qlen++);
260 local_irq_restore(flags);
261}
262
263
264
265
266
267
268void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
269{
270 __call_rcu(head, func, &rcu_sched_ctrlblk);
271}
272EXPORT_SYMBOL_GPL(call_rcu_sched);
273
274
275
276
277
278void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
279{
280 __call_rcu(head, func, &rcu_bh_ctrlblk);
281}
282EXPORT_SYMBOL_GPL(call_rcu_bh);
283
284void 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
291 call_rcu_bh(&rcu.head, wakeme_after_rcu);
292
293 wait_for_completion(&rcu.completion);
294 destroy_rcu_head_on_stack(&rcu.head);
295}
296EXPORT_SYMBOL_GPL(rcu_barrier_bh);
297
298void 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
305 call_rcu_sched(&rcu.head, wakeme_after_rcu);
306
307 wait_for_completion(&rcu.completion);
308 destroy_rcu_head_on_stack(&rcu.head);
309}
310EXPORT_SYMBOL_GPL(rcu_barrier_sched);
311
312
313
314
315static int __init rcu_spawn_kthreads(void)
316{
317 struct sched_param sp;
318
319 rcu_kthread_task = kthread_run(rcu_kthread, NULL, "rcu_kthread");
320 sp.sched_priority = RCU_BOOST_PRIO;
321 sched_setscheduler_nocheck(rcu_kthread_task, SCHED_FIFO, &sp);
322 return 0;
323}
324early_initcall(rcu_spawn_kthreads);
325