1 /* $NetBSD: completion.h,v 1.5 2014/09/02 09:54:20 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Notes on porting:
34 *
35 * - Linux does not have destroy_completion. You must add it yourself
36 * in the appropriate place.
37 *
38 * - Some Linux code does `completion->done++' or similar. Convert
39 * that to complete(completion) and suggest the same change upstream,
40 * unless it turns out there actually is a good reason to do that, in
41 * which case the Linux completion API should be extended with a
42 * sensible name for this that doesn't expose the guts of `struct
43 * completion'.
44 */
45
46 #ifndef _LINUX_COMPLETION_H_
47 #define _LINUX_COMPLETION_H_
48
49 #include <sys/types.h>
50 #include <sys/condvar.h>
51 #include <sys/mutex.h>
52
53 #include <machine/limits.h>
54
55 #include <linux/errno.h>
56
57 struct completion {
58 kmutex_t c_lock;
59 kcondvar_t c_cv;
60
61 /*
62 * c_done is either
63 *
64 * . -1, meaning it's open season and we're done for good and
65 * nobody need wait any more;
66 *
67 * . 0, meaning nothing is done, so waiters must block; or
68 *
69 * . a positive integer, meaning that many waiters can
70 * proceed before further waiters must block.
71 *
72 * Negative values other than -1 are not allowed.
73 */
74 int c_done;
75 };
76
77 /*
78 * Initialize a new completion object.
79 */
80 static inline void
init_completion(struct completion * completion)81 init_completion(struct completion *completion)
82 {
83
84 mutex_init(&completion->c_lock, MUTEX_DEFAULT, IPL_SCHED);
85 cv_init(&completion->c_cv, "lnxcmplt");
86 completion->c_done = 0;
87 }
88
89 /*
90 * Destroy a completion object.
91 */
92 static inline void
destroy_completion(struct completion * completion)93 destroy_completion(struct completion *completion)
94 {
95 KASSERT(!cv_has_waiters(&completion->c_cv));
96 cv_destroy(&completion->c_cv);
97 mutex_destroy(&completion->c_lock);
98 }
99
100 /*
101 * Notify one waiter of completion, but not any future ones.
102 */
103 static inline void
complete(struct completion * completion)104 complete(struct completion *completion)
105 {
106
107 mutex_enter(&completion->c_lock);
108
109 /* If it's not open season, wake one waiter. */
110 if (completion->c_done >= 0) {
111 KASSERT(completion->c_done < INT_MAX); /* XXX check */
112 completion->c_done++;
113 cv_signal(&completion->c_cv);
114 } else {
115 KASSERT(completion->c_done == -1);
116 }
117
118 mutex_exit(&completion->c_lock);
119 }
120
121 /*
122 * Notify all waiters, present and future (until INIT_COMPLETION), of
123 * completion.
124 */
125 static inline void
complete_all(struct completion * completion)126 complete_all(struct completion *completion)
127 {
128
129 mutex_enter(&completion->c_lock);
130
131 /* If it's not open season, make it open season and wake everyone. */
132 if (completion->c_done >= 0) {
133 completion->c_done = -1;
134 cv_broadcast(&completion->c_cv);
135 } else {
136 KASSERT(completion->c_done == -1);
137 }
138
139 mutex_exit(&completion->c_lock);
140 }
141
142 /*
143 * Reverse the effect of complete_all so that subsequent waiters block
144 * until someone calls complete or complete_all.
145 *
146 * This operation is very different from its lowercase counterpart.
147 *
148 * For some reason this works on the completion object itself, not on a
149 * pointer thereto, so it must be a macro.
150 */
151 #define INIT_COMPLETION(COMPLETION) INIT_COMPLETION_blorp(&(COMPLETION))
152
153 static inline void
INIT_COMPLETION_blorp(struct completion * completion)154 INIT_COMPLETION_blorp(struct completion *completion)
155 {
156
157 mutex_enter(&completion->c_lock);
158 completion->c_done = 0;
159 /* No notify -- waiters are interested only in nonzero values. */
160 mutex_exit(&completion->c_lock);
161 }
162
163 static inline void
_completion_claim(struct completion * completion)164 _completion_claim(struct completion *completion)
165 {
166
167 KASSERT(mutex_owned(&completion->c_lock));
168 KASSERT(completion->c_done != 0);
169 if (completion->c_done > 0)
170 completion->c_done--;
171 else
172 KASSERT(completion->c_done == -1);
173 }
174
175 /*
176 * Wait interruptibly with a timeout for someone to call complete or
177 * complete_all.
178 */
179 static inline int
wait_for_completion_interruptible_timeout(struct completion * completion,unsigned long ticks)180 wait_for_completion_interruptible_timeout(struct completion *completion,
181 unsigned long ticks)
182 {
183 /* XXX Arithmetic overflow...? */
184 unsigned int start = hardclock_ticks, now;
185 int error;
186
187 mutex_enter(&completion->c_lock);
188
189 /* Wait until c_done is nonzero. */
190 while (completion->c_done == 0) {
191 error = cv_timedwait_sig(&completion->c_cv,
192 &completion->c_lock, ticks);
193 if (error)
194 goto out;
195 now = hardclock_ticks;
196 if (ticks < (now - start)) {
197 error = EWOULDBLOCK;
198 goto out;
199 }
200 ticks -= (now - start);
201 start = now;
202 }
203
204 /* Success! */
205 _completion_claim(completion);
206 error = 0;
207
208 out: mutex_exit(&completion->c_lock);
209 if (error == EWOULDBLOCK) {
210 return 0;
211 } else if ((error == EINTR) || (error == ERESTART)) {
212 return -ERESTARTSYS;
213 } else {
214 KASSERTMSG((error == 0), "error = %d", error);
215 return ticks;
216 }
217 }
218
219 /*
220 * Wait interruptibly for someone to call complete or complete_all.
221 */
222 static inline int
wait_for_completion_interruptible(struct completion * completion)223 wait_for_completion_interruptible(struct completion *completion)
224 {
225 int error;
226
227 mutex_enter(&completion->c_lock);
228
229 /* Wait until c_done is nonzero. */
230 while (completion->c_done == 0) {
231 error = cv_wait_sig(&completion->c_cv, &completion->c_lock);
232 if (error)
233 goto out;
234 }
235
236 /* Success! */
237 _completion_claim(completion);
238 error = 0;
239
240 out: mutex_exit(&completion->c_lock);
241 if ((error == EINTR) || (error == ERESTART))
242 error = -ERESTARTSYS;
243 return error;
244 }
245
246 /*
247 * Wait uninterruptibly, except by SIGKILL, for someone to call
248 * complete or complete_all.
249 *
250 * XXX In this implementation, any signal will actually wake us, not
251 * just SIGKILL.
252 */
253 static inline int
wait_for_completion_killable(struct completion * completion)254 wait_for_completion_killable(struct completion *completion)
255 {
256
257 return wait_for_completion_interruptible(completion);
258 }
259
260 /*
261 * Try to claim a completion immediately. Return true on success, false
262 * if it would block.
263 */
264 static inline bool
try_wait_for_completion(struct completion * completion)265 try_wait_for_completion(struct completion *completion)
266 {
267 bool ok;
268
269 mutex_enter(&completion->c_lock);
270 if (completion->c_done == 0) {
271 ok = false;
272 } else {
273 _completion_claim(completion);
274 ok = true;
275 }
276 mutex_exit(&completion->c_lock);
277
278 return ok;
279 }
280
281 #endif /* _LINUX_COMPLETION_H_ */
282