kern_umtx.c revision 115310
1/* 2 * Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/kern/kern_umtx.c 115310 2003-05-25 18:18:32Z jeff $ 27 * 28 */ 29 30#include <sys/param.h> 31#include <sys/kernel.h> 32#include <sys/lock.h> 33#include <sys/mutex.h> 34#include <sys/proc.h> 35#include <sys/signalvar.h> 36#include <sys/sysent.h> 37#include <sys/systm.h> 38#include <sys/sysproto.h> 39#include <sys/thr.h> 40#include <sys/umtx.h> 41 42#define UMTX_LOCK() mtx_lock(&umtx_lock); 43#define UMTX_UNLOCK() mtx_unlock(&umtx_lock); 44 45struct mtx umtx_lock; 46 47MTX_SYSINIT(umtx, &umtx_lock, "User-land mutex lock", MTX_DEF); 48 49int 50_umtx_lock(struct thread *td, struct _umtx_lock_args *uap) 51 /* struct umtx *umtx */ 52{ 53 struct umtx *umtx; 54 struct thread *blocked; 55 intptr_t owner; 56 intptr_t old; 57 int error; 58 59 error = 0; 60 61 /* 62 * Care must be exercised when dealing with this structure. It 63 * can fault on any access. 64 */ 65 umtx = uap->umtx; 66 67 UMTX_LOCK(); 68 69 for (;;) { 70 /* 71 * Try the uncontested case. This should be done in userland. 72 */ 73 owner = casuptr((intptr_t *)&umtx->u_owner, 74 UMTX_UNOWNED, (intptr_t)td); 75 76 /* The acquire succeeded. */ 77 if (owner == UMTX_UNOWNED) { 78 error = 0; 79 goto out; 80 } 81 82 /* The address was invalid. */ 83 if (owner == -1) { 84 error = EFAULT; 85 goto out; 86 } 87 88 if (owner & UMTX_CONTESTED) 89 break; 90 91 /* 92 * Set the contested bit so that a release in user space 93 * knows to use the system call for unlock. If this fails 94 * either some one else has acquired the lock or it has been 95 * released. 96 */ 97 old = casuptr((intptr_t *)&umtx->u_owner, owner, 98 owner | UMTX_CONTESTED); 99 100 /* We set the contested bit. */ 101 if (old == owner) 102 break; 103 104 /* The address was invalid. */ 105 if (old == -1) { 106 error = EFAULT; 107 goto out; 108 } 109 /* We didn't set the contested bit, try again. */ 110 } 111 112 /* 113 * We are now protected from further races via umtx_lock. 114 * If userland messes with their mutex without using cmpset 115 * they will deadlock themselves but they will still be 116 * killable via signals. 117 */ 118 119 if ((owner = fuword(&umtx->u_blocked)) == -1) { 120 error = EFAULT; 121 goto out; 122 } 123 124 if (owner == UMTX_UNOWNED) { 125 if (suword(&umtx->u_blocked, (long)td) == -1) { 126 error = EFAULT; 127 goto out; 128 } 129 /* 130 * Other blocked threads will reside here. 131 */ 132 STAILQ_INIT(&td->td_umtxq); 133 } else { 134 FOREACH_THREAD_IN_PROC(td->td_proc, blocked) 135 if (blocked == (struct thread *)(owner)) 136 break; 137 138 if (blocked == NULL) { 139 error = EINVAL; 140 goto out; 141 } 142 /* 143 * Insert us onto the end of the TAILQ. 144 */ 145 STAILQ_INSERT_TAIL(&blocked->td_umtxq, td, td_umtx); 146 } 147 148 for (;;) { 149 /* 150 * Sleep until we can acquire the lock. We must still deliver 151 * signals so that they are not deferred until we acquire the 152 * lock which may be never. The threads actual priority is 153 * used to maintain proper ordering. 154 */ 155 156 error = msleep(&td->td_umtx, &umtx_lock, 157 td->td_priority | PCATCH, "umtx", 0); 158 159 /* 160 * When we are woken up we need to see if we now own the lock 161 * even if a signal was delivered. 162 */ 163 if ((owner = fuword(&umtx->u_owner)) == -1) { 164 error = EFAULT; 165 break; 166 } 167 owner &= ~UMTX_CONTESTED; 168 if ((struct thread *)owner == td) { 169 error = 0; 170 break; 171 } 172 173 /* 174 * We may have signals to deliver. 175 */ 176 if (error) 177 break; 178 } 179 180out: 181 UMTX_UNLOCK(); 182 183 return (error); 184} 185 186int 187_umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap) 188 /* struct umtx *umtx */ 189{ 190 struct thread *td0; 191 struct umtx *umtx; 192 intptr_t owner; 193 intptr_t blocked; 194 intptr_t old; 195 int error; 196 197 error = 0; 198 umtx = uap->umtx; 199 200 UMTX_LOCK(); 201 202 /* 203 * Make sure we own this mtx. 204 * 205 * XXX Need a {fu,su}ptr this is not correct on arch where 206 * sizeof(intptr_t) != sizeof(long). 207 */ 208 if ((owner = fuword(&umtx->u_owner)) == -1) { 209 error = EFAULT; 210 goto out; 211 } 212 if ((struct thread *)(owner & ~UMTX_CONTESTED) != td) { 213 error = EPERM; 214 goto out; 215 } 216 /* 217 * If we own it but it isn't contested then we can just release and 218 * return. 219 */ 220 if ((owner & UMTX_CONTESTED) == 0) { 221 owner = casuptr((intptr_t *)&umtx->u_owner, 222 (intptr_t)td, UMTX_UNOWNED); 223 224 if (owner == -1) 225 error = EFAULT; 226 /* 227 * If this failed someone modified the memory without going 228 * through this api. 229 */ 230 else if (owner != (intptr_t)td) 231 error = EINVAL; 232 else 233 error = 0; 234 235 goto out; 236 } 237 238 /* 239 * Since we own the mutex and the proc lock we are free to inspect 240 * the blocked queue. It must have one valid entry since the 241 * CONTESTED bit was set. 242 */ 243 blocked = fuword(&umtx->u_blocked); 244 if (blocked == -1) { 245 error = EFAULT; 246 goto out; 247 } 248 if (blocked == 0) { 249 error = EINVAL; 250 goto out; 251 } 252 253 FOREACH_THREAD_IN_PROC(td->td_proc, td0) 254 if (td0 == (struct thread *)blocked) 255 break; 256 257 if (td0 == NULL) { 258 error = EINVAL; 259 goto out; 260 } 261 262 if (!STAILQ_EMPTY(&td0->td_umtxq)) { 263 struct thread *next; 264 265 blocked |= UMTX_CONTESTED; 266 next = STAILQ_FIRST(&td0->td_umtxq); 267 if (suword(&umtx->u_blocked, (long)next) == -1) { 268 error = EFAULT; 269 goto out; 270 } 271 STAILQ_REMOVE_HEAD(&td0->td_umtxq, td_umtx); 272 273 /* 274 * Switch the queue over to the next blocked thread. 275 */ 276 if (!STAILQ_EMPTY(&td0->td_umtxq)) { 277 next->td_umtxq = td0->td_umtxq; 278 STAILQ_INIT(&td0->td_umtxq); 279 } else 280 STAILQ_INIT(&next->td_umtxq); 281 } else { 282 if (suword(&umtx->u_blocked, UMTX_UNOWNED) == -1) { 283 error = EFAULT; 284 goto out; 285 } 286 } 287 /* 288 * Now directly assign this mutex to the first thread that was 289 * blocked on it. 290 */ 291 old = casuptr((intptr_t *)&umtx->u_owner, owner, blocked); 292 293 /* 294 * This will only happen if someone modifies the lock without going 295 * through this api. 296 */ 297 if (old != owner) { 298 error = EINVAL; 299 goto out; 300 } 301 if (old == -1) { 302 error = EFAULT; 303 goto out; 304 } 305 /* Success. */ 306 error = 0; 307 wakeup(&td0->td_umtx); 308 309out: 310 UMTX_UNLOCK(); 311 312 return (error); 313} 314