diff options
Diffstat (limited to 'kernel/async.c')
| -rw-r--r-- | kernel/async.c | 43 | 
1 files changed, 41 insertions, 2 deletions
diff --git a/kernel/async.c b/kernel/async.c index ba5491dfa99..9d311838485 100644 --- a/kernel/async.c +++ b/kernel/async.c @@ -63,7 +63,9 @@ static async_cookie_t next_cookie = 1;  static LIST_HEAD(async_pending);  static ASYNC_DOMAIN(async_running); +static LIST_HEAD(async_domains);  static DEFINE_SPINLOCK(async_lock); +static DEFINE_MUTEX(async_register_mutex);  struct async_entry {  	struct list_head	list; @@ -145,6 +147,8 @@ static void async_run_entry_fn(struct work_struct *work)  	/* 3) remove self from the running queue */  	spin_lock_irqsave(&async_lock, flags);  	list_del(&entry->list); +	if (running->registered && --running->count == 0) +		list_del_init(&running->node);  	/* 4) free the entry */  	kfree(entry); @@ -187,6 +191,8 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct a  	spin_lock_irqsave(&async_lock, flags);  	newcookie = entry->cookie = next_cookie++;  	list_add_tail(&entry->list, &async_pending); +	if (running->registered && running->count++ == 0) +		list_add_tail(&running->node, &async_domains);  	atomic_inc(&entry_count);  	spin_unlock_irqrestore(&async_lock, flags); @@ -236,13 +242,43 @@ EXPORT_SYMBOL_GPL(async_schedule_domain);   */  void async_synchronize_full(void)  { +	mutex_lock(&async_register_mutex);  	do { -		async_synchronize_cookie(next_cookie); -	} while (!list_empty(&async_running.domain) || !list_empty(&async_pending)); +		struct async_domain *domain = NULL; + +		spin_lock_irq(&async_lock); +		if (!list_empty(&async_domains)) +			domain = list_first_entry(&async_domains, typeof(*domain), node); +		spin_unlock_irq(&async_lock); + +		async_synchronize_cookie_domain(next_cookie, domain); +	} while (!list_empty(&async_domains)); +	mutex_unlock(&async_register_mutex);  }  EXPORT_SYMBOL_GPL(async_synchronize_full);  /** + * 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) +{ +	mutex_lock(&async_register_mutex); +	spin_lock_irq(&async_lock); +	WARN_ON(!domain->registered || !list_empty(&domain->node) || +		!list_empty(&domain->domain)); +	domain->registered = 0; +	spin_unlock_irq(&async_lock); +	mutex_unlock(&async_register_mutex); +} +EXPORT_SYMBOL_GPL(async_unregister_domain); + +/**   * async_synchronize_full_domain - synchronize all asynchronous function within a certain domain   * @domain: running list to synchronize on   * @@ -268,6 +304,9 @@ void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain  {  	ktime_t uninitialized_var(starttime), delta, endtime; +	if (!running) +		return; +  	if (initcall_debug && system_state == SYSTEM_BOOTING) {  		printk(KERN_DEBUG "async_waiting @ %i\n", task_pid_nr(current));  		starttime = ktime_get();  |