diff options
Diffstat (limited to 'include/linux/rcuwait.h')
-rw-r--r-- | include/linux/rcuwait.h | 40 |
1 files changed, 32 insertions, 8 deletions
diff --git a/include/linux/rcuwait.h b/include/linux/rcuwait.h index 75c97e4bbc57..8052d34da782 100644 --- a/include/linux/rcuwait.h +++ b/include/linux/rcuwait.h @@ -3,6 +3,7 @@ #define _LINUX_RCUWAIT_H_ #include <linux/rcupdate.h> +#include <linux/sched/signal.h> /* * rcuwait provides a way of blocking and waking up a single @@ -24,29 +25,52 @@ static inline void rcuwait_init(struct rcuwait *w) w->task = NULL; } -extern void rcuwait_wake_up(struct rcuwait *w); +/* + * Note: this provides no serialization and, just as with waitqueues, + * requires care to estimate as to whether or not the wait is active. + */ +static inline int rcuwait_active(struct rcuwait *w) +{ + return !!rcu_access_pointer(w->task); +} + +extern int rcuwait_wake_up(struct rcuwait *w); /* * The caller is responsible for locking around rcuwait_wait_event(), - * such that writes to @task are properly serialized. + * and [prepare_to/finish]_rcuwait() such that writes to @task are + * properly serialized. */ -#define rcuwait_wait_event(w, condition) \ + +static inline void prepare_to_rcuwait(struct rcuwait *w) +{ + rcu_assign_pointer(w->task, current); +} + +extern void finish_rcuwait(struct rcuwait *w); + +#define rcuwait_wait_event(w, condition, state) \ ({ \ - rcu_assign_pointer((w)->task, current); \ + int __ret = 0; \ + prepare_to_rcuwait(w); \ for (;;) { \ /* \ * Implicit barrier (A) pairs with (B) in \ * rcuwait_wake_up(). \ */ \ - set_current_state(TASK_UNINTERRUPTIBLE); \ + set_current_state(state); \ if (condition) \ break; \ \ + if (signal_pending_state(state, current)) { \ + __ret = -EINTR; \ + break; \ + } \ + \ schedule(); \ } \ - \ - WRITE_ONCE((w)->task, NULL); \ - __set_current_state(TASK_RUNNING); \ + finish_rcuwait(w); \ + __ret; \ }) #endif /* _LINUX_RCUWAIT_H_ */ |