aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/async.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/async.c')
-rw-r--r--kernel/async.c159
1 files changed, 76 insertions, 83 deletions
diff --git a/kernel/async.c b/kernel/async.c
index 9d3118384858..8ddee2c3e5b0 100644
--- a/kernel/async.c
+++ b/kernel/async.c
@@ -57,56 +57,52 @@ asynchronous and synchronous parts of the kernel.
57#include <linux/slab.h> 57#include <linux/slab.h>
58#include <linux/workqueue.h> 58#include <linux/workqueue.h>
59 59
60#include "workqueue_internal.h"
61
60static async_cookie_t next_cookie = 1; 62static async_cookie_t next_cookie = 1;
61 63
62#define MAX_WORK 32768 64#define MAX_WORK 32768
65#define ASYNC_COOKIE_MAX ULLONG_MAX /* infinity cookie */
63 66
64static LIST_HEAD(async_pending); 67static LIST_HEAD(async_global_pending); /* pending from all registered doms */
65static ASYNC_DOMAIN(async_running); 68static ASYNC_DOMAIN(async_dfl_domain);
66static LIST_HEAD(async_domains);
67static DEFINE_SPINLOCK(async_lock); 69static DEFINE_SPINLOCK(async_lock);
68static DEFINE_MUTEX(async_register_mutex);
69 70
70struct async_entry { 71struct async_entry {
71 struct list_head list; 72 struct list_head domain_list;
73 struct list_head global_list;
72 struct work_struct work; 74 struct work_struct work;
73 async_cookie_t cookie; 75 async_cookie_t cookie;
74 async_func_ptr *func; 76 async_func_ptr *func;
75 void *data; 77 void *data;
76 struct async_domain *running; 78 struct async_domain *domain;
77}; 79};
78 80
79static DECLARE_WAIT_QUEUE_HEAD(async_done); 81static DECLARE_WAIT_QUEUE_HEAD(async_done);
80 82
81static atomic_t entry_count; 83static atomic_t entry_count;
82 84
83 85static async_cookie_t lowest_in_progress(struct async_domain *domain)
84/*
85 * MUST be called with the lock held!
86 */
87static async_cookie_t __lowest_in_progress(struct async_domain *running)
88{ 86{
89 struct async_entry *entry; 87 struct async_entry *first = NULL;
90 88 async_cookie_t ret = ASYNC_COOKIE_MAX;
91 if (!list_empty(&running->domain)) { 89 unsigned long flags;
92 entry = list_first_entry(&running->domain, typeof(*entry), list);
93 return entry->cookie;
94 }
95 90
96 list_for_each_entry(entry, &async_pending, list) 91 spin_lock_irqsave(&async_lock, flags);
97 if (entry->running == running)
98 return entry->cookie;
99 92
100 return next_cookie; /* "infinity" value */ 93 if (domain) {
101} 94 if (!list_empty(&domain->pending))
95 first = list_first_entry(&domain->pending,
96 struct async_entry, domain_list);
97 } else {
98 if (!list_empty(&async_global_pending))
99 first = list_first_entry(&async_global_pending,
100 struct async_entry, global_list);
101 }
102 102
103static async_cookie_t lowest_in_progress(struct async_domain *running) 103 if (first)
104{ 104 ret = first->cookie;
105 unsigned long flags;
106 async_cookie_t ret;
107 105
108 spin_lock_irqsave(&async_lock, flags);
109 ret = __lowest_in_progress(running);
110 spin_unlock_irqrestore(&async_lock, flags); 106 spin_unlock_irqrestore(&async_lock, flags);
111 return ret; 107 return ret;
112} 108}
@@ -120,14 +116,8 @@ static void async_run_entry_fn(struct work_struct *work)
120 container_of(work, struct async_entry, work); 116 container_of(work, struct async_entry, work);
121 unsigned long flags; 117 unsigned long flags;
122 ktime_t uninitialized_var(calltime), delta, rettime; 118 ktime_t uninitialized_var(calltime), delta, rettime;
123 struct async_domain *running = entry->running;
124 119
125 /* 1) move self to the running queue */ 120 /* 1) run (and print duration) */
126 spin_lock_irqsave(&async_lock, flags);
127 list_move_tail(&entry->list, &running->domain);
128 spin_unlock_irqrestore(&async_lock, flags);
129
130 /* 2) run (and print duration) */
131 if (initcall_debug && system_state == SYSTEM_BOOTING) { 121 if (initcall_debug && system_state == SYSTEM_BOOTING) {
132 printk(KERN_DEBUG "calling %lli_%pF @ %i\n", 122 printk(KERN_DEBUG "calling %lli_%pF @ %i\n",
133 (long long)entry->cookie, 123 (long long)entry->cookie,
@@ -144,23 +134,22 @@ static void async_run_entry_fn(struct work_struct *work)
144 (long long)ktime_to_ns(delta) >> 10); 134 (long long)ktime_to_ns(delta) >> 10);
145 } 135 }
146 136
147 /* 3) remove self from the running queue */ 137 /* 2) remove self from the pending queues */
148 spin_lock_irqsave(&async_lock, flags); 138 spin_lock_irqsave(&async_lock, flags);
149 list_del(&entry->list); 139 list_del_init(&entry->domain_list);
150 if (running->registered && --running->count == 0) 140 list_del_init(&entry->global_list);
151 list_del_init(&running->node);
152 141
153 /* 4) free the entry */ 142 /* 3) free the entry */
154 kfree(entry); 143 kfree(entry);
155 atomic_dec(&entry_count); 144 atomic_dec(&entry_count);
156 145
157 spin_unlock_irqrestore(&async_lock, flags); 146 spin_unlock_irqrestore(&async_lock, flags);
158 147
159 /* 5) wake up any waiters */ 148 /* 4) wake up any waiters */
160 wake_up(&async_done); 149 wake_up(&async_done);
161} 150}
162 151
163static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct async_domain *running) 152static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct async_domain *domain)
164{ 153{
165 struct async_entry *entry; 154 struct async_entry *entry;
166 unsigned long flags; 155 unsigned long flags;
@@ -183,19 +172,28 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct a
183 ptr(data, newcookie); 172 ptr(data, newcookie);
184 return newcookie; 173 return newcookie;
185 } 174 }
175 INIT_LIST_HEAD(&entry->domain_list);
176 INIT_LIST_HEAD(&entry->global_list);
186 INIT_WORK(&entry->work, async_run_entry_fn); 177 INIT_WORK(&entry->work, async_run_entry_fn);
187 entry->func = ptr; 178 entry->func = ptr;
188 entry->data = data; 179 entry->data = data;
189 entry->running = running; 180 entry->domain = domain;
190 181
191 spin_lock_irqsave(&async_lock, flags); 182 spin_lock_irqsave(&async_lock, flags);
183
184 /* allocate cookie and queue */
192 newcookie = entry->cookie = next_cookie++; 185 newcookie = entry->cookie = next_cookie++;
193 list_add_tail(&entry->list, &async_pending); 186
194 if (running->registered && running->count++ == 0) 187 list_add_tail(&entry->domain_list, &domain->pending);
195 list_add_tail(&running->node, &async_domains); 188 if (domain->registered)
189 list_add_tail(&entry->global_list, &async_global_pending);
190
196 atomic_inc(&entry_count); 191 atomic_inc(&entry_count);
197 spin_unlock_irqrestore(&async_lock, flags); 192 spin_unlock_irqrestore(&async_lock, flags);
198 193
194 /* mark that this task has queued an async job, used by module init */
195 current->flags |= PF_USED_ASYNC;
196
199 /* schedule for execution */ 197 /* schedule for execution */
200 queue_work(system_unbound_wq, &entry->work); 198 queue_work(system_unbound_wq, &entry->work);
201 199
@@ -212,7 +210,7 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct a
212 */ 210 */
213async_cookie_t async_schedule(async_func_ptr *ptr, void *data) 211async_cookie_t async_schedule(async_func_ptr *ptr, void *data)
214{ 212{
215 return __async_schedule(ptr, data, &async_running); 213 return __async_schedule(ptr, data, &async_dfl_domain);
216} 214}
217EXPORT_SYMBOL_GPL(async_schedule); 215EXPORT_SYMBOL_GPL(async_schedule);
218 216
@@ -220,18 +218,18 @@ EXPORT_SYMBOL_GPL(async_schedule);
220 * async_schedule_domain - schedule a function for asynchronous execution within a certain domain 218 * async_schedule_domain - schedule a function for asynchronous execution within a certain domain
221 * @ptr: function to execute asynchronously 219 * @ptr: function to execute asynchronously
222 * @data: data pointer to pass to the function 220 * @data: data pointer to pass to the function
223 * @running: running list for the domain 221 * @domain: the domain
224 * 222 *
225 * Returns an async_cookie_t that may be used for checkpointing later. 223 * Returns an async_cookie_t that may be used for checkpointing later.
226 * @running may be used in the async_synchronize_*_domain() functions 224 * @domain may be used in the async_synchronize_*_domain() functions to
227 * to wait within a certain synchronization domain rather than globally. 225 * wait within a certain synchronization domain rather than globally. A
228 * A synchronization domain is specified via the running queue @running to use. 226 * synchronization domain is specified via @domain. Note: This function
229 * Note: This function may be called from atomic or non-atomic contexts. 227 * may be called from atomic or non-atomic contexts.
230 */ 228 */
231async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data, 229async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data,
232 struct async_domain *running) 230 struct async_domain *domain)
233{ 231{
234 return __async_schedule(ptr, data, running); 232 return __async_schedule(ptr, data, domain);
235} 233}
236EXPORT_SYMBOL_GPL(async_schedule_domain); 234EXPORT_SYMBOL_GPL(async_schedule_domain);
237 235
@@ -242,18 +240,7 @@ EXPORT_SYMBOL_GPL(async_schedule_domain);
242 */ 240 */
243void async_synchronize_full(void) 241void async_synchronize_full(void)
244{ 242{
245 mutex_lock(&async_register_mutex); 243 async_synchronize_full_domain(NULL);
246 do {
247 struct async_domain *domain = NULL;
248
249 spin_lock_irq(&async_lock);
250 if (!list_empty(&async_domains))
251 domain = list_first_entry(&async_domains, typeof(*domain), node);
252 spin_unlock_irq(&async_lock);
253
254 async_synchronize_cookie_domain(next_cookie, domain);
255 } while (!list_empty(&async_domains));
256 mutex_unlock(&async_register_mutex);
257} 244}
258EXPORT_SYMBOL_GPL(async_synchronize_full); 245EXPORT_SYMBOL_GPL(async_synchronize_full);
259 246
@@ -268,51 +255,45 @@ EXPORT_SYMBOL_GPL(async_synchronize_full);
268 */ 255 */
269void async_unregister_domain(struct async_domain *domain) 256void async_unregister_domain(struct async_domain *domain)
270{ 257{
271 mutex_lock(&async_register_mutex);
272 spin_lock_irq(&async_lock); 258 spin_lock_irq(&async_lock);
273 WARN_ON(!domain->registered || !list_empty(&domain->node) || 259 WARN_ON(!domain->registered || !list_empty(&domain->pending));
274 !list_empty(&domain->domain));
275 domain->registered = 0; 260 domain->registered = 0;
276 spin_unlock_irq(&async_lock); 261 spin_unlock_irq(&async_lock);
277 mutex_unlock(&async_register_mutex);
278} 262}
279EXPORT_SYMBOL_GPL(async_unregister_domain); 263EXPORT_SYMBOL_GPL(async_unregister_domain);
280 264
281/** 265/**
282 * async_synchronize_full_domain - synchronize all asynchronous function within a certain domain 266 * async_synchronize_full_domain - synchronize all asynchronous function within a certain domain
283 * @domain: running list to synchronize on 267 * @domain: the domain to synchronize
284 * 268 *
285 * This function waits until all asynchronous function calls for the 269 * This function waits until all asynchronous function calls for the
286 * synchronization domain specified by the running list @domain have been done. 270 * synchronization domain specified by @domain have been done.
287 */ 271 */
288void async_synchronize_full_domain(struct async_domain *domain) 272void async_synchronize_full_domain(struct async_domain *domain)
289{ 273{
290 async_synchronize_cookie_domain(next_cookie, domain); 274 async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, domain);
291} 275}
292EXPORT_SYMBOL_GPL(async_synchronize_full_domain); 276EXPORT_SYMBOL_GPL(async_synchronize_full_domain);
293 277
294/** 278/**
295 * async_synchronize_cookie_domain - synchronize asynchronous function calls within a certain domain with cookie checkpointing 279 * async_synchronize_cookie_domain - synchronize asynchronous function calls within a certain domain with cookie checkpointing
296 * @cookie: async_cookie_t to use as checkpoint 280 * @cookie: async_cookie_t to use as checkpoint
297 * @running: running list to synchronize on 281 * @domain: the domain to synchronize (%NULL for all registered domains)
298 * 282 *
299 * This function waits until all asynchronous function calls for the 283 * This function waits until all asynchronous function calls for the
300 * synchronization domain specified by running list @running submitted 284 * synchronization domain specified by @domain submitted prior to @cookie
301 * prior to @cookie have been done. 285 * have been done.
302 */ 286 */
303void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *running) 287void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *domain)
304{ 288{
305 ktime_t uninitialized_var(starttime), delta, endtime; 289 ktime_t uninitialized_var(starttime), delta, endtime;
306 290
307 if (!running)
308 return;
309
310 if (initcall_debug && system_state == SYSTEM_BOOTING) { 291 if (initcall_debug && system_state == SYSTEM_BOOTING) {
311 printk(KERN_DEBUG "async_waiting @ %i\n", task_pid_nr(current)); 292 printk(KERN_DEBUG "async_waiting @ %i\n", task_pid_nr(current));
312 starttime = ktime_get(); 293 starttime = ktime_get();
313 } 294 }
314 295
315 wait_event(async_done, lowest_in_progress(running) >= cookie); 296 wait_event(async_done, lowest_in_progress(domain) >= cookie);
316 297
317 if (initcall_debug && system_state == SYSTEM_BOOTING) { 298 if (initcall_debug && system_state == SYSTEM_BOOTING) {
318 endtime = ktime_get(); 299 endtime = ktime_get();
@@ -334,6 +315,18 @@ EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);
334 */ 315 */
335void async_synchronize_cookie(async_cookie_t cookie) 316void async_synchronize_cookie(async_cookie_t cookie)
336{ 317{
337 async_synchronize_cookie_domain(cookie, &async_running); 318 async_synchronize_cookie_domain(cookie, &async_dfl_domain);
338} 319}
339EXPORT_SYMBOL_GPL(async_synchronize_cookie); 320EXPORT_SYMBOL_GPL(async_synchronize_cookie);
321
322/**
323 * current_is_async - is %current an async worker task?
324 *
325 * Returns %true if %current is an async worker task.
326 */
327bool current_is_async(void)
328{
329 struct worker *worker = current_wq_worker();
330
331 return worker && worker->current_func == async_run_entry_fn;
332}