xref: /linux/kernel/async.c (revision 52722794d6a48162fd8906d54618ae60a4abdb21)
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
65c68eee14STejun Heo #define ASYNC_COOKIE_MAX	ULLONG_MAX	/* infinity cookie */
6622a9d645SArjan van de Ven 
678723d503STejun 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;
788723d503STejun 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 
858723d503STejun Heo static async_cookie_t lowest_in_progress(struct async_domain *domain)
8637a76bd4SArjan van de Ven {
87*52722794STejun Heo 	async_cookie_t ret = ASYNC_COOKIE_MAX;
8837a76bd4SArjan van de Ven 	unsigned long flags;
8937a76bd4SArjan van de Ven 
9037a76bd4SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
91*52722794STejun Heo 	if (!list_empty(&domain->pending)) {
92*52722794STejun Heo 		struct async_entry *first = list_first_entry(&domain->pending,
93*52722794STejun Heo 						struct async_entry, list);
94*52722794STejun Heo 		ret = first->cookie;
95*52722794STejun Heo 	}
9637a76bd4SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
9737a76bd4SArjan van de Ven 	return ret;
9837a76bd4SArjan van de Ven }
99083b804cSTejun Heo 
10022a9d645SArjan van de Ven /*
10122a9d645SArjan van de Ven  * pick the first pending entry and run it
10222a9d645SArjan van de Ven  */
103083b804cSTejun Heo static void async_run_entry_fn(struct work_struct *work)
10422a9d645SArjan van de Ven {
105083b804cSTejun Heo 	struct async_entry *entry =
106083b804cSTejun Heo 		container_of(work, struct async_entry, work);
10722a9d645SArjan van de Ven 	unsigned long flags;
108124ff4e5SVitaliy Ivanov 	ktime_t uninitialized_var(calltime), delta, rettime;
1098723d503STejun Heo 	struct async_domain *domain = entry->domain;
11022a9d645SArjan van de Ven 
111*52722794STejun Heo 	/* 1) run (and print duration) */
112ad160d23SArjan van de Ven 	if (initcall_debug && system_state == SYSTEM_BOOTING) {
11384c15027SPaul McQuade 		printk(KERN_DEBUG "calling  %lli_%pF @ %i\n",
11484c15027SPaul McQuade 			(long long)entry->cookie,
11558763a29SAndrew Morton 			entry->func, task_pid_nr(current));
11622a9d645SArjan van de Ven 		calltime = ktime_get();
11722a9d645SArjan van de Ven 	}
11822a9d645SArjan van de Ven 	entry->func(entry->data, entry->cookie);
119ad160d23SArjan van de Ven 	if (initcall_debug && system_state == SYSTEM_BOOTING) {
12022a9d645SArjan van de Ven 		rettime = ktime_get();
12122a9d645SArjan van de Ven 		delta = ktime_sub(rettime, calltime);
12284c15027SPaul McQuade 		printk(KERN_DEBUG "initcall %lli_%pF returned 0 after %lld usecs\n",
12358763a29SAndrew Morton 			(long long)entry->cookie,
12458763a29SAndrew Morton 			entry->func,
12558763a29SAndrew Morton 			(long long)ktime_to_ns(delta) >> 10);
12622a9d645SArjan van de Ven 	}
12722a9d645SArjan van de Ven 
128*52722794STejun Heo 	/* 2) remove self from the pending queues */
12922a9d645SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
13022a9d645SArjan van de Ven 	list_del(&entry->list);
131*52722794STejun Heo 	if (domain->registered && list_empty(&domain->pending))
1328723d503STejun Heo 		list_del_init(&domain->node);
13322a9d645SArjan van de Ven 
134*52722794STejun Heo 	/* 3) free the entry */
13522a9d645SArjan van de Ven 	kfree(entry);
13622a9d645SArjan van de Ven 	atomic_dec(&entry_count);
13722a9d645SArjan van de Ven 
13822a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
13922a9d645SArjan van de Ven 
140*52722794STejun Heo 	/* 4) wake up any waiters */
14122a9d645SArjan van de Ven 	wake_up(&async_done);
14222a9d645SArjan van de Ven }
14322a9d645SArjan van de Ven 
1448723d503STejun Heo static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct async_domain *domain)
14522a9d645SArjan van de Ven {
14622a9d645SArjan van de Ven 	struct async_entry *entry;
14722a9d645SArjan van de Ven 	unsigned long flags;
14822a9d645SArjan van de Ven 	async_cookie_t newcookie;
14922a9d645SArjan van de Ven 
15022a9d645SArjan van de Ven 	/* allow irq-off callers */
15122a9d645SArjan van de Ven 	entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
15222a9d645SArjan van de Ven 
15322a9d645SArjan van de Ven 	/*
15422a9d645SArjan van de Ven 	 * If we're out of memory or if there's too much work
15522a9d645SArjan van de Ven 	 * pending already, we execute synchronously.
15622a9d645SArjan van de Ven 	 */
157083b804cSTejun Heo 	if (!entry || atomic_read(&entry_count) > MAX_WORK) {
15822a9d645SArjan van de Ven 		kfree(entry);
15922a9d645SArjan van de Ven 		spin_lock_irqsave(&async_lock, flags);
16022a9d645SArjan van de Ven 		newcookie = next_cookie++;
16122a9d645SArjan van de Ven 		spin_unlock_irqrestore(&async_lock, flags);
16222a9d645SArjan van de Ven 
16322a9d645SArjan van de Ven 		/* low on memory.. run synchronously */
16422a9d645SArjan van de Ven 		ptr(data, newcookie);
16522a9d645SArjan van de Ven 		return newcookie;
16622a9d645SArjan van de Ven 	}
167083b804cSTejun Heo 	INIT_WORK(&entry->work, async_run_entry_fn);
16822a9d645SArjan van de Ven 	entry->func = ptr;
16922a9d645SArjan van de Ven 	entry->data = data;
1708723d503STejun Heo 	entry->domain = domain;
17122a9d645SArjan van de Ven 
17222a9d645SArjan van de Ven 	spin_lock_irqsave(&async_lock, flags);
17322a9d645SArjan van de Ven 	newcookie = entry->cookie = next_cookie++;
174*52722794STejun Heo 	if (domain->registered && list_empty(&domain->pending))
1758723d503STejun Heo 		list_add_tail(&domain->node, &async_domains);
176*52722794STejun Heo 	list_add_tail(&entry->list, &domain->pending);
17722a9d645SArjan van de Ven 	atomic_inc(&entry_count);
17822a9d645SArjan van de Ven 	spin_unlock_irqrestore(&async_lock, flags);
179083b804cSTejun Heo 
180774a1221STejun Heo 	/* mark that this task has queued an async job, used by module init */
181774a1221STejun Heo 	current->flags |= PF_USED_ASYNC;
182774a1221STejun Heo 
183083b804cSTejun Heo 	/* schedule for execution */
184083b804cSTejun Heo 	queue_work(system_unbound_wq, &entry->work);
185083b804cSTejun Heo 
18622a9d645SArjan van de Ven 	return newcookie;
18722a9d645SArjan van de Ven }
18822a9d645SArjan van de Ven 
189f30d5b30SCornelia Huck /**
190f30d5b30SCornelia Huck  * async_schedule - schedule a function for asynchronous execution
191f30d5b30SCornelia Huck  * @ptr: function to execute asynchronously
192f30d5b30SCornelia Huck  * @data: data pointer to pass to the function
193f30d5b30SCornelia Huck  *
194f30d5b30SCornelia Huck  * Returns an async_cookie_t that may be used for checkpointing later.
195f30d5b30SCornelia Huck  * Note: This function may be called from atomic or non-atomic contexts.
196f30d5b30SCornelia Huck  */
19722a9d645SArjan van de Ven async_cookie_t async_schedule(async_func_ptr *ptr, void *data)
19822a9d645SArjan van de Ven {
1998723d503STejun Heo 	return __async_schedule(ptr, data, &async_dfl_domain);
20022a9d645SArjan van de Ven }
20122a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_schedule);
20222a9d645SArjan van de Ven 
203f30d5b30SCornelia Huck /**
204766ccb9eSCornelia Huck  * async_schedule_domain - schedule a function for asynchronous execution within a certain domain
205f30d5b30SCornelia Huck  * @ptr: function to execute asynchronously
206f30d5b30SCornelia Huck  * @data: data pointer to pass to the function
2078723d503STejun Heo  * @domain: the domain
208f30d5b30SCornelia Huck  *
209f30d5b30SCornelia Huck  * Returns an async_cookie_t that may be used for checkpointing later.
2108723d503STejun Heo  * @domain may be used in the async_synchronize_*_domain() functions to
2118723d503STejun Heo  * wait within a certain synchronization domain rather than globally.  A
2128723d503STejun Heo  * synchronization domain is specified via @domain.  Note: This function
2138723d503STejun Heo  * may be called from atomic or non-atomic contexts.
214f30d5b30SCornelia Huck  */
215766ccb9eSCornelia Huck async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data,
2168723d503STejun Heo 				     struct async_domain *domain)
21722a9d645SArjan van de Ven {
2188723d503STejun Heo 	return __async_schedule(ptr, data, domain);
21922a9d645SArjan van de Ven }
220766ccb9eSCornelia Huck EXPORT_SYMBOL_GPL(async_schedule_domain);
22122a9d645SArjan van de Ven 
222f30d5b30SCornelia Huck /**
223f30d5b30SCornelia Huck  * async_synchronize_full - synchronize all asynchronous function calls
224f30d5b30SCornelia Huck  *
225f30d5b30SCornelia Huck  * This function waits until all asynchronous function calls have been done.
226f30d5b30SCornelia Huck  */
22722a9d645SArjan van de Ven void async_synchronize_full(void)
22822a9d645SArjan van de Ven {
229a4683487SDan Williams 	mutex_lock(&async_register_mutex);
23033b04b93SArjan van de Ven 	do {
231a4683487SDan Williams 		struct async_domain *domain = NULL;
232a4683487SDan Williams 
233a4683487SDan Williams 		spin_lock_irq(&async_lock);
234a4683487SDan Williams 		if (!list_empty(&async_domains))
235a4683487SDan Williams 			domain = list_first_entry(&async_domains, typeof(*domain), node);
236a4683487SDan Williams 		spin_unlock_irq(&async_lock);
237a4683487SDan Williams 
238c68eee14STejun Heo 		async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, domain);
239a4683487SDan Williams 	} while (!list_empty(&async_domains));
240a4683487SDan Williams 	mutex_unlock(&async_register_mutex);
24122a9d645SArjan van de Ven }
24222a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_full);
24322a9d645SArjan van de Ven 
244f30d5b30SCornelia Huck /**
245a4683487SDan Williams  * async_unregister_domain - ensure no more anonymous waiters on this domain
246a4683487SDan Williams  * @domain: idle domain to flush out of any async_synchronize_full instances
247a4683487SDan Williams  *
248a4683487SDan Williams  * async_synchronize_{cookie|full}_domain() are not flushed since callers
249a4683487SDan Williams  * of these routines should know the lifetime of @domain
250a4683487SDan Williams  *
251a4683487SDan Williams  * Prefer ASYNC_DOMAIN_EXCLUSIVE() declarations over flushing
252a4683487SDan Williams  */
253a4683487SDan Williams void async_unregister_domain(struct async_domain *domain)
254a4683487SDan Williams {
255a4683487SDan Williams 	mutex_lock(&async_register_mutex);
256a4683487SDan Williams 	spin_lock_irq(&async_lock);
257a4683487SDan Williams 	WARN_ON(!domain->registered || !list_empty(&domain->node) ||
258*52722794STejun Heo 		!list_empty(&domain->pending));
259a4683487SDan Williams 	domain->registered = 0;
260a4683487SDan Williams 	spin_unlock_irq(&async_lock);
261a4683487SDan Williams 	mutex_unlock(&async_register_mutex);
262a4683487SDan Williams }
263a4683487SDan Williams EXPORT_SYMBOL_GPL(async_unregister_domain);
264a4683487SDan Williams 
265a4683487SDan Williams /**
266766ccb9eSCornelia Huck  * async_synchronize_full_domain - synchronize all asynchronous function within a certain domain
2678723d503STejun Heo  * @domain: the domain to synchronize
268f30d5b30SCornelia Huck  *
269766ccb9eSCornelia Huck  * This function waits until all asynchronous function calls for the
2708723d503STejun Heo  * synchronization domain specified by @domain have been done.
271f30d5b30SCornelia Huck  */
2722955b47dSDan Williams void async_synchronize_full_domain(struct async_domain *domain)
27322a9d645SArjan van de Ven {
274c68eee14STejun Heo 	async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, domain);
27522a9d645SArjan van de Ven }
276766ccb9eSCornelia Huck EXPORT_SYMBOL_GPL(async_synchronize_full_domain);
27722a9d645SArjan van de Ven 
278f30d5b30SCornelia Huck /**
279766ccb9eSCornelia Huck  * async_synchronize_cookie_domain - synchronize asynchronous function calls within a certain domain with cookie checkpointing
280f30d5b30SCornelia Huck  * @cookie: async_cookie_t to use as checkpoint
2818723d503STejun Heo  * @domain: the domain to synchronize
282f30d5b30SCornelia Huck  *
283766ccb9eSCornelia Huck  * This function waits until all asynchronous function calls for the
2848723d503STejun Heo  * synchronization domain specified by @domain submitted prior to @cookie
2858723d503STejun Heo  * have been done.
286f30d5b30SCornelia Huck  */
2878723d503STejun Heo void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *domain)
28822a9d645SArjan van de Ven {
289124ff4e5SVitaliy Ivanov 	ktime_t uninitialized_var(starttime), delta, endtime;
29022a9d645SArjan van de Ven 
2918723d503STejun Heo 	if (!domain)
292a4683487SDan Williams 		return;
293a4683487SDan Williams 
294ad160d23SArjan van de Ven 	if (initcall_debug && system_state == SYSTEM_BOOTING) {
29584c15027SPaul McQuade 		printk(KERN_DEBUG "async_waiting @ %i\n", task_pid_nr(current));
29622a9d645SArjan van de Ven 		starttime = ktime_get();
29722a9d645SArjan van de Ven 	}
29822a9d645SArjan van de Ven 
2998723d503STejun Heo 	wait_event(async_done, lowest_in_progress(domain) >= cookie);
30022a9d645SArjan van de Ven 
301ad160d23SArjan van de Ven 	if (initcall_debug && system_state == SYSTEM_BOOTING) {
30222a9d645SArjan van de Ven 		endtime = ktime_get();
30322a9d645SArjan van de Ven 		delta = ktime_sub(endtime, starttime);
30422a9d645SArjan van de Ven 
30584c15027SPaul McQuade 		printk(KERN_DEBUG "async_continuing @ %i after %lli usec\n",
30658763a29SAndrew Morton 			task_pid_nr(current),
30758763a29SAndrew Morton 			(long long)ktime_to_ns(delta) >> 10);
30822a9d645SArjan van de Ven 	}
30922a9d645SArjan van de Ven }
310766ccb9eSCornelia Huck EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);
31122a9d645SArjan van de Ven 
312f30d5b30SCornelia Huck /**
313f30d5b30SCornelia Huck  * async_synchronize_cookie - synchronize asynchronous function calls with cookie checkpointing
314f30d5b30SCornelia Huck  * @cookie: async_cookie_t to use as checkpoint
315f30d5b30SCornelia Huck  *
316f30d5b30SCornelia Huck  * This function waits until all asynchronous function calls prior to @cookie
317f30d5b30SCornelia Huck  * have been done.
318f30d5b30SCornelia Huck  */
31922a9d645SArjan van de Ven void async_synchronize_cookie(async_cookie_t cookie)
32022a9d645SArjan van de Ven {
3218723d503STejun Heo 	async_synchronize_cookie_domain(cookie, &async_dfl_domain);
32222a9d645SArjan van de Ven }
32322a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_cookie);
32484b233adSTejun Heo 
32584b233adSTejun Heo /**
32684b233adSTejun Heo  * current_is_async - is %current an async worker task?
32784b233adSTejun Heo  *
32884b233adSTejun Heo  * Returns %true if %current is an async worker task.
32984b233adSTejun Heo  */
33084b233adSTejun Heo bool current_is_async(void)
33184b233adSTejun Heo {
33284b233adSTejun Heo 	struct worker *worker = current_wq_worker();
33384b233adSTejun Heo 
33484b233adSTejun Heo 	return worker && worker->current_func == async_run_entry_fn;
33584b233adSTejun Heo }
336