The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kernel/task_work.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 #include <linux/spinlock.h>
    2 #include <linux/task_work.h>
    3 #include <linux/tracehook.h>
    4 
    5 static struct callback_head work_exited; /* all we need is ->next == NULL */
    6 
    7 int
    8 task_work_add(struct task_struct *task, struct callback_head *work, bool notify)
    9 {
   10         struct callback_head *head;
   11 
   12         do {
   13                 head = ACCESS_ONCE(task->task_works);
   14                 if (unlikely(head == &work_exited))
   15                         return -ESRCH;
   16                 work->next = head;
   17         } while (cmpxchg(&task->task_works, head, work) != head);
   18 
   19         if (notify)
   20                 set_notify_resume(task);
   21         return 0;
   22 }
   23 
   24 struct callback_head *
   25 task_work_cancel(struct task_struct *task, task_work_func_t func)
   26 {
   27         struct callback_head **pprev = &task->task_works;
   28         struct callback_head *work = NULL;
   29         unsigned long flags;
   30         /*
   31          * If cmpxchg() fails we continue without updating pprev.
   32          * Either we raced with task_work_add() which added the
   33          * new entry before this work, we will find it again. Or
   34          * we raced with task_work_run(), *pprev == NULL/exited.
   35          */
   36         raw_spin_lock_irqsave(&task->pi_lock, flags);
   37         while ((work = ACCESS_ONCE(*pprev))) {
   38                 read_barrier_depends();
   39                 if (work->func != func)
   40                         pprev = &work->next;
   41                 else if (cmpxchg(pprev, work, work->next) == work)
   42                         break;
   43         }
   44         raw_spin_unlock_irqrestore(&task->pi_lock, flags);
   45 
   46         return work;
   47 }
   48 
   49 void task_work_run(void)
   50 {
   51         struct task_struct *task = current;
   52         struct callback_head *work, *head, *next;
   53 
   54         for (;;) {
   55                 /*
   56                  * work->func() can do task_work_add(), do not set
   57                  * work_exited unless the list is empty.
   58                  */
   59                 do {
   60                         work = ACCESS_ONCE(task->task_works);
   61                         head = !work && (task->flags & PF_EXITING) ?
   62                                 &work_exited : NULL;
   63                 } while (cmpxchg(&task->task_works, work, head) != work);
   64 
   65                 if (!work)
   66                         break;
   67                 /*
   68                  * Synchronize with task_work_cancel(). It can't remove
   69                  * the first entry == work, cmpxchg(task_works) should
   70                  * fail, but it can play with *work and other entries.
   71                  */
   72                 raw_spin_unlock_wait(&task->pi_lock);
   73                 smp_mb();
   74 
   75                 /* Reverse the list to run the works in fifo order */
   76                 head = NULL;
   77                 do {
   78                         next = work->next;
   79                         work->next = head;
   80                         head = work;
   81                         work = next;
   82                 } while (work);
   83 
   84                 work = head;
   85                 do {
   86                         next = work->next;
   87                         work->func(work);
   88                         work = next;
   89                         cond_resched();
   90                 } while (work);
   91         }
   92 }

Cache object: 24d427f6f00bdb1760b281463cc43674


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.