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/bug.h>
53#include <linux/module.h>
54#include <linux/wait.h>
55#include <linux/sched.h>
56#include <linux/init.h>
57#include <linux/kthread.h>
58#include <linux/delay.h>
59#include <linux/slab.h>
60#include <asm/atomic.h>
61
62static async_cookie_t next_cookie = 1;
63
64#define MAX_THREADS 256
65#define MAX_WORK 32768
66
67static LIST_HEAD(async_pending);
68static LIST_HEAD(async_running);
69static DEFINE_SPINLOCK(async_lock);
70
71static int async_enabled = 0;
72
73struct async_entry {
74 struct list_head list;
75 async_cookie_t cookie;
76 async_func_ptr *func;
77 void *data;
78 struct list_head *running;
79};
80
81static DECLARE_WAIT_QUEUE_HEAD(async_done);
82static DECLARE_WAIT_QUEUE_HEAD(async_new);
83
84static atomic_t entry_count;
85static atomic_t thread_count;
86
87extern int initcall_debug;
88
89
90
91
92
93static async_cookie_t __lowest_in_progress(struct list_head *running)
94{
95 struct async_entry *entry;
96
97 if (!list_empty(running)) {
98 entry = list_first_entry(running,
99 struct async_entry, list);
100 return entry->cookie;
101 }
102
103 list_for_each_entry(entry, &async_pending, list)
104 if (entry->running == running)
105 return entry->cookie;
106
107 return next_cookie;
108}
109
110static async_cookie_t lowest_in_progress(struct list_head *running)
111{
112 unsigned long flags;
113 async_cookie_t ret;
114
115 spin_lock_irqsave(&async_lock, flags);
116 ret = __lowest_in_progress(running);
117 spin_unlock_irqrestore(&async_lock, flags);
118 return ret;
119}
120
121
122
123static void run_one_entry(void)
124{
125 unsigned long flags;
126 struct async_entry *entry;
127 ktime_t calltime, delta, rettime;
128
129
130
131 spin_lock_irqsave(&async_lock, flags);
132 if (list_empty(&async_pending))
133 goto out;
134 entry = list_first_entry(&async_pending, struct async_entry, list);
135
136
137 list_move_tail(&entry->list, entry->running);
138 spin_unlock_irqrestore(&async_lock, flags);
139
140
141 if (initcall_debug && system_state == SYSTEM_BOOTING) {
142 printk("calling %lli_%pF @ %i\n", (long long)entry->cookie,
143 entry->func, task_pid_nr(current));
144 calltime = ktime_get();
145 }
146 entry->func(entry->data, entry->cookie);
147 if (initcall_debug && system_state == SYSTEM_BOOTING) {
148 rettime = ktime_get();
149 delta = ktime_sub(rettime, calltime);
150 printk("initcall %lli_%pF returned 0 after %lld usecs\n",
151 (long long)entry->cookie,
152 entry->func,
153 (long long)ktime_to_ns(delta) >> 10);
154 }
155
156
157 spin_lock_irqsave(&async_lock, flags);
158 list_del(&entry->list);
159
160
161 kfree(entry);
162 atomic_dec(&entry_count);
163
164 spin_unlock_irqrestore(&async_lock, flags);
165
166
167 wake_up(&async_done);
168 return;
169
170out:
171 spin_unlock_irqrestore(&async_lock, flags);
172}
173
174
175static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct list_head *running)
176{
177 struct async_entry *entry;
178 unsigned long flags;
179 async_cookie_t newcookie;
180
181
182
183 entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
184
185
186
187
188
189 if (!async_enabled || !entry || atomic_read(&entry_count) > MAX_WORK) {
190 kfree(entry);
191 spin_lock_irqsave(&async_lock, flags);
192 newcookie = next_cookie++;
193 spin_unlock_irqrestore(&async_lock, flags);
194
195
196 ptr(data, newcookie);
197 return newcookie;
198 }
199 entry->func = ptr;
200 entry->data = data;
201 entry->running = running;
202
203 spin_lock_irqsave(&async_lock, flags);
204 newcookie = entry->cookie = next_cookie++;
205 list_add_tail(&entry->list, &async_pending);
206 atomic_inc(&entry_count);
207 spin_unlock_irqrestore(&async_lock, flags);
208 wake_up(&async_new);
209 return newcookie;
210}
211
212
213
214
215
216
217
218
219
220async_cookie_t async_schedule(async_func_ptr *ptr, void *data)
221{
222 return __async_schedule(ptr, data, &async_running);
223}
224EXPORT_SYMBOL_GPL(async_schedule);
225
226
227
228
229
230
231
232
233
234
235
236
237
238async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data,
239 struct list_head *running)
240{
241 return __async_schedule(ptr, data, running);
242}
243EXPORT_SYMBOL_GPL(async_schedule_domain);
244
245
246
247
248
249
250void async_synchronize_full(void)
251{
252 do {
253 async_synchronize_cookie(next_cookie);
254 } while (!list_empty(&async_running) || !list_empty(&async_pending));
255}
256EXPORT_SYMBOL_GPL(async_synchronize_full);
257
258
259
260
261
262
263
264
265void async_synchronize_full_domain(struct list_head *list)
266{
267 async_synchronize_cookie_domain(next_cookie, list);
268}
269EXPORT_SYMBOL_GPL(async_synchronize_full_domain);
270
271
272
273
274
275
276
277
278
279
280void async_synchronize_cookie_domain(async_cookie_t cookie,
281 struct list_head *running)
282{
283 ktime_t starttime, delta, endtime;
284
285 if (initcall_debug && system_state == SYSTEM_BOOTING) {
286 printk("async_waiting @ %i\n", task_pid_nr(current));
287 starttime = ktime_get();
288 }
289
290 wait_event(async_done, lowest_in_progress(running) >= cookie);
291
292 if (initcall_debug && system_state == SYSTEM_BOOTING) {
293 endtime = ktime_get();
294 delta = ktime_sub(endtime, starttime);
295
296 printk("async_continuing @ %i after %lli usec\n",
297 task_pid_nr(current),
298 (long long)ktime_to_ns(delta) >> 10);
299 }
300}
301EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);
302
303
304
305
306
307
308
309
310void async_synchronize_cookie(async_cookie_t cookie)
311{
312 async_synchronize_cookie_domain(cookie, &async_running);
313}
314EXPORT_SYMBOL_GPL(async_synchronize_cookie);
315
316
317static int async_thread(void *unused)
318{
319 DECLARE_WAITQUEUE(wq, current);
320 add_wait_queue(&async_new, &wq);
321
322 while (!kthread_should_stop()) {
323 int ret = HZ;
324 set_current_state(TASK_INTERRUPTIBLE);
325
326
327
328
329
330 rmb();
331 if (!list_empty(&async_pending))
332 run_one_entry();
333 else
334 ret = schedule_timeout(HZ);
335
336 if (ret == 0) {
337
338
339
340
341
342 atomic_dec(&thread_count);
343 wmb();
344 if (list_empty(&async_pending))
345 break;
346
347
348
349
350 atomic_inc(&thread_count);
351 }
352 }
353 remove_wait_queue(&async_new, &wq);
354
355 return 0;
356}
357
358static int async_manager_thread(void *unused)
359{
360 DECLARE_WAITQUEUE(wq, current);
361 add_wait_queue(&async_new, &wq);
362
363 while (!kthread_should_stop()) {
364 int tc, ec;
365
366 set_current_state(TASK_INTERRUPTIBLE);
367
368 tc = atomic_read(&thread_count);
369 rmb();
370 ec = atomic_read(&entry_count);
371
372 while (tc < ec && tc < MAX_THREADS) {
373 if (IS_ERR(kthread_run(async_thread, NULL, "async/%i",
374 tc))) {
375 msleep(100);
376 continue;
377 }
378 atomic_inc(&thread_count);
379 tc++;
380 }
381
382 schedule();
383 }
384 remove_wait_queue(&async_new, &wq);
385
386 return 0;
387}
388
389static int __init async_init(void)
390{
391 async_enabled =
392 !IS_ERR(kthread_run(async_manager_thread, NULL, "async/mgr"));
393
394 WARN_ON(!async_enabled);
395 return 0;
396}
397
398core_initcall(async_init);
399