diff options
Diffstat (limited to 'kernel/async.c')
| -rw-r--r-- | kernel/async.c | 76 | 
1 files changed, 57 insertions, 19 deletions
diff --git a/kernel/async.c b/kernel/async.c index bd0c168a3bbe..9d3118384858 100644 --- a/kernel/async.c +++ b/kernel/async.c @@ -62,8 +62,10 @@ static async_cookie_t next_cookie = 1;  #define MAX_WORK	32768  static LIST_HEAD(async_pending); -static LIST_HEAD(async_running); +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; @@ -71,7 +73,7 @@ struct async_entry {  	async_cookie_t		cookie;  	async_func_ptr		*func;  	void			*data; -	struct list_head	*running; +	struct async_domain	*running;  };  static DECLARE_WAIT_QUEUE_HEAD(async_done); @@ -82,13 +84,12 @@ static atomic_t entry_count;  /*   * MUST be called with the lock held!   */ -static async_cookie_t  __lowest_in_progress(struct list_head *running) +static async_cookie_t  __lowest_in_progress(struct async_domain *running)  {  	struct async_entry *entry; -	if (!list_empty(running)) { -		entry = list_first_entry(running, -			struct async_entry, list); +	if (!list_empty(&running->domain)) { +		entry = list_first_entry(&running->domain, typeof(*entry), list);  		return entry->cookie;  	} @@ -99,7 +100,7 @@ static async_cookie_t  __lowest_in_progress(struct list_head *running)  	return next_cookie;	/* "infinity" value */  } -static async_cookie_t  lowest_in_progress(struct list_head *running) +static async_cookie_t  lowest_in_progress(struct async_domain *running)  {  	unsigned long flags;  	async_cookie_t ret; @@ -119,10 +120,11 @@ static void async_run_entry_fn(struct work_struct *work)  		container_of(work, struct async_entry, work);  	unsigned long flags;  	ktime_t uninitialized_var(calltime), delta, rettime; +	struct async_domain *running = entry->running;  	/* 1) move self to the running queue */  	spin_lock_irqsave(&async_lock, flags); -	list_move_tail(&entry->list, entry->running); +	list_move_tail(&entry->list, &running->domain);  	spin_unlock_irqrestore(&async_lock, flags);  	/* 2) run (and print duration) */ @@ -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); @@ -156,7 +160,7 @@ static void async_run_entry_fn(struct work_struct *work)  	wake_up(&async_done);  } -static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct list_head *running) +static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct async_domain *running)  {  	struct async_entry *entry;  	unsigned long flags; @@ -187,6 +191,8 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct l  	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); @@ -223,7 +229,7 @@ EXPORT_SYMBOL_GPL(async_schedule);   * Note: This function may be called from atomic or non-atomic contexts.   */  async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data, -				     struct list_head *running) +				     struct async_domain *running)  {  	return __async_schedule(ptr, data, running);  } @@ -236,22 +242,52 @@ 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) || !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 - * @list: running list to synchronize on + * @domain: running list to synchronize on   *   * This function waits until all asynchronous function calls for the - * synchronization domain specified by the running list @list have been done. + * synchronization domain specified by the running list @domain have been done.   */ -void async_synchronize_full_domain(struct list_head *list) +void async_synchronize_full_domain(struct async_domain *domain)  { -	async_synchronize_cookie_domain(next_cookie, list); +	async_synchronize_cookie_domain(next_cookie, domain);  }  EXPORT_SYMBOL_GPL(async_synchronize_full_domain); @@ -261,14 +297,16 @@ EXPORT_SYMBOL_GPL(async_synchronize_full_domain);   * @running: running list to synchronize on   *   * This function waits until all asynchronous function calls for the - * synchronization domain specified by the running list @list submitted + * synchronization domain specified by running list @running submitted   * prior to @cookie have been done.   */ -void async_synchronize_cookie_domain(async_cookie_t cookie, -				     struct list_head *running) +void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *running)  {  	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();  | 
