diff options
Diffstat (limited to 'kernel/task_work.c')
| -rw-r--r-- | kernel/task_work.c | 94 | 
1 files changed, 45 insertions, 49 deletions
diff --git a/kernel/task_work.c b/kernel/task_work.c index 82d1c794066d..91d4e1742a0c 100644 --- a/kernel/task_work.c +++ b/kernel/task_work.c @@ -3,82 +3,78 @@  #include <linux/tracehook.h>  int -task_work_add(struct task_struct *task, struct task_work *twork, bool notify) +task_work_add(struct task_struct *task, struct callback_head *twork, bool notify)  { +	struct callback_head *last, *first;  	unsigned long flags; -	int err = -ESRCH; -#ifndef TIF_NOTIFY_RESUME -	if (notify) -		return -ENOTSUPP; -#endif  	/* -	 * We must not insert the new work if the task has already passed -	 * exit_task_work(). We rely on do_exit()->raw_spin_unlock_wait() -	 * and check PF_EXITING under pi_lock. +	 * Not inserting the new work if the task has already passed +	 * exit_task_work() is the responisbility of callers.  	 */  	raw_spin_lock_irqsave(&task->pi_lock, flags); -	if (likely(!(task->flags & PF_EXITING))) { -		hlist_add_head(&twork->hlist, &task->task_works); -		err = 0; -	} +	last = task->task_works; +	first = last ? last->next : twork; +	twork->next = first; +	if (last) +		last->next = twork; +	task->task_works = twork;  	raw_spin_unlock_irqrestore(&task->pi_lock, flags);  	/* test_and_set_bit() implies mb(), see tracehook_notify_resume(). */ -	if (likely(!err) && notify) +	if (notify)  		set_notify_resume(task); -	return err; +	return 0;  } -struct task_work * +struct callback_head *  task_work_cancel(struct task_struct *task, task_work_func_t func)  {  	unsigned long flags; -	struct task_work *twork; -	struct hlist_node *pos; +	struct callback_head *last, *res = NULL;  	raw_spin_lock_irqsave(&task->pi_lock, flags); -	hlist_for_each_entry(twork, pos, &task->task_works, hlist) { -		if (twork->func == func) { -			hlist_del(&twork->hlist); -			goto found; +	last = task->task_works; +	if (last) { +		struct callback_head *q = last, *p = q->next; +		while (1) { +			if (p->func == func) { +				q->next = p->next; +				if (p == last) +					task->task_works = q == p ? NULL : q; +				res = p; +				break; +			} +			if (p == last) +				break; +			q = p; +			p = q->next;  		}  	} -	twork = NULL; - found:  	raw_spin_unlock_irqrestore(&task->pi_lock, flags); - -	return twork; +	return res;  }  void task_work_run(void)  {  	struct task_struct *task = current; -	struct hlist_head task_works; -	struct hlist_node *pos; +	struct callback_head *p, *q; -	raw_spin_lock_irq(&task->pi_lock); -	hlist_move_list(&task->task_works, &task_works); -	raw_spin_unlock_irq(&task->pi_lock); +	while (1) { +		raw_spin_lock_irq(&task->pi_lock); +		p = task->task_works; +		task->task_works = NULL; +		raw_spin_unlock_irq(&task->pi_lock); -	if (unlikely(hlist_empty(&task_works))) -		return; -	/* -	 * We use hlist to save the space in task_struct, but we want fifo. -	 * Find the last entry, the list should be short, then process them -	 * in reverse order. -	 */ -	for (pos = task_works.first; pos->next; pos = pos->next) -		; +		if (unlikely(!p)) +			return; -	for (;;) { -		struct hlist_node **pprev = pos->pprev; -		struct task_work *twork = container_of(pos, struct task_work, -							hlist); -		twork->func(twork); - -		if (pprev == &task_works.first) -			break; -		pos = container_of(pprev, struct hlist_node, next); +		q = p->next; /* head */ +		p->next = NULL; /* cut it */ +		while (q) { +			p = q->next; +			q->func(q); +			q = p; +		}  	}  }  | 
