xref: /freebsd/sys/kern/kern_umtx.c (revision e653b48c80fb85b2a10372d664a4b55dbdc51dae)
1 /*
2  * Copyright (c) 2004, David Xu <davidxu@freebsd.org>
3  * Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/limits.h>
34 #include <sys/lock.h>
35 #include <sys/malloc.h>
36 #include <sys/mutex.h>
37 #include <sys/proc.h>
38 #include <sys/sysent.h>
39 #include <sys/systm.h>
40 #include <sys/sysproto.h>
41 #include <sys/eventhandler.h>
42 #include <sys/thr.h>
43 #include <sys/umtx.h>
44 
45 #include <vm/vm.h>
46 #include <vm/vm_param.h>
47 #include <vm/pmap.h>
48 #include <vm/vm_map.h>
49 #include <vm/vm_object.h>
50 
51 #define UMTX_PRIVATE	0
52 #define UMTX_SHARED	1
53 
54 #define UMTX_STATIC_SHARED
55 
56 struct umtx_key {
57 	int	type;
58 	union {
59 		struct {
60 			vm_object_t	object;
61 			long		offset;
62 		} shared;
63 		struct {
64 			struct umtx	*umtx;
65 			long		pid;
66 		} private;
67 		struct {
68 			void		*ptr;
69 			long		word;
70 		} both;
71 	} info;
72 };
73 
74 struct umtx_q {
75 	LIST_ENTRY(umtx_q)	uq_next;	/* Linked list for the hash. */
76 	struct umtx_key		uq_key;		/* Umtx key. */
77 	struct thread		*uq_thread;	/* The thread waits on */
78 	LIST_ENTRY(umtx_q)	uq_rqnext;	/* Linked list for requeuing. */
79 	vm_offset_t		uq_addr;	/* Umtx's virtual address. */
80 };
81 
82 LIST_HEAD(umtx_head, umtx_q);
83 struct umtxq_chain {
84 	struct mtx		uc_lock;	/* Lock for this chain. */
85 	struct umtx_head	uc_queue;	/* List of sleep queues. */
86 };
87 
88 #define	GOLDEN_RATIO_PRIME	2654404609U
89 #define	UMTX_CHAINS		128
90 #define	UMTX_SHIFTS		(__WORD_BIT - 7)
91 
92 static struct umtxq_chain umtxq_chains[UMTX_CHAINS];
93 static MALLOC_DEFINE(M_UMTX, "umtx", "UMTX queue memory");
94 
95 #define	UMTX_CONTESTED	LONG_MIN
96 
97 static void umtxq_init_chains(void *);
98 static int umtxq_hash(struct umtx_key *key);
99 static struct mtx *umtxq_mtx(int chain);
100 static void umtxq_lock(struct umtx_key *key);
101 static void umtxq_unlock(struct umtx_key *key);
102 static void umtxq_insert(struct umtx_q *uq);
103 static void umtxq_remove(struct umtx_q *uq);
104 static int umtxq_sleep(struct thread *td, struct umtx_key *key,
105 	int prio, const char *wmesg, int timo);
106 static int  umtxq_count(struct umtx_key *key);
107 static void umtxq_signal(struct umtx_key *key);
108 static void umtxq_broadcast(struct umtx_key *key);
109 #ifdef UMTX_DYNAMIC_SHARED
110 static void fork_handler(void *arg, struct proc *p1, struct proc *p2,
111 	int flags);
112 #endif
113 static int umtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2);
114 static int umtx_key_get(struct thread *td, struct umtx *umtx,
115 	struct umtx_key *key);
116 static void umtx_key_release(struct umtx_key *key);
117 
118 SYSINIT(umtx, SI_SUB_EVENTHANDLER+1, SI_ORDER_MIDDLE, umtxq_init_chains, NULL);
119 
120 static void
121 umtxq_init_chains(void *arg __unused)
122 {
123 	int i;
124 
125 	for (i = 0; i < UMTX_CHAINS; ++i) {
126 		mtx_init(&umtxq_chains[i].uc_lock, "umtxq_lock", NULL,
127 			 MTX_DEF | MTX_DUPOK);
128 		LIST_INIT(&umtxq_chains[i].uc_queue);
129 	}
130 #ifdef UMTX_DYNAMIC_SHARED
131 	EVENTHANDLER_REGISTER(process_fork, fork_handler, 0, 10000);
132 #endif
133 }
134 
135 static inline int
136 umtxq_hash(struct umtx_key *key)
137 {
138 	unsigned n = (uintptr_t)key->info.both.ptr + key->info.both.word;
139 	return (((n * GOLDEN_RATIO_PRIME) >> UMTX_SHIFTS) % UMTX_CHAINS);
140 }
141 
142 static inline int
143 umtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2)
144 {
145 	return (k1->type == k2->type &&
146 		k1->info.both.ptr == k2->info.both.ptr &&
147 	        k1->info.both.word == k2->info.both.word);
148 }
149 
150 static inline struct mtx *
151 umtxq_mtx(int chain)
152 {
153 	return (&umtxq_chains[chain].uc_lock);
154 }
155 
156 static inline void
157 umtxq_lock(struct umtx_key *key)
158 {
159 	int chain = umtxq_hash(key);
160 	mtx_lock(umtxq_mtx(chain));
161 }
162 
163 static inline void
164 umtxq_unlock(struct umtx_key *key)
165 {
166 	int chain = umtxq_hash(key);
167 	mtx_unlock(umtxq_mtx(chain));
168 }
169 
170 /*
171  * Insert a thread onto the umtx queue.
172  */
173 static inline void
174 umtxq_insert(struct umtx_q *uq)
175 {
176 	struct umtx_head *head;
177 	int chain = umtxq_hash(&uq->uq_key);
178 
179 	head = &umtxq_chains[chain].uc_queue;
180 	LIST_INSERT_HEAD(head, uq, uq_next);
181 	uq->uq_thread->td_umtxq = uq;
182 	mtx_lock_spin(&sched_lock);
183 	uq->uq_thread->td_flags |= TDF_UMTXQ;
184 	mtx_unlock_spin(&sched_lock);
185 }
186 
187 /*
188  * Remove thread from the umtx queue.
189  */
190 static inline void
191 umtxq_remove(struct umtx_q *uq)
192 {
193 	if (uq->uq_thread->td_flags & TDF_UMTXQ) {
194 		LIST_REMOVE(uq, uq_next);
195 		uq->uq_thread->td_umtxq = NULL;
196 		/* turning off TDF_UMTXQ should be the last thing. */
197 		mtx_lock_spin(&sched_lock);
198 		uq->uq_thread->td_flags &= ~TDF_UMTXQ;
199 		mtx_unlock_spin(&sched_lock);
200 	}
201 }
202 
203 static int
204 umtxq_count(struct umtx_key *key)
205 {
206 	struct umtx_q *uq;
207 	struct umtx_head *head;
208 	int chain, count = 0;
209 
210 	chain = umtxq_hash(key);
211 	umtxq_lock(key);
212 	head = &umtxq_chains[chain].uc_queue;
213 	LIST_FOREACH(uq, head, uq_next) {
214 		if (umtx_key_match(&uq->uq_key, key)) {
215 			if (++count > 1)
216 				break;
217 		}
218 	}
219 	umtxq_unlock(key);
220 	return (count);
221 }
222 
223 static void
224 umtxq_signal(struct umtx_key *key)
225 {
226 	struct umtx_q *uq;
227 	struct umtx_head *head;
228 	struct thread *blocked = NULL;
229 	int chain;
230 
231 	chain = umtxq_hash(key);
232 	umtxq_lock(key);
233 	head = &umtxq_chains[chain].uc_queue;
234 	LIST_FOREACH(uq, head, uq_next) {
235 		if (umtx_key_match(&uq->uq_key, key)) {
236 			blocked = uq->uq_thread;
237 			umtxq_remove(uq);
238 			break;
239 		}
240 	}
241 	umtxq_unlock(key);
242 	if (blocked != NULL)
243 		wakeup(blocked);
244 }
245 
246 static void
247 umtxq_broadcast(struct umtx_key *key)
248 {
249 	struct umtx_q *uq, *next;
250 	struct umtx_head *head;
251 	struct thread *blocked;
252 	int chain;
253 
254 	chain = umtxq_hash(key);
255 	umtxq_lock(key);
256 	head = &umtxq_chains[chain].uc_queue;
257 	for (uq = LIST_FIRST(head); uq != NULL; uq = next) {
258 		next = LIST_NEXT(uq, uq_next);
259 		if (umtx_key_match(&uq->uq_key, key)) {
260 			blocked = uq->uq_thread;
261 			umtxq_remove(uq);
262 			wakeup(blocked);
263 		}
264 		uq = next;
265 	}
266 	umtxq_unlock(key);
267 }
268 
269 static inline int
270 umtxq_sleep(struct thread *td, struct umtx_key *key, int priority,
271 	    const char *wmesg, int timo)
272 {
273 	int error;
274 	int chain = umtxq_hash(key);
275 
276 	error = msleep(td, umtxq_mtx(chain), priority, wmesg, timo);
277 	return (error);
278 }
279 
280 static int
281 umtx_key_get(struct thread *td, struct umtx *umtx, struct umtx_key *key)
282 {
283 #if defined(UMTX_DYNAMIC_SHARED) || defined(UMTX_STATIC_SHARED)
284 	vm_map_t map;
285 	vm_map_entry_t entry;
286 	vm_pindex_t pindex;
287 	vm_prot_t prot;
288 	boolean_t wired;
289 
290 	map = &td->td_proc->p_vmspace->vm_map;
291 	if (vm_map_lookup(&map, (vm_offset_t)umtx, VM_PROT_WRITE,
292 	    &entry, &key->info.shared.object, &pindex, &prot,
293 	    &wired) != KERN_SUCCESS) {
294 		return EFAULT;
295 	}
296 #endif
297 
298 #if defined(UMTX_DYNAMIC_SHARED)
299 	key->type = UMTX_SHARED;
300 	key->info.shared.offset = entry->offset + entry->start -
301 		(vm_offset_t)umtx;
302 	/*
303 	 * Add object reference, if we don't do this, a buggy application
304 	 * deallocates the object, the object will be reused by other
305 	 * applications, then unlock will wake wrong thread.
306 	 */
307 	vm_object_reference(key->info.shared.object);
308 	vm_map_lookup_done(map, entry);
309 #elif defined(UMTX_STATIC_SHARED)
310 	if (VM_INHERIT_SHARE == entry->inheritance) {
311 		key->type = UMTX_SHARED;
312 		key->info.shared.offset = entry->offset + entry->start -
313 			(vm_offset_t)umtx;
314 		vm_object_reference(key->info.shared.object);
315 	} else {
316 		key->type = UMTX_PRIVATE;
317 		key->info.private.umtx = umtx;
318 		key->info.private.pid  = td->td_proc->p_pid;
319 	}
320 	vm_map_lookup_done(map, entry);
321 #else
322 	key->type = UMTX_PRIVATE;
323 	key->info.private.umtx = umtx;
324 	key->info.private.pid  = td->td_proc->p_pid;
325 #endif
326 	return (0);
327 }
328 
329 static inline void
330 umtx_key_release(struct umtx_key *key)
331 {
332 	if (key->type == UMTX_SHARED)
333 		vm_object_deallocate(key->info.shared.object);
334 }
335 
336 static inline int
337 umtxq_queue_me(struct thread *td, struct umtx *umtx, struct umtx_q *uq)
338 {
339 	int error;
340 
341 	if ((error = umtx_key_get(td, umtx, &uq->uq_key)) != 0)
342 		return (error);
343 
344 	uq->uq_addr = (vm_offset_t)umtx;
345 	uq->uq_thread = td;
346 	umtxq_lock(&uq->uq_key);
347 	umtxq_insert(uq);
348 	umtxq_unlock(&uq->uq_key);
349 	return (0);
350 }
351 
352 #if defined(UMTX_DYNAMIC_SHARED)
353 static void
354 fork_handler(void *arg, struct proc *p1, struct proc *p2, int flags)
355 {
356 	vm_map_t map;
357 	vm_map_entry_t entry;
358 	vm_object_t object;
359 	vm_pindex_t pindex;
360 	vm_prot_t prot;
361 	boolean_t wired;
362 	struct umtx_key key;
363 	LIST_HEAD(, umtx_q) workq;
364 	struct umtx_q *uq;
365 	struct thread *td;
366 	int onq;
367 
368 	LIST_INIT(&workq);
369 
370 	/* Collect threads waiting on umtxq */
371 	PROC_LOCK(p1);
372 	FOREACH_THREAD_IN_PROC(p1, td) {
373 		if (td->td_flags & TDF_UMTXQ) {
374 			uq = td->td_umtxq;
375 			if (uq)
376 				LIST_INSERT_HEAD(&workq, uq, uq_rqnext);
377 		}
378 	}
379 	PROC_UNLOCK(p1);
380 
381 	LIST_FOREACH(uq, &workq, uq_rqnext) {
382 		map = &p1->p_vmspace->vm_map;
383 		if (vm_map_lookup(&map, uq->uq_addr, VM_PROT_WRITE,
384 		    &entry, &object, &pindex, &prot, &wired) != KERN_SUCCESS) {
385 			continue;
386 		}
387 		key.type = UMTX_SHARED;
388 		key.info.shared.object = object;
389 		key.info.shared.offset = entry->offset + entry->start -
390 			uq->uq_addr;
391 		if (umtx_key_match(&key, &uq->uq_key)) {
392 			vm_map_lookup_done(map, entry);
393 			continue;
394 		}
395 
396 		umtxq_lock(&uq->uq_key);
397 		if (uq->uq_thread->td_flags & TDF_UMTXQ) {
398 			umtxq_remove(uq);
399 			onq = 1;
400 		} else
401 			onq = 0;
402 		umtxq_unlock(&uq->uq_key);
403 		if (onq) {
404 			vm_object_deallocate(uq->uq_key.info.shared.object);
405 			uq->uq_key = key;
406 			umtxq_lock(&uq->uq_key);
407 			umtxq_insert(uq);
408 			umtxq_unlock(&uq->uq_key);
409 			vm_object_reference(uq->uq_key.info.shared.object);
410 		}
411 		vm_map_lookup_done(map, entry);
412 	}
413 }
414 #endif
415 
416 static int
417 _do_lock(struct thread *td, struct umtx *umtx, long id, int timo)
418 {
419 	struct umtx_q uq;
420 	intptr_t owner;
421 	intptr_t old;
422 	int error = 0;
423 
424 	/*
425 	 * Care must be exercised when dealing with umtx structure.  It
426 	 * can fault on any access.
427 	 */
428 
429 	for (;;) {
430 		/*
431 		 * Try the uncontested case.  This should be done in userland.
432 		 */
433 		owner = casuptr((intptr_t *)&umtx->u_owner,
434 		    UMTX_UNOWNED, id);
435 
436 		/* The acquire succeeded. */
437 		if (owner == UMTX_UNOWNED)
438 			return (0);
439 
440 		/* The address was invalid. */
441 		if (owner == -1)
442 			return (EFAULT);
443 
444 		/* If no one owns it but it is contested try to acquire it. */
445 		if (owner == UMTX_CONTESTED) {
446 			owner = casuptr((intptr_t *)&umtx->u_owner,
447 			    UMTX_CONTESTED, id | UMTX_CONTESTED);
448 
449 			if (owner == UMTX_CONTESTED)
450 				return (0);
451 
452 			/* The address was invalid. */
453 			if (owner == -1)
454 				return (EFAULT);
455 
456 			/* If this failed the lock has changed, restart. */
457 			continue;
458 		}
459 
460 		/*
461 		 * If we caught a signal, we have retried and now
462 		 * exit immediately.
463 		 */
464 		if (error || (error = umtxq_queue_me(td, umtx, &uq)) != 0)
465 			return (error);
466 
467 		/*
468 		 * Set the contested bit so that a release in user space
469 		 * knows to use the system call for unlock.  If this fails
470 		 * either some one else has acquired the lock or it has been
471 		 * released.
472 		 */
473 		old = casuptr((intptr_t *)&umtx->u_owner, owner,
474 		    owner | UMTX_CONTESTED);
475 
476 		/* The address was invalid. */
477 		if (old == -1) {
478 			umtxq_lock(&uq.uq_key);
479 			umtxq_remove(&uq);
480 			umtxq_unlock(&uq.uq_key);
481 			umtx_key_release(&uq.uq_key);
482 			return (EFAULT);
483 		}
484 
485 		/*
486 		 * We set the contested bit, sleep. Otherwise the lock changed
487 		 * and we need to retry or we lost a race to the thread
488 		 * unlocking the umtx.
489 		 */
490 		umtxq_lock(&uq.uq_key);
491 		if (old == owner && (td->td_flags & TDF_UMTXQ)) {
492 			error = umtxq_sleep(td, &uq.uq_key,
493 				       td->td_priority | PCATCH | PDROP,
494 				       "umtx", timo);
495 			if (td->td_flags & TDF_UMTXQ) {
496 				umtxq_lock(&uq.uq_key);
497 				umtxq_remove(&uq);
498 				umtxq_unlock(&uq.uq_key);
499 			}
500 		} else {
501 			umtxq_remove(&uq);
502 			umtxq_unlock(&uq.uq_key);
503 			error = 0;
504 		}
505 		umtx_key_release(&uq.uq_key);
506 	}
507 
508 	return (0);
509 }
510 
511 static int
512 do_lock(struct thread *td, struct umtx *umtx, long id,
513 	struct timespec *abstime)
514 {
515 	struct timespec ts1, ts2;
516 	struct timeval tv;
517 	int timo, error;
518 
519 	if (abstime == NULL) {
520 		error = _do_lock(td, umtx, id, 0);
521 	} else {
522 		for (;;) {
523 			ts1 = *abstime;
524 			getnanotime(&ts2);
525 			timespecsub(&ts1, &ts2);
526 			TIMESPEC_TO_TIMEVAL(&tv, &ts1);
527 			if (tv.tv_sec < 0) {
528 				error = EWOULDBLOCK;
529 				break;
530 			}
531 			timo = tvtohz(&tv);
532 			error = _do_lock(td, umtx, id, timo);
533 			if (error != EWOULDBLOCK) {
534 				if (error == ERESTART)
535 					error = EINTR;
536 				break;
537 			}
538 		}
539 	}
540 	return (error);
541 }
542 
543 static int
544 do_unlock(struct thread *td, struct umtx *umtx, long id)
545 {
546 	struct umtx_key key;
547 	intptr_t owner;
548 	intptr_t old;
549 	int count, error;
550 
551 	/*
552 	 * Make sure we own this mtx.
553 	 *
554 	 * XXX Need a {fu,su}ptr this is not correct on arch where
555 	 * sizeof(intptr_t) != sizeof(long).
556 	 */
557 	if ((owner = fuword(&umtx->u_owner)) == -1)
558 		return (EFAULT);
559 
560 	if ((owner & ~UMTX_CONTESTED) != id)
561 		return (EPERM);
562 
563 	/* We should only ever be in here for contested locks */
564 	if ((owner & UMTX_CONTESTED) == 0)
565 		return (EINVAL);
566 
567 	/*
568 	 * When unlocking the umtx, it must be marked as unowned if
569 	 * there is zero or one thread only waiting for it.
570 	 * Otherwise, it must be marked as contested.
571 	 */
572 	old = casuptr((intptr_t *)&umtx->u_owner, owner, UMTX_UNOWNED);
573 	if (old == -1)
574 		return (EFAULT);
575 	if (old != owner)
576 		return (EINVAL);
577 
578 	if ((error = umtx_key_get(td, umtx, &key)) != 0)
579 		return (error);
580 
581 	/*
582 	 * At the point, a new thread can lock the umtx before we
583 	 * reach here, so contested bit will not be set, if there
584 	 * are two or more threads on wait queue, we should set
585 	 * contensted bit for them.
586 	 */
587 	count = umtxq_count(&key);
588 	if (count <= 0) {
589 		umtx_key_release(&key);
590 		return (0);
591 	}
592 
593 	/*
594 	 * If there is second thread waiting on umtx, set contested bit,
595 	 * if they are resumed before we reach here, it is harmless,
596 	 * just a bit unefficient.
597 	 */
598 	if (count > 1) {
599 		owner = UMTX_UNOWNED;
600 		for (;;) {
601 			old = casuptr((intptr_t *)&umtx->u_owner, owner,
602 				    owner | UMTX_CONTESTED);
603 			if (old == owner)
604 				break;
605 			if (old == -1) {
606 				umtx_key_release(&key);
607 				return (EFAULT);
608 			}
609 			owner = old;
610 		}
611 		/*
612 		 * Another thread locked the umtx before us, so don't bother
613 		 * to wake more threads, that thread will do it when it unlocks
614 		 * the umtx.
615 		 */
616 		if ((owner & ~UMTX_CONTESTED) != 0) {
617 			umtx_key_release(&key);
618 			return (0);
619 		}
620 	}
621 
622 	/* Wake blocked thread. */
623 	umtxq_signal(&key);
624 	umtx_key_release(&key);
625 
626 	return (0);
627 }
628 
629 static int
630 do_unlock_and_wait(struct thread *td, struct umtx *umtx, long id, void *uaddr,
631 	struct timespec *abstime)
632 {
633 	struct umtx_q uq;
634 	intptr_t owner;
635 	intptr_t old;
636 	struct timespec ts1, ts2;
637 	struct timeval tv;
638 	int timo, error = 0;
639 
640 	if (umtx == uaddr)
641 		return (EINVAL);
642 
643 	/*
644 	 * Make sure we own this mtx.
645 	 *
646 	 * XXX Need a {fu,su}ptr this is not correct on arch where
647 	 * sizeof(intptr_t) != sizeof(long).
648 	 */
649 	if ((owner = fuword(&umtx->u_owner)) == -1)
650 		return (EFAULT);
651 
652 	if ((owner & ~UMTX_CONTESTED) != id)
653 		return (EPERM);
654 
655 	if ((error = umtxq_queue_me(td, uaddr, &uq)) != 0)
656 		return (error);
657 
658 	old = casuptr((intptr_t *)&umtx->u_owner, id, UMTX_UNOWNED);
659 	if (old == -1) {
660 		umtxq_lock(&uq.uq_key);
661 		umtxq_remove(&uq);
662 		umtxq_unlock(&uq.uq_key);
663 		umtx_key_release(&uq.uq_key);
664 		return (EFAULT);
665 	}
666 	if (old != id) {
667 		error = do_unlock(td, umtx, id);
668 		if (error) {
669 			umtxq_lock(&uq.uq_key);
670 			umtxq_remove(&uq);
671 			umtxq_unlock(&uq.uq_key);
672 			umtx_key_release(&uq.uq_key);
673 			return (error);
674 		}
675 	}
676 	if (abstime == NULL) {
677 		umtxq_lock(&uq.uq_key);
678 		if (td->td_flags & TDF_UMTXQ)
679 			error = umtxq_sleep(td, &uq.uq_key,
680 			       td->td_priority | PCATCH, "ucond", 0);
681 		umtxq_remove(&uq);
682 		umtxq_unlock(&uq.uq_key);
683 		if (error == ERESTART)
684 			error = EINTR;
685 	} else {
686 		for (;;) {
687 			ts1 = *abstime;
688 			getnanotime(&ts2);
689 			timespecsub(&ts1, &ts2);
690 			TIMESPEC_TO_TIMEVAL(&tv, &ts1);
691 			if (tv.tv_sec < 0) {
692 				error = EWOULDBLOCK;
693 				break;
694 			}
695 			timo = tvtohz(&tv);
696 			umtxq_lock(&uq.uq_key);
697 			if (td->td_flags & TDF_UMTXQ) {
698 				error = umtxq_sleep(td, &uq.uq_key,
699 					    td->td_priority | PCATCH | PDROP,
700 					    "ucond", timo);
701 				if (!(td->td_flags & TDF_UMTXQ)) {
702 					error = 0;
703 					break;
704 				}
705 				if (error != 0 && error != EWOULDBLOCK) {
706 					if (error == ERESTART)
707 						error = EINTR;
708 					break;
709 				}
710 			} else {
711 				umtxq_unlock(&uq.uq_key);
712 				error = 0;
713 				break;
714 			}
715 		}
716 		if (td->td_flags & TDF_UMTXQ) {
717 			umtxq_lock(&uq.uq_key);
718 			umtxq_remove(&uq);
719 			umtxq_unlock(&uq.uq_key);
720 		}
721 	}
722 	umtx_key_release(&uq.uq_key);
723 	return (error);
724 }
725 
726 static int
727 do_wake(struct thread *td, void *uaddr, int broadcast)
728 {
729 	struct umtx_key key;
730 	int error;
731 
732 	if ((error = umtx_key_get(td, uaddr, &key)) != 0)
733 		return (error);
734 	if (!broadcast)
735 		umtxq_signal(&key);
736 	else
737 		umtxq_broadcast(&key);
738 	umtx_key_release(&key);
739 	return (0);
740 }
741 
742 int
743 _umtx_lock(struct thread *td, struct _umtx_lock_args *uap)
744     /* struct umtx *umtx */
745 {
746 	return _do_lock(td, uap->umtx, td->td_tid, 0);
747 }
748 
749 int
750 _umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap)
751     /* struct umtx *umtx */
752 {
753 	return do_unlock(td, uap->umtx, td->td_tid);
754 }
755 
756 int
757 _umtx_op(struct thread *td, struct _umtx_op_args *uap)
758 {
759 	struct timespec abstime;
760 	struct timespec *ts;
761 	int error;
762 
763 	switch(uap->op) {
764 	case UMTX_OP_LOCK:
765 		/* Allow a null timespec (wait forever). */
766 		if (uap->abstime == NULL)
767 			ts = NULL;
768 		else {
769 			error = copyin(uap->abstime, &abstime, sizeof(abstime));
770 			if (error != 0)
771 				return (error);
772 			if (abstime.tv_nsec >= 1000000000 ||
773 			    abstime.tv_nsec < 0)
774 				return (EINVAL);
775 			ts = &abstime;
776 		}
777 		return do_lock(td, uap->umtx, uap->id, ts);
778 	case UMTX_OP_UNLOCK:
779 		return do_unlock(td, uap->umtx, uap->id);
780 	case UMTX_OP_UNLOCK_AND_WAIT:
781 		/* Allow a null timespec (wait forever). */
782 		if (uap->abstime == NULL)
783 			ts = NULL;
784 		else {
785 			error = copyin(uap->abstime, &abstime, sizeof(abstime));
786 			if (error != 0)
787 				return (error);
788 			if (abstime.tv_nsec >= 1000000000 ||
789 			    abstime.tv_nsec < 0)
790 				return (EINVAL);
791 			ts = &abstime;
792 		}
793 		return do_unlock_and_wait(td, uap->umtx, uap->id,
794 					  uap->uaddr, ts);
795 	case UMTX_OP_WAKE:
796 		return do_wake(td, uap->uaddr, uap->id);
797 	default:
798 		return (EINVAL);
799 	}
800 }
801