xref: /linux/kernel/async.c (revision 22a9d645677feefd402befd02edd59b122289ef1)
1*22a9d645SArjan van de Ven /*
2*22a9d645SArjan van de Ven  * async.c: Asynchronous function calls for boot performance
3*22a9d645SArjan van de Ven  *
4*22a9d645SArjan van de Ven  * (C) Copyright 2009 Intel Corporation
5*22a9d645SArjan van de Ven  * Author: Arjan van de Ven <arjan@linux.intel.com>
6*22a9d645SArjan van de Ven  *
7*22a9d645SArjan van de Ven  * This program is free software; you can redistribute it and/or
8*22a9d645SArjan van de Ven  * modify it under the terms of the GNU General Public License
9*22a9d645SArjan van de Ven  * as published by the Free Software Foundation; version 2
10*22a9d645SArjan van de Ven  * of the License.
11*22a9d645SArjan van de Ven  */
12*22a9d645SArjan van de Ven 
13*22a9d645SArjan van de Ven 
14*22a9d645SArjan van de Ven /*
15*22a9d645SArjan van de Ven 
16*22a9d645SArjan van de Ven Goals and Theory of Operation
17*22a9d645SArjan van de Ven 
18*22a9d645SArjan van de Ven The primary goal of this feature is to reduce the kernel boot time,
19*22a9d645SArjan van de Ven by doing various independent hardware delays and discovery operations
20*22a9d645SArjan van de Ven decoupled and not strictly serialized.
21*22a9d645SArjan van de Ven 
22*22a9d645SArjan van de Ven More specifically, the asynchronous function call concept allows
23*22a9d645SArjan van de Ven certain operations (primarily during system boot) to happen
24*22a9d645SArjan van de Ven asynchronously, out of order, while these operations still
25*22a9d645SArjan van de Ven have their externally visible parts happen sequentially and in-order.
26*22a9d645SArjan van de Ven (not unlike how out-of-order CPUs retire their instructions in order)
27*22a9d645SArjan van de Ven 
28*22a9d645SArjan van de Ven Key to the asynchronous function call implementation is the concept of
29*22a9d645SArjan van de Ven a "sequence cookie" (which, although it has an abstracted type, can be
30*22a9d645SArjan van de Ven thought of as a monotonically incrementing number).
31*22a9d645SArjan van de Ven 
32*22a9d645SArjan van de Ven The async core will assign each scheduled event such a sequence cookie and
33*22a9d645SArjan van de Ven pass this to the called functions.
34*22a9d645SArjan van de Ven 
35*22a9d645SArjan van de Ven The asynchronously called function should before doing a globally visible
36*22a9d645SArjan van de Ven operation, such as registering device numbers, call the
37*22a9d645SArjan van de Ven async_synchronize_cookie() function and pass in its own cookie. The
38*22a9d645SArjan van de Ven async_synchronize_cookie() function will make sure that all asynchronous
39*22a9d645SArjan van de Ven operations that were scheduled prior to the operation corresponding with the
40*22a9d645SArjan van de Ven cookie have completed.
41*22a9d645SArjan van de Ven 
42*22a9d645SArjan van de Ven Subsystem/driver initialization code that scheduled asynchronous probe
43*22a9d645SArjan van de Ven functions, but which shares global resources with other drivers/subsystems
44*22a9d645SArjan van de Ven that do not use the asynchronous call feature, need to do a full
45*22a9d645SArjan van de Ven synchronization with the async_synchronize_full() function, before returning
46*22a9d645SArjan van de Ven from their init function. This is to maintain strict ordering between the
47*22a9d645SArjan van de Ven asynchronous and synchronous parts of the kernel.
48*22a9d645SArjan van de Ven 
49*22a9d645SArjan van de Ven */
50*22a9d645SArjan van de Ven 
51*22a9d645SArjan van de Ven #include <linux/async.h>
52*22a9d645SArjan van de Ven #include <linux/module.h>
53*22a9d645SArjan van de Ven #include <linux/wait.h>
54*22a9d645SArjan van de Ven #include <linux/sched.h>
55*22a9d645SArjan van de Ven #include <linux/init.h>
56*22a9d645SArjan van de Ven #include <linux/kthread.h>
57*22a9d645SArjan van de Ven #include <asm/atomic.h>
58*22a9d645SArjan van de Ven 
59*22a9d645SArjan van de Ven static async_cookie_t next_cookie = 1;
60*22a9d645SArjan van de Ven 
61*22a9d645SArjan van de Ven #define MAX_THREADS	256
62*22a9d645SArjan van de Ven #define MAX_WORK	32768
63*22a9d645SArjan van de Ven 
64*22a9d645SArjan van de Ven static LIST_HEAD(async_pending);
65*22a9d645SArjan van de Ven static LIST_HEAD(async_running);
66*22a9d645SArjan van de Ven static DEFINE_SPINLOCK(async_lock);
67*22a9d645SArjan van de Ven 
68*22a9d645SArjan van de Ven struct async_entry {
69*22a9d645SArjan van de Ven 	struct list_head list;
70*22a9d645SArjan van de Ven 	async_cookie_t   cookie;
71*22a9d645SArjan van de Ven 	async_func_ptr	 *func;
72*22a9d645SArjan van de Ven 	void             *data;
73*22a9d645SArjan van de Ven 	struct list_head *running;
74*22a9d645SArjan van de Ven };
75*22a9d645SArjan van de Ven 
76*22a9d645SArjan van de Ven static DECLARE_WAIT_QUEUE_HEAD(async_done);
77*22a9d645SArjan van de Ven static DECLARE_WAIT_QUEUE_HEAD(async_new);
78*22a9d645SArjan van de Ven 
79*22a9d645SArjan van de Ven static atomic_t entry_count;
80*22a9d645SArjan van de Ven static atomic_t thread_count;
81*22a9d645SArjan van de Ven 
82*22a9d645SArjan van de Ven extern int initcall_debug;
83*22a9d645SArjan van de Ven 
84*22a9d645SArjan van de Ven 
85*22a9d645SArjan van de Ven /*
86*22a9d645SArjan van de Ven  * MUST be called with the lock held!
87*22a9d645SArjan van de Ven  */
88*22a9d645SArjan van de Ven static async_cookie_t  __lowest_in_progress(struct list_head *running)
89*22a9d645SArjan van de Ven {
90*22a9d645SArjan van de Ven 	struct async_entry *entry;
91*22a9d645SArjan van de Ven 	if (!list_empty(&async_pending)) {
92*22a9d645SArjan van de Ven 		entry = list_first_entry(&async_pending,
93*22a9d645SArjan van de Ven 			struct async_entry, list);
94*22a9d645SArjan van de Ven 		return entry->cookie;
95*22a9d645SArjan van de Ven 	} else if (!list_empty(running)) {
96*22a9d645SArjan van de Ven 		entry = list_first_entry(running,
97*22a9d645SArjan van de Ven 			struct async_entry, list);
98*22a9d645SArjan van de Ven 		return entry->cookie;
99*22a9d645SArjan van de Ven 	} else {
100*22a9d645SArjan van de Ven 		/* nothing in progress... next_cookie is "infinity" */
101*22a9d645SArjan van de Ven 		return next_cookie;
102*22a9d645SArjan van de Ven 	}
103*22a9d645SArjan van de Ven 
104*22a9d645SArjan van de Ven }
105*22a9d645SArjan van de Ven /*
106*22a9d645SArjan van de Ven  * pick the first pending entry and run it
107*22a9d645SArjan van de Ven  */
108*22a9d645SArjan van de Ven static void run_one_entry(void)
109*22a9d645SArjan van de Ven {
110*22a9d645SArjan van de Ven 	unsigned long flags;
111*22a9d645SArjan van de Ven 	struct async_entry *entry;
112*22a9d645SArjan van de Ven 	ktime_t calltime, delta, rettime;
113*22a9d645SArjan van de Ven 
114*22a9d645SArjan van de Ven 	/* 1) pick one task from the pending queue */
115*22a9d645SArjan van de Ven 
116*22a9d645SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
117*22a9d645SArjan van de Ven 	if (list_empty(&async_pending))
118*22a9d645SArjan van de Ven 		goto out;
119*22a9d645SArjan van de Ven 	entry = list_first_entry(&async_pending, struct async_entry, list);
120*22a9d645SArjan van de Ven 
121*22a9d645SArjan van de Ven 	/* 2) move it to the running queue */
122*22a9d645SArjan van de Ven 	list_del(&entry->list);
123*22a9d645SArjan van de Ven 	list_add_tail(&entry->list, &async_running);
124*22a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
125*22a9d645SArjan van de Ven 
126*22a9d645SArjan van de Ven 	/* 3) run it (and print duration)*/
127*22a9d645SArjan van de Ven 	if (initcall_debug) {
128*22a9d645SArjan van de Ven 		printk("calling  %lli_%pF @ %i\n", entry->cookie, entry->func, task_pid_nr(current));
129*22a9d645SArjan van de Ven 		calltime = ktime_get();
130*22a9d645SArjan van de Ven 	}
131*22a9d645SArjan van de Ven 	entry->func(entry->data, entry->cookie);
132*22a9d645SArjan van de Ven 	if (initcall_debug) {
133*22a9d645SArjan van de Ven 		rettime = ktime_get();
134*22a9d645SArjan van de Ven 		delta = ktime_sub(rettime, calltime);
135*22a9d645SArjan van de Ven 		printk("initcall %lli_%pF returned 0 after %lld usecs\n", entry->cookie,
136*22a9d645SArjan van de Ven 			entry->func, ktime_to_ns(delta) >> 10);
137*22a9d645SArjan van de Ven 	}
138*22a9d645SArjan van de Ven 
139*22a9d645SArjan van de Ven 	/* 4) remove it from the running queue */
140*22a9d645SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
141*22a9d645SArjan van de Ven 	list_del(&entry->list);
142*22a9d645SArjan van de Ven 
143*22a9d645SArjan van de Ven 	/* 5) free the entry  */
144*22a9d645SArjan van de Ven 	kfree(entry);
145*22a9d645SArjan van de Ven 	atomic_dec(&entry_count);
146*22a9d645SArjan van de Ven 
147*22a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
148*22a9d645SArjan van de Ven 
149*22a9d645SArjan van de Ven 	/* 6) wake up any waiters. */
150*22a9d645SArjan van de Ven 	wake_up(&async_done);
151*22a9d645SArjan van de Ven 	return;
152*22a9d645SArjan van de Ven 
153*22a9d645SArjan van de Ven out:
154*22a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
155*22a9d645SArjan van de Ven }
156*22a9d645SArjan van de Ven 
157*22a9d645SArjan van de Ven 
158*22a9d645SArjan van de Ven static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct list_head *running)
159*22a9d645SArjan van de Ven {
160*22a9d645SArjan van de Ven 	struct async_entry *entry;
161*22a9d645SArjan van de Ven 	unsigned long flags;
162*22a9d645SArjan van de Ven 	async_cookie_t newcookie;
163*22a9d645SArjan van de Ven 
164*22a9d645SArjan van de Ven 
165*22a9d645SArjan van de Ven 	/* allow irq-off callers */
166*22a9d645SArjan van de Ven 	entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
167*22a9d645SArjan van de Ven 
168*22a9d645SArjan van de Ven 	/*
169*22a9d645SArjan van de Ven 	 * If we're out of memory or if there's too much work
170*22a9d645SArjan van de Ven 	 * pending already, we execute synchronously.
171*22a9d645SArjan van de Ven 	 */
172*22a9d645SArjan van de Ven 	if (!entry || atomic_read(&entry_count) > MAX_WORK) {
173*22a9d645SArjan van de Ven 		kfree(entry);
174*22a9d645SArjan van de Ven 		spin_lock_irqsave(&async_lock, flags);
175*22a9d645SArjan van de Ven 		newcookie = next_cookie++;
176*22a9d645SArjan van de Ven 		spin_unlock_irqrestore(&async_lock, flags);
177*22a9d645SArjan van de Ven 
178*22a9d645SArjan van de Ven 		/* low on memory.. run synchronously */
179*22a9d645SArjan van de Ven 		ptr(data, newcookie);
180*22a9d645SArjan van de Ven 		return newcookie;
181*22a9d645SArjan van de Ven 	}
182*22a9d645SArjan van de Ven 	entry->func = ptr;
183*22a9d645SArjan van de Ven 	entry->data = data;
184*22a9d645SArjan van de Ven 	entry->running = running;
185*22a9d645SArjan van de Ven 
186*22a9d645SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
187*22a9d645SArjan van de Ven 	newcookie = entry->cookie = next_cookie++;
188*22a9d645SArjan van de Ven 	list_add_tail(&entry->list, &async_pending);
189*22a9d645SArjan van de Ven 	atomic_inc(&entry_count);
190*22a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
191*22a9d645SArjan van de Ven 	wake_up(&async_new);
192*22a9d645SArjan van de Ven 	return newcookie;
193*22a9d645SArjan van de Ven }
194*22a9d645SArjan van de Ven 
195*22a9d645SArjan van de Ven async_cookie_t async_schedule(async_func_ptr *ptr, void *data)
196*22a9d645SArjan van de Ven {
197*22a9d645SArjan van de Ven 	return __async_schedule(ptr, data, &async_pending);
198*22a9d645SArjan van de Ven }
199*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_schedule);
200*22a9d645SArjan van de Ven 
201*22a9d645SArjan van de Ven async_cookie_t async_schedule_special(async_func_ptr *ptr, void *data, struct list_head *running)
202*22a9d645SArjan van de Ven {
203*22a9d645SArjan van de Ven 	return __async_schedule(ptr, data, running);
204*22a9d645SArjan van de Ven }
205*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_schedule_special);
206*22a9d645SArjan van de Ven 
207*22a9d645SArjan van de Ven void async_synchronize_full(void)
208*22a9d645SArjan van de Ven {
209*22a9d645SArjan van de Ven 	async_synchronize_cookie(next_cookie);
210*22a9d645SArjan van de Ven }
211*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_full);
212*22a9d645SArjan van de Ven 
213*22a9d645SArjan van de Ven void async_synchronize_full_special(struct list_head *list)
214*22a9d645SArjan van de Ven {
215*22a9d645SArjan van de Ven 	async_synchronize_cookie_special(next_cookie, list);
216*22a9d645SArjan van de Ven }
217*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_full_special);
218*22a9d645SArjan van de Ven 
219*22a9d645SArjan van de Ven void async_synchronize_cookie_special(async_cookie_t cookie, struct list_head *running)
220*22a9d645SArjan van de Ven {
221*22a9d645SArjan van de Ven 	ktime_t starttime, delta, endtime;
222*22a9d645SArjan van de Ven 
223*22a9d645SArjan van de Ven 	if (initcall_debug) {
224*22a9d645SArjan van de Ven 		printk("async_waiting @ %i\n", task_pid_nr(current));
225*22a9d645SArjan van de Ven 		starttime = ktime_get();
226*22a9d645SArjan van de Ven 	}
227*22a9d645SArjan van de Ven 
228*22a9d645SArjan van de Ven 	wait_event(async_done, __lowest_in_progress(running) >= cookie);
229*22a9d645SArjan van de Ven 
230*22a9d645SArjan van de Ven 	if (initcall_debug) {
231*22a9d645SArjan van de Ven 		endtime = ktime_get();
232*22a9d645SArjan van de Ven 		delta = ktime_sub(endtime, starttime);
233*22a9d645SArjan van de Ven 
234*22a9d645SArjan van de Ven 		printk("async_continuing @ %i after %lli usec\n",
235*22a9d645SArjan van de Ven 			task_pid_nr(current), ktime_to_ns(delta) >> 10);
236*22a9d645SArjan van de Ven 	}
237*22a9d645SArjan van de Ven }
238*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_cookie_special);
239*22a9d645SArjan van de Ven 
240*22a9d645SArjan van de Ven void async_synchronize_cookie(async_cookie_t cookie)
241*22a9d645SArjan van de Ven {
242*22a9d645SArjan van de Ven 	async_synchronize_cookie_special(cookie, &async_running);
243*22a9d645SArjan van de Ven }
244*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_cookie);
245*22a9d645SArjan van de Ven 
246*22a9d645SArjan van de Ven 
247*22a9d645SArjan van de Ven static int async_thread(void *unused)
248*22a9d645SArjan van de Ven {
249*22a9d645SArjan van de Ven 	DECLARE_WAITQUEUE(wq, current);
250*22a9d645SArjan van de Ven 	add_wait_queue(&async_new, &wq);
251*22a9d645SArjan van de Ven 
252*22a9d645SArjan van de Ven 	while (!kthread_should_stop()) {
253*22a9d645SArjan van de Ven 		int ret = HZ;
254*22a9d645SArjan van de Ven 		set_current_state(TASK_INTERRUPTIBLE);
255*22a9d645SArjan van de Ven 		/*
256*22a9d645SArjan van de Ven 		 * check the list head without lock.. false positives
257*22a9d645SArjan van de Ven 		 * are dealt with inside run_one_entry() while holding
258*22a9d645SArjan van de Ven 		 * the lock.
259*22a9d645SArjan van de Ven 		 */
260*22a9d645SArjan van de Ven 		rmb();
261*22a9d645SArjan van de Ven 		if (!list_empty(&async_pending))
262*22a9d645SArjan van de Ven 			run_one_entry();
263*22a9d645SArjan van de Ven 		else
264*22a9d645SArjan van de Ven 			ret = schedule_timeout(HZ);
265*22a9d645SArjan van de Ven 
266*22a9d645SArjan van de Ven 		if (ret == 0) {
267*22a9d645SArjan van de Ven 			/*
268*22a9d645SArjan van de Ven 			 * we timed out, this means we as thread are redundant.
269*22a9d645SArjan van de Ven 			 * we sign off and die, but we to avoid any races there
270*22a9d645SArjan van de Ven 			 * is a last-straw check to see if work snuck in.
271*22a9d645SArjan van de Ven 			 */
272*22a9d645SArjan van de Ven 			atomic_dec(&thread_count);
273*22a9d645SArjan van de Ven 			wmb(); /* manager must see our departure first */
274*22a9d645SArjan van de Ven 			if (list_empty(&async_pending))
275*22a9d645SArjan van de Ven 				break;
276*22a9d645SArjan van de Ven 			/*
277*22a9d645SArjan van de Ven 			 * woops work came in between us timing out and us
278*22a9d645SArjan van de Ven 			 * signing off; we need to stay alive and keep working.
279*22a9d645SArjan van de Ven 			 */
280*22a9d645SArjan van de Ven 			atomic_inc(&thread_count);
281*22a9d645SArjan van de Ven 		}
282*22a9d645SArjan van de Ven 	}
283*22a9d645SArjan van de Ven 	remove_wait_queue(&async_new, &wq);
284*22a9d645SArjan van de Ven 
285*22a9d645SArjan van de Ven 	return 0;
286*22a9d645SArjan van de Ven }
287*22a9d645SArjan van de Ven 
288*22a9d645SArjan van de Ven static int async_manager_thread(void *unused)
289*22a9d645SArjan van de Ven {
290*22a9d645SArjan van de Ven 	DECLARE_WAITQUEUE(wq, current);
291*22a9d645SArjan van de Ven 	add_wait_queue(&async_new, &wq);
292*22a9d645SArjan van de Ven 
293*22a9d645SArjan van de Ven 	while (!kthread_should_stop()) {
294*22a9d645SArjan van de Ven 		int tc, ec;
295*22a9d645SArjan van de Ven 
296*22a9d645SArjan van de Ven 		set_current_state(TASK_INTERRUPTIBLE);
297*22a9d645SArjan van de Ven 
298*22a9d645SArjan van de Ven 		tc = atomic_read(&thread_count);
299*22a9d645SArjan van de Ven 		rmb();
300*22a9d645SArjan van de Ven 		ec = atomic_read(&entry_count);
301*22a9d645SArjan van de Ven 
302*22a9d645SArjan van de Ven 		while (tc < ec && tc < MAX_THREADS) {
303*22a9d645SArjan van de Ven 			kthread_run(async_thread, NULL, "async/%i", tc);
304*22a9d645SArjan van de Ven 			atomic_inc(&thread_count);
305*22a9d645SArjan van de Ven 			tc++;
306*22a9d645SArjan van de Ven 		}
307*22a9d645SArjan van de Ven 
308*22a9d645SArjan van de Ven 		schedule();
309*22a9d645SArjan van de Ven 	}
310*22a9d645SArjan van de Ven 	remove_wait_queue(&async_new, &wq);
311*22a9d645SArjan van de Ven 
312*22a9d645SArjan van de Ven 	return 0;
313*22a9d645SArjan van de Ven }
314*22a9d645SArjan van de Ven 
315*22a9d645SArjan van de Ven static int __init async_init(void)
316*22a9d645SArjan van de Ven {
317*22a9d645SArjan van de Ven 	kthread_run(async_manager_thread, NULL, "async/mgr");
318*22a9d645SArjan van de Ven 	return 0;
319*22a9d645SArjan van de Ven }
320*22a9d645SArjan van de Ven 
321*22a9d645SArjan van de Ven core_initcall(async_init);
322