xref: /linux/kernel/async.c (revision 8723d5037cafea09c7242303c6c8e5d7058cec61)
122a9d645SArjan van de Ven /*
222a9d645SArjan van de Ven  * async.c: Asynchronous function calls for boot performance
322a9d645SArjan van de Ven  *
422a9d645SArjan van de Ven  * (C) Copyright 2009 Intel Corporation
522a9d645SArjan van de Ven  * Author: Arjan van de Ven <arjan@linux.intel.com>
622a9d645SArjan van de Ven  *
722a9d645SArjan van de Ven  * This program is free software; you can redistribute it and/or
822a9d645SArjan van de Ven  * modify it under the terms of the GNU General Public License
922a9d645SArjan van de Ven  * as published by the Free Software Foundation; version 2
1022a9d645SArjan van de Ven  * of the License.
1122a9d645SArjan van de Ven  */
1222a9d645SArjan van de Ven 
1322a9d645SArjan van de Ven 
1422a9d645SArjan van de Ven /*
1522a9d645SArjan van de Ven 
1622a9d645SArjan van de Ven Goals and Theory of Operation
1722a9d645SArjan van de Ven 
1822a9d645SArjan van de Ven The primary goal of this feature is to reduce the kernel boot time,
1922a9d645SArjan van de Ven by doing various independent hardware delays and discovery operations
2022a9d645SArjan van de Ven decoupled and not strictly serialized.
2122a9d645SArjan van de Ven 
2222a9d645SArjan van de Ven More specifically, the asynchronous function call concept allows
2322a9d645SArjan van de Ven certain operations (primarily during system boot) to happen
2422a9d645SArjan van de Ven asynchronously, out of order, while these operations still
2522a9d645SArjan van de Ven have their externally visible parts happen sequentially and in-order.
2622a9d645SArjan van de Ven (not unlike how out-of-order CPUs retire their instructions in order)
2722a9d645SArjan van de Ven 
2822a9d645SArjan van de Ven Key to the asynchronous function call implementation is the concept of
2922a9d645SArjan van de Ven a "sequence cookie" (which, although it has an abstracted type, can be
3022a9d645SArjan van de Ven thought of as a monotonically incrementing number).
3122a9d645SArjan van de Ven 
3222a9d645SArjan van de Ven The async core will assign each scheduled event such a sequence cookie and
3322a9d645SArjan van de Ven pass this to the called functions.
3422a9d645SArjan van de Ven 
3522a9d645SArjan van de Ven The asynchronously called function should before doing a globally visible
3622a9d645SArjan van de Ven operation, such as registering device numbers, call the
3722a9d645SArjan van de Ven async_synchronize_cookie() function and pass in its own cookie. The
3822a9d645SArjan van de Ven async_synchronize_cookie() function will make sure that all asynchronous
3922a9d645SArjan van de Ven operations that were scheduled prior to the operation corresponding with the
4022a9d645SArjan van de Ven cookie have completed.
4122a9d645SArjan van de Ven 
4222a9d645SArjan van de Ven Subsystem/driver initialization code that scheduled asynchronous probe
4322a9d645SArjan van de Ven functions, but which shares global resources with other drivers/subsystems
4422a9d645SArjan van de Ven that do not use the asynchronous call feature, need to do a full
4522a9d645SArjan van de Ven synchronization with the async_synchronize_full() function, before returning
4622a9d645SArjan van de Ven from their init function. This is to maintain strict ordering between the
4722a9d645SArjan van de Ven asynchronous and synchronous parts of the kernel.
4822a9d645SArjan van de Ven 
4922a9d645SArjan van de Ven */
5022a9d645SArjan van de Ven 
5122a9d645SArjan van de Ven #include <linux/async.h>
5284c15027SPaul McQuade #include <linux/atomic.h>
5384c15027SPaul McQuade #include <linux/ktime.h>
549984de1aSPaul Gortmaker #include <linux/export.h>
5522a9d645SArjan van de Ven #include <linux/wait.h>
5622a9d645SArjan van de Ven #include <linux/sched.h>
575a0e3ad6STejun Heo #include <linux/slab.h>
58083b804cSTejun Heo #include <linux/workqueue.h>
5922a9d645SArjan van de Ven 
6084b233adSTejun Heo #include "workqueue_internal.h"
6184b233adSTejun Heo 
6222a9d645SArjan van de Ven static async_cookie_t next_cookie = 1;
6322a9d645SArjan van de Ven 
6422a9d645SArjan van de Ven #define MAX_WORK	32768
6522a9d645SArjan van de Ven 
6622a9d645SArjan van de Ven static LIST_HEAD(async_pending);
67*8723d503STejun Heo static ASYNC_DOMAIN(async_dfl_domain);
68a4683487SDan Williams static LIST_HEAD(async_domains);
6922a9d645SArjan van de Ven static DEFINE_SPINLOCK(async_lock);
70a4683487SDan Williams static DEFINE_MUTEX(async_register_mutex);
7122a9d645SArjan van de Ven 
7222a9d645SArjan van de Ven struct async_entry {
7322a9d645SArjan van de Ven 	struct list_head	list;
74083b804cSTejun Heo 	struct work_struct	work;
7522a9d645SArjan van de Ven 	async_cookie_t		cookie;
7622a9d645SArjan van de Ven 	async_func_ptr		*func;
7722a9d645SArjan van de Ven 	void			*data;
78*8723d503STejun Heo 	struct async_domain	*domain;
7922a9d645SArjan van de Ven };
8022a9d645SArjan van de Ven 
8122a9d645SArjan van de Ven static DECLARE_WAIT_QUEUE_HEAD(async_done);
8222a9d645SArjan van de Ven 
8322a9d645SArjan van de Ven static atomic_t entry_count;
8422a9d645SArjan van de Ven 
8522a9d645SArjan van de Ven 
8622a9d645SArjan van de Ven /*
8722a9d645SArjan van de Ven  * MUST be called with the lock held!
8822a9d645SArjan van de Ven  */
89*8723d503STejun Heo static async_cookie_t __lowest_in_progress(struct async_domain *domain)
9022a9d645SArjan van de Ven {
91f56c3196STejun Heo 	async_cookie_t first_running = next_cookie;	/* infinity value */
92f56c3196STejun Heo 	async_cookie_t first_pending = next_cookie;	/* ditto */
9322a9d645SArjan van de Ven 	struct async_entry *entry;
94d5a877e8SJames Bottomley 
95f56c3196STejun Heo 	/*
96f56c3196STejun Heo 	 * Both running and pending lists are sorted but not disjoint.
97f56c3196STejun Heo 	 * Take the first cookies from both and return the min.
98f56c3196STejun Heo 	 */
99*8723d503STejun Heo 	if (!list_empty(&domain->running)) {
100*8723d503STejun Heo 		entry = list_first_entry(&domain->running, typeof(*entry), list);
101f56c3196STejun Heo 		first_running = entry->cookie;
10222a9d645SArjan van de Ven 	}
10322a9d645SArjan van de Ven 
104f56c3196STejun Heo 	list_for_each_entry(entry, &async_pending, list) {
105*8723d503STejun Heo 		if (entry->domain == domain) {
106f56c3196STejun Heo 			first_pending = entry->cookie;
107f56c3196STejun Heo 			break;
108f56c3196STejun Heo 		}
109f56c3196STejun Heo 	}
110d5a877e8SJames Bottomley 
111f56c3196STejun Heo 	return min(first_running, first_pending);
11222a9d645SArjan van de Ven }
11337a76bd4SArjan van de Ven 
114*8723d503STejun Heo static async_cookie_t lowest_in_progress(struct async_domain *domain)
11537a76bd4SArjan van de Ven {
11637a76bd4SArjan van de Ven 	unsigned long flags;
11737a76bd4SArjan van de Ven 	async_cookie_t ret;
11837a76bd4SArjan van de Ven 
11937a76bd4SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
120*8723d503STejun Heo 	ret = __lowest_in_progress(domain);
12137a76bd4SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
12237a76bd4SArjan van de Ven 	return ret;
12337a76bd4SArjan van de Ven }
124083b804cSTejun Heo 
12522a9d645SArjan van de Ven /*
12622a9d645SArjan van de Ven  * pick the first pending entry and run it
12722a9d645SArjan van de Ven  */
128083b804cSTejun Heo static void async_run_entry_fn(struct work_struct *work)
12922a9d645SArjan van de Ven {
130083b804cSTejun Heo 	struct async_entry *entry =
131083b804cSTejun Heo 		container_of(work, struct async_entry, work);
132f56c3196STejun Heo 	struct async_entry *pos;
13322a9d645SArjan van de Ven 	unsigned long flags;
134124ff4e5SVitaliy Ivanov 	ktime_t uninitialized_var(calltime), delta, rettime;
135*8723d503STejun Heo 	struct async_domain *domain = entry->domain;
13622a9d645SArjan van de Ven 
137f56c3196STejun Heo 	/* 1) move self to the running queue, make sure it stays sorted */
13822a9d645SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
139*8723d503STejun Heo 	list_for_each_entry_reverse(pos, &domain->running, list)
140f56c3196STejun Heo 		if (entry->cookie < pos->cookie)
141f56c3196STejun Heo 			break;
142f56c3196STejun Heo 	list_move_tail(&entry->list, &pos->list);
14322a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
14422a9d645SArjan van de Ven 
145083b804cSTejun Heo 	/* 2) run (and print duration) */
146ad160d23SArjan van de Ven 	if (initcall_debug && system_state == SYSTEM_BOOTING) {
14784c15027SPaul McQuade 		printk(KERN_DEBUG "calling  %lli_%pF @ %i\n",
14884c15027SPaul McQuade 			(long long)entry->cookie,
14958763a29SAndrew Morton 			entry->func, task_pid_nr(current));
15022a9d645SArjan van de Ven 		calltime = ktime_get();
15122a9d645SArjan van de Ven 	}
15222a9d645SArjan van de Ven 	entry->func(entry->data, entry->cookie);
153ad160d23SArjan van de Ven 	if (initcall_debug && system_state == SYSTEM_BOOTING) {
15422a9d645SArjan van de Ven 		rettime = ktime_get();
15522a9d645SArjan van de Ven 		delta = ktime_sub(rettime, calltime);
15684c15027SPaul McQuade 		printk(KERN_DEBUG "initcall %lli_%pF returned 0 after %lld usecs\n",
15758763a29SAndrew Morton 			(long long)entry->cookie,
15858763a29SAndrew Morton 			entry->func,
15958763a29SAndrew Morton 			(long long)ktime_to_ns(delta) >> 10);
16022a9d645SArjan van de Ven 	}
16122a9d645SArjan van de Ven 
162083b804cSTejun Heo 	/* 3) remove self from the running queue */
16322a9d645SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
16422a9d645SArjan van de Ven 	list_del(&entry->list);
165*8723d503STejun Heo 	if (domain->registered && --domain->count == 0)
166*8723d503STejun Heo 		list_del_init(&domain->node);
16722a9d645SArjan van de Ven 
168083b804cSTejun Heo 	/* 4) free the entry */
16922a9d645SArjan van de Ven 	kfree(entry);
17022a9d645SArjan van de Ven 	atomic_dec(&entry_count);
17122a9d645SArjan van de Ven 
17222a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
17322a9d645SArjan van de Ven 
174083b804cSTejun Heo 	/* 5) wake up any waiters */
17522a9d645SArjan van de Ven 	wake_up(&async_done);
17622a9d645SArjan van de Ven }
17722a9d645SArjan van de Ven 
178*8723d503STejun Heo static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct async_domain *domain)
17922a9d645SArjan van de Ven {
18022a9d645SArjan van de Ven 	struct async_entry *entry;
18122a9d645SArjan van de Ven 	unsigned long flags;
18222a9d645SArjan van de Ven 	async_cookie_t newcookie;
18322a9d645SArjan van de Ven 
18422a9d645SArjan van de Ven 	/* allow irq-off callers */
18522a9d645SArjan van de Ven 	entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
18622a9d645SArjan van de Ven 
18722a9d645SArjan van de Ven 	/*
18822a9d645SArjan van de Ven 	 * If we're out of memory or if there's too much work
18922a9d645SArjan van de Ven 	 * pending already, we execute synchronously.
19022a9d645SArjan van de Ven 	 */
191083b804cSTejun Heo 	if (!entry || atomic_read(&entry_count) > MAX_WORK) {
19222a9d645SArjan van de Ven 		kfree(entry);
19322a9d645SArjan van de Ven 		spin_lock_irqsave(&async_lock, flags);
19422a9d645SArjan van de Ven 		newcookie = next_cookie++;
19522a9d645SArjan van de Ven 		spin_unlock_irqrestore(&async_lock, flags);
19622a9d645SArjan van de Ven 
19722a9d645SArjan van de Ven 		/* low on memory.. run synchronously */
19822a9d645SArjan van de Ven 		ptr(data, newcookie);
19922a9d645SArjan van de Ven 		return newcookie;
20022a9d645SArjan van de Ven 	}
201083b804cSTejun Heo 	INIT_WORK(&entry->work, async_run_entry_fn);
20222a9d645SArjan van de Ven 	entry->func = ptr;
20322a9d645SArjan van de Ven 	entry->data = data;
204*8723d503STejun Heo 	entry->domain = domain;
20522a9d645SArjan van de Ven 
20622a9d645SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
20722a9d645SArjan van de Ven 	newcookie = entry->cookie = next_cookie++;
20822a9d645SArjan van de Ven 	list_add_tail(&entry->list, &async_pending);
209*8723d503STejun Heo 	if (domain->registered && domain->count++ == 0)
210*8723d503STejun Heo 		list_add_tail(&domain->node, &async_domains);
21122a9d645SArjan van de Ven 	atomic_inc(&entry_count);
21222a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
213083b804cSTejun Heo 
214774a1221STejun Heo 	/* mark that this task has queued an async job, used by module init */
215774a1221STejun Heo 	current->flags |= PF_USED_ASYNC;
216774a1221STejun Heo 
217083b804cSTejun Heo 	/* schedule for execution */
218083b804cSTejun Heo 	queue_work(system_unbound_wq, &entry->work);
219083b804cSTejun Heo 
22022a9d645SArjan van de Ven 	return newcookie;
22122a9d645SArjan van de Ven }
22222a9d645SArjan van de Ven 
223f30d5b30SCornelia Huck /**
224f30d5b30SCornelia Huck  * async_schedule - schedule a function for asynchronous execution
225f30d5b30SCornelia Huck  * @ptr: function to execute asynchronously
226f30d5b30SCornelia Huck  * @data: data pointer to pass to the function
227f30d5b30SCornelia Huck  *
228f30d5b30SCornelia Huck  * Returns an async_cookie_t that may be used for checkpointing later.
229f30d5b30SCornelia Huck  * Note: This function may be called from atomic or non-atomic contexts.
230f30d5b30SCornelia Huck  */
23122a9d645SArjan van de Ven async_cookie_t async_schedule(async_func_ptr *ptr, void *data)
23222a9d645SArjan van de Ven {
233*8723d503STejun Heo 	return __async_schedule(ptr, data, &async_dfl_domain);
23422a9d645SArjan van de Ven }
23522a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_schedule);
23622a9d645SArjan van de Ven 
237f30d5b30SCornelia Huck /**
238766ccb9eSCornelia Huck  * async_schedule_domain - schedule a function for asynchronous execution within a certain domain
239f30d5b30SCornelia Huck  * @ptr: function to execute asynchronously
240f30d5b30SCornelia Huck  * @data: data pointer to pass to the function
241*8723d503STejun Heo  * @domain: the domain
242f30d5b30SCornelia Huck  *
243f30d5b30SCornelia Huck  * Returns an async_cookie_t that may be used for checkpointing later.
244*8723d503STejun Heo  * @domain may be used in the async_synchronize_*_domain() functions to
245*8723d503STejun Heo  * wait within a certain synchronization domain rather than globally.  A
246*8723d503STejun Heo  * synchronization domain is specified via @domain.  Note: This function
247*8723d503STejun Heo  * may be called from atomic or non-atomic contexts.
248f30d5b30SCornelia Huck  */
249766ccb9eSCornelia Huck async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data,
250*8723d503STejun Heo 				     struct async_domain *domain)
25122a9d645SArjan van de Ven {
252*8723d503STejun Heo 	return __async_schedule(ptr, data, domain);
25322a9d645SArjan van de Ven }
254766ccb9eSCornelia Huck EXPORT_SYMBOL_GPL(async_schedule_domain);
25522a9d645SArjan van de Ven 
256f30d5b30SCornelia Huck /**
257f30d5b30SCornelia Huck  * async_synchronize_full - synchronize all asynchronous function calls
258f30d5b30SCornelia Huck  *
259f30d5b30SCornelia Huck  * This function waits until all asynchronous function calls have been done.
260f30d5b30SCornelia Huck  */
26122a9d645SArjan van de Ven void async_synchronize_full(void)
26222a9d645SArjan van de Ven {
263a4683487SDan Williams 	mutex_lock(&async_register_mutex);
26433b04b93SArjan van de Ven 	do {
265a4683487SDan Williams 		struct async_domain *domain = NULL;
266a4683487SDan Williams 
267a4683487SDan Williams 		spin_lock_irq(&async_lock);
268a4683487SDan Williams 		if (!list_empty(&async_domains))
269a4683487SDan Williams 			domain = list_first_entry(&async_domains, typeof(*domain), node);
270a4683487SDan Williams 		spin_unlock_irq(&async_lock);
271a4683487SDan Williams 
272a4683487SDan Williams 		async_synchronize_cookie_domain(next_cookie, domain);
273a4683487SDan Williams 	} while (!list_empty(&async_domains));
274a4683487SDan Williams 	mutex_unlock(&async_register_mutex);
27522a9d645SArjan van de Ven }
27622a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_full);
27722a9d645SArjan van de Ven 
278f30d5b30SCornelia Huck /**
279a4683487SDan Williams  * async_unregister_domain - ensure no more anonymous waiters on this domain
280a4683487SDan Williams  * @domain: idle domain to flush out of any async_synchronize_full instances
281a4683487SDan Williams  *
282a4683487SDan Williams  * async_synchronize_{cookie|full}_domain() are not flushed since callers
283a4683487SDan Williams  * of these routines should know the lifetime of @domain
284a4683487SDan Williams  *
285a4683487SDan Williams  * Prefer ASYNC_DOMAIN_EXCLUSIVE() declarations over flushing
286a4683487SDan Williams  */
287a4683487SDan Williams void async_unregister_domain(struct async_domain *domain)
288a4683487SDan Williams {
289a4683487SDan Williams 	mutex_lock(&async_register_mutex);
290a4683487SDan Williams 	spin_lock_irq(&async_lock);
291a4683487SDan Williams 	WARN_ON(!domain->registered || !list_empty(&domain->node) ||
292*8723d503STejun Heo 		!list_empty(&domain->running));
293a4683487SDan Williams 	domain->registered = 0;
294a4683487SDan Williams 	spin_unlock_irq(&async_lock);
295a4683487SDan Williams 	mutex_unlock(&async_register_mutex);
296a4683487SDan Williams }
297a4683487SDan Williams EXPORT_SYMBOL_GPL(async_unregister_domain);
298a4683487SDan Williams 
299a4683487SDan Williams /**
300766ccb9eSCornelia Huck  * async_synchronize_full_domain - synchronize all asynchronous function within a certain domain
301*8723d503STejun Heo  * @domain: the domain to synchronize
302f30d5b30SCornelia Huck  *
303766ccb9eSCornelia Huck  * This function waits until all asynchronous function calls for the
304*8723d503STejun Heo  * synchronization domain specified by @domain have been done.
305f30d5b30SCornelia Huck  */
3062955b47dSDan Williams void async_synchronize_full_domain(struct async_domain *domain)
30722a9d645SArjan van de Ven {
3082955b47dSDan Williams 	async_synchronize_cookie_domain(next_cookie, domain);
30922a9d645SArjan van de Ven }
310766ccb9eSCornelia Huck EXPORT_SYMBOL_GPL(async_synchronize_full_domain);
31122a9d645SArjan van de Ven 
312f30d5b30SCornelia Huck /**
313766ccb9eSCornelia Huck  * async_synchronize_cookie_domain - synchronize asynchronous function calls within a certain domain with cookie checkpointing
314f30d5b30SCornelia Huck  * @cookie: async_cookie_t to use as checkpoint
315*8723d503STejun Heo  * @domain: the domain to synchronize
316f30d5b30SCornelia Huck  *
317766ccb9eSCornelia Huck  * This function waits until all asynchronous function calls for the
318*8723d503STejun Heo  * synchronization domain specified by @domain submitted prior to @cookie
319*8723d503STejun Heo  * have been done.
320f30d5b30SCornelia Huck  */
321*8723d503STejun Heo void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *domain)
32222a9d645SArjan van de Ven {
323124ff4e5SVitaliy Ivanov 	ktime_t uninitialized_var(starttime), delta, endtime;
32422a9d645SArjan van de Ven 
325*8723d503STejun Heo 	if (!domain)
326a4683487SDan Williams 		return;
327a4683487SDan Williams 
328ad160d23SArjan van de Ven 	if (initcall_debug && system_state == SYSTEM_BOOTING) {
32984c15027SPaul McQuade 		printk(KERN_DEBUG "async_waiting @ %i\n", task_pid_nr(current));
33022a9d645SArjan van de Ven 		starttime = ktime_get();
33122a9d645SArjan van de Ven 	}
33222a9d645SArjan van de Ven 
333*8723d503STejun Heo 	wait_event(async_done, lowest_in_progress(domain) >= cookie);
33422a9d645SArjan van de Ven 
335ad160d23SArjan van de Ven 	if (initcall_debug && system_state == SYSTEM_BOOTING) {
33622a9d645SArjan van de Ven 		endtime = ktime_get();
33722a9d645SArjan van de Ven 		delta = ktime_sub(endtime, starttime);
33822a9d645SArjan van de Ven 
33984c15027SPaul McQuade 		printk(KERN_DEBUG "async_continuing @ %i after %lli usec\n",
34058763a29SAndrew Morton 			task_pid_nr(current),
34158763a29SAndrew Morton 			(long long)ktime_to_ns(delta) >> 10);
34222a9d645SArjan van de Ven 	}
34322a9d645SArjan van de Ven }
344766ccb9eSCornelia Huck EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);
34522a9d645SArjan van de Ven 
346f30d5b30SCornelia Huck /**
347f30d5b30SCornelia Huck  * async_synchronize_cookie - synchronize asynchronous function calls with cookie checkpointing
348f30d5b30SCornelia Huck  * @cookie: async_cookie_t to use as checkpoint
349f30d5b30SCornelia Huck  *
350f30d5b30SCornelia Huck  * This function waits until all asynchronous function calls prior to @cookie
351f30d5b30SCornelia Huck  * have been done.
352f30d5b30SCornelia Huck  */
35322a9d645SArjan van de Ven void async_synchronize_cookie(async_cookie_t cookie)
35422a9d645SArjan van de Ven {
355*8723d503STejun Heo 	async_synchronize_cookie_domain(cookie, &async_dfl_domain);
35622a9d645SArjan van de Ven }
35722a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_cookie);
35884b233adSTejun Heo 
35984b233adSTejun Heo /**
36084b233adSTejun Heo  * current_is_async - is %current an async worker task?
36184b233adSTejun Heo  *
36284b233adSTejun Heo  * Returns %true if %current is an async worker task.
36384b233adSTejun Heo  */
36484b233adSTejun Heo bool current_is_async(void)
36584b233adSTejun Heo {
36684b233adSTejun Heo 	struct worker *worker = current_wq_worker();
36784b233adSTejun Heo 
36884b233adSTejun Heo 	return worker && worker->current_func == async_run_entry_fn;
36984b233adSTejun Heo }
370