xref: /openbmc/linux/kernel/task_work.c (revision a2d4c71d)
1e73f8959SOleg Nesterov #include <linux/spinlock.h>
2e73f8959SOleg Nesterov #include <linux/task_work.h>
3e73f8959SOleg Nesterov #include <linux/tracehook.h>
4e73f8959SOleg Nesterov 
5e73f8959SOleg Nesterov int
667d12145SAl Viro task_work_add(struct task_struct *task, struct callback_head *twork, bool notify)
7e73f8959SOleg Nesterov {
8ed3e694dSAl Viro 	struct callback_head *last, *first;
9e73f8959SOleg Nesterov 	unsigned long flags;
10e73f8959SOleg Nesterov 
11e73f8959SOleg Nesterov 	/*
12ed3e694dSAl Viro 	 * Not inserting the new work if the task has already passed
13ed3e694dSAl Viro 	 * exit_task_work() is the responisbility of callers.
14e73f8959SOleg Nesterov 	 */
15e73f8959SOleg Nesterov 	raw_spin_lock_irqsave(&task->pi_lock, flags);
16ed3e694dSAl Viro 	last = task->task_works;
17ed3e694dSAl Viro 	first = last ? last->next : twork;
18158e1645SAl Viro 	twork->next = first;
19158e1645SAl Viro 	if (last)
20158e1645SAl Viro 		last->next = twork;
21158e1645SAl Viro 	task->task_works = twork;
22e73f8959SOleg Nesterov 	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
23e73f8959SOleg Nesterov 
24e73f8959SOleg Nesterov 	/* test_and_set_bit() implies mb(), see tracehook_notify_resume(). */
25ed3e694dSAl Viro 	if (notify)
26e73f8959SOleg Nesterov 		set_notify_resume(task);
27ed3e694dSAl Viro 	return 0;
28e73f8959SOleg Nesterov }
29e73f8959SOleg Nesterov 
3067d12145SAl Viro struct callback_head *
31e73f8959SOleg Nesterov task_work_cancel(struct task_struct *task, task_work_func_t func)
32e73f8959SOleg Nesterov {
33e73f8959SOleg Nesterov 	unsigned long flags;
3467d12145SAl Viro 	struct callback_head *last, *res = NULL;
35e73f8959SOleg Nesterov 
36e73f8959SOleg Nesterov 	raw_spin_lock_irqsave(&task->pi_lock, flags);
37158e1645SAl Viro 	last = task->task_works;
38158e1645SAl Viro 	if (last) {
3967d12145SAl Viro 		struct callback_head *q = last, *p = q->next;
40158e1645SAl Viro 		while (1) {
41158e1645SAl Viro 			if (p->func == func) {
42158e1645SAl Viro 				q->next = p->next;
43158e1645SAl Viro 				if (p == last)
44158e1645SAl Viro 					task->task_works = q == p ? NULL : q;
45158e1645SAl Viro 				res = p;
46158e1645SAl Viro 				break;
47158e1645SAl Viro 			}
48158e1645SAl Viro 			if (p == last)
49158e1645SAl Viro 				break;
50158e1645SAl Viro 			q = p;
51158e1645SAl Viro 			p = q->next;
52e73f8959SOleg Nesterov 		}
53e73f8959SOleg Nesterov 	}
54e73f8959SOleg Nesterov 	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
55158e1645SAl Viro 	return res;
56e73f8959SOleg Nesterov }
57e73f8959SOleg Nesterov 
58e73f8959SOleg Nesterov void task_work_run(void)
59e73f8959SOleg Nesterov {
60e73f8959SOleg Nesterov 	struct task_struct *task = current;
6167d12145SAl Viro 	struct callback_head *p, *q;
62e73f8959SOleg Nesterov 
63a2d4c71dSAl Viro 	while (1) {
64e73f8959SOleg Nesterov 		raw_spin_lock_irq(&task->pi_lock);
65158e1645SAl Viro 		p = task->task_works;
66158e1645SAl Viro 		task->task_works = NULL;
67e73f8959SOleg Nesterov 		raw_spin_unlock_irq(&task->pi_lock);
68e73f8959SOleg Nesterov 
69158e1645SAl Viro 		if (unlikely(!p))
70e73f8959SOleg Nesterov 			return;
71e73f8959SOleg Nesterov 
72158e1645SAl Viro 		q = p->next; /* head */
73158e1645SAl Viro 		p->next = NULL; /* cut it */
74158e1645SAl Viro 		while (q) {
75158e1645SAl Viro 			p = q->next;
76158e1645SAl Viro 			q->func(q);
77158e1645SAl Viro 			q = p;
78e73f8959SOleg Nesterov 		}
79e73f8959SOleg Nesterov 	}
80a2d4c71dSAl Viro }
81