completion.h revision 1.6 1 /* $NetBSD: completion.h,v 1.6 2017/01/11 11:42:09 skrll 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
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 * re-initialize a completion object.
91 */
92 static inline void
93 reinit_completion(struct completion *completion)
94 {
95
96 completion->c_done = 0;
97 }
98
99 /*
100 * Destroy a completion object.
101 */
102 static inline void
103 destroy_completion(struct completion *completion)
104 {
105 KASSERT(!cv_has_waiters(&completion->c_cv));
106 cv_destroy(&completion->c_cv);
107 mutex_destroy(&completion->c_lock);
108 }
109
110 /*
111 * Notify one waiter of completion, but not any future ones.
112 */
113 static inline void
114 complete(struct completion *completion)
115 {
116
117 mutex_enter(&completion->c_lock);
118
119 /* If it's not open season, wake one waiter. */
120 if (completion->c_done >= 0) {
121 KASSERT(completion->c_done < INT_MAX); /* XXX check */
122 completion->c_done++;
123 cv_signal(&completion->c_cv);
124 } else {
125 KASSERT(completion->c_done == -1);
126 }
127
128 mutex_exit(&completion->c_lock);
129 }
130
131 /*
132 * Notify all waiters, present and future (until INIT_COMPLETION), of
133 * completion.
134 */
135 static inline void
136 complete_all(struct completion *completion)
137 {
138
139 mutex_enter(&completion->c_lock);
140
141 /* If it's not open season, make it open season and wake everyone. */
142 if (completion->c_done >= 0) {
143 completion->c_done = -1;
144 cv_broadcast(&completion->c_cv);
145 } else {
146 KASSERT(completion->c_done == -1);
147 }
148
149 mutex_exit(&completion->c_lock);
150 }
151
152 /*
153 * Reverse the effect of complete_all so that subsequent waiters block
154 * until someone calls complete or complete_all.
155 *
156 * This operation is very different from its lowercase counterpart.
157 *
158 * For some reason this works on the completion object itself, not on a
159 * pointer thereto, so it must be a macro.
160 */
161 #define INIT_COMPLETION(COMPLETION) INIT_COMPLETION_blorp(&(COMPLETION))
162
163 static inline void
164 INIT_COMPLETION_blorp(struct completion *completion)
165 {
166
167 mutex_enter(&completion->c_lock);
168 completion->c_done = 0;
169 /* No notify -- waiters are interested only in nonzero values. */
170 mutex_exit(&completion->c_lock);
171 }
172
173 static inline void
174 _completion_claim(struct completion *completion)
175 {
176
177 KASSERT(mutex_owned(&completion->c_lock));
178 KASSERT(completion->c_done != 0);
179 if (completion->c_done > 0)
180 completion->c_done--;
181 else
182 KASSERT(completion->c_done == -1);
183 }
184
185 /*
186 * Wait interruptibly with a timeout for someone to call complete or
187 * complete_all.
188 */
189 static inline int
190 wait_for_completion_interruptible_timeout(struct completion *completion,
191 unsigned long ticks)
192 {
193 /* XXX Arithmetic overflow...? */
194 unsigned int start = hardclock_ticks, now;
195 int error;
196
197 mutex_enter(&completion->c_lock);
198
199 /* Wait until c_done is nonzero. */
200 while (completion->c_done == 0) {
201 error = cv_timedwait_sig(&completion->c_cv,
202 &completion->c_lock, ticks);
203 if (error)
204 goto out;
205 now = hardclock_ticks;
206 if (ticks < (now - start)) {
207 error = EWOULDBLOCK;
208 goto out;
209 }
210 ticks -= (now - start);
211 start = now;
212 }
213
214 /* Success! */
215 _completion_claim(completion);
216 error = 0;
217
218 out: mutex_exit(&completion->c_lock);
219 if (error == EWOULDBLOCK) {
220 return 0;
221 } else if ((error == EINTR) || (error == ERESTART)) {
222 return -ERESTARTSYS;
223 } else {
224 KASSERTMSG((error == 0), "error = %d", error);
225 return ticks;
226 }
227 }
228
229 /*
230 * Wait interruptibly for someone to call complete or complete_all.
231 */
232 static inline int
233 wait_for_completion_interruptible(struct completion *completion)
234 {
235 int error;
236
237 mutex_enter(&completion->c_lock);
238
239 /* Wait until c_done is nonzero. */
240 while (completion->c_done == 0) {
241 error = cv_wait_sig(&completion->c_cv, &completion->c_lock);
242 if (error)
243 goto out;
244 }
245
246 /* Success! */
247 _completion_claim(completion);
248 error = 0;
249
250 out: mutex_exit(&completion->c_lock);
251 if ((error == EINTR) || (error == ERESTART))
252 error = -ERESTARTSYS;
253 return error;
254 }
255
256 /*
257 * Wait uninterruptibly, except by SIGKILL, for someone to call
258 * complete or complete_all.
259 *
260 * XXX In this implementation, any signal will actually wake us, not
261 * just SIGKILL.
262 */
263 static inline int
264 wait_for_completion_killable(struct completion *completion)
265 {
266
267 return wait_for_completion_interruptible(completion);
268 }
269
270 /*
271 * Try to claim a completion immediately. Return true on success, false
272 * if it would block.
273 */
274 static inline bool
275 try_wait_for_completion(struct completion *completion)
276 {
277 bool ok;
278
279 mutex_enter(&completion->c_lock);
280 if (completion->c_done == 0) {
281 ok = false;
282 } else {
283 _completion_claim(completion);
284 ok = true;
285 }
286 mutex_exit(&completion->c_lock);
287
288 return ok;
289 }
290
291 #endif /* _LINUX_COMPLETION_H_ */
292