Back to home page

LXR

 
 

    


0001 #include <linux/spinlock.h>
0002 #include <linux/task_work.h>
0003 #include <linux/tracehook.h>
0004 
0005 static struct callback_head work_exited; /* all we need is ->next == NULL */
0006 
0007 /**
0008  * task_work_add - ask the @task to execute @work->func()
0009  * @task: the task which should run the callback
0010  * @work: the callback to run
0011  * @notify: send the notification if true
0012  *
0013  * Queue @work for task_work_run() below and notify the @task if @notify.
0014  * Fails if the @task is exiting/exited and thus it can't process this @work.
0015  * Otherwise @work->func() will be called when the @task returns from kernel
0016  * mode or exits.
0017  *
0018  * This is like the signal handler which runs in kernel mode, but it doesn't
0019  * try to wake up the @task.
0020  *
0021  * Note: there is no ordering guarantee on works queued here.
0022  *
0023  * RETURNS:
0024  * 0 if succeeds or -ESRCH.
0025  */
0026 int
0027 task_work_add(struct task_struct *task, struct callback_head *work, bool notify)
0028 {
0029     struct callback_head *head;
0030 
0031     do {
0032         head = READ_ONCE(task->task_works);
0033         if (unlikely(head == &work_exited))
0034             return -ESRCH;
0035         work->next = head;
0036     } while (cmpxchg(&task->task_works, head, work) != head);
0037 
0038     if (notify)
0039         set_notify_resume(task);
0040     return 0;
0041 }
0042 
0043 /**
0044  * task_work_cancel - cancel a pending work added by task_work_add()
0045  * @task: the task which should execute the work
0046  * @func: identifies the work to remove
0047  *
0048  * Find the last queued pending work with ->func == @func and remove
0049  * it from queue.
0050  *
0051  * RETURNS:
0052  * The found work or NULL if not found.
0053  */
0054 struct callback_head *
0055 task_work_cancel(struct task_struct *task, task_work_func_t func)
0056 {
0057     struct callback_head **pprev = &task->task_works;
0058     struct callback_head *work;
0059     unsigned long flags;
0060 
0061     if (likely(!task->task_works))
0062         return NULL;
0063     /*
0064      * If cmpxchg() fails we continue without updating pprev.
0065      * Either we raced with task_work_add() which added the
0066      * new entry before this work, we will find it again. Or
0067      * we raced with task_work_run(), *pprev == NULL/exited.
0068      */
0069     raw_spin_lock_irqsave(&task->pi_lock, flags);
0070     while ((work = lockless_dereference(*pprev))) {
0071         if (work->func != func)
0072             pprev = &work->next;
0073         else if (cmpxchg(pprev, work, work->next) == work)
0074             break;
0075     }
0076     raw_spin_unlock_irqrestore(&task->pi_lock, flags);
0077 
0078     return work;
0079 }
0080 
0081 /**
0082  * task_work_run - execute the works added by task_work_add()
0083  *
0084  * Flush the pending works. Should be used by the core kernel code.
0085  * Called before the task returns to the user-mode or stops, or when
0086  * it exits. In the latter case task_work_add() can no longer add the
0087  * new work after task_work_run() returns.
0088  */
0089 void task_work_run(void)
0090 {
0091     struct task_struct *task = current;
0092     struct callback_head *work, *head, *next;
0093 
0094     for (;;) {
0095         /*
0096          * work->func() can do task_work_add(), do not set
0097          * work_exited unless the list is empty.
0098          */
0099         do {
0100             work = READ_ONCE(task->task_works);
0101             head = !work && (task->flags & PF_EXITING) ?
0102                 &work_exited : NULL;
0103         } while (cmpxchg(&task->task_works, work, head) != work);
0104 
0105         if (!work)
0106             break;
0107         /*
0108          * Synchronize with task_work_cancel(). It can't remove
0109          * the first entry == work, cmpxchg(task_works) should
0110          * fail, but it can play with *work and other entries.
0111          */
0112         raw_spin_unlock_wait(&task->pi_lock);
0113 
0114         do {
0115             next = work->next;
0116             work->func(work);
0117             work = next;
0118             cond_resched();
0119         } while (work);
0120     }
0121 }