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