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/completion.h>
26#include <linux/interrupt.h>
27#include <linux/notifier.h>
28#include <linux/rcupdate.h>
29#include <linux/kernel.h>
30#include <linux/export.h>
31#include <linux/mutex.h>
32#include <linux/sched.h>
33#include <linux/types.h>
34#include <linux/init.h>
35#include <linux/time.h>
36#include <linux/cpu.h>
37#include <linux/prefetch.h>
38
39#ifdef CONFIG_RCU_TRACE
40#include <trace/events/rcu.h>
41#endif
42
43#include "rcu.h"
44
45
46struct rcu_ctrlblk;
47static void invoke_rcu_callbacks(void);
48static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp);
49static void rcu_process_callbacks(struct softirq_action *unused);
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
102
103void rcu_sched_qs(int cpu)
104{
105 unsigned long flags;
106
107 local_irq_save(flags);
108 if (rcu_qsctr_help(&rcu_sched_ctrlblk) +
109 rcu_qsctr_help(&rcu_bh_ctrlblk))
110 invoke_rcu_callbacks();
111 local_irq_restore(flags);
112}
113
114
115
116
117void rcu_bh_qs(int cpu)
118{
119 unsigned long flags;
120
121 local_irq_save(flags);
122 if (rcu_qsctr_help(&rcu_bh_ctrlblk))
123 invoke_rcu_callbacks();
124 local_irq_restore(flags);
125}
126
127
128
129
130
131void rcu_check_callbacks(int cpu, int user)
132{
133 if (user ||
134 (idle_cpu(cpu) &&
135 !in_softirq() &&
136 hardirq_count() <= (1 << HARDIRQ_SHIFT)))
137 rcu_sched_qs(cpu);
138 else if (!in_softirq())
139 rcu_bh_qs(cpu);
140 rcu_preempt_check_callbacks();
141}
142
143
144
145
146
147static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp)
148{
149 char *rn = NULL;
150 struct rcu_head *next, *list;
151 unsigned long flags;
152 RCU_TRACE(int cb_count = 0);
153
154
155 if (&rcp->rcucblist == rcp->donetail) {
156 RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, -1));
157 RCU_TRACE(trace_rcu_batch_end(rcp->name, 0));
158 return;
159 }
160
161
162 local_irq_save(flags);
163 RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, -1));
164 list = rcp->rcucblist;
165 rcp->rcucblist = *rcp->donetail;
166 *rcp->donetail = NULL;
167 if (rcp->curtail == rcp->donetail)
168 rcp->curtail = &rcp->rcucblist;
169 rcu_preempt_remove_callbacks(rcp);
170 rcp->donetail = &rcp->rcucblist;
171 local_irq_restore(flags);
172
173
174 RCU_TRACE(rn = rcp->name);
175 while (list) {
176 next = list->next;
177 prefetch(next);
178 debug_rcu_head_unqueue(list);
179 local_bh_disable();
180 __rcu_reclaim(rn, list);
181 local_bh_enable();
182 list = next;
183 RCU_TRACE(cb_count++);
184 }
185 RCU_TRACE(rcu_trace_sub_qlen(rcp, cb_count));
186 RCU_TRACE(trace_rcu_batch_end(rcp->name, cb_count));
187}
188
189static void rcu_process_callbacks(struct softirq_action *unused)
190{
191 __rcu_process_callbacks(&rcu_sched_ctrlblk);
192 __rcu_process_callbacks(&rcu_bh_ctrlblk);
193 rcu_preempt_process_callbacks();
194}
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209void synchronize_sched(void)
210{
211 cond_resched();
212}
213EXPORT_SYMBOL_GPL(synchronize_sched);
214
215
216
217
218static void __call_rcu(struct rcu_head *head,
219 void (*func)(struct rcu_head *rcu),
220 struct rcu_ctrlblk *rcp)
221{
222 unsigned long flags;
223
224 debug_rcu_head_queue(head);
225 head->func = func;
226 head->next = NULL;
227
228 local_irq_save(flags);
229 *rcp->curtail = head;
230 rcp->curtail = &head->next;
231 RCU_TRACE(rcp->qlen++);
232 local_irq_restore(flags);
233}
234
235
236
237
238
239
240void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
241{
242 __call_rcu(head, func, &rcu_sched_ctrlblk);
243}
244EXPORT_SYMBOL_GPL(call_rcu_sched);
245
246
247
248
249
250void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
251{
252 __call_rcu(head, func, &rcu_bh_ctrlblk);
253}
254EXPORT_SYMBOL_GPL(call_rcu_bh);
255