1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * async.c: Asynchronous function calls for boot performance
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * (C) Copyright 2009 Intel Corporation
6*4882a593Smuzhiyun * Author: Arjan van de Ven <arjan@linux.intel.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun /*
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun Goals and Theory of Operation
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun The primary goal of this feature is to reduce the kernel boot time,
15*4882a593Smuzhiyun by doing various independent hardware delays and discovery operations
16*4882a593Smuzhiyun decoupled and not strictly serialized.
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun More specifically, the asynchronous function call concept allows
19*4882a593Smuzhiyun certain operations (primarily during system boot) to happen
20*4882a593Smuzhiyun asynchronously, out of order, while these operations still
21*4882a593Smuzhiyun have their externally visible parts happen sequentially and in-order.
22*4882a593Smuzhiyun (not unlike how out-of-order CPUs retire their instructions in order)
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun Key to the asynchronous function call implementation is the concept of
25*4882a593Smuzhiyun a "sequence cookie" (which, although it has an abstracted type, can be
26*4882a593Smuzhiyun thought of as a monotonically incrementing number).
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun The async core will assign each scheduled event such a sequence cookie and
29*4882a593Smuzhiyun pass this to the called functions.
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun The asynchronously called function should before doing a globally visible
32*4882a593Smuzhiyun operation, such as registering device numbers, call the
33*4882a593Smuzhiyun async_synchronize_cookie() function and pass in its own cookie. The
34*4882a593Smuzhiyun async_synchronize_cookie() function will make sure that all asynchronous
35*4882a593Smuzhiyun operations that were scheduled prior to the operation corresponding with the
36*4882a593Smuzhiyun cookie have completed.
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun Subsystem/driver initialization code that scheduled asynchronous probe
39*4882a593Smuzhiyun functions, but which shares global resources with other drivers/subsystems
40*4882a593Smuzhiyun that do not use the asynchronous call feature, need to do a full
41*4882a593Smuzhiyun synchronization with the async_synchronize_full() function, before returning
42*4882a593Smuzhiyun from their init function. This is to maintain strict ordering between the
43*4882a593Smuzhiyun asynchronous and synchronous parts of the kernel.
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun */
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun #include <linux/async.h>
48*4882a593Smuzhiyun #include <linux/atomic.h>
49*4882a593Smuzhiyun #include <linux/ktime.h>
50*4882a593Smuzhiyun #include <linux/export.h>
51*4882a593Smuzhiyun #include <linux/wait.h>
52*4882a593Smuzhiyun #include <linux/sched.h>
53*4882a593Smuzhiyun #include <linux/slab.h>
54*4882a593Smuzhiyun #include <linux/workqueue.h>
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun #include "workqueue_internal.h"
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun static async_cookie_t next_cookie = 1;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun #define MAX_WORK 32768
61*4882a593Smuzhiyun #define ASYNC_COOKIE_MAX ULLONG_MAX /* infinity cookie */
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun static LIST_HEAD(async_global_pending); /* pending from all registered doms */
64*4882a593Smuzhiyun static ASYNC_DOMAIN(async_dfl_domain);
65*4882a593Smuzhiyun static DEFINE_SPINLOCK(async_lock);
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun struct async_entry {
68*4882a593Smuzhiyun struct list_head domain_list;
69*4882a593Smuzhiyun struct list_head global_list;
70*4882a593Smuzhiyun struct work_struct work;
71*4882a593Smuzhiyun async_cookie_t cookie;
72*4882a593Smuzhiyun async_func_t func;
73*4882a593Smuzhiyun void *data;
74*4882a593Smuzhiyun struct async_domain *domain;
75*4882a593Smuzhiyun };
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun static DECLARE_WAIT_QUEUE_HEAD(async_done);
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun static atomic_t entry_count;
80*4882a593Smuzhiyun
lowest_in_progress(struct async_domain * domain)81*4882a593Smuzhiyun static async_cookie_t lowest_in_progress(struct async_domain *domain)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun struct async_entry *first = NULL;
84*4882a593Smuzhiyun async_cookie_t ret = ASYNC_COOKIE_MAX;
85*4882a593Smuzhiyun unsigned long flags;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun spin_lock_irqsave(&async_lock, flags);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun if (domain) {
90*4882a593Smuzhiyun if (!list_empty(&domain->pending))
91*4882a593Smuzhiyun first = list_first_entry(&domain->pending,
92*4882a593Smuzhiyun struct async_entry, domain_list);
93*4882a593Smuzhiyun } else {
94*4882a593Smuzhiyun if (!list_empty(&async_global_pending))
95*4882a593Smuzhiyun first = list_first_entry(&async_global_pending,
96*4882a593Smuzhiyun struct async_entry, global_list);
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun if (first)
100*4882a593Smuzhiyun ret = first->cookie;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun spin_unlock_irqrestore(&async_lock, flags);
103*4882a593Smuzhiyun return ret;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun /*
107*4882a593Smuzhiyun * pick the first pending entry and run it
108*4882a593Smuzhiyun */
async_run_entry_fn(struct work_struct * work)109*4882a593Smuzhiyun static void async_run_entry_fn(struct work_struct *work)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun struct async_entry *entry =
112*4882a593Smuzhiyun container_of(work, struct async_entry, work);
113*4882a593Smuzhiyun unsigned long flags;
114*4882a593Smuzhiyun ktime_t calltime, delta, rettime;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun /* 1) run (and print duration) */
117*4882a593Smuzhiyun if (initcall_debug && system_state < SYSTEM_RUNNING) {
118*4882a593Smuzhiyun pr_debug("calling %lli_%pS @ %i\n",
119*4882a593Smuzhiyun (long long)entry->cookie,
120*4882a593Smuzhiyun entry->func, task_pid_nr(current));
121*4882a593Smuzhiyun calltime = ktime_get();
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun entry->func(entry->data, entry->cookie);
124*4882a593Smuzhiyun if (initcall_debug && system_state < SYSTEM_RUNNING) {
125*4882a593Smuzhiyun rettime = ktime_get();
126*4882a593Smuzhiyun delta = ktime_sub(rettime, calltime);
127*4882a593Smuzhiyun pr_debug("initcall %lli_%pS returned 0 after %lld usecs\n",
128*4882a593Smuzhiyun (long long)entry->cookie,
129*4882a593Smuzhiyun entry->func,
130*4882a593Smuzhiyun (long long)ktime_to_ns(delta) >> 10);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun /* 2) remove self from the pending queues */
134*4882a593Smuzhiyun spin_lock_irqsave(&async_lock, flags);
135*4882a593Smuzhiyun list_del_init(&entry->domain_list);
136*4882a593Smuzhiyun list_del_init(&entry->global_list);
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun /* 3) free the entry */
139*4882a593Smuzhiyun kfree(entry);
140*4882a593Smuzhiyun atomic_dec(&entry_count);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun spin_unlock_irqrestore(&async_lock, flags);
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* 4) wake up any waiters */
145*4882a593Smuzhiyun wake_up(&async_done);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun /**
149*4882a593Smuzhiyun * async_schedule_node_domain - NUMA specific version of async_schedule_domain
150*4882a593Smuzhiyun * @func: function to execute asynchronously
151*4882a593Smuzhiyun * @data: data pointer to pass to the function
152*4882a593Smuzhiyun * @node: NUMA node that we want to schedule this on or close to
153*4882a593Smuzhiyun * @domain: the domain
154*4882a593Smuzhiyun *
155*4882a593Smuzhiyun * Returns an async_cookie_t that may be used for checkpointing later.
156*4882a593Smuzhiyun * @domain may be used in the async_synchronize_*_domain() functions to
157*4882a593Smuzhiyun * wait within a certain synchronization domain rather than globally.
158*4882a593Smuzhiyun *
159*4882a593Smuzhiyun * Note: This function may be called from atomic or non-atomic contexts.
160*4882a593Smuzhiyun *
161*4882a593Smuzhiyun * The node requested will be honored on a best effort basis. If the node
162*4882a593Smuzhiyun * has no CPUs associated with it then the work is distributed among all
163*4882a593Smuzhiyun * available CPUs.
164*4882a593Smuzhiyun */
async_schedule_node_domain(async_func_t func,void * data,int node,struct async_domain * domain)165*4882a593Smuzhiyun async_cookie_t async_schedule_node_domain(async_func_t func, void *data,
166*4882a593Smuzhiyun int node, struct async_domain *domain)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun struct async_entry *entry;
169*4882a593Smuzhiyun unsigned long flags;
170*4882a593Smuzhiyun async_cookie_t newcookie;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun /* allow irq-off callers */
173*4882a593Smuzhiyun entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun /*
176*4882a593Smuzhiyun * If we're out of memory or if there's too much work
177*4882a593Smuzhiyun * pending already, we execute synchronously.
178*4882a593Smuzhiyun */
179*4882a593Smuzhiyun if (!entry || atomic_read(&entry_count) > MAX_WORK) {
180*4882a593Smuzhiyun kfree(entry);
181*4882a593Smuzhiyun spin_lock_irqsave(&async_lock, flags);
182*4882a593Smuzhiyun newcookie = next_cookie++;
183*4882a593Smuzhiyun spin_unlock_irqrestore(&async_lock, flags);
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun /* low on memory.. run synchronously */
186*4882a593Smuzhiyun func(data, newcookie);
187*4882a593Smuzhiyun return newcookie;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun INIT_LIST_HEAD(&entry->domain_list);
190*4882a593Smuzhiyun INIT_LIST_HEAD(&entry->global_list);
191*4882a593Smuzhiyun INIT_WORK(&entry->work, async_run_entry_fn);
192*4882a593Smuzhiyun entry->func = func;
193*4882a593Smuzhiyun entry->data = data;
194*4882a593Smuzhiyun entry->domain = domain;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun spin_lock_irqsave(&async_lock, flags);
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun /* allocate cookie and queue */
199*4882a593Smuzhiyun newcookie = entry->cookie = next_cookie++;
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun list_add_tail(&entry->domain_list, &domain->pending);
202*4882a593Smuzhiyun if (domain->registered)
203*4882a593Smuzhiyun list_add_tail(&entry->global_list, &async_global_pending);
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun atomic_inc(&entry_count);
206*4882a593Smuzhiyun spin_unlock_irqrestore(&async_lock, flags);
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun /* schedule for execution */
209*4882a593Smuzhiyun queue_work_node(node, system_unbound_wq, &entry->work);
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun return newcookie;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(async_schedule_node_domain);
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /**
216*4882a593Smuzhiyun * async_schedule_node - NUMA specific version of async_schedule
217*4882a593Smuzhiyun * @func: function to execute asynchronously
218*4882a593Smuzhiyun * @data: data pointer to pass to the function
219*4882a593Smuzhiyun * @node: NUMA node that we want to schedule this on or close to
220*4882a593Smuzhiyun *
221*4882a593Smuzhiyun * Returns an async_cookie_t that may be used for checkpointing later.
222*4882a593Smuzhiyun * Note: This function may be called from atomic or non-atomic contexts.
223*4882a593Smuzhiyun *
224*4882a593Smuzhiyun * The node requested will be honored on a best effort basis. If the node
225*4882a593Smuzhiyun * has no CPUs associated with it then the work is distributed among all
226*4882a593Smuzhiyun * available CPUs.
227*4882a593Smuzhiyun */
async_schedule_node(async_func_t func,void * data,int node)228*4882a593Smuzhiyun async_cookie_t async_schedule_node(async_func_t func, void *data, int node)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun return async_schedule_node_domain(func, data, node, &async_dfl_domain);
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(async_schedule_node);
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /**
235*4882a593Smuzhiyun * async_synchronize_full - synchronize all asynchronous function calls
236*4882a593Smuzhiyun *
237*4882a593Smuzhiyun * This function waits until all asynchronous function calls have been done.
238*4882a593Smuzhiyun */
async_synchronize_full(void)239*4882a593Smuzhiyun void async_synchronize_full(void)
240*4882a593Smuzhiyun {
241*4882a593Smuzhiyun async_synchronize_full_domain(NULL);
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(async_synchronize_full);
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun /**
246*4882a593Smuzhiyun * async_unregister_domain - ensure no more anonymous waiters on this domain
247*4882a593Smuzhiyun * @domain: idle domain to flush out of any async_synchronize_full instances
248*4882a593Smuzhiyun *
249*4882a593Smuzhiyun * async_synchronize_{cookie|full}_domain() are not flushed since callers
250*4882a593Smuzhiyun * of these routines should know the lifetime of @domain
251*4882a593Smuzhiyun *
252*4882a593Smuzhiyun * Prefer ASYNC_DOMAIN_EXCLUSIVE() declarations over flushing
253*4882a593Smuzhiyun */
async_unregister_domain(struct async_domain * domain)254*4882a593Smuzhiyun void async_unregister_domain(struct async_domain *domain)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun spin_lock_irq(&async_lock);
257*4882a593Smuzhiyun WARN_ON(!domain->registered || !list_empty(&domain->pending));
258*4882a593Smuzhiyun domain->registered = 0;
259*4882a593Smuzhiyun spin_unlock_irq(&async_lock);
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(async_unregister_domain);
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun /**
264*4882a593Smuzhiyun * async_synchronize_full_domain - synchronize all asynchronous function within a certain domain
265*4882a593Smuzhiyun * @domain: the domain to synchronize
266*4882a593Smuzhiyun *
267*4882a593Smuzhiyun * This function waits until all asynchronous function calls for the
268*4882a593Smuzhiyun * synchronization domain specified by @domain have been done.
269*4882a593Smuzhiyun */
async_synchronize_full_domain(struct async_domain * domain)270*4882a593Smuzhiyun void async_synchronize_full_domain(struct async_domain *domain)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, domain);
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(async_synchronize_full_domain);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun /**
277*4882a593Smuzhiyun * async_synchronize_cookie_domain - synchronize asynchronous function calls within a certain domain with cookie checkpointing
278*4882a593Smuzhiyun * @cookie: async_cookie_t to use as checkpoint
279*4882a593Smuzhiyun * @domain: the domain to synchronize (%NULL for all registered domains)
280*4882a593Smuzhiyun *
281*4882a593Smuzhiyun * This function waits until all asynchronous function calls for the
282*4882a593Smuzhiyun * synchronization domain specified by @domain submitted prior to @cookie
283*4882a593Smuzhiyun * have been done.
284*4882a593Smuzhiyun */
async_synchronize_cookie_domain(async_cookie_t cookie,struct async_domain * domain)285*4882a593Smuzhiyun void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *domain)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun ktime_t starttime, delta, endtime;
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun if (initcall_debug && system_state < SYSTEM_RUNNING) {
290*4882a593Smuzhiyun pr_debug("async_waiting @ %i\n", task_pid_nr(current));
291*4882a593Smuzhiyun starttime = ktime_get();
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun wait_event(async_done, lowest_in_progress(domain) >= cookie);
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun if (initcall_debug && system_state < SYSTEM_RUNNING) {
297*4882a593Smuzhiyun endtime = ktime_get();
298*4882a593Smuzhiyun delta = ktime_sub(endtime, starttime);
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun pr_debug("async_continuing @ %i after %lli usec\n",
301*4882a593Smuzhiyun task_pid_nr(current),
302*4882a593Smuzhiyun (long long)ktime_to_ns(delta) >> 10);
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun /**
308*4882a593Smuzhiyun * async_synchronize_cookie - synchronize asynchronous function calls with cookie checkpointing
309*4882a593Smuzhiyun * @cookie: async_cookie_t to use as checkpoint
310*4882a593Smuzhiyun *
311*4882a593Smuzhiyun * This function waits until all asynchronous function calls prior to @cookie
312*4882a593Smuzhiyun * have been done.
313*4882a593Smuzhiyun */
async_synchronize_cookie(async_cookie_t cookie)314*4882a593Smuzhiyun void async_synchronize_cookie(async_cookie_t cookie)
315*4882a593Smuzhiyun {
316*4882a593Smuzhiyun async_synchronize_cookie_domain(cookie, &async_dfl_domain);
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(async_synchronize_cookie);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun /**
321*4882a593Smuzhiyun * current_is_async - is %current an async worker task?
322*4882a593Smuzhiyun *
323*4882a593Smuzhiyun * Returns %true if %current is an async worker task.
324*4882a593Smuzhiyun */
current_is_async(void)325*4882a593Smuzhiyun bool current_is_async(void)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun struct worker *worker = current_wq_worker();
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun return worker && worker->current_func == async_run_entry_fn;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(current_is_async);
332