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 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 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 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 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 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 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 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 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 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 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