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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51#include <linux/async.h>
52#include <linux/atomic.h>
53#include <linux/ktime.h>
54#include <linux/export.h>
55#include <linux/wait.h>
56#include <linux/sched.h>
57#include <linux/slab.h>
58#include <linux/workqueue.h>
59
60static async_cookie_t next_cookie = 1;
61
62#define MAX_WORK 32768
63
64static LIST_HEAD(async_pending);
65static ASYNC_DOMAIN(async_running);
66static LIST_HEAD(async_domains);
67static DEFINE_SPINLOCK(async_lock);
68static DEFINE_MUTEX(async_register_mutex);
69
70struct async_entry {
71 struct list_head list;
72 struct work_struct work;
73 async_cookie_t cookie;
74 async_func_ptr *func;
75 void *data;
76 struct async_domain *running;
77};
78
79static DECLARE_WAIT_QUEUE_HEAD(async_done);
80
81static atomic_t entry_count;
82
83
84
85
86
87static async_cookie_t __lowest_in_progress(struct async_domain *running)
88{
89 struct async_entry *entry;
90
91 if (!list_empty(&running->domain)) {
92 entry = list_first_entry(&running->domain, typeof(*entry), list);
93 return entry->cookie;
94 }
95
96 list_for_each_entry(entry, &async_pending, list)
97 if (entry->running == running)
98 return entry->cookie;
99
100 return next_cookie;
101}
102
103static async_cookie_t lowest_in_progress(struct async_domain *running)
104{
105 unsigned long flags;
106 async_cookie_t ret;
107
108 spin_lock_irqsave(&async_lock, flags);
109 ret = __lowest_in_progress(running);
110 spin_unlock_irqrestore(&async_lock, flags);
111 return ret;
112}
113
114
115
116
117static void async_run_entry_fn(struct work_struct *work)
118{
119 struct async_entry *entry =
120 container_of(work, struct async_entry, work);
121 unsigned long flags;
122 ktime_t uninitialized_var(calltime), delta, rettime;
123 struct async_domain *running = entry->running;
124
125
126 spin_lock_irqsave(&async_lock, flags);
127 list_move_tail(&entry->list, &running->domain);
128 spin_unlock_irqrestore(&async_lock, flags);
129
130
131 if (initcall_debug && system_state == SYSTEM_BOOTING) {
132 printk(KERN_DEBUG "calling %lli_%pF @ %i\n",
133 (long long)entry->cookie,
134 entry->func, task_pid_nr(current));
135 calltime = ktime_get();
136 }
137 entry->func(entry->data, entry->cookie);
138 if (initcall_debug && system_state == SYSTEM_BOOTING) {
139 rettime = ktime_get();
140 delta = ktime_sub(rettime, calltime);
141 printk(KERN_DEBUG "initcall %lli_%pF returned 0 after %lld usecs\n",
142 (long long)entry->cookie,
143 entry->func,
144 (long long)ktime_to_ns(delta) >> 10);
145 }
146
147
148 spin_lock_irqsave(&async_lock, flags);
149 list_del(&entry->list);
150 if (running->registered && --running->count == 0)
151 list_del_init(&running->node);
152
153
154 kfree(entry);
155 atomic_dec(&entry_count);
156
157 spin_unlock_irqrestore(&async_lock, flags);
158
159
160 wake_up(&async_done);
161}
162
163static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct async_domain *running)
164{
165 struct async_entry *entry;
166 unsigned long flags;
167 async_cookie_t newcookie;
168
169
170 entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
171
172
173
174
175
176 if (!entry || atomic_read(&entry_count) > MAX_WORK) {
177 kfree(entry);
178 spin_lock_irqsave(&async_lock, flags);
179 newcookie = next_cookie++;
180 spin_unlock_irqrestore(&async_lock, flags);
181
182
183 ptr(data, newcookie);
184 return newcookie;
185 }
186 INIT_WORK(&entry->work, async_run_entry_fn);
187 entry->func = ptr;
188 entry->data = data;
189 entry->running = running;
190
191 spin_lock_irqsave(&async_lock, flags);
192 newcookie = entry->cookie = next_cookie++;
193 list_add_tail(&entry->list, &async_pending);
194 if (running->registered && running->count++ == 0)
195 list_add_tail(&running->node, &async_domains);
196 atomic_inc(&entry_count);
197 spin_unlock_irqrestore(&async_lock, flags);
198
199
200 queue_work(system_unbound_wq, &entry->work);
201
202 return newcookie;
203}
204
205
206
207
208
209
210
211
212
213async_cookie_t async_schedule(async_func_ptr *ptr, void *data)
214{
215 return __async_schedule(ptr, data, &async_running);
216}
217EXPORT_SYMBOL_GPL(async_schedule);
218
219
220
221
222
223
224
225
226
227
228
229
230
231async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data,
232 struct async_domain *running)
233{
234 return __async_schedule(ptr, data, running);
235}
236EXPORT_SYMBOL_GPL(async_schedule_domain);
237
238
239
240
241
242
243void async_synchronize_full(void)
244{
245 mutex_lock(&async_register_mutex);
246 do {
247 struct async_domain *domain = NULL;
248
249 spin_lock_irq(&async_lock);
250 if (!list_empty(&async_domains))
251 domain = list_first_entry(&async_domains, typeof(*domain), node);
252 spin_unlock_irq(&async_lock);
253
254 async_synchronize_cookie_domain(next_cookie, domain);
255 } while (!list_empty(&async_domains));
256 mutex_unlock(&async_register_mutex);
257}
258EXPORT_SYMBOL_GPL(async_synchronize_full);
259
260
261
262
263
264
265
266
267
268
269void async_unregister_domain(struct async_domain *domain)
270{
271 mutex_lock(&async_register_mutex);
272 spin_lock_irq(&async_lock);
273 WARN_ON(!domain->registered || !list_empty(&domain->node) ||
274 !list_empty(&domain->domain));
275 domain->registered = 0;
276 spin_unlock_irq(&async_lock);
277 mutex_unlock(&async_register_mutex);
278}
279EXPORT_SYMBOL_GPL(async_unregister_domain);
280
281
282
283
284
285
286
287
288void async_synchronize_full_domain(struct async_domain *domain)
289{
290 async_synchronize_cookie_domain(next_cookie, domain);
291}
292EXPORT_SYMBOL_GPL(async_synchronize_full_domain);
293
294
295
296
297
298
299
300
301
302
303void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *running)
304{
305 ktime_t uninitialized_var(starttime), delta, endtime;
306
307 if (!running)
308 return;
309
310 if (initcall_debug && system_state == SYSTEM_BOOTING) {
311 printk(KERN_DEBUG "async_waiting @ %i\n", task_pid_nr(current));
312 starttime = ktime_get();
313 }
314
315 wait_event(async_done, lowest_in_progress(running) >= cookie);
316
317 if (initcall_debug && system_state == SYSTEM_BOOTING) {
318 endtime = ktime_get();
319 delta = ktime_sub(endtime, starttime);
320
321 printk(KERN_DEBUG "async_continuing @ %i after %lli usec\n",
322 task_pid_nr(current),
323 (long long)ktime_to_ns(delta) >> 10);
324 }
325}
326EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);
327
328
329
330
331
332
333
334
335void async_synchronize_cookie(async_cookie_t cookie)
336{
337 async_synchronize_cookie_domain(cookie, &async_running);
338}
339EXPORT_SYMBOL_GPL(async_synchronize_cookie);
340