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 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/lock.h> 33 #include <sys/malloc.h> 34 #include <sys/mutex.h> 35 #include <sys/proc.h> 36 #include <sys/signalvar.h> 37 #include <sys/sysent.h> 38 #include <sys/systm.h> 39 #include <sys/sysproto.h> 40 #include <sys/sx.h> 41 #include <sys/thr.h> 42 #include <sys/umtx.h> 43 44 struct umtx_q { 45 LIST_ENTRY(umtx_q) uq_next; /* Linked list for the hash. */ 46 TAILQ_HEAD(, thread) uq_tdq; /* List of threads blocked here. */ 47 struct umtx *uq_umtx; /* Pointer key component. */ 48 pid_t uq_pid; /* Pid key component. */ 49 }; 50 51 #define UMTX_QUEUES 128 52 #define UMTX_HASH(pid, umtx) \ 53 (((uintptr_t)pid + ((uintptr_t)umtx & ~65535)) % UMTX_QUEUES) 54 55 LIST_HEAD(umtx_head, umtx_q); 56 static struct umtx_head queues[UMTX_QUEUES]; 57 static MALLOC_DEFINE(M_UMTX, "umtx", "UMTX queue memory"); 58 59 static struct mtx umtx_lock; 60 MTX_SYSINIT(umtx, &umtx_lock, "umtx", MTX_DEF); 61 62 #define UMTX_LOCK() mtx_lock(&umtx_lock); 63 #define UMTX_UNLOCK() mtx_unlock(&umtx_lock); 64 65 66 static struct umtx_q *umtx_lookup(struct thread *, struct umtx *umtx); 67 static struct umtx_q *umtx_insert(struct thread *, struct umtx *umtx); 68 69 static struct umtx_q * 70 umtx_lookup(struct thread *td, struct umtx *umtx) 71 { 72 struct umtx_head *head; 73 struct umtx_q *uq; 74 pid_t pid; 75 76 pid = td->td_proc->p_pid; 77 78 head = &queues[UMTX_HASH(td->td_proc->p_pid, umtx)]; 79 80 LIST_FOREACH(uq, head, uq_next) { 81 if (uq->uq_pid == pid && uq->uq_umtx == umtx) 82 return (uq); 83 } 84 85 return (NULL); 86 } 87 88 /* 89 * Insert a thread onto the umtx queue. 90 */ 91 static struct umtx_q * 92 umtx_insert(struct thread *td, struct umtx *umtx) 93 { 94 struct umtx_head *head; 95 struct umtx_q *uq; 96 pid_t pid; 97 98 pid = td->td_proc->p_pid; 99 100 if ((uq = umtx_lookup(td, umtx)) == NULL) { 101 struct umtx_q *ins; 102 103 UMTX_UNLOCK(); 104 ins = malloc(sizeof(*uq), M_UMTX, M_ZERO | M_WAITOK); 105 UMTX_LOCK(); 106 107 /* 108 * Some one else could have succeeded while we were blocked 109 * waiting on memory. 110 */ 111 if ((uq = umtx_lookup(td, umtx)) == NULL) { 112 head = &queues[UMTX_HASH(pid, umtx)]; 113 uq = ins; 114 uq->uq_pid = pid; 115 uq->uq_umtx = umtx; 116 LIST_INSERT_HEAD(head, uq, uq_next); 117 TAILQ_INIT(&uq->uq_tdq); 118 } else 119 free(ins, M_UMTX); 120 } 121 122 /* 123 * Insert us onto the end of the TAILQ. 124 */ 125 TAILQ_INSERT_TAIL(&uq->uq_tdq, td, td_umtx); 126 127 return (uq); 128 } 129 130 static void 131 umtx_remove(struct umtx_q *uq, struct thread *td) 132 { 133 TAILQ_REMOVE(&uq->uq_tdq, td, td_umtx); 134 135 if (TAILQ_EMPTY(&uq->uq_tdq)) { 136 LIST_REMOVE(uq, uq_next); 137 free(uq, M_UMTX); 138 } 139 } 140 141 int 142 _umtx_lock(struct thread *td, struct _umtx_lock_args *uap) 143 /* struct umtx *umtx */ 144 { 145 struct umtx_q *uq; 146 struct umtx *umtx; 147 intptr_t owner; 148 intptr_t old; 149 int error; 150 151 uq = NULL; 152 153 /* 154 * Care must be exercised when dealing with this structure. It 155 * can fault on any access. 156 */ 157 umtx = uap->umtx; 158 159 for (;;) { 160 /* 161 * Try the uncontested case. This should be done in userland. 162 */ 163 owner = casuptr((intptr_t *)&umtx->u_owner, 164 UMTX_UNOWNED, (intptr_t)td); 165 166 /* The address was invalid. */ 167 if (owner == -1) 168 return (EFAULT); 169 170 /* The acquire succeeded. */ 171 if (owner == UMTX_UNOWNED) 172 return (0); 173 174 /* If no one owns it but it is contested try to acquire it. */ 175 if (owner == UMTX_CONTESTED) { 176 owner = casuptr((intptr_t *)&umtx->u_owner, 177 UMTX_CONTESTED, ((intptr_t)td | UMTX_CONTESTED)); 178 179 /* The address was invalid. */ 180 if (owner == -1) 181 return (EFAULT); 182 183 if (owner == UMTX_CONTESTED) 184 return (0); 185 186 /* If this failed the lock has changed, restart. */ 187 continue; 188 } 189 190 191 UMTX_LOCK(); 192 uq = umtx_insert(td, umtx); 193 UMTX_UNLOCK(); 194 195 /* 196 * Set the contested bit so that a release in user space 197 * knows to use the system call for unlock. If this fails 198 * either some one else has acquired the lock or it has been 199 * released. 200 */ 201 old = casuptr((intptr_t *)&umtx->u_owner, owner, 202 owner | UMTX_CONTESTED); 203 204 /* The address was invalid. */ 205 if (old == -1) { 206 UMTX_LOCK(); 207 umtx_remove(uq, td); 208 UMTX_UNLOCK(); 209 return (EFAULT); 210 } 211 212 /* 213 * We set the contested bit, sleep. Otherwise the lock changed 214 * and we need to retry or we lost a race to the thread 215 * unlocking the umtx. 216 */ 217 UMTX_LOCK(); 218 mtx_lock_spin(&sched_lock); 219 if (old == owner && (td->td_flags & TDF_UMTXWAKEUP) == 0) { 220 mtx_unlock_spin(&sched_lock); 221 error = msleep(td, &umtx_lock, 222 td->td_priority | PCATCH, "umtx", 0); 223 mtx_lock_spin(&sched_lock); 224 } else 225 error = 0; 226 td->td_flags &= ~TDF_UMTXWAKEUP; 227 mtx_unlock_spin(&sched_lock); 228 229 umtx_remove(uq, td); 230 UMTX_UNLOCK(); 231 232 /* 233 * If we caught a signal we might have to retry or exit 234 * immediately. 235 */ 236 if (error) 237 return (error); 238 } 239 240 return (0); 241 } 242 243 int 244 _umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap) 245 /* struct umtx *umtx */ 246 { 247 struct thread *blocked; 248 struct umtx *umtx; 249 struct umtx_q *uq; 250 intptr_t owner; 251 intptr_t old; 252 253 umtx = uap->umtx; 254 255 /* 256 * Make sure we own this mtx. 257 * 258 * XXX Need a {fu,su}ptr this is not correct on arch where 259 * sizeof(intptr_t) != sizeof(long). 260 */ 261 if ((owner = fuword(&umtx->u_owner)) == -1) 262 return (EFAULT); 263 264 if ((struct thread *)(owner & ~UMTX_CONTESTED) != td) 265 return (EPERM); 266 267 /* We should only ever be in here for contested locks */ 268 if ((owner & UMTX_CONTESTED) == 0) 269 return (EINVAL); 270 blocked = NULL; 271 272 /* 273 * When unlocking the umtx, it must be marked as unowned if 274 * there is zero or one thread only waiting for it. 275 * Otherwise, it must be marked as contested. 276 */ 277 UMTX_LOCK(); 278 uq = umtx_lookup(td, umtx); 279 if (uq == NULL || 280 (uq != NULL && (blocked = TAILQ_FIRST(&uq->uq_tdq)) != NULL && 281 TAILQ_NEXT(blocked, td_umtx) == NULL)) { 282 UMTX_UNLOCK(); 283 old = casuptr((intptr_t *)&umtx->u_owner, owner, 284 UMTX_UNOWNED); 285 if (old == -1) 286 return (EFAULT); 287 if (old != owner) 288 return (EINVAL); 289 290 /* 291 * Recheck the umtx queue to make sure another thread 292 * didn't put itself on it after it was unlocked. 293 */ 294 UMTX_LOCK(); 295 uq = umtx_lookup(td, umtx); 296 if (uq != NULL && 297 ((blocked = TAILQ_FIRST(&uq->uq_tdq)) != NULL && 298 TAILQ_NEXT(blocked, td_umtx) != NULL)) { 299 UMTX_UNLOCK(); 300 old = casuptr((intptr_t *)&umtx->u_owner, 301 UMTX_UNOWNED, UMTX_CONTESTED); 302 } else { 303 UMTX_UNLOCK(); 304 } 305 } else { 306 UMTX_UNLOCK(); 307 old = casuptr((intptr_t *)&umtx->u_owner, 308 owner, UMTX_CONTESTED); 309 if (old != -1 && old != owner) 310 return (EINVAL); 311 } 312 313 if (old == -1) 314 return (EFAULT); 315 316 /* 317 * If there is a thread waiting on the umtx, wake it up. 318 */ 319 if (blocked != NULL) { 320 mtx_lock_spin(&sched_lock); 321 blocked->td_flags |= TDF_UMTXWAKEUP; 322 mtx_unlock_spin(&sched_lock); 323 wakeup(blocked); 324 } 325 326 return (0); 327 } 328