diff options
| author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-08-03 22:41:33 -0300 |
|---|---|---|
| committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-08-03 22:41:33 -0300 |
| commit | f9cd49033b349b8be3bb1f01b39eed837853d880 (patch) | |
| tree | 7f10a70e420f83cce2709e67ff993270276f0277 /kernel/task_work.c | |
| parent | 2f2da1e2995c9362babd7da3cc9d340be184ea73 (diff) | |
| parent | 0d7614f09c1ebdbaa1599a5aba7593f147bf96ee (diff) | |
Merge tag 'v3.6-rc1' into staging/for_v3.6
Linux 3.6-rc1
* tag 'v3.6-rc1': (18733 commits)
Linux 3.6-rc1
mm: remove node_start_pfn checking in new WARN_ON for now
ARM: mmp: add missing irqs.h
arm: mvebu: fix typo in .dtsi comment for Armada XP SoCs
ARM: PRIMA2: delete redundant codes to restore LATCHED when timer resumes
libceph: fix crypto key null deref, memory leak
ceph: simplify+fix atomic_open
sh: explicitly include sh_dma.h in setup-sh7722.c
um: Add arch/x86/um to MAINTAINERS
um: pass siginfo to guest process
um: fix ubd_file_size for read-only files
md/dm-raid: DM_RAID should select MD_RAID10
md/raid1: submit IO from originating thread instead of md thread.
raid5: raid5d handle stripe in batch way
raid5: make_request use batch stripe release
um: pull interrupt_end() into userspace()
um: split syscall_trace(), pass pt_regs to it
um: switch UPT_SET_RETURN_VALUE and regs_return_value to pt_regs
MIPS: Loongson 2: Sort out clock managment.
locks: remove unused lm_release_private
...
Diffstat (limited to 'kernel/task_work.c')
| -rw-r--r-- | kernel/task_work.c | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/kernel/task_work.c b/kernel/task_work.c new file mode 100644 index 000000000000..91d4e1742a0c --- /dev/null +++ b/kernel/task_work.c @@ -0,0 +1,80 @@ +#include <linux/spinlock.h> +#include <linux/task_work.h> +#include <linux/tracehook.h> + +int +task_work_add(struct task_struct *task, struct callback_head *twork, bool notify) +{ + struct callback_head *last, *first; + unsigned long flags; + + /* + * 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); + 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 (notify) + set_notify_resume(task); + return 0; +} + +struct callback_head * +task_work_cancel(struct task_struct *task, task_work_func_t func) +{ + unsigned long flags; + struct callback_head *last, *res = NULL; + + raw_spin_lock_irqsave(&task->pi_lock, flags); + 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; + } + } + raw_spin_unlock_irqrestore(&task->pi_lock, flags); + return res; +} + +void task_work_run(void) +{ + struct task_struct *task = current; + struct callback_head *p, *q; + + 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(!p)) + return; + + q = p->next; /* head */ + p->next = NULL; /* cut it */ + while (q) { + p = q->next; + q->func(q); + q = p; + } + } +} |
