1*22a9d645SArjan van de Ven /* 2*22a9d645SArjan van de Ven * async.c: Asynchronous function calls for boot performance 3*22a9d645SArjan van de Ven * 4*22a9d645SArjan van de Ven * (C) Copyright 2009 Intel Corporation 5*22a9d645SArjan van de Ven * Author: Arjan van de Ven <arjan@linux.intel.com> 6*22a9d645SArjan van de Ven * 7*22a9d645SArjan van de Ven * This program is free software; you can redistribute it and/or 8*22a9d645SArjan van de Ven * modify it under the terms of the GNU General Public License 9*22a9d645SArjan van de Ven * as published by the Free Software Foundation; version 2 10*22a9d645SArjan van de Ven * of the License. 11*22a9d645SArjan van de Ven */ 12*22a9d645SArjan van de Ven 13*22a9d645SArjan van de Ven 14*22a9d645SArjan van de Ven /* 15*22a9d645SArjan van de Ven 16*22a9d645SArjan van de Ven Goals and Theory of Operation 17*22a9d645SArjan van de Ven 18*22a9d645SArjan van de Ven The primary goal of this feature is to reduce the kernel boot time, 19*22a9d645SArjan van de Ven by doing various independent hardware delays and discovery operations 20*22a9d645SArjan van de Ven decoupled and not strictly serialized. 21*22a9d645SArjan van de Ven 22*22a9d645SArjan van de Ven More specifically, the asynchronous function call concept allows 23*22a9d645SArjan van de Ven certain operations (primarily during system boot) to happen 24*22a9d645SArjan van de Ven asynchronously, out of order, while these operations still 25*22a9d645SArjan van de Ven have their externally visible parts happen sequentially and in-order. 26*22a9d645SArjan van de Ven (not unlike how out-of-order CPUs retire their instructions in order) 27*22a9d645SArjan van de Ven 28*22a9d645SArjan van de Ven Key to the asynchronous function call implementation is the concept of 29*22a9d645SArjan van de Ven a "sequence cookie" (which, although it has an abstracted type, can be 30*22a9d645SArjan van de Ven thought of as a monotonically incrementing number). 31*22a9d645SArjan van de Ven 32*22a9d645SArjan van de Ven The async core will assign each scheduled event such a sequence cookie and 33*22a9d645SArjan van de Ven pass this to the called functions. 34*22a9d645SArjan van de Ven 35*22a9d645SArjan van de Ven The asynchronously called function should before doing a globally visible 36*22a9d645SArjan van de Ven operation, such as registering device numbers, call the 37*22a9d645SArjan van de Ven async_synchronize_cookie() function and pass in its own cookie. The 38*22a9d645SArjan van de Ven async_synchronize_cookie() function will make sure that all asynchronous 39*22a9d645SArjan van de Ven operations that were scheduled prior to the operation corresponding with the 40*22a9d645SArjan van de Ven cookie have completed. 41*22a9d645SArjan van de Ven 42*22a9d645SArjan van de Ven Subsystem/driver initialization code that scheduled asynchronous probe 43*22a9d645SArjan van de Ven functions, but which shares global resources with other drivers/subsystems 44*22a9d645SArjan van de Ven that do not use the asynchronous call feature, need to do a full 45*22a9d645SArjan van de Ven synchronization with the async_synchronize_full() function, before returning 46*22a9d645SArjan van de Ven from their init function. This is to maintain strict ordering between the 47*22a9d645SArjan van de Ven asynchronous and synchronous parts of the kernel. 48*22a9d645SArjan van de Ven 49*22a9d645SArjan van de Ven */ 50*22a9d645SArjan van de Ven 51*22a9d645SArjan van de Ven #include <linux/async.h> 52*22a9d645SArjan van de Ven #include <linux/module.h> 53*22a9d645SArjan van de Ven #include <linux/wait.h> 54*22a9d645SArjan van de Ven #include <linux/sched.h> 55*22a9d645SArjan van de Ven #include <linux/init.h> 56*22a9d645SArjan van de Ven #include <linux/kthread.h> 57*22a9d645SArjan van de Ven #include <asm/atomic.h> 58*22a9d645SArjan van de Ven 59*22a9d645SArjan van de Ven static async_cookie_t next_cookie = 1; 60*22a9d645SArjan van de Ven 61*22a9d645SArjan van de Ven #define MAX_THREADS 256 62*22a9d645SArjan van de Ven #define MAX_WORK 32768 63*22a9d645SArjan van de Ven 64*22a9d645SArjan van de Ven static LIST_HEAD(async_pending); 65*22a9d645SArjan van de Ven static LIST_HEAD(async_running); 66*22a9d645SArjan van de Ven static DEFINE_SPINLOCK(async_lock); 67*22a9d645SArjan van de Ven 68*22a9d645SArjan van de Ven struct async_entry { 69*22a9d645SArjan van de Ven struct list_head list; 70*22a9d645SArjan van de Ven async_cookie_t cookie; 71*22a9d645SArjan van de Ven async_func_ptr *func; 72*22a9d645SArjan van de Ven void *data; 73*22a9d645SArjan van de Ven struct list_head *running; 74*22a9d645SArjan van de Ven }; 75*22a9d645SArjan van de Ven 76*22a9d645SArjan van de Ven static DECLARE_WAIT_QUEUE_HEAD(async_done); 77*22a9d645SArjan van de Ven static DECLARE_WAIT_QUEUE_HEAD(async_new); 78*22a9d645SArjan van de Ven 79*22a9d645SArjan van de Ven static atomic_t entry_count; 80*22a9d645SArjan van de Ven static atomic_t thread_count; 81*22a9d645SArjan van de Ven 82*22a9d645SArjan van de Ven extern int initcall_debug; 83*22a9d645SArjan van de Ven 84*22a9d645SArjan van de Ven 85*22a9d645SArjan van de Ven /* 86*22a9d645SArjan van de Ven * MUST be called with the lock held! 87*22a9d645SArjan van de Ven */ 88*22a9d645SArjan van de Ven static async_cookie_t __lowest_in_progress(struct list_head *running) 89*22a9d645SArjan van de Ven { 90*22a9d645SArjan van de Ven struct async_entry *entry; 91*22a9d645SArjan van de Ven if (!list_empty(&async_pending)) { 92*22a9d645SArjan van de Ven entry = list_first_entry(&async_pending, 93*22a9d645SArjan van de Ven struct async_entry, list); 94*22a9d645SArjan van de Ven return entry->cookie; 95*22a9d645SArjan van de Ven } else if (!list_empty(running)) { 96*22a9d645SArjan van de Ven entry = list_first_entry(running, 97*22a9d645SArjan van de Ven struct async_entry, list); 98*22a9d645SArjan van de Ven return entry->cookie; 99*22a9d645SArjan van de Ven } else { 100*22a9d645SArjan van de Ven /* nothing in progress... next_cookie is "infinity" */ 101*22a9d645SArjan van de Ven return next_cookie; 102*22a9d645SArjan van de Ven } 103*22a9d645SArjan van de Ven 104*22a9d645SArjan van de Ven } 105*22a9d645SArjan van de Ven /* 106*22a9d645SArjan van de Ven * pick the first pending entry and run it 107*22a9d645SArjan van de Ven */ 108*22a9d645SArjan van de Ven static void run_one_entry(void) 109*22a9d645SArjan van de Ven { 110*22a9d645SArjan van de Ven unsigned long flags; 111*22a9d645SArjan van de Ven struct async_entry *entry; 112*22a9d645SArjan van de Ven ktime_t calltime, delta, rettime; 113*22a9d645SArjan van de Ven 114*22a9d645SArjan van de Ven /* 1) pick one task from the pending queue */ 115*22a9d645SArjan van de Ven 116*22a9d645SArjan van de Ven spin_lock_irqsave(&async_lock, flags); 117*22a9d645SArjan van de Ven if (list_empty(&async_pending)) 118*22a9d645SArjan van de Ven goto out; 119*22a9d645SArjan van de Ven entry = list_first_entry(&async_pending, struct async_entry, list); 120*22a9d645SArjan van de Ven 121*22a9d645SArjan van de Ven /* 2) move it to the running queue */ 122*22a9d645SArjan van de Ven list_del(&entry->list); 123*22a9d645SArjan van de Ven list_add_tail(&entry->list, &async_running); 124*22a9d645SArjan van de Ven spin_unlock_irqrestore(&async_lock, flags); 125*22a9d645SArjan van de Ven 126*22a9d645SArjan van de Ven /* 3) run it (and print duration)*/ 127*22a9d645SArjan van de Ven if (initcall_debug) { 128*22a9d645SArjan van de Ven printk("calling %lli_%pF @ %i\n", entry->cookie, entry->func, task_pid_nr(current)); 129*22a9d645SArjan van de Ven calltime = ktime_get(); 130*22a9d645SArjan van de Ven } 131*22a9d645SArjan van de Ven entry->func(entry->data, entry->cookie); 132*22a9d645SArjan van de Ven if (initcall_debug) { 133*22a9d645SArjan van de Ven rettime = ktime_get(); 134*22a9d645SArjan van de Ven delta = ktime_sub(rettime, calltime); 135*22a9d645SArjan van de Ven printk("initcall %lli_%pF returned 0 after %lld usecs\n", entry->cookie, 136*22a9d645SArjan van de Ven entry->func, ktime_to_ns(delta) >> 10); 137*22a9d645SArjan van de Ven } 138*22a9d645SArjan van de Ven 139*22a9d645SArjan van de Ven /* 4) remove it from the running queue */ 140*22a9d645SArjan van de Ven spin_lock_irqsave(&async_lock, flags); 141*22a9d645SArjan van de Ven list_del(&entry->list); 142*22a9d645SArjan van de Ven 143*22a9d645SArjan van de Ven /* 5) free the entry */ 144*22a9d645SArjan van de Ven kfree(entry); 145*22a9d645SArjan van de Ven atomic_dec(&entry_count); 146*22a9d645SArjan van de Ven 147*22a9d645SArjan van de Ven spin_unlock_irqrestore(&async_lock, flags); 148*22a9d645SArjan van de Ven 149*22a9d645SArjan van de Ven /* 6) wake up any waiters. */ 150*22a9d645SArjan van de Ven wake_up(&async_done); 151*22a9d645SArjan van de Ven return; 152*22a9d645SArjan van de Ven 153*22a9d645SArjan van de Ven out: 154*22a9d645SArjan van de Ven spin_unlock_irqrestore(&async_lock, flags); 155*22a9d645SArjan van de Ven } 156*22a9d645SArjan van de Ven 157*22a9d645SArjan van de Ven 158*22a9d645SArjan van de Ven static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct list_head *running) 159*22a9d645SArjan van de Ven { 160*22a9d645SArjan van de Ven struct async_entry *entry; 161*22a9d645SArjan van de Ven unsigned long flags; 162*22a9d645SArjan van de Ven async_cookie_t newcookie; 163*22a9d645SArjan van de Ven 164*22a9d645SArjan van de Ven 165*22a9d645SArjan van de Ven /* allow irq-off callers */ 166*22a9d645SArjan van de Ven entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC); 167*22a9d645SArjan van de Ven 168*22a9d645SArjan van de Ven /* 169*22a9d645SArjan van de Ven * If we're out of memory or if there's too much work 170*22a9d645SArjan van de Ven * pending already, we execute synchronously. 171*22a9d645SArjan van de Ven */ 172*22a9d645SArjan van de Ven if (!entry || atomic_read(&entry_count) > MAX_WORK) { 173*22a9d645SArjan van de Ven kfree(entry); 174*22a9d645SArjan van de Ven spin_lock_irqsave(&async_lock, flags); 175*22a9d645SArjan van de Ven newcookie = next_cookie++; 176*22a9d645SArjan van de Ven spin_unlock_irqrestore(&async_lock, flags); 177*22a9d645SArjan van de Ven 178*22a9d645SArjan van de Ven /* low on memory.. run synchronously */ 179*22a9d645SArjan van de Ven ptr(data, newcookie); 180*22a9d645SArjan van de Ven return newcookie; 181*22a9d645SArjan van de Ven } 182*22a9d645SArjan van de Ven entry->func = ptr; 183*22a9d645SArjan van de Ven entry->data = data; 184*22a9d645SArjan van de Ven entry->running = running; 185*22a9d645SArjan van de Ven 186*22a9d645SArjan van de Ven spin_lock_irqsave(&async_lock, flags); 187*22a9d645SArjan van de Ven newcookie = entry->cookie = next_cookie++; 188*22a9d645SArjan van de Ven list_add_tail(&entry->list, &async_pending); 189*22a9d645SArjan van de Ven atomic_inc(&entry_count); 190*22a9d645SArjan van de Ven spin_unlock_irqrestore(&async_lock, flags); 191*22a9d645SArjan van de Ven wake_up(&async_new); 192*22a9d645SArjan van de Ven return newcookie; 193*22a9d645SArjan van de Ven } 194*22a9d645SArjan van de Ven 195*22a9d645SArjan van de Ven async_cookie_t async_schedule(async_func_ptr *ptr, void *data) 196*22a9d645SArjan van de Ven { 197*22a9d645SArjan van de Ven return __async_schedule(ptr, data, &async_pending); 198*22a9d645SArjan van de Ven } 199*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_schedule); 200*22a9d645SArjan van de Ven 201*22a9d645SArjan van de Ven async_cookie_t async_schedule_special(async_func_ptr *ptr, void *data, struct list_head *running) 202*22a9d645SArjan van de Ven { 203*22a9d645SArjan van de Ven return __async_schedule(ptr, data, running); 204*22a9d645SArjan van de Ven } 205*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_schedule_special); 206*22a9d645SArjan van de Ven 207*22a9d645SArjan van de Ven void async_synchronize_full(void) 208*22a9d645SArjan van de Ven { 209*22a9d645SArjan van de Ven async_synchronize_cookie(next_cookie); 210*22a9d645SArjan van de Ven } 211*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_full); 212*22a9d645SArjan van de Ven 213*22a9d645SArjan van de Ven void async_synchronize_full_special(struct list_head *list) 214*22a9d645SArjan van de Ven { 215*22a9d645SArjan van de Ven async_synchronize_cookie_special(next_cookie, list); 216*22a9d645SArjan van de Ven } 217*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_full_special); 218*22a9d645SArjan van de Ven 219*22a9d645SArjan van de Ven void async_synchronize_cookie_special(async_cookie_t cookie, struct list_head *running) 220*22a9d645SArjan van de Ven { 221*22a9d645SArjan van de Ven ktime_t starttime, delta, endtime; 222*22a9d645SArjan van de Ven 223*22a9d645SArjan van de Ven if (initcall_debug) { 224*22a9d645SArjan van de Ven printk("async_waiting @ %i\n", task_pid_nr(current)); 225*22a9d645SArjan van de Ven starttime = ktime_get(); 226*22a9d645SArjan van de Ven } 227*22a9d645SArjan van de Ven 228*22a9d645SArjan van de Ven wait_event(async_done, __lowest_in_progress(running) >= cookie); 229*22a9d645SArjan van de Ven 230*22a9d645SArjan van de Ven if (initcall_debug) { 231*22a9d645SArjan van de Ven endtime = ktime_get(); 232*22a9d645SArjan van de Ven delta = ktime_sub(endtime, starttime); 233*22a9d645SArjan van de Ven 234*22a9d645SArjan van de Ven printk("async_continuing @ %i after %lli usec\n", 235*22a9d645SArjan van de Ven task_pid_nr(current), ktime_to_ns(delta) >> 10); 236*22a9d645SArjan van de Ven } 237*22a9d645SArjan van de Ven } 238*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_cookie_special); 239*22a9d645SArjan van de Ven 240*22a9d645SArjan van de Ven void async_synchronize_cookie(async_cookie_t cookie) 241*22a9d645SArjan van de Ven { 242*22a9d645SArjan van de Ven async_synchronize_cookie_special(cookie, &async_running); 243*22a9d645SArjan van de Ven } 244*22a9d645SArjan van de Ven EXPORT_SYMBOL_GPL(async_synchronize_cookie); 245*22a9d645SArjan van de Ven 246*22a9d645SArjan van de Ven 247*22a9d645SArjan van de Ven static int async_thread(void *unused) 248*22a9d645SArjan van de Ven { 249*22a9d645SArjan van de Ven DECLARE_WAITQUEUE(wq, current); 250*22a9d645SArjan van de Ven add_wait_queue(&async_new, &wq); 251*22a9d645SArjan van de Ven 252*22a9d645SArjan van de Ven while (!kthread_should_stop()) { 253*22a9d645SArjan van de Ven int ret = HZ; 254*22a9d645SArjan van de Ven set_current_state(TASK_INTERRUPTIBLE); 255*22a9d645SArjan van de Ven /* 256*22a9d645SArjan van de Ven * check the list head without lock.. false positives 257*22a9d645SArjan van de Ven * are dealt with inside run_one_entry() while holding 258*22a9d645SArjan van de Ven * the lock. 259*22a9d645SArjan van de Ven */ 260*22a9d645SArjan van de Ven rmb(); 261*22a9d645SArjan van de Ven if (!list_empty(&async_pending)) 262*22a9d645SArjan van de Ven run_one_entry(); 263*22a9d645SArjan van de Ven else 264*22a9d645SArjan van de Ven ret = schedule_timeout(HZ); 265*22a9d645SArjan van de Ven 266*22a9d645SArjan van de Ven if (ret == 0) { 267*22a9d645SArjan van de Ven /* 268*22a9d645SArjan van de Ven * we timed out, this means we as thread are redundant. 269*22a9d645SArjan van de Ven * we sign off and die, but we to avoid any races there 270*22a9d645SArjan van de Ven * is a last-straw check to see if work snuck in. 271*22a9d645SArjan van de Ven */ 272*22a9d645SArjan van de Ven atomic_dec(&thread_count); 273*22a9d645SArjan van de Ven wmb(); /* manager must see our departure first */ 274*22a9d645SArjan van de Ven if (list_empty(&async_pending)) 275*22a9d645SArjan van de Ven break; 276*22a9d645SArjan van de Ven /* 277*22a9d645SArjan van de Ven * woops work came in between us timing out and us 278*22a9d645SArjan van de Ven * signing off; we need to stay alive and keep working. 279*22a9d645SArjan van de Ven */ 280*22a9d645SArjan van de Ven atomic_inc(&thread_count); 281*22a9d645SArjan van de Ven } 282*22a9d645SArjan van de Ven } 283*22a9d645SArjan van de Ven remove_wait_queue(&async_new, &wq); 284*22a9d645SArjan van de Ven 285*22a9d645SArjan van de Ven return 0; 286*22a9d645SArjan van de Ven } 287*22a9d645SArjan van de Ven 288*22a9d645SArjan van de Ven static int async_manager_thread(void *unused) 289*22a9d645SArjan van de Ven { 290*22a9d645SArjan van de Ven DECLARE_WAITQUEUE(wq, current); 291*22a9d645SArjan van de Ven add_wait_queue(&async_new, &wq); 292*22a9d645SArjan van de Ven 293*22a9d645SArjan van de Ven while (!kthread_should_stop()) { 294*22a9d645SArjan van de Ven int tc, ec; 295*22a9d645SArjan van de Ven 296*22a9d645SArjan van de Ven set_current_state(TASK_INTERRUPTIBLE); 297*22a9d645SArjan van de Ven 298*22a9d645SArjan van de Ven tc = atomic_read(&thread_count); 299*22a9d645SArjan van de Ven rmb(); 300*22a9d645SArjan van de Ven ec = atomic_read(&entry_count); 301*22a9d645SArjan van de Ven 302*22a9d645SArjan van de Ven while (tc < ec && tc < MAX_THREADS) { 303*22a9d645SArjan van de Ven kthread_run(async_thread, NULL, "async/%i", tc); 304*22a9d645SArjan van de Ven atomic_inc(&thread_count); 305*22a9d645SArjan van de Ven tc++; 306*22a9d645SArjan van de Ven } 307*22a9d645SArjan van de Ven 308*22a9d645SArjan van de Ven schedule(); 309*22a9d645SArjan van de Ven } 310*22a9d645SArjan van de Ven remove_wait_queue(&async_new, &wq); 311*22a9d645SArjan van de Ven 312*22a9d645SArjan van de Ven return 0; 313*22a9d645SArjan van de Ven } 314*22a9d645SArjan van de Ven 315*22a9d645SArjan van de Ven static int __init async_init(void) 316*22a9d645SArjan van de Ven { 317*22a9d645SArjan van de Ven kthread_run(async_manager_thread, NULL, "async/mgr"); 318*22a9d645SArjan van de Ven return 0; 319*22a9d645SArjan van de Ven } 320*22a9d645SArjan van de Ven 321*22a9d645SArjan van de Ven core_initcall(async_init); 322