Blame view

kernel/async.c 9.94 KB
22a9d6456   Arjan van de Ven   async: Asynchrono...
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
  /*
   * async.c: Asynchronous function calls for boot performance
   *
   * (C) Copyright 2009 Intel Corporation
   * Author: Arjan van de Ven <arjan@linux.intel.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; version 2
   * of the License.
   */
  
  
  /*
  
  Goals and Theory of Operation
  
  The primary goal of this feature is to reduce the kernel boot time,
  by doing various independent hardware delays and discovery operations
  decoupled and not strictly serialized.
  
  More specifically, the asynchronous function call concept allows
  certain operations (primarily during system boot) to happen
  asynchronously, out of order, while these operations still
  have their externally visible parts happen sequentially and in-order.
  (not unlike how out-of-order CPUs retire their instructions in order)
  
  Key to the asynchronous function call implementation is the concept of
  a "sequence cookie" (which, although it has an abstracted type, can be
  thought of as a monotonically incrementing number).
  
  The async core will assign each scheduled event such a sequence cookie and
  pass this to the called functions.
  
  The asynchronously called function should before doing a globally visible
  operation, such as registering device numbers, call the
  async_synchronize_cookie() function and pass in its own cookie. The
  async_synchronize_cookie() function will make sure that all asynchronous
  operations that were scheduled prior to the operation corresponding with the
  cookie have completed.
  
  Subsystem/driver initialization code that scheduled asynchronous probe
  functions, but which shares global resources with other drivers/subsystems
  that do not use the asynchronous call feature, need to do a full
  synchronization with the async_synchronize_full() function, before returning
  from their init function. This is to maintain strict ordering between the
  asynchronous and synchronous parts of the kernel.
  
  */
  
  #include <linux/async.h>
84c15027a   Paul McQuade   async: Fixed an i...
52
53
  #include <linux/atomic.h>
  #include <linux/ktime.h>
9984de1a5   Paul Gortmaker   kernel: Map most ...
54
  #include <linux/export.h>
22a9d6456   Arjan van de Ven   async: Asynchrono...
55
56
  #include <linux/wait.h>
  #include <linux/sched.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
57
  #include <linux/slab.h>
083b804c4   Tejun Heo   async: use workqu...
58
  #include <linux/workqueue.h>
22a9d6456   Arjan van de Ven   async: Asynchrono...
59

84b233adc   Tejun Heo   workqueue: implem...
60
  #include "workqueue_internal.h"
22a9d6456   Arjan van de Ven   async: Asynchrono...
61
  static async_cookie_t next_cookie = 1;
c68eee14e   Tejun Heo   async: use ULLONG...
62
63
  #define MAX_WORK		32768
  #define ASYNC_COOKIE_MAX	ULLONG_MAX	/* infinity cookie */
22a9d6456   Arjan van de Ven   async: Asynchrono...
64

9fdb04cdc   Tejun Heo   async: replace li...
65
  static LIST_HEAD(async_global_pending);	/* pending from all registered doms */
8723d5037   Tejun Heo   async: bring sani...
66
  static ASYNC_DOMAIN(async_dfl_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
67
68
69
  static DEFINE_SPINLOCK(async_lock);
  
  struct async_entry {
9fdb04cdc   Tejun Heo   async: replace li...
70
71
  	struct list_head	domain_list;
  	struct list_head	global_list;
083b804c4   Tejun Heo   async: use workqu...
72
73
  	struct work_struct	work;
  	async_cookie_t		cookie;
362f2b098   Lai Jiangshan   async: rename and...
74
  	async_func_t		func;
083b804c4   Tejun Heo   async: use workqu...
75
  	void			*data;
8723d5037   Tejun Heo   async: bring sani...
76
  	struct async_domain	*domain;
22a9d6456   Arjan van de Ven   async: Asynchrono...
77
78
79
  };
  
  static DECLARE_WAIT_QUEUE_HEAD(async_done);
22a9d6456   Arjan van de Ven   async: Asynchrono...
80
81
  
  static atomic_t entry_count;
22a9d6456   Arjan van de Ven   async: Asynchrono...
82

8723d5037   Tejun Heo   async: bring sani...
83
  static async_cookie_t lowest_in_progress(struct async_domain *domain)
37a76bd4f   Arjan van de Ven   async: fix __lowe...
84
  {
92266d6ef   Lai Jiangshan   async: simplify l...
85
  	struct list_head *pending;
52722794d   Tejun Heo   async: keep pendi...
86
  	async_cookie_t ret = ASYNC_COOKIE_MAX;
37a76bd4f   Arjan van de Ven   async: fix __lowe...
87
  	unsigned long flags;
37a76bd4f   Arjan van de Ven   async: fix __lowe...
88
89
  
  	spin_lock_irqsave(&async_lock, flags);
9fdb04cdc   Tejun Heo   async: replace li...
90

92266d6ef   Lai Jiangshan   async: simplify l...
91
92
93
94
  	if (domain)
  		pending = &domain->pending;
  	else
  		pending = &async_global_pending;
9fdb04cdc   Tejun Heo   async: replace li...
95

92266d6ef   Lai Jiangshan   async: simplify l...
96
97
98
  	if (!list_empty(pending))
  		ret = list_first_entry(pending, struct async_entry,
  				       domain_list)->cookie;
9fdb04cdc   Tejun Heo   async: replace li...
99

37a76bd4f   Arjan van de Ven   async: fix __lowe...
100
101
102
  	spin_unlock_irqrestore(&async_lock, flags);
  	return ret;
  }
083b804c4   Tejun Heo   async: use workqu...
103

22a9d6456   Arjan van de Ven   async: Asynchrono...
104
105
106
  /*
   * pick the first pending entry and run it
   */
083b804c4   Tejun Heo   async: use workqu...
107
  static void async_run_entry_fn(struct work_struct *work)
22a9d6456   Arjan van de Ven   async: Asynchrono...
108
  {
083b804c4   Tejun Heo   async: use workqu...
109
110
  	struct async_entry *entry =
  		container_of(work, struct async_entry, work);
22a9d6456   Arjan van de Ven   async: Asynchrono...
111
  	unsigned long flags;
124ff4e53   Vitaliy Ivanov   async: uninitiali...
112
  	ktime_t uninitialized_var(calltime), delta, rettime;
22a9d6456   Arjan van de Ven   async: Asynchrono...
113

52722794d   Tejun Heo   async: keep pendi...
114
  	/* 1) run (and print duration) */
ad160d231   Arjan van de Ven   async: don't do t...
115
  	if (initcall_debug && system_state == SYSTEM_BOOTING) {
84c15027a   Paul McQuade   async: Fixed an i...
116
117
118
  		printk(KERN_DEBUG "calling  %lli_%pF @ %i
  ",
  			(long long)entry->cookie,
58763a297   Andrew Morton   kernel/async.c: f...
119
  			entry->func, task_pid_nr(current));
22a9d6456   Arjan van de Ven   async: Asynchrono...
120
121
122
  		calltime = ktime_get();
  	}
  	entry->func(entry->data, entry->cookie);
ad160d231   Arjan van de Ven   async: don't do t...
123
  	if (initcall_debug && system_state == SYSTEM_BOOTING) {
22a9d6456   Arjan van de Ven   async: Asynchrono...
124
125
  		rettime = ktime_get();
  		delta = ktime_sub(rettime, calltime);
84c15027a   Paul McQuade   async: Fixed an i...
126
127
  		printk(KERN_DEBUG "initcall %lli_%pF returned 0 after %lld usecs
  ",
58763a297   Andrew Morton   kernel/async.c: f...
128
129
130
  			(long long)entry->cookie,
  			entry->func,
  			(long long)ktime_to_ns(delta) >> 10);
22a9d6456   Arjan van de Ven   async: Asynchrono...
131
  	}
52722794d   Tejun Heo   async: keep pendi...
132
  	/* 2) remove self from the pending queues */
22a9d6456   Arjan van de Ven   async: Asynchrono...
133
  	spin_lock_irqsave(&async_lock, flags);
9fdb04cdc   Tejun Heo   async: replace li...
134
135
  	list_del_init(&entry->domain_list);
  	list_del_init(&entry->global_list);
22a9d6456   Arjan van de Ven   async: Asynchrono...
136

52722794d   Tejun Heo   async: keep pendi...
137
  	/* 3) free the entry */
22a9d6456   Arjan van de Ven   async: Asynchrono...
138
139
140
141
  	kfree(entry);
  	atomic_dec(&entry_count);
  
  	spin_unlock_irqrestore(&async_lock, flags);
52722794d   Tejun Heo   async: keep pendi...
142
  	/* 4) wake up any waiters */
22a9d6456   Arjan van de Ven   async: Asynchrono...
143
  	wake_up(&async_done);
22a9d6456   Arjan van de Ven   async: Asynchrono...
144
  }
362f2b098   Lai Jiangshan   async: rename and...
145
  static async_cookie_t __async_schedule(async_func_t func, void *data, struct async_domain *domain)
22a9d6456   Arjan van de Ven   async: Asynchrono...
146
147
148
149
  {
  	struct async_entry *entry;
  	unsigned long flags;
  	async_cookie_t newcookie;
22a9d6456   Arjan van de Ven   async: Asynchrono...
150
151
152
153
154
155
156
157
  
  	/* allow irq-off callers */
  	entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
  
  	/*
  	 * If we're out of memory or if there's too much work
  	 * pending already, we execute synchronously.
  	 */
083b804c4   Tejun Heo   async: use workqu...
158
  	if (!entry || atomic_read(&entry_count) > MAX_WORK) {
22a9d6456   Arjan van de Ven   async: Asynchrono...
159
160
161
162
163
164
  		kfree(entry);
  		spin_lock_irqsave(&async_lock, flags);
  		newcookie = next_cookie++;
  		spin_unlock_irqrestore(&async_lock, flags);
  
  		/* low on memory.. run synchronously */
362f2b098   Lai Jiangshan   async: rename and...
165
  		func(data, newcookie);
22a9d6456   Arjan van de Ven   async: Asynchrono...
166
167
  		return newcookie;
  	}
a0327ff0e   James Hogan   async: initialise...
168
169
  	INIT_LIST_HEAD(&entry->domain_list);
  	INIT_LIST_HEAD(&entry->global_list);
083b804c4   Tejun Heo   async: use workqu...
170
  	INIT_WORK(&entry->work, async_run_entry_fn);
362f2b098   Lai Jiangshan   async: rename and...
171
  	entry->func = func;
22a9d6456   Arjan van de Ven   async: Asynchrono...
172
  	entry->data = data;
8723d5037   Tejun Heo   async: bring sani...
173
  	entry->domain = domain;
22a9d6456   Arjan van de Ven   async: Asynchrono...
174
175
  
  	spin_lock_irqsave(&async_lock, flags);
9fdb04cdc   Tejun Heo   async: replace li...
176
177
  
  	/* allocate cookie and queue */
22a9d6456   Arjan van de Ven   async: Asynchrono...
178
  	newcookie = entry->cookie = next_cookie++;
9fdb04cdc   Tejun Heo   async: replace li...
179
180
181
182
  
  	list_add_tail(&entry->domain_list, &domain->pending);
  	if (domain->registered)
  		list_add_tail(&entry->global_list, &async_global_pending);
22a9d6456   Arjan van de Ven   async: Asynchrono...
183
184
  	atomic_inc(&entry_count);
  	spin_unlock_irqrestore(&async_lock, flags);
083b804c4   Tejun Heo   async: use workqu...
185

774a1221e   Tejun Heo   module, async: as...
186
187
  	/* mark that this task has queued an async job, used by module init */
  	current->flags |= PF_USED_ASYNC;
083b804c4   Tejun Heo   async: use workqu...
188
189
  	/* schedule for execution */
  	queue_work(system_unbound_wq, &entry->work);
22a9d6456   Arjan van de Ven   async: Asynchrono...
190
191
  	return newcookie;
  }
f30d5b307   Cornelia Huck   async: Add some d...
192
193
  /**
   * async_schedule - schedule a function for asynchronous execution
362f2b098   Lai Jiangshan   async: rename and...
194
   * @func: function to execute asynchronously
f30d5b307   Cornelia Huck   async: Add some d...
195
196
197
198
199
   * @data: data pointer to pass to the function
   *
   * Returns an async_cookie_t that may be used for checkpointing later.
   * Note: This function may be called from atomic or non-atomic contexts.
   */
362f2b098   Lai Jiangshan   async: rename and...
200
  async_cookie_t async_schedule(async_func_t func, void *data)
22a9d6456   Arjan van de Ven   async: Asynchrono...
201
  {
362f2b098   Lai Jiangshan   async: rename and...
202
  	return __async_schedule(func, data, &async_dfl_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
203
204
  }
  EXPORT_SYMBOL_GPL(async_schedule);
f30d5b307   Cornelia Huck   async: Add some d...
205
  /**
766ccb9ed   Cornelia Huck   async: Rename _sp...
206
   * async_schedule_domain - schedule a function for asynchronous execution within a certain domain
362f2b098   Lai Jiangshan   async: rename and...
207
   * @func: function to execute asynchronously
f30d5b307   Cornelia Huck   async: Add some d...
208
   * @data: data pointer to pass to the function
8723d5037   Tejun Heo   async: bring sani...
209
   * @domain: the domain
f30d5b307   Cornelia Huck   async: Add some d...
210
211
   *
   * Returns an async_cookie_t that may be used for checkpointing later.
8723d5037   Tejun Heo   async: bring sani...
212
213
214
215
   * @domain may be used in the async_synchronize_*_domain() functions to
   * wait within a certain synchronization domain rather than globally.  A
   * synchronization domain is specified via @domain.  Note: This function
   * may be called from atomic or non-atomic contexts.
f30d5b307   Cornelia Huck   async: Add some d...
216
   */
362f2b098   Lai Jiangshan   async: rename and...
217
  async_cookie_t async_schedule_domain(async_func_t func, void *data,
8723d5037   Tejun Heo   async: bring sani...
218
  				     struct async_domain *domain)
22a9d6456   Arjan van de Ven   async: Asynchrono...
219
  {
362f2b098   Lai Jiangshan   async: rename and...
220
  	return __async_schedule(func, data, domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
221
  }
766ccb9ed   Cornelia Huck   async: Rename _sp...
222
  EXPORT_SYMBOL_GPL(async_schedule_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
223

f30d5b307   Cornelia Huck   async: Add some d...
224
225
226
227
228
  /**
   * async_synchronize_full - synchronize all asynchronous function calls
   *
   * This function waits until all asynchronous function calls have been done.
   */
22a9d6456   Arjan van de Ven   async: Asynchrono...
229
230
  void async_synchronize_full(void)
  {
9fdb04cdc   Tejun Heo   async: replace li...
231
  	async_synchronize_full_domain(NULL);
22a9d6456   Arjan van de Ven   async: Asynchrono...
232
233
  }
  EXPORT_SYMBOL_GPL(async_synchronize_full);
f30d5b307   Cornelia Huck   async: Add some d...
234
  /**
a4683487f   Dan Williams   [SCSI] async: mak...
235
236
237
238
239
240
241
242
243
244
   * async_unregister_domain - ensure no more anonymous waiters on this domain
   * @domain: idle domain to flush out of any async_synchronize_full instances
   *
   * async_synchronize_{cookie|full}_domain() are not flushed since callers
   * of these routines should know the lifetime of @domain
   *
   * Prefer ASYNC_DOMAIN_EXCLUSIVE() declarations over flushing
   */
  void async_unregister_domain(struct async_domain *domain)
  {
a4683487f   Dan Williams   [SCSI] async: mak...
245
  	spin_lock_irq(&async_lock);
9fdb04cdc   Tejun Heo   async: replace li...
246
  	WARN_ON(!domain->registered || !list_empty(&domain->pending));
a4683487f   Dan Williams   [SCSI] async: mak...
247
248
  	domain->registered = 0;
  	spin_unlock_irq(&async_lock);
a4683487f   Dan Williams   [SCSI] async: mak...
249
250
251
252
  }
  EXPORT_SYMBOL_GPL(async_unregister_domain);
  
  /**
766ccb9ed   Cornelia Huck   async: Rename _sp...
253
   * async_synchronize_full_domain - synchronize all asynchronous function within a certain domain
8723d5037   Tejun Heo   async: bring sani...
254
   * @domain: the domain to synchronize
f30d5b307   Cornelia Huck   async: Add some d...
255
   *
766ccb9ed   Cornelia Huck   async: Rename _sp...
256
   * This function waits until all asynchronous function calls for the
8723d5037   Tejun Heo   async: bring sani...
257
   * synchronization domain specified by @domain have been done.
f30d5b307   Cornelia Huck   async: Add some d...
258
   */
2955b47d2   Dan Williams   [SCSI] async: int...
259
  void async_synchronize_full_domain(struct async_domain *domain)
22a9d6456   Arjan van de Ven   async: Asynchrono...
260
  {
c68eee14e   Tejun Heo   async: use ULLONG...
261
  	async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
262
  }
766ccb9ed   Cornelia Huck   async: Rename _sp...
263
  EXPORT_SYMBOL_GPL(async_synchronize_full_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
264

f30d5b307   Cornelia Huck   async: Add some d...
265
  /**
766ccb9ed   Cornelia Huck   async: Rename _sp...
266
   * async_synchronize_cookie_domain - synchronize asynchronous function calls within a certain domain with cookie checkpointing
f30d5b307   Cornelia Huck   async: Add some d...
267
   * @cookie: async_cookie_t to use as checkpoint
9fdb04cdc   Tejun Heo   async: replace li...
268
   * @domain: the domain to synchronize (%NULL for all registered domains)
f30d5b307   Cornelia Huck   async: Add some d...
269
   *
766ccb9ed   Cornelia Huck   async: Rename _sp...
270
   * This function waits until all asynchronous function calls for the
8723d5037   Tejun Heo   async: bring sani...
271
272
   * synchronization domain specified by @domain submitted prior to @cookie
   * have been done.
f30d5b307   Cornelia Huck   async: Add some d...
273
   */
8723d5037   Tejun Heo   async: bring sani...
274
  void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *domain)
22a9d6456   Arjan van de Ven   async: Asynchrono...
275
  {
124ff4e53   Vitaliy Ivanov   async: uninitiali...
276
  	ktime_t uninitialized_var(starttime), delta, endtime;
22a9d6456   Arjan van de Ven   async: Asynchrono...
277

ad160d231   Arjan van de Ven   async: don't do t...
278
  	if (initcall_debug && system_state == SYSTEM_BOOTING) {
84c15027a   Paul McQuade   async: Fixed an i...
279
280
  		printk(KERN_DEBUG "async_waiting @ %i
  ", task_pid_nr(current));
22a9d6456   Arjan van de Ven   async: Asynchrono...
281
282
  		starttime = ktime_get();
  	}
8723d5037   Tejun Heo   async: bring sani...
283
  	wait_event(async_done, lowest_in_progress(domain) >= cookie);
22a9d6456   Arjan van de Ven   async: Asynchrono...
284

ad160d231   Arjan van de Ven   async: don't do t...
285
  	if (initcall_debug && system_state == SYSTEM_BOOTING) {
22a9d6456   Arjan van de Ven   async: Asynchrono...
286
287
  		endtime = ktime_get();
  		delta = ktime_sub(endtime, starttime);
84c15027a   Paul McQuade   async: Fixed an i...
288
289
  		printk(KERN_DEBUG "async_continuing @ %i after %lli usec
  ",
58763a297   Andrew Morton   kernel/async.c: f...
290
291
  			task_pid_nr(current),
  			(long long)ktime_to_ns(delta) >> 10);
22a9d6456   Arjan van de Ven   async: Asynchrono...
292
293
  	}
  }
766ccb9ed   Cornelia Huck   async: Rename _sp...
294
  EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
295

f30d5b307   Cornelia Huck   async: Add some d...
296
297
298
299
300
301
302
  /**
   * async_synchronize_cookie - synchronize asynchronous function calls with cookie checkpointing
   * @cookie: async_cookie_t to use as checkpoint
   *
   * This function waits until all asynchronous function calls prior to @cookie
   * have been done.
   */
22a9d6456   Arjan van de Ven   async: Asynchrono...
303
304
  void async_synchronize_cookie(async_cookie_t cookie)
  {
8723d5037   Tejun Heo   async: bring sani...
305
  	async_synchronize_cookie_domain(cookie, &async_dfl_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
306
307
  }
  EXPORT_SYMBOL_GPL(async_synchronize_cookie);
84b233adc   Tejun Heo   workqueue: implem...
308
309
310
311
312
313
314
315
316
317
318
319
  
  /**
   * current_is_async - is %current an async worker task?
   *
   * Returns %true if %current is an async worker task.
   */
  bool current_is_async(void)
  {
  	struct worker *worker = current_wq_worker();
  
  	return worker && worker->current_func == async_run_entry_fn;
  }