Commit | Line | Data |
---|---|---|
b2441318 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
8f95c90c DB |
2 | #ifndef _LINUX_RCUWAIT_H_ |
3 | #define _LINUX_RCUWAIT_H_ | |
4 | ||
5 | #include <linux/rcupdate.h> | |
6 | ||
7 | /* | |
8 | * rcuwait provides a way of blocking and waking up a single | |
9 | * task in an rcu-safe manner; where it is forbidden to use | |
10 | * after exit_notify(). task_struct is not properly rcu protected, | |
11 | * unless dealing with rcu-aware lists, ie: find_task_by_*(). | |
12 | * | |
13 | * Alternatively we have task_rcu_dereference(), but the return | |
14 | * semantics have different implications which would break the | |
15 | * wakeup side. The only time @task is non-nil is when a user is | |
16 | * blocked (or checking if it needs to) on a condition, and reset | |
17 | * as soon as we know that the condition has succeeded and are | |
18 | * awoken. | |
19 | */ | |
20 | struct rcuwait { | |
21 | struct task_struct *task; | |
22 | }; | |
23 | ||
24 | #define __RCUWAIT_INITIALIZER(name) \ | |
25 | { .task = NULL, } | |
26 | ||
27 | static inline void rcuwait_init(struct rcuwait *w) | |
28 | { | |
29 | w->task = NULL; | |
30 | } | |
31 | ||
32 | extern void rcuwait_wake_up(struct rcuwait *w); | |
33 | ||
34 | /* | |
35 | * The caller is responsible for locking around rcuwait_wait_event(), | |
36 | * such that writes to @task are properly serialized. | |
37 | */ | |
38 | #define rcuwait_wait_event(w, condition) \ | |
39 | ({ \ | |
40 | /* \ | |
41 | * Complain if we are called after do_exit()/exit_notify(), \ | |
42 | * as we cannot rely on the rcu critical region for the \ | |
43 | * wakeup side. \ | |
44 | */ \ | |
45 | WARN_ON(current->exit_state); \ | |
46 | \ | |
47 | rcu_assign_pointer((w)->task, current); \ | |
48 | for (;;) { \ | |
49 | /* \ | |
50 | * Implicit barrier (A) pairs with (B) in \ | |
7e1f9467 | 51 | * rcuwait_wake_up(). \ |
8f95c90c DB |
52 | */ \ |
53 | set_current_state(TASK_UNINTERRUPTIBLE); \ | |
54 | if (condition) \ | |
55 | break; \ | |
56 | \ | |
57 | schedule(); \ | |
58 | } \ | |
59 | \ | |
60 | WRITE_ONCE((w)->task, NULL); \ | |
61 | __set_current_state(TASK_RUNNING); \ | |
62 | }) | |
63 | ||
64 | #endif /* _LINUX_RCUWAIT_H_ */ |