xref: /freebsd/sys/kern/kern_umtx.c (revision c0b9f4fe659b6839541970eb5675e57f4d814969)
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 #define	UCF_BUSY		0x01
87 #define	UCF_WANT		0x02
88 	int			uc_flags;
89 };
90 
91 #define	GOLDEN_RATIO_PRIME	2654404609U
92 #define	UMTX_CHAINS		128
93 #define	UMTX_SHIFTS		(__WORD_BIT - 7)
94 
95 static struct umtxq_chain umtxq_chains[UMTX_CHAINS];
96 static MALLOC_DEFINE(M_UMTX, "umtx", "UMTX queue memory");
97 
98 static void umtxq_init_chains(void *);
99 static int umtxq_hash(struct umtx_key *key);
100 static struct mtx *umtxq_mtx(int chain);
101 static void umtxq_lock(struct umtx_key *key);
102 static void umtxq_unlock(struct umtx_key *key);
103 static void umtxq_busy(struct umtx_key *key);
104 static void umtxq_unbusy(struct umtx_key *key);
105 static void umtxq_insert(struct umtx_q *uq);
106 static void umtxq_remove(struct umtx_q *uq);
107 static int umtxq_sleep(struct thread *td, struct umtx_key *key,
108 	int prio, const char *wmesg, int timo);
109 static int umtxq_count(struct umtx_key *key);
110 static int umtxq_signal(struct umtx_key *key, int nr_wakeup);
111 #ifdef UMTX_DYNAMIC_SHARED
112 static void fork_handler(void *arg, struct proc *p1, struct proc *p2,
113 	int flags);
114 #endif
115 static int umtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2);
116 static int umtx_key_get(struct thread *td, struct umtx *umtx,
117 	struct umtx_key *key);
118 static void umtx_key_release(struct umtx_key *key);
119 
120 SYSINIT(umtx, SI_SUB_EVENTHANDLER+1, SI_ORDER_MIDDLE, umtxq_init_chains, NULL);
121 
122 struct umtx_q *
123 umtxq_alloc(void)
124 {
125 	return (malloc(sizeof(struct umtx_q), M_UMTX, M_WAITOK));
126 }
127 
128 void
129 umtxq_free(struct umtx_q *uq)
130 {
131 	free(uq, M_UMTX);
132 }
133 
134 static void
135 umtxq_init_chains(void *arg __unused)
136 {
137 	int i;
138 
139 	for (i = 0; i < UMTX_CHAINS; ++i) {
140 		mtx_init(&umtxq_chains[i].uc_lock, "umtxq_lock", NULL,
141 			 MTX_DEF | MTX_DUPOK);
142 		LIST_INIT(&umtxq_chains[i].uc_queue);
143 		umtxq_chains[i].uc_flags = 0;
144 	}
145 #ifdef UMTX_DYNAMIC_SHARED
146 	EVENTHANDLER_REGISTER(process_fork, fork_handler, 0, 10000);
147 #endif
148 }
149 
150 static inline int
151 umtxq_hash(struct umtx_key *key)
152 {
153 	unsigned n = (uintptr_t)key->info.both.ptr + key->info.both.word;
154 	return (((n * GOLDEN_RATIO_PRIME) >> UMTX_SHIFTS) % UMTX_CHAINS);
155 }
156 
157 static inline int
158 umtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2)
159 {
160 	return (k1->type == k2->type &&
161 		k1->info.both.ptr == k2->info.both.ptr &&
162 	        k1->info.both.word == k2->info.both.word);
163 }
164 
165 static inline struct mtx *
166 umtxq_mtx(int chain)
167 {
168 	return (&umtxq_chains[chain].uc_lock);
169 }
170 
171 static inline void
172 umtxq_busy(struct umtx_key *key)
173 {
174 	int chain = umtxq_hash(key);
175 
176 	mtx_assert(umtxq_mtx(chain), MA_OWNED);
177 	while (umtxq_chains[chain].uc_flags & UCF_BUSY) {
178 		umtxq_chains[chain].uc_flags |= UCF_WANT;
179 		msleep(&umtxq_chains[chain], umtxq_mtx(chain),
180 		       curthread->td_priority, "umtxq_busy", 0);
181 	}
182 	umtxq_chains[chain].uc_flags |= UCF_BUSY;
183 }
184 
185 static inline void
186 umtxq_unbusy(struct umtx_key *key)
187 {
188 	int chain = umtxq_hash(key);
189 
190 	mtx_assert(umtxq_mtx(chain), MA_OWNED);
191 	KASSERT(umtxq_chains[chain].uc_flags & UCF_BUSY, ("not busy"));
192 	umtxq_chains[chain].uc_flags &= ~UCF_BUSY;
193 	if (umtxq_chains[chain].uc_flags & UCF_WANT) {
194 		umtxq_chains[chain].uc_flags &= ~UCF_WANT;
195 		wakeup(&umtxq_chains[chain]);
196 	}
197 }
198 
199 static inline void
200 umtxq_lock(struct umtx_key *key)
201 {
202 	int chain = umtxq_hash(key);
203 	mtx_lock(umtxq_mtx(chain));
204 }
205 
206 static inline void
207 umtxq_unlock(struct umtx_key *key)
208 {
209 	int chain = umtxq_hash(key);
210 	mtx_unlock(umtxq_mtx(chain));
211 }
212 
213 /*
214  * Insert a thread onto the umtx queue.
215  */
216 static inline void
217 umtxq_insert(struct umtx_q *uq)
218 {
219 	struct umtx_head *head;
220 	int chain = umtxq_hash(&uq->uq_key);
221 
222 	mtx_assert(umtxq_mtx(chain), MA_OWNED);
223 	head = &umtxq_chains[chain].uc_queue;
224 	LIST_INSERT_HEAD(head, uq, uq_next);
225 	mtx_lock_spin(&sched_lock);
226 	uq->uq_thread->td_flags |= TDF_UMTXQ;
227 	mtx_unlock_spin(&sched_lock);
228 }
229 
230 /*
231  * Remove thread from the umtx queue.
232  */
233 static inline void
234 umtxq_remove(struct umtx_q *uq)
235 {
236 	mtx_assert(umtxq_mtx(umtxq_hash(&uq->uq_key)), MA_OWNED);
237 	if (uq->uq_thread->td_flags & TDF_UMTXQ) {
238 		LIST_REMOVE(uq, uq_next);
239 		/* turning off TDF_UMTXQ should be the last thing. */
240 		mtx_lock_spin(&sched_lock);
241 		uq->uq_thread->td_flags &= ~TDF_UMTXQ;
242 		mtx_unlock_spin(&sched_lock);
243 	}
244 }
245 
246 static int
247 umtxq_count(struct umtx_key *key)
248 {
249 	struct umtx_q *uq;
250 	struct umtx_head *head;
251 	int chain, count = 0;
252 
253 	chain = umtxq_hash(key);
254 	mtx_assert(umtxq_mtx(chain), MA_OWNED);
255 	head = &umtxq_chains[chain].uc_queue;
256 	LIST_FOREACH(uq, head, uq_next) {
257 		if (umtx_key_match(&uq->uq_key, key)) {
258 			if (++count > 1)
259 				break;
260 		}
261 	}
262 	return (count);
263 }
264 
265 static int
266 umtxq_signal(struct umtx_key *key, int n_wake)
267 {
268 	struct umtx_q *uq, *next;
269 	struct umtx_head *head;
270 	struct thread *blocked = NULL;
271 	int chain, ret;
272 
273 	ret = 0;
274 	chain = umtxq_hash(key);
275 	mtx_assert(umtxq_mtx(chain), MA_OWNED);
276 	head = &umtxq_chains[chain].uc_queue;
277 	for (uq = LIST_FIRST(head); uq; uq = next) {
278 		next = LIST_NEXT(uq, uq_next);
279 		if (umtx_key_match(&uq->uq_key, key)) {
280 			blocked = uq->uq_thread;
281 			umtxq_remove(uq);
282 			wakeup(blocked);
283 			if (++ret >= n_wake)
284 				break;
285 		}
286 	}
287 	return (ret);
288 }
289 
290 static inline int
291 umtxq_sleep(struct thread *td, struct umtx_key *key, int priority,
292 	    const char *wmesg, int timo)
293 {
294 	int chain = umtxq_hash(key);
295 	int error = msleep(td, umtxq_mtx(chain), priority, wmesg, timo);
296 	if (error == EWOULDBLOCK)
297 		error = ETIMEDOUT;
298 	return (error);
299 }
300 
301 static int
302 umtx_key_get(struct thread *td, struct umtx *umtx, struct umtx_key *key)
303 {
304 #if defined(UMTX_DYNAMIC_SHARED) || defined(UMTX_STATIC_SHARED)
305 	vm_map_t map;
306 	vm_map_entry_t entry;
307 	vm_pindex_t pindex;
308 	vm_prot_t prot;
309 	boolean_t wired;
310 
311 	map = &td->td_proc->p_vmspace->vm_map;
312 	if (vm_map_lookup(&map, (vm_offset_t)umtx, VM_PROT_WRITE,
313 	    &entry, &key->info.shared.object, &pindex, &prot,
314 	    &wired) != KERN_SUCCESS) {
315 		return EFAULT;
316 	}
317 #endif
318 
319 #if defined(UMTX_DYNAMIC_SHARED)
320 	key->type = UMTX_SHARED;
321 	key->info.shared.offset = entry->offset + entry->start -
322 		(vm_offset_t)umtx;
323 	/*
324 	 * Add object reference, if we don't do this, a buggy application
325 	 * deallocates the object, the object will be reused by other
326 	 * applications, then unlock will wake wrong thread.
327 	 */
328 	vm_object_reference(key->info.shared.object);
329 	vm_map_lookup_done(map, entry);
330 #elif defined(UMTX_STATIC_SHARED)
331 	if (VM_INHERIT_SHARE == entry->inheritance) {
332 		key->type = UMTX_SHARED;
333 		key->info.shared.offset = entry->offset + entry->start -
334 			(vm_offset_t)umtx;
335 		vm_object_reference(key->info.shared.object);
336 	} else {
337 		key->type = UMTX_PRIVATE;
338 		key->info.private.umtx = umtx;
339 		key->info.private.pid  = td->td_proc->p_pid;
340 	}
341 	vm_map_lookup_done(map, entry);
342 #else
343 	key->type = UMTX_PRIVATE;
344 	key->info.private.umtx = umtx;
345 	key->info.private.pid  = td->td_proc->p_pid;
346 #endif
347 	return (0);
348 }
349 
350 static inline void
351 umtx_key_release(struct umtx_key *key)
352 {
353 	if (key->type == UMTX_SHARED)
354 		vm_object_deallocate(key->info.shared.object);
355 }
356 
357 static inline int
358 umtxq_queue_me(struct thread *td, struct umtx *umtx, struct umtx_q *uq)
359 {
360 	int error;
361 
362 	if ((error = umtx_key_get(td, umtx, &uq->uq_key)) != 0)
363 		return (error);
364 
365 	uq->uq_addr = (vm_offset_t)umtx;
366 	uq->uq_thread = td;
367 	umtxq_lock(&uq->uq_key);
368 	/* hmm, for condition variable, we don't need busy flag. */
369 	umtxq_busy(&uq->uq_key);
370 	umtxq_insert(uq);
371 	umtxq_unbusy(&uq->uq_key);
372 	umtxq_unlock(&uq->uq_key);
373 	return (0);
374 }
375 
376 #if defined(UMTX_DYNAMIC_SHARED)
377 static void
378 fork_handler(void *arg, struct proc *p1, struct proc *p2, int flags)
379 {
380 	vm_map_t map;
381 	vm_map_entry_t entry;
382 	vm_object_t object;
383 	vm_pindex_t pindex;
384 	vm_prot_t prot;
385 	boolean_t wired;
386 	struct umtx_key key;
387 	LIST_HEAD(, umtx_q) workq;
388 	struct umtx_q *uq;
389 	struct thread *td;
390 	int onq;
391 
392 	LIST_INIT(&workq);
393 
394 	/* Collect threads waiting on umtxq */
395 	PROC_LOCK(p1);
396 	FOREACH_THREAD_IN_PROC(p1, td) {
397 		if (td->td_flags & TDF_UMTXQ) {
398 			uq = td->td_umtxq;
399 			if (uq)
400 				LIST_INSERT_HEAD(&workq, uq, uq_rqnext);
401 		}
402 	}
403 	PROC_UNLOCK(p1);
404 
405 	LIST_FOREACH(uq, &workq, uq_rqnext) {
406 		map = &p1->p_vmspace->vm_map;
407 		if (vm_map_lookup(&map, uq->uq_addr, VM_PROT_WRITE,
408 		    &entry, &object, &pindex, &prot, &wired) != KERN_SUCCESS) {
409 			continue;
410 		}
411 		key.type = UMTX_SHARED;
412 		key.info.shared.object = object;
413 		key.info.shared.offset = entry->offset + entry->start -
414 			uq->uq_addr;
415 		if (umtx_key_match(&key, &uq->uq_key)) {
416 			vm_map_lookup_done(map, entry);
417 			continue;
418 		}
419 
420 		umtxq_lock(&uq->uq_key);
421 		umtxq_busy(&uq->uq_key);
422 		if (uq->uq_thread->td_flags & TDF_UMTXQ) {
423 			umtxq_remove(uq);
424 			onq = 1;
425 		} else
426 			onq = 0;
427 		umtxq_unbusy(&uq->uq_key);
428 		umtxq_unlock(&uq->uq_key);
429 		if (onq) {
430 			vm_object_deallocate(uq->uq_key.info.shared.object);
431 			uq->uq_key = key;
432 			umtxq_lock(&uq->uq_key);
433 			umtxq_busy(&uq->uq_key);
434 			umtxq_insert(uq);
435 			umtxq_unbusy(&uq->uq_key);
436 			umtxq_unlock(&uq->uq_key);
437 			vm_object_reference(uq->uq_key.info.shared.object);
438 		}
439 		vm_map_lookup_done(map, entry);
440 	}
441 }
442 #endif
443 
444 static int
445 _do_lock(struct thread *td, struct umtx *umtx, long id, int timo)
446 {
447 	struct umtx_q *uq;
448 	intptr_t owner;
449 	intptr_t old;
450 	int error = 0;
451 
452 	uq = td->td_umtxq;
453 	/*
454 	 * Care must be exercised when dealing with umtx structure.  It
455 	 * can fault on any access.
456 	 */
457 
458 	for (;;) {
459 		/*
460 		 * Try the uncontested case.  This should be done in userland.
461 		 */
462 		owner = casuptr((intptr_t *)&umtx->u_owner,
463 		    UMTX_UNOWNED, id);
464 
465 		/* The acquire succeeded. */
466 		if (owner == UMTX_UNOWNED)
467 			return (0);
468 
469 		/* The address was invalid. */
470 		if (owner == -1)
471 			return (EFAULT);
472 
473 		/* If no one owns it but it is contested try to acquire it. */
474 		if (owner == UMTX_CONTESTED) {
475 			owner = casuptr((intptr_t *)&umtx->u_owner,
476 			    UMTX_CONTESTED, id | UMTX_CONTESTED);
477 
478 			if (owner == UMTX_CONTESTED)
479 				return (0);
480 
481 			/* The address was invalid. */
482 			if (owner == -1)
483 				return (EFAULT);
484 
485 			/* If this failed the lock has changed, restart. */
486 			continue;
487 		}
488 
489 		/*
490 		 * If we caught a signal, we have retried and now
491 		 * exit immediately.
492 		 */
493 		if (error || (error = umtxq_queue_me(td, umtx, uq)) != 0)
494 			return (error);
495 
496 		/*
497 		 * Set the contested bit so that a release in user space
498 		 * knows to use the system call for unlock.  If this fails
499 		 * either some one else has acquired the lock or it has been
500 		 * released.
501 		 */
502 		old = casuptr((intptr_t *)&umtx->u_owner, owner,
503 		    owner | UMTX_CONTESTED);
504 
505 		/* The address was invalid. */
506 		if (old == -1) {
507 			umtxq_lock(&uq->uq_key);
508 			umtxq_busy(&uq->uq_key);
509 			umtxq_remove(uq);
510 			umtxq_unbusy(&uq->uq_key);
511 			umtxq_unlock(&uq->uq_key);
512 			umtx_key_release(&uq->uq_key);
513 			return (EFAULT);
514 		}
515 
516 		/*
517 		 * We set the contested bit, sleep. Otherwise the lock changed
518 		 * and we need to retry or we lost a race to the thread
519 		 * unlocking the umtx.
520 		 */
521 		umtxq_lock(&uq->uq_key);
522 		if (old == owner && (td->td_flags & TDF_UMTXQ)) {
523 			error = umtxq_sleep(td, &uq->uq_key,
524 				       td->td_priority | PCATCH,
525 				       "umtx", timo);
526 		}
527 		umtxq_busy(&uq->uq_key);
528 		umtxq_remove(uq);
529 		umtxq_unbusy(&uq->uq_key);
530 		umtxq_unlock(&uq->uq_key);
531 		umtx_key_release(&uq->uq_key);
532 	}
533 
534 	return (0);
535 }
536 
537 static int
538 do_lock(struct thread *td, struct umtx *umtx, long id,
539 	struct timespec *timeout)
540 {
541 	struct timespec ts, ts2, ts3;
542 	struct timeval tv;
543 	int error;
544 
545 	if (timeout == NULL) {
546 		error = _do_lock(td, umtx, id, 0);
547 	} else {
548 		getnanouptime(&ts);
549 		timespecadd(&ts, timeout);
550 		TIMESPEC_TO_TIMEVAL(&tv, timeout);
551 		for (;;) {
552 			error = _do_lock(td, umtx, id, tvtohz(&tv));
553 			if (error != ETIMEDOUT)
554 				break;
555 			getnanouptime(&ts2);
556 			if (timespeccmp(&ts2, &ts, >=)) {
557 				error = ETIMEDOUT;
558 				break;
559 			}
560 			ts3 = ts;
561 			timespecsub(&ts3, &ts2);
562 			TIMESPEC_TO_TIMEVAL(&tv, &ts3);
563 		}
564 	}
565 	/*
566 	 * This lets userland back off critical region if needed.
567 	 */
568 	if (error == ERESTART)
569 		error = EINTR;
570 	return (error);
571 }
572 
573 static int
574 do_unlock(struct thread *td, struct umtx *umtx, long id)
575 {
576 	struct umtx_key key;
577 	intptr_t owner;
578 	intptr_t old;
579 	int error;
580 	int count;
581 
582 	/*
583 	 * Make sure we own this mtx.
584 	 *
585 	 * XXX Need a {fu,su}ptr this is not correct on arch where
586 	 * sizeof(intptr_t) != sizeof(long).
587 	 */
588 	if ((owner = fuword(&umtx->u_owner)) == -1)
589 		return (EFAULT);
590 
591 	if ((owner & ~UMTX_CONTESTED) != id)
592 		return (EPERM);
593 
594 	/* We should only ever be in here for contested locks */
595 	if ((owner & UMTX_CONTESTED) == 0)
596 		return (EINVAL);
597 
598 	if ((error = umtx_key_get(td, umtx, &key)) != 0)
599 		return (error);
600 
601 	umtxq_lock(&key);
602 	umtxq_busy(&key);
603 	count = umtxq_count(&key);
604 	umtxq_unlock(&key);
605 
606 	/*
607 	 * When unlocking the umtx, it must be marked as unowned if
608 	 * there is zero or one thread only waiting for it.
609 	 * Otherwise, it must be marked as contested.
610 	 */
611 	old = casuptr((intptr_t *)&umtx->u_owner, owner,
612 			count <= 1 ? UMTX_UNOWNED : UMTX_CONTESTED);
613 	umtxq_lock(&key);
614 	umtxq_signal(&key, 0);
615 	umtxq_unbusy(&key);
616 	umtxq_unlock(&key);
617 	umtx_key_release(&key);
618 	if (old == -1)
619 		return (EFAULT);
620 	if (old != owner)
621 		return (EINVAL);
622 	return (0);
623 }
624 
625 static int
626 do_wait(struct thread *td, struct umtx *umtx, long id, struct timespec *timeout)
627 {
628 	struct umtx_q *uq;
629 	struct timespec ts, ts2, ts3;
630 	struct timeval tv;
631 	long tmp;
632 	int error = 0;
633 
634 	uq = td->td_umtxq;
635 	if ((error = umtxq_queue_me(td, umtx, uq)) != 0)
636 		return (error);
637 	tmp = fuword(&umtx->u_owner);
638 	if (tmp != id) {
639 		umtxq_lock(&uq->uq_key);
640 		umtxq_remove(uq);
641 		umtxq_unlock(&uq->uq_key);
642 	} else if (timeout == NULL) {
643 		umtxq_lock(&uq->uq_key);
644 		if (td->td_flags & TDF_UMTXQ)
645 			error = umtxq_sleep(td, &uq->uq_key,
646 			       td->td_priority | PCATCH, "ucond", 0);
647 		if (!(td->td_flags & TDF_UMTXQ))
648 			error = 0;
649 		else
650 			umtxq_remove(uq);
651 		umtxq_unlock(&uq->uq_key);
652 	} else {
653 		getnanouptime(&ts);
654 		timespecadd(&ts, timeout);
655 		TIMESPEC_TO_TIMEVAL(&tv, timeout);
656 		for (;;) {
657 			umtxq_lock(&uq->uq_key);
658 			if (td->td_flags & TDF_UMTXQ) {
659 				error = umtxq_sleep(td, &uq->uq_key,
660 					    td->td_priority | PCATCH,
661 					    "ucond", tvtohz(&tv));
662 			}
663 			if (!(td->td_flags & TDF_UMTXQ)) {
664 				umtxq_unlock(&uq->uq_key);
665 				goto out;
666 			}
667 			umtxq_unlock(&uq->uq_key);
668 			if (error != ETIMEDOUT)
669 				break;
670 			getnanouptime(&ts2);
671 			if (timespeccmp(&ts2, &ts, >=)) {
672 				error = ETIMEDOUT;
673 				break;
674 			}
675 			ts3 = ts;
676 			timespecsub(&ts3, &ts2);
677 			TIMESPEC_TO_TIMEVAL(&tv, &ts3);
678 		}
679 		umtxq_lock(&uq->uq_key);
680 		umtxq_remove(uq);
681 		umtxq_unlock(&uq->uq_key);
682 	}
683 out:
684 	umtx_key_release(&uq->uq_key);
685 	if (error == ERESTART)
686 		error = EINTR;
687 	return (error);
688 }
689 
690 int
691 kern_umtx_wake(struct thread *td, void *uaddr, int n_wake)
692 {
693 	struct umtx_key key;
694 	int ret;
695 
696 	if ((ret = umtx_key_get(td, uaddr, &key)) != 0)
697 		return (ret);
698 	umtxq_lock(&key);
699 	ret = umtxq_signal(&key, n_wake);
700 	umtxq_unlock(&key);
701 	umtx_key_release(&key);
702 	return (0);
703 }
704 
705 int
706 _umtx_lock(struct thread *td, struct _umtx_lock_args *uap)
707     /* struct umtx *umtx */
708 {
709 	return _do_lock(td, uap->umtx, td->td_tid, 0);
710 }
711 
712 int
713 _umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap)
714     /* struct umtx *umtx */
715 {
716 	return do_unlock(td, uap->umtx, td->td_tid);
717 }
718 
719 int
720 _umtx_op(struct thread *td, struct _umtx_op_args *uap)
721 {
722 	struct timespec timeout;
723 	struct timespec *ts;
724 	int error;
725 
726 	switch(uap->op) {
727 	case UMTX_OP_LOCK:
728 		/* Allow a null timespec (wait forever). */
729 		if (uap->uaddr2 == NULL)
730 			ts = NULL;
731 		else {
732 			error = copyin(uap->uaddr2, &timeout, sizeof(timeout));
733 			if (error != 0)
734 				break;
735 			if (timeout.tv_nsec >= 1000000000 ||
736 			    timeout.tv_nsec < 0) {
737 				error = EINVAL;
738 				break;
739 			}
740 			ts = &timeout;
741 		}
742 		error = do_lock(td, uap->umtx, uap->id, ts);
743 		break;
744 	case UMTX_OP_UNLOCK:
745 		error = do_unlock(td, uap->umtx, uap->id);
746 		break;
747 	case UMTX_OP_WAIT:
748 		/* Allow a null timespec (wait forever). */
749 		if (uap->uaddr2 == NULL)
750 			ts = NULL;
751 		else {
752 			error = copyin(uap->uaddr2, &timeout, sizeof(timeout));
753 			if (error != 0)
754 				break;
755 			if (timeout.tv_nsec >= 1000000000 ||
756 			    timeout.tv_nsec < 0) {
757 				error = EINVAL;
758 				break;
759 			}
760 			ts = &timeout;
761 		}
762 		error = do_wait(td, uap->umtx, uap->id, ts);
763 		break;
764 	case UMTX_OP_WAKE:
765 		error = kern_umtx_wake(td, uap->umtx, uap->id);
766 		break;
767 	default:
768 		error = EINVAL;
769 		break;
770 	}
771 	return (error);
772 }
773