Blame view

kernel/async.c 10.1 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
  {
838f9cc94   Rasmus Villemoes   kernel/async.c: r...
85
  	struct async_entry *first = NULL;
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

838f9cc94   Rasmus Villemoes   kernel/async.c: r...
91
92
93
94
95
96
97
98
99
  	if (domain) {
  		if (!list_empty(&domain->pending))
  			first = list_first_entry(&domain->pending,
  					struct async_entry, domain_list);
  	} else {
  		if (!list_empty(&async_global_pending))
  			first = list_first_entry(&async_global_pending,
  					struct async_entry, global_list);
  	}
9fdb04cdc   Tejun Heo   async: replace li...
100

838f9cc94   Rasmus Villemoes   kernel/async.c: r...
101
102
  	if (first)
  		ret = first->cookie;
9fdb04cdc   Tejun Heo   async: replace li...
103

37a76bd4f   Arjan van de Ven   async: fix __lowe...
104
105
106
  	spin_unlock_irqrestore(&async_lock, flags);
  	return ret;
  }
083b804c4   Tejun Heo   async: use workqu...
107

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

52722794d   Tejun Heo   async: keep pendi...
118
  	/* 1) run (and print duration) */
b4def4272   Thomas Gleixner   async: Adjust sys...
119
  	if (initcall_debug && system_state < SYSTEM_RUNNING) {
27fb10edc   Ionut Alexa   kernel/async.c: s...
120
121
  		pr_debug("calling  %lli_%pF @ %i
  ",
84c15027a   Paul McQuade   async: Fixed an i...
122
  			(long long)entry->cookie,
58763a297   Andrew Morton   kernel/async.c: f...
123
  			entry->func, task_pid_nr(current));
22a9d6456   Arjan van de Ven   async: Asynchrono...
124
125
126
  		calltime = ktime_get();
  	}
  	entry->func(entry->data, entry->cookie);
b4def4272   Thomas Gleixner   async: Adjust sys...
127
  	if (initcall_debug && system_state < SYSTEM_RUNNING) {
22a9d6456   Arjan van de Ven   async: Asynchrono...
128
129
  		rettime = ktime_get();
  		delta = ktime_sub(rettime, calltime);
27fb10edc   Ionut Alexa   kernel/async.c: s...
130
131
  		pr_debug("initcall %lli_%pF returned 0 after %lld usecs
  ",
58763a297   Andrew Morton   kernel/async.c: f...
132
133
134
  			(long long)entry->cookie,
  			entry->func,
  			(long long)ktime_to_ns(delta) >> 10);
22a9d6456   Arjan van de Ven   async: Asynchrono...
135
  	}
52722794d   Tejun Heo   async: keep pendi...
136
  	/* 2) remove self from the pending queues */
22a9d6456   Arjan van de Ven   async: Asynchrono...
137
  	spin_lock_irqsave(&async_lock, flags);
9fdb04cdc   Tejun Heo   async: replace li...
138
139
  	list_del_init(&entry->domain_list);
  	list_del_init(&entry->global_list);
22a9d6456   Arjan van de Ven   async: Asynchrono...
140

52722794d   Tejun Heo   async: keep pendi...
141
  	/* 3) free the entry */
22a9d6456   Arjan van de Ven   async: Asynchrono...
142
143
144
145
  	kfree(entry);
  	atomic_dec(&entry_count);
  
  	spin_unlock_irqrestore(&async_lock, flags);
52722794d   Tejun Heo   async: keep pendi...
146
  	/* 4) wake up any waiters */
22a9d6456   Arjan van de Ven   async: Asynchrono...
147
  	wake_up(&async_done);
22a9d6456   Arjan van de Ven   async: Asynchrono...
148
  }
362f2b098   Lai Jiangshan   async: rename and...
149
  static async_cookie_t __async_schedule(async_func_t func, void *data, struct async_domain *domain)
22a9d6456   Arjan van de Ven   async: Asynchrono...
150
151
152
153
  {
  	struct async_entry *entry;
  	unsigned long flags;
  	async_cookie_t newcookie;
22a9d6456   Arjan van de Ven   async: Asynchrono...
154
155
156
157
158
159
160
161
  
  	/* 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...
162
  	if (!entry || atomic_read(&entry_count) > MAX_WORK) {
22a9d6456   Arjan van de Ven   async: Asynchrono...
163
164
165
166
167
168
  		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...
169
  		func(data, newcookie);
22a9d6456   Arjan van de Ven   async: Asynchrono...
170
171
  		return newcookie;
  	}
a0327ff0e   James Hogan   async: initialise...
172
173
  	INIT_LIST_HEAD(&entry->domain_list);
  	INIT_LIST_HEAD(&entry->global_list);
083b804c4   Tejun Heo   async: use workqu...
174
  	INIT_WORK(&entry->work, async_run_entry_fn);
362f2b098   Lai Jiangshan   async: rename and...
175
  	entry->func = func;
22a9d6456   Arjan van de Ven   async: Asynchrono...
176
  	entry->data = data;
8723d5037   Tejun Heo   async: bring sani...
177
  	entry->domain = domain;
22a9d6456   Arjan van de Ven   async: Asynchrono...
178
179
  
  	spin_lock_irqsave(&async_lock, flags);
9fdb04cdc   Tejun Heo   async: replace li...
180
181
  
  	/* allocate cookie and queue */
22a9d6456   Arjan van de Ven   async: Asynchrono...
182
  	newcookie = entry->cookie = next_cookie++;
9fdb04cdc   Tejun Heo   async: replace li...
183
184
185
186
  
  	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...
187
188
  	atomic_inc(&entry_count);
  	spin_unlock_irqrestore(&async_lock, flags);
083b804c4   Tejun Heo   async: use workqu...
189

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

f30d5b307   Cornelia Huck   async: Add some d...
228
229
230
231
232
  /**
   * 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...
233
234
  void async_synchronize_full(void)
  {
9fdb04cdc   Tejun Heo   async: replace li...
235
  	async_synchronize_full_domain(NULL);
22a9d6456   Arjan van de Ven   async: Asynchrono...
236
237
  }
  EXPORT_SYMBOL_GPL(async_synchronize_full);
f30d5b307   Cornelia Huck   async: Add some d...
238
  /**
a4683487f   Dan Williams   [SCSI] async: mak...
239
240
241
242
243
244
245
246
247
248
   * 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...
249
  	spin_lock_irq(&async_lock);
9fdb04cdc   Tejun Heo   async: replace li...
250
  	WARN_ON(!domain->registered || !list_empty(&domain->pending));
a4683487f   Dan Williams   [SCSI] async: mak...
251
252
  	domain->registered = 0;
  	spin_unlock_irq(&async_lock);
a4683487f   Dan Williams   [SCSI] async: mak...
253
254
255
256
  }
  EXPORT_SYMBOL_GPL(async_unregister_domain);
  
  /**
766ccb9ed   Cornelia Huck   async: Rename _sp...
257
   * async_synchronize_full_domain - synchronize all asynchronous function within a certain domain
8723d5037   Tejun Heo   async: bring sani...
258
   * @domain: the domain to synchronize
f30d5b307   Cornelia Huck   async: Add some d...
259
   *
766ccb9ed   Cornelia Huck   async: Rename _sp...
260
   * This function waits until all asynchronous function calls for the
8723d5037   Tejun Heo   async: bring sani...
261
   * synchronization domain specified by @domain have been done.
f30d5b307   Cornelia Huck   async: Add some d...
262
   */
2955b47d2   Dan Williams   [SCSI] async: int...
263
  void async_synchronize_full_domain(struct async_domain *domain)
22a9d6456   Arjan van de Ven   async: Asynchrono...
264
  {
c68eee14e   Tejun Heo   async: use ULLONG...
265
  	async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
266
  }
766ccb9ed   Cornelia Huck   async: Rename _sp...
267
  EXPORT_SYMBOL_GPL(async_synchronize_full_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
268

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

b4def4272   Thomas Gleixner   async: Adjust sys...
282
  	if (initcall_debug && system_state < SYSTEM_RUNNING) {
27fb10edc   Ionut Alexa   kernel/async.c: s...
283
284
  		pr_debug("async_waiting @ %i
  ", task_pid_nr(current));
22a9d6456   Arjan van de Ven   async: Asynchrono...
285
286
  		starttime = ktime_get();
  	}
8723d5037   Tejun Heo   async: bring sani...
287
  	wait_event(async_done, lowest_in_progress(domain) >= cookie);
22a9d6456   Arjan van de Ven   async: Asynchrono...
288

b4def4272   Thomas Gleixner   async: Adjust sys...
289
  	if (initcall_debug && system_state < SYSTEM_RUNNING) {
22a9d6456   Arjan van de Ven   async: Asynchrono...
290
291
  		endtime = ktime_get();
  		delta = ktime_sub(endtime, starttime);
27fb10edc   Ionut Alexa   kernel/async.c: s...
292
293
  		pr_debug("async_continuing @ %i after %lli usec
  ",
58763a297   Andrew Morton   kernel/async.c: f...
294
295
  			task_pid_nr(current),
  			(long long)ktime_to_ns(delta) >> 10);
22a9d6456   Arjan van de Ven   async: Asynchrono...
296
297
  	}
  }
766ccb9ed   Cornelia Huck   async: Rename _sp...
298
  EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
299

f30d5b307   Cornelia Huck   async: Add some d...
300
301
302
303
304
305
306
  /**
   * 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...
307
308
  void async_synchronize_cookie(async_cookie_t cookie)
  {
8723d5037   Tejun Heo   async: bring sani...
309
  	async_synchronize_cookie_domain(cookie, &async_dfl_domain);
22a9d6456   Arjan van de Ven   async: Asynchrono...
310
311
  }
  EXPORT_SYMBOL_GPL(async_synchronize_cookie);
84b233adc   Tejun Heo   workqueue: implem...
312
313
314
315
316
317
318
319
320
321
322
323
  
  /**
   * 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;
  }
581da2cab   Lukas Wunner   async: export cur...
324
  EXPORT_SYMBOL_GPL(current_is_async);