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
57struct 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 */
80static inline void
81init_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 */
92static inline void
93destroy_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 */
103static inline void
104complete(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 */
125static inline void
126complete_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
153static inline void
154INIT_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
163static inline void
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 */
179static inline int
180wait_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
208out: 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 */
222static inline int
223wait_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
240out: 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 */
253static inline int
254wait_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 */
264static inline bool
265try_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