1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _LINUX_RCUWAIT_H_
3 #define _LINUX_RCUWAIT_H_
4 
5 #include <linux/rcupdate.h>
6 #include <linux/sched/signal.h>
7 #include <linux/types.h>
8 
9 #define __RCUWAIT_INITIALIZER(name)		\
10 	{ .task = NULL, }
11 
rcuwait_init(struct rcuwait * w)12 static inline void rcuwait_init(struct rcuwait *w)
13 {
14 	w->task = NULL;
15 }
16 
17 /*
18  * Note: this provides no serialization and, just as with waitqueues,
19  * requires care to estimate as to whether or not the wait is active.
20  */
rcuwait_active(struct rcuwait * w)21 static inline int rcuwait_active(struct rcuwait *w)
22 {
23 	return !!rcu_access_pointer(w->task);
24 }
25 
26 extern int rcuwait_wake_up(struct rcuwait *w);
27 
28 /*
29  * The caller is responsible for locking around rcuwait_wait_event(),
30  * and [prepare_to/finish]_rcuwait() such that writes to @task are
31  * properly serialized.
32  */
33 
prepare_to_rcuwait(struct rcuwait * w)34 static inline void prepare_to_rcuwait(struct rcuwait *w)
35 {
36 	rcu_assign_pointer(w->task, current);
37 }
38 
39 extern void finish_rcuwait(struct rcuwait *w);
40 
41 #define ___rcuwait_wait_event(w, condition, state, ret, cmd)		\
42 ({									\
43 	long __ret = ret;						\
44 	prepare_to_rcuwait(w);						\
45 	for (;;) {							\
46 		/*							\
47 		 * Implicit barrier (A) pairs with (B) in		\
48 		 * rcuwait_wake_up().					\
49 		 */							\
50 		set_current_state(state);				\
51 		if (condition)						\
52 			break;						\
53 									\
54 		if (signal_pending_state(state, current)) {		\
55 			__ret = -EINTR;					\
56 			break;						\
57 		}							\
58 									\
59 		cmd;							\
60 	}								\
61 	finish_rcuwait(w);						\
62 	__ret;								\
63 })
64 
65 #define rcuwait_wait_event(w, condition, state)				\
66 	___rcuwait_wait_event(w, condition, state, 0, schedule())
67 
68 #define __rcuwait_wait_event_timeout(w, condition, state, timeout)	\
69 	___rcuwait_wait_event(w, ___wait_cond_timeout(condition),	\
70 			      state, timeout,				\
71 			      __ret = schedule_timeout(__ret))
72 
73 #define rcuwait_wait_event_timeout(w, condition, state, timeout)	\
74 ({									\
75 	long __ret = timeout;						\
76 	if (!___wait_cond_timeout(condition))				\
77 		__ret = __rcuwait_wait_event_timeout(w, condition,	\
78 						     state, timeout);	\
79 	__ret;								\
80 })
81 
82 #endif /* _LINUX_RCUWAIT_H_ */
83