/*1* async.c: Asynchronous function calls for boot performance2*3* (C) Copyright 2009 Intel Corporation4* Author: Arjan van de Ven <[email protected]>5*6* This program is free software; you can redistribute it and/or7* modify it under the terms of the GNU General Public License8* as published by the Free Software Foundation; version 29* of the License.10*/111213/*1415Goals and Theory of Operation1617The primary goal of this feature is to reduce the kernel boot time,18by doing various independent hardware delays and discovery operations19decoupled and not strictly serialized.2021More specifically, the asynchronous function call concept allows22certain operations (primarily during system boot) to happen23asynchronously, out of order, while these operations still24have their externally visible parts happen sequentially and in-order.25(not unlike how out-of-order CPUs retire their instructions in order)2627Key to the asynchronous function call implementation is the concept of28a "sequence cookie" (which, although it has an abstracted type, can be29thought of as a monotonically incrementing number).3031The async core will assign each scheduled event such a sequence cookie and32pass this to the called functions.3334The asynchronously called function should before doing a globally visible35operation, such as registering device numbers, call the36async_synchronize_cookie() function and pass in its own cookie. The37async_synchronize_cookie() function will make sure that all asynchronous38operations that were scheduled prior to the operation corresponding with the39cookie have completed.4041Subsystem/driver initialization code that scheduled asynchronous probe42functions, but which shares global resources with other drivers/subsystems43that do not use the asynchronous call feature, need to do a full44synchronization with the async_synchronize_full() function, before returning45from their init function. This is to maintain strict ordering between the46asynchronous and synchronous parts of the kernel.4748*/4950#include <linux/async.h>51#include <linux/module.h>52#include <linux/wait.h>53#include <linux/sched.h>54#include <linux/slab.h>55#include <linux/workqueue.h>56#include <asm/atomic.h>5758static async_cookie_t next_cookie = 1;5960#define MAX_WORK 327686162static LIST_HEAD(async_pending);63static LIST_HEAD(async_running);64static DEFINE_SPINLOCK(async_lock);6566struct async_entry {67struct list_head list;68struct work_struct work;69async_cookie_t cookie;70async_func_ptr *func;71void *data;72struct list_head *running;73};7475static DECLARE_WAIT_QUEUE_HEAD(async_done);7677static atomic_t entry_count;7879extern int initcall_debug;808182/*83* MUST be called with the lock held!84*/85static async_cookie_t __lowest_in_progress(struct list_head *running)86{87struct async_entry *entry;8889if (!list_empty(running)) {90entry = list_first_entry(running,91struct async_entry, list);92return entry->cookie;93}9495list_for_each_entry(entry, &async_pending, list)96if (entry->running == running)97return entry->cookie;9899return next_cookie; /* "infinity" value */100}101102static async_cookie_t lowest_in_progress(struct list_head *running)103{104unsigned long flags;105async_cookie_t ret;106107spin_lock_irqsave(&async_lock, flags);108ret = __lowest_in_progress(running);109spin_unlock_irqrestore(&async_lock, flags);110return ret;111}112113/*114* pick the first pending entry and run it115*/116static void async_run_entry_fn(struct work_struct *work)117{118struct async_entry *entry =119container_of(work, struct async_entry, work);120unsigned long flags;121ktime_t calltime, delta, rettime;122123/* 1) move self to the running queue */124spin_lock_irqsave(&async_lock, flags);125list_move_tail(&entry->list, entry->running);126spin_unlock_irqrestore(&async_lock, flags);127128/* 2) run (and print duration) */129if (initcall_debug && system_state == SYSTEM_BOOTING) {130printk("calling %lli_%pF @ %i\n", (long long)entry->cookie,131entry->func, task_pid_nr(current));132calltime = ktime_get();133}134entry->func(entry->data, entry->cookie);135if (initcall_debug && system_state == SYSTEM_BOOTING) {136rettime = ktime_get();137delta = ktime_sub(rettime, calltime);138printk("initcall %lli_%pF returned 0 after %lld usecs\n",139(long long)entry->cookie,140entry->func,141(long long)ktime_to_ns(delta) >> 10);142}143144/* 3) remove self from the running queue */145spin_lock_irqsave(&async_lock, flags);146list_del(&entry->list);147148/* 4) free the entry */149kfree(entry);150atomic_dec(&entry_count);151152spin_unlock_irqrestore(&async_lock, flags);153154/* 5) wake up any waiters */155wake_up(&async_done);156}157158static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct list_head *running)159{160struct async_entry *entry;161unsigned long flags;162async_cookie_t newcookie;163164/* allow irq-off callers */165entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);166167/*168* If we're out of memory or if there's too much work169* pending already, we execute synchronously.170*/171if (!entry || atomic_read(&entry_count) > MAX_WORK) {172kfree(entry);173spin_lock_irqsave(&async_lock, flags);174newcookie = next_cookie++;175spin_unlock_irqrestore(&async_lock, flags);176177/* low on memory.. run synchronously */178ptr(data, newcookie);179return newcookie;180}181INIT_WORK(&entry->work, async_run_entry_fn);182entry->func = ptr;183entry->data = data;184entry->running = running;185186spin_lock_irqsave(&async_lock, flags);187newcookie = entry->cookie = next_cookie++;188list_add_tail(&entry->list, &async_pending);189atomic_inc(&entry_count);190spin_unlock_irqrestore(&async_lock, flags);191192/* schedule for execution */193queue_work(system_unbound_wq, &entry->work);194195return newcookie;196}197198/**199* async_schedule - schedule a function for asynchronous execution200* @ptr: function to execute asynchronously201* @data: data pointer to pass to the function202*203* Returns an async_cookie_t that may be used for checkpointing later.204* Note: This function may be called from atomic or non-atomic contexts.205*/206async_cookie_t async_schedule(async_func_ptr *ptr, void *data)207{208return __async_schedule(ptr, data, &async_running);209}210EXPORT_SYMBOL_GPL(async_schedule);211212/**213* async_schedule_domain - schedule a function for asynchronous execution within a certain domain214* @ptr: function to execute asynchronously215* @data: data pointer to pass to the function216* @running: running list for the domain217*218* Returns an async_cookie_t that may be used for checkpointing later.219* @running may be used in the async_synchronize_*_domain() functions220* to wait within a certain synchronization domain rather than globally.221* A synchronization domain is specified via the running queue @running to use.222* Note: This function may be called from atomic or non-atomic contexts.223*/224async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data,225struct list_head *running)226{227return __async_schedule(ptr, data, running);228}229EXPORT_SYMBOL_GPL(async_schedule_domain);230231/**232* async_synchronize_full - synchronize all asynchronous function calls233*234* This function waits until all asynchronous function calls have been done.235*/236void async_synchronize_full(void)237{238do {239async_synchronize_cookie(next_cookie);240} while (!list_empty(&async_running) || !list_empty(&async_pending));241}242EXPORT_SYMBOL_GPL(async_synchronize_full);243244/**245* async_synchronize_full_domain - synchronize all asynchronous function within a certain domain246* @list: running list to synchronize on247*248* This function waits until all asynchronous function calls for the249* synchronization domain specified by the running list @list have been done.250*/251void async_synchronize_full_domain(struct list_head *list)252{253async_synchronize_cookie_domain(next_cookie, list);254}255EXPORT_SYMBOL_GPL(async_synchronize_full_domain);256257/**258* async_synchronize_cookie_domain - synchronize asynchronous function calls within a certain domain with cookie checkpointing259* @cookie: async_cookie_t to use as checkpoint260* @running: running list to synchronize on261*262* This function waits until all asynchronous function calls for the263* synchronization domain specified by the running list @list submitted264* prior to @cookie have been done.265*/266void async_synchronize_cookie_domain(async_cookie_t cookie,267struct list_head *running)268{269ktime_t starttime, delta, endtime;270271if (initcall_debug && system_state == SYSTEM_BOOTING) {272printk("async_waiting @ %i\n", task_pid_nr(current));273starttime = ktime_get();274}275276wait_event(async_done, lowest_in_progress(running) >= cookie);277278if (initcall_debug && system_state == SYSTEM_BOOTING) {279endtime = ktime_get();280delta = ktime_sub(endtime, starttime);281282printk("async_continuing @ %i after %lli usec\n",283task_pid_nr(current),284(long long)ktime_to_ns(delta) >> 10);285}286}287EXPORT_SYMBOL_GPL(async_synchronize_cookie_domain);288289/**290* async_synchronize_cookie - synchronize asynchronous function calls with cookie checkpointing291* @cookie: async_cookie_t to use as checkpoint292*293* This function waits until all asynchronous function calls prior to @cookie294* have been done.295*/296void async_synchronize_cookie(async_cookie_t cookie)297{298async_synchronize_cookie_domain(cookie, &async_running);299}300EXPORT_SYMBOL_GPL(async_synchronize_cookie);301302303