xref: /freebsd-13-stable/sys/compat/cloudabi/cloudabi_futex.c (revision 3bc80996974a61a4223eae4c1ccd47b6ee32a48a)
1 /*-
2  * Copyright (c) 2015 Nuxi, https://nuxi.nl/
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 #include <sys/param.h>
28 #include <sys/kernel.h>
29 #include <sys/limits.h>
30 #include <sys/lock.h>
31 #include <sys/malloc.h>
32 #include <sys/mutex.h>
33 #include <sys/proc.h>
34 #include <sys/sx.h>
35 #include <sys/systm.h>
36 #include <sys/umtxvar.h>
37 
38 #include <contrib/cloudabi/cloudabi_types_common.h>
39 
40 #include <compat/cloudabi/cloudabi_proto.h>
41 #include <compat/cloudabi/cloudabi_util.h>
42 
43 /*
44  * Futexes for CloudABI.
45  *
46  * On most systems, futexes are implemented as objects of a single type
47  * on which a set of operations can be performed. CloudABI makes a clear
48  * distinction between locks and condition variables. A lock may have
49  * zero or more associated condition variables. A condition variable is
50  * always associated with exactly one lock. There is a strict topology.
51  * This approach has two advantages:
52  *
53  * - This topology is guaranteed to be acyclic. Requeueing of threads
54  *   only happens in one direction (from condition variables to locks).
55  *   This eases locking.
56  * - It means that a futex object for a lock exists when it is unlocked,
57  *   but has threads waiting on associated condition variables. Threads
58  *   can be requeued to a lock even if the thread performing the wakeup
59  *   does not have the lock mapped in its address space.
60  *
61  * This futex implementation only implements a single lock type, namely
62  * a read-write lock. A regular mutex type would not be necessary, as
63  * the read-write lock is as efficient as a mutex if used as such.
64  * Userspace futex locks are 32 bits in size:
65  *
66  * - 1 bit: has threads waiting in kernel-space.
67  * - 1 bit: is write-locked.
68  * - 30 bits:
69  *   - if write-locked: thread ID of owner.
70  *   - if not write-locked: number of read locks held.
71  *
72  * Condition variables are also 32 bits in size. Its value is modified
73  * by kernel-space exclusively. Zero indicates that it has no waiting
74  * threads. Non-zero indicates the opposite.
75  *
76  * This implementation is optimal, in the sense that it only wakes up
77  * threads if they can actually continue execution. It does not suffer
78  * from the thundering herd problem. If multiple threads waiting on a
79  * condition variable need to be woken up, only a single thread is
80  * scheduled. All other threads are 'donated' to this thread. After the
81  * thread manages to reacquire the lock, it requeues its donated threads
82  * to the lock.
83  *
84  * TODO(ed): Integrate this functionality into kern_umtx.c instead.
85  * TODO(ed): Store futex objects in a hash table.
86  * TODO(ed): Add actual priority inheritance.
87  * TODO(ed): Let futex_queue also take priorities into account.
88  * TODO(ed): Make locking fine-grained.
89  * TODO(ed): Perform sleeps until an actual absolute point in time,
90  *           instead of converting the timestamp to a relative value.
91  */
92 
93 struct futex_address;
94 struct futex_condvar;
95 struct futex_lock;
96 struct futex_queue;
97 struct futex_waiter;
98 
99 /* Identifier of a location in memory. */
100 struct futex_address {
101 	struct umtx_key			fa_key;
102 };
103 
104 /* A set of waiting threads. */
105 struct futex_queue {
106 	STAILQ_HEAD(, futex_waiter)	fq_list;
107 	unsigned int			fq_count;
108 };
109 
110 /* Condition variables. */
111 struct futex_condvar {
112 	/* Address of the condition variable. */
113 	struct futex_address		fc_address;
114 
115 	/* The lock the waiters should be moved to when signalled. */
116 	struct futex_lock *		fc_lock;
117 
118 	/* Threads waiting on the condition variable. */
119 	struct futex_queue		fc_waiters;
120 	/*
121 	 * Number of threads blocked on this condition variable, or
122 	 * being blocked on the lock after being requeued.
123 	 */
124 	unsigned int			fc_waitcount;
125 
126 	/* Global list pointers. */
127 	LIST_ENTRY(futex_condvar)	fc_next;
128 };
129 
130 /* Read-write locks. */
131 struct futex_lock {
132 	/* Address of the lock. */
133 	struct futex_address		fl_address;
134 
135 	/*
136 	 * Current owner of the lock. LOCK_UNMANAGED if the lock is
137 	 * currently not owned by the kernel. LOCK_OWNER_UNKNOWN in case
138 	 * the owner is not known (e.g., when the lock is read-locked).
139 	 */
140 	cloudabi_tid_t			fl_owner;
141 #define LOCK_UNMANAGED 0x0
142 #define LOCK_OWNER_UNKNOWN 0x1
143 
144 	/* Writers blocked on the lock. */
145 	struct futex_queue		fl_writers;
146 	/* Readers blocked on the lock. */
147 	struct futex_queue		fl_readers;
148 	/* Number of threads blocked on this lock + condition variables. */
149 	unsigned int			fl_waitcount;
150 
151 	/* Global list pointers. */
152 	LIST_ENTRY(futex_lock)		fl_next;
153 };
154 
155 /* Information associated with a thread blocked on an object. */
156 struct futex_waiter {
157 	/* Thread ID. */
158 	cloudabi_tid_t			fw_tid;
159 	/* Condition variable used for waiting. */
160 	struct cv			fw_wait;
161 
162 	/* Queue this waiter is currently placed in. */
163 	struct futex_queue *		fw_queue;
164 	/* List pointers of fw_queue. */
165 	STAILQ_ENTRY(futex_waiter)	fw_next;
166 
167 	/* Lock has been acquired. */
168 	bool				fw_locked;
169 	/* If not locked, threads that should block after acquiring. */
170 	struct futex_queue		fw_donated;
171 };
172 
173 /* Global data structures. */
174 static MALLOC_DEFINE(M_FUTEX, "futex", "CloudABI futex");
175 
176 static struct sx futex_global_lock;
177 SX_SYSINIT(futex_global_lock, &futex_global_lock, "CloudABI futex global lock");
178 
179 static LIST_HEAD(, futex_lock) futex_lock_list =
180     LIST_HEAD_INITIALIZER(&futex_lock_list);
181 static LIST_HEAD(, futex_condvar) futex_condvar_list =
182     LIST_HEAD_INITIALIZER(&futex_condvar_list);
183 
184 /* Utility functions. */
185 static void futex_lock_assert(const struct futex_lock *);
186 static struct futex_lock *futex_lock_lookup_locked(struct futex_address *);
187 static void futex_lock_release(struct futex_lock *);
188 static int futex_lock_tryrdlock(struct futex_lock *, cloudabi_lock_t *);
189 static int futex_lock_unmanage(struct futex_lock *, cloudabi_lock_t *);
190 static int futex_lock_update_owner(struct futex_lock *, cloudabi_lock_t *);
191 static int futex_lock_wake_up_next(struct futex_lock *, cloudabi_lock_t *);
192 static unsigned int futex_queue_count(const struct futex_queue *);
193 static void futex_queue_init(struct futex_queue *);
194 static void futex_queue_requeue(struct futex_queue *, struct futex_queue *,
195     unsigned int);
196 static int futex_queue_sleep(struct futex_queue *, struct futex_lock *,
197     struct futex_waiter *, struct thread *, cloudabi_clockid_t,
198     cloudabi_timestamp_t, cloudabi_timestamp_t, bool);
199 static cloudabi_tid_t futex_queue_tid_best(const struct futex_queue *);
200 static void futex_queue_wake_up_all(struct futex_queue *);
201 static void futex_queue_wake_up_best(struct futex_queue *);
202 static void futex_queue_wake_up_donate(struct futex_queue *, unsigned int);
203 static int futex_user_load(uint32_t *, uint32_t *);
204 static int futex_user_store(uint32_t *, uint32_t);
205 static int futex_user_cmpxchg(uint32_t *, uint32_t, uint32_t *, uint32_t);
206 
207 /*
208  * futex_address operations.
209  */
210 
211 static int
futex_address_create(struct futex_address * fa,struct thread * td,const void * object,cloudabi_scope_t scope)212 futex_address_create(struct futex_address *fa, struct thread *td,
213     const void *object, cloudabi_scope_t scope)
214 {
215 
216 	KASSERT(td == curthread,
217 	    ("Can only create umtx keys for the current thread"));
218 	switch (scope) {
219 	case CLOUDABI_SCOPE_PRIVATE:
220 		return (umtx_key_get(object, TYPE_FUTEX, THREAD_SHARE,
221 		    &fa->fa_key));
222 	case CLOUDABI_SCOPE_SHARED:
223 		return (umtx_key_get(object, TYPE_FUTEX, AUTO_SHARE,
224 		    &fa->fa_key));
225 	default:
226 		return (EINVAL);
227 	}
228 }
229 
230 static void
futex_address_free(struct futex_address * fa)231 futex_address_free(struct futex_address *fa)
232 {
233 
234 	umtx_key_release(&fa->fa_key);
235 }
236 
237 static bool
futex_address_match(const struct futex_address * fa1,const struct futex_address * fa2)238 futex_address_match(const struct futex_address *fa1,
239     const struct futex_address *fa2)
240 {
241 
242 	return (umtx_key_match(&fa1->fa_key, &fa2->fa_key));
243 }
244 
245 /*
246  * futex_condvar operations.
247  */
248 
249 static void
futex_condvar_assert(const struct futex_condvar * fc)250 futex_condvar_assert(const struct futex_condvar *fc)
251 {
252 
253 	KASSERT(fc->fc_waitcount >= futex_queue_count(&fc->fc_waiters),
254 	    ("Total number of waiters cannot be smaller than the wait queue"));
255 	futex_lock_assert(fc->fc_lock);
256 }
257 
258 static int
futex_condvar_lookup(struct thread * td,const cloudabi_condvar_t * address,cloudabi_scope_t scope,struct futex_condvar ** fcret)259 futex_condvar_lookup(struct thread *td, const cloudabi_condvar_t *address,
260     cloudabi_scope_t scope, struct futex_condvar **fcret)
261 {
262 	struct futex_address fa_condvar;
263 	struct futex_condvar *fc;
264 	int error;
265 
266 	error = futex_address_create(&fa_condvar, td, address, scope);
267 	if (error != 0)
268 		return (error);
269 
270 	sx_xlock(&futex_global_lock);
271 	LIST_FOREACH(fc, &futex_condvar_list, fc_next) {
272 		if (futex_address_match(&fc->fc_address, &fa_condvar)) {
273 			/* Found matching lock object. */
274 			futex_address_free(&fa_condvar);
275 			futex_condvar_assert(fc);
276 			*fcret = fc;
277 			return (0);
278 		}
279 	}
280 	sx_xunlock(&futex_global_lock);
281 	futex_address_free(&fa_condvar);
282 	return (ENOENT);
283 }
284 
285 static int
futex_condvar_lookup_or_create(struct thread * td,const cloudabi_condvar_t * condvar,cloudabi_scope_t condvar_scope,const cloudabi_lock_t * lock,cloudabi_scope_t lock_scope,struct futex_condvar ** fcret)286 futex_condvar_lookup_or_create(struct thread *td,
287     const cloudabi_condvar_t *condvar, cloudabi_scope_t condvar_scope,
288     const cloudabi_lock_t *lock, cloudabi_scope_t lock_scope,
289     struct futex_condvar **fcret)
290 {
291 	struct futex_address fa_condvar, fa_lock;
292 	struct futex_condvar *fc;
293 	struct futex_lock *fl;
294 	int error;
295 
296 	error = futex_address_create(&fa_condvar, td, condvar, condvar_scope);
297 	if (error != 0)
298 		return (error);
299 	error = futex_address_create(&fa_lock, td, lock, lock_scope);
300 	if (error != 0) {
301 		futex_address_free(&fa_condvar);
302 		return (error);
303 	}
304 
305 	sx_xlock(&futex_global_lock);
306 	LIST_FOREACH(fc, &futex_condvar_list, fc_next) {
307 		if (!futex_address_match(&fc->fc_address, &fa_condvar))
308 			continue;
309 		fl = fc->fc_lock;
310 		if (!futex_address_match(&fl->fl_address, &fa_lock)) {
311 			/* Condition variable is owned by a different lock. */
312 			futex_address_free(&fa_condvar);
313 			futex_address_free(&fa_lock);
314 			sx_xunlock(&futex_global_lock);
315 			return (EINVAL);
316 		}
317 
318 		/* Found fully matching condition variable. */
319 		futex_address_free(&fa_condvar);
320 		futex_address_free(&fa_lock);
321 		futex_condvar_assert(fc);
322 		*fcret = fc;
323 		return (0);
324 	}
325 
326 	/* None found. Create new condition variable object. */
327 	fc = malloc(sizeof(*fc), M_FUTEX, M_WAITOK);
328 	fc->fc_address = fa_condvar;
329 	fc->fc_lock = futex_lock_lookup_locked(&fa_lock);
330 	futex_queue_init(&fc->fc_waiters);
331 	fc->fc_waitcount = 0;
332 	LIST_INSERT_HEAD(&futex_condvar_list, fc, fc_next);
333 	*fcret = fc;
334 	return (0);
335 }
336 
337 static void
futex_condvar_release(struct futex_condvar * fc)338 futex_condvar_release(struct futex_condvar *fc)
339 {
340 	struct futex_lock *fl;
341 
342 	futex_condvar_assert(fc);
343 	fl = fc->fc_lock;
344 	if (fc->fc_waitcount == 0) {
345 		/* Condition variable has no waiters. Deallocate it. */
346 		futex_address_free(&fc->fc_address);
347 		LIST_REMOVE(fc, fc_next);
348 		free(fc, M_FUTEX);
349 	}
350 	futex_lock_release(fl);
351 }
352 
353 static int
futex_condvar_unmanage(struct futex_condvar * fc,cloudabi_condvar_t * condvar)354 futex_condvar_unmanage(struct futex_condvar *fc,
355     cloudabi_condvar_t *condvar)
356 {
357 
358 	if (futex_queue_count(&fc->fc_waiters) != 0)
359 		return (0);
360 	return (futex_user_store(condvar, CLOUDABI_CONDVAR_HAS_NO_WAITERS));
361 }
362 
363 /*
364  * futex_lock operations.
365  */
366 
367 static void
futex_lock_assert(const struct futex_lock * fl)368 futex_lock_assert(const struct futex_lock *fl)
369 {
370 
371 	/*
372 	 * A futex lock can only be kernel-managed if it has waiters.
373 	 * Vice versa: if a futex lock has waiters, it must be
374 	 * kernel-managed.
375 	 */
376 	KASSERT((fl->fl_owner == LOCK_UNMANAGED) ==
377 	    (futex_queue_count(&fl->fl_readers) == 0 &&
378 	    futex_queue_count(&fl->fl_writers) == 0),
379 	    ("Managed locks must have waiting threads"));
380 	KASSERT(fl->fl_waitcount != 0 || fl->fl_owner == LOCK_UNMANAGED,
381 	    ("Lock with no waiters must be unmanaged"));
382 }
383 
384 static int
futex_lock_lookup(struct thread * td,const cloudabi_lock_t * address,cloudabi_scope_t scope,struct futex_lock ** flret)385 futex_lock_lookup(struct thread *td, const cloudabi_lock_t *address,
386     cloudabi_scope_t scope, struct futex_lock **flret)
387 {
388 	struct futex_address fa;
389 	int error;
390 
391 	error = futex_address_create(&fa, td, address, scope);
392 	if (error != 0)
393 		return (error);
394 
395 	sx_xlock(&futex_global_lock);
396 	*flret = futex_lock_lookup_locked(&fa);
397 	return (0);
398 }
399 
400 static struct futex_lock *
futex_lock_lookup_locked(struct futex_address * fa)401 futex_lock_lookup_locked(struct futex_address *fa)
402 {
403 	struct futex_lock *fl;
404 
405 	LIST_FOREACH(fl, &futex_lock_list, fl_next) {
406 		if (futex_address_match(&fl->fl_address, fa)) {
407 			/* Found matching lock object. */
408 			futex_address_free(fa);
409 			futex_lock_assert(fl);
410 			return (fl);
411 		}
412 	}
413 
414 	/* None found. Create new lock object. */
415 	fl = malloc(sizeof(*fl), M_FUTEX, M_WAITOK);
416 	fl->fl_address = *fa;
417 	fl->fl_owner = LOCK_UNMANAGED;
418 	futex_queue_init(&fl->fl_readers);
419 	futex_queue_init(&fl->fl_writers);
420 	fl->fl_waitcount = 0;
421 	LIST_INSERT_HEAD(&futex_lock_list, fl, fl_next);
422 	return (fl);
423 }
424 
425 static int
futex_lock_rdlock(struct futex_lock * fl,struct thread * td,cloudabi_lock_t * lock,cloudabi_clockid_t clock_id,cloudabi_timestamp_t timeout,cloudabi_timestamp_t precision,bool abstime)426 futex_lock_rdlock(struct futex_lock *fl, struct thread *td,
427     cloudabi_lock_t *lock, cloudabi_clockid_t clock_id,
428     cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision, bool abstime)
429 {
430 	struct futex_waiter fw;
431 	int error;
432 
433 	error = futex_lock_tryrdlock(fl, lock);
434 	if (error == EBUSY) {
435 		/* Suspend execution. */
436 		KASSERT(fl->fl_owner != LOCK_UNMANAGED,
437 		    ("Attempted to sleep on an unmanaged lock"));
438 		error = futex_queue_sleep(&fl->fl_readers, fl, &fw, td,
439 		    clock_id, timeout, precision, abstime);
440 		KASSERT((error == 0) == fw.fw_locked,
441 		    ("Should have locked write lock on success"));
442 		KASSERT(futex_queue_count(&fw.fw_donated) == 0,
443 		    ("Lock functions cannot receive threads"));
444 	}
445 	if (error != 0)
446 		futex_lock_unmanage(fl, lock);
447 	return (error);
448 }
449 
450 static void
futex_lock_release(struct futex_lock * fl)451 futex_lock_release(struct futex_lock *fl)
452 {
453 
454 	futex_lock_assert(fl);
455 	if (fl->fl_waitcount == 0) {
456 		/* Lock object is unreferenced. Deallocate it. */
457 		KASSERT(fl->fl_owner == LOCK_UNMANAGED,
458 		    ("Attempted to free a managed lock"));
459 		futex_address_free(&fl->fl_address);
460 		LIST_REMOVE(fl, fl_next);
461 		free(fl, M_FUTEX);
462 	}
463 	sx_xunlock(&futex_global_lock);
464 }
465 
466 static int
futex_lock_unmanage(struct futex_lock * fl,cloudabi_lock_t * lock)467 futex_lock_unmanage(struct futex_lock *fl, cloudabi_lock_t *lock)
468 {
469 	cloudabi_lock_t cmp, old;
470 	int error;
471 
472 	if (futex_queue_count(&fl->fl_readers) == 0 &&
473 	    futex_queue_count(&fl->fl_writers) == 0) {
474 		/* Lock should be unmanaged. */
475 		fl->fl_owner = LOCK_UNMANAGED;
476 
477 		/* Clear kernel-managed bit. */
478 		error = futex_user_load(lock, &old);
479 		if (error != 0)
480 			return (error);
481 		for (;;) {
482 			cmp = old;
483 			error = futex_user_cmpxchg(lock, cmp, &old,
484 			    cmp & ~CLOUDABI_LOCK_KERNEL_MANAGED);
485 			if (error != 0)
486 				return (error);
487 			if (old == cmp)
488 				break;
489 		}
490 	}
491 	return (0);
492 }
493 
494 /* Sets an owner of a lock, based on a userspace lock value. */
495 static void
futex_lock_set_owner(struct futex_lock * fl,cloudabi_lock_t lock)496 futex_lock_set_owner(struct futex_lock *fl, cloudabi_lock_t lock)
497 {
498 
499 	/* Lock has no explicit owner. */
500 	if ((lock & ~CLOUDABI_LOCK_WRLOCKED) == 0) {
501 		fl->fl_owner = LOCK_OWNER_UNKNOWN;
502 		return;
503 	}
504 	lock &= ~(CLOUDABI_LOCK_WRLOCKED | CLOUDABI_LOCK_KERNEL_MANAGED);
505 
506 	/* Don't allow userspace to silently unlock. */
507 	if (lock == LOCK_UNMANAGED) {
508 		fl->fl_owner = LOCK_OWNER_UNKNOWN;
509 		return;
510 	}
511 	fl->fl_owner = lock;
512 }
513 
514 static int
futex_lock_unlock(struct futex_lock * fl,struct thread * td,cloudabi_lock_t * lock)515 futex_lock_unlock(struct futex_lock *fl, struct thread *td,
516     cloudabi_lock_t *lock)
517 {
518 	int error;
519 
520 	/* Validate that this thread is allowed to unlock. */
521 	error = futex_lock_update_owner(fl, lock);
522 	if (error != 0)
523 		return (error);
524 	if (fl->fl_owner != LOCK_UNMANAGED && fl->fl_owner != td->td_tid)
525 		return (EPERM);
526 	return (futex_lock_wake_up_next(fl, lock));
527 }
528 
529 /* Syncs in the owner of the lock from userspace if needed. */
530 static int
futex_lock_update_owner(struct futex_lock * fl,cloudabi_lock_t * address)531 futex_lock_update_owner(struct futex_lock *fl, cloudabi_lock_t *address)
532 {
533 	cloudabi_lock_t lock;
534 	int error;
535 
536 	if (fl->fl_owner == LOCK_OWNER_UNKNOWN) {
537 		error = futex_user_load(address, &lock);
538 		if (error != 0)
539 			return (error);
540 		futex_lock_set_owner(fl, lock);
541 	}
542 	return (0);
543 }
544 
545 static int
futex_lock_tryrdlock(struct futex_lock * fl,cloudabi_lock_t * address)546 futex_lock_tryrdlock(struct futex_lock *fl, cloudabi_lock_t *address)
547 {
548 	cloudabi_lock_t old, cmp;
549 	int error;
550 
551 	if (fl->fl_owner != LOCK_UNMANAGED) {
552 		/* Lock is already acquired. */
553 		return (EBUSY);
554 	}
555 
556 	old = CLOUDABI_LOCK_UNLOCKED;
557 	for (;;) {
558 		if ((old & CLOUDABI_LOCK_KERNEL_MANAGED) != 0) {
559 			/*
560 			 * Userspace lock is kernel-managed, even though
561 			 * the kernel disagrees.
562 			 */
563 			return (EINVAL);
564 		}
565 
566 		if ((old & CLOUDABI_LOCK_WRLOCKED) == 0) {
567 			/*
568 			 * Lock is not write-locked. Attempt to acquire
569 			 * it by increasing the read count.
570 			 */
571 			cmp = old;
572 			error = futex_user_cmpxchg(address, cmp, &old, cmp + 1);
573 			if (error != 0)
574 				return (error);
575 			if (old == cmp) {
576 				/* Success. */
577 				return (0);
578 			}
579 		} else {
580 			/* Lock is write-locked. Make it kernel-managed. */
581 			cmp = old;
582 			error = futex_user_cmpxchg(address, cmp, &old,
583 			    cmp | CLOUDABI_LOCK_KERNEL_MANAGED);
584 			if (error != 0)
585 				return (error);
586 			if (old == cmp) {
587 				/* Success. */
588 				futex_lock_set_owner(fl, cmp);
589 				return (EBUSY);
590 			}
591 		}
592 	}
593 }
594 
595 static int
futex_lock_trywrlock(struct futex_lock * fl,cloudabi_lock_t * address,cloudabi_tid_t tid,bool force_kernel_managed)596 futex_lock_trywrlock(struct futex_lock *fl, cloudabi_lock_t *address,
597     cloudabi_tid_t tid, bool force_kernel_managed)
598 {
599 	cloudabi_lock_t old, new, cmp;
600 	int error;
601 
602 	if (fl->fl_owner == tid) {
603 		/* Attempted to acquire lock recursively. */
604 		return (EDEADLK);
605 	}
606 	if (fl->fl_owner != LOCK_UNMANAGED) {
607 		/* Lock is already acquired. */
608 		return (EBUSY);
609 	}
610 
611 	old = CLOUDABI_LOCK_UNLOCKED;
612 	for (;;) {
613 		if ((old & CLOUDABI_LOCK_KERNEL_MANAGED) != 0) {
614 			/*
615 			 * Userspace lock is kernel-managed, even though
616 			 * the kernel disagrees.
617 			 */
618 			return (EINVAL);
619 		}
620 		if (old == (tid | CLOUDABI_LOCK_WRLOCKED)) {
621 			/* Attempted to acquire lock recursively. */
622 			return (EDEADLK);
623 		}
624 
625 		if (old == CLOUDABI_LOCK_UNLOCKED) {
626 			/* Lock is unlocked. Attempt to acquire it. */
627 			new = tid | CLOUDABI_LOCK_WRLOCKED;
628 			if (force_kernel_managed)
629 				new |= CLOUDABI_LOCK_KERNEL_MANAGED;
630 			error = futex_user_cmpxchg(address,
631 			    CLOUDABI_LOCK_UNLOCKED, &old, new);
632 			if (error != 0)
633 				return (error);
634 			if (old == CLOUDABI_LOCK_UNLOCKED) {
635 				/* Success. */
636 				if (force_kernel_managed)
637 					fl->fl_owner = tid;
638 				return (0);
639 			}
640 		} else {
641 			/* Lock is still locked. Make it kernel-managed. */
642 			cmp = old;
643 			error = futex_user_cmpxchg(address, cmp, &old,
644 			    cmp | CLOUDABI_LOCK_KERNEL_MANAGED);
645 			if (error != 0)
646 				return (error);
647 			if (old == cmp) {
648 				/* Success. */
649 				futex_lock_set_owner(fl, cmp);
650 				return (EBUSY);
651 			}
652 		}
653 	}
654 }
655 
656 static int
futex_lock_wake_up_next(struct futex_lock * fl,cloudabi_lock_t * lock)657 futex_lock_wake_up_next(struct futex_lock *fl, cloudabi_lock_t *lock)
658 {
659 	cloudabi_tid_t tid;
660 	int error;
661 
662 	/*
663 	 * Determine which thread(s) to wake up. Prefer waking up
664 	 * writers over readers to prevent write starvation.
665 	 */
666 	if (futex_queue_count(&fl->fl_writers) > 0) {
667 		/* Transfer ownership to a single write-locker. */
668 		if (futex_queue_count(&fl->fl_writers) > 1 ||
669 		    futex_queue_count(&fl->fl_readers) > 0) {
670 			/* Lock should remain managed afterwards. */
671 			tid = futex_queue_tid_best(&fl->fl_writers);
672 			error = futex_user_store(lock,
673 			    tid | CLOUDABI_LOCK_WRLOCKED |
674 			    CLOUDABI_LOCK_KERNEL_MANAGED);
675 			if (error != 0)
676 				return (error);
677 
678 			futex_queue_wake_up_best(&fl->fl_writers);
679 			fl->fl_owner = tid;
680 		} else {
681 			/* Lock can become unmanaged afterwards. */
682 			error = futex_user_store(lock,
683 			    futex_queue_tid_best(&fl->fl_writers) |
684 			    CLOUDABI_LOCK_WRLOCKED);
685 			if (error != 0)
686 				return (error);
687 
688 			futex_queue_wake_up_best(&fl->fl_writers);
689 			fl->fl_owner = LOCK_UNMANAGED;
690 		}
691 	} else {
692 		/* Transfer ownership to all read-lockers (if any). */
693 		error = futex_user_store(lock,
694 		    futex_queue_count(&fl->fl_readers));
695 		if (error != 0)
696 			return (error);
697 
698 		/* Wake up all threads. */
699 		futex_queue_wake_up_all(&fl->fl_readers);
700 		fl->fl_owner = LOCK_UNMANAGED;
701 	}
702 	return (0);
703 }
704 
705 static int
futex_lock_wrlock(struct futex_lock * fl,struct thread * td,cloudabi_lock_t * lock,cloudabi_clockid_t clock_id,cloudabi_timestamp_t timeout,cloudabi_timestamp_t precision,bool abstime,struct futex_queue * donated)706 futex_lock_wrlock(struct futex_lock *fl, struct thread *td,
707     cloudabi_lock_t *lock, cloudabi_clockid_t clock_id,
708     cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision, bool abstime,
709     struct futex_queue *donated)
710 {
711 	struct futex_waiter fw;
712 	int error;
713 
714 	error = futex_lock_trywrlock(fl, lock, td->td_tid,
715 	    futex_queue_count(donated) > 0);
716 
717 	if (error == 0 || error == EBUSY) {
718 		/* Put donated threads in queue before suspending. */
719 		KASSERT(futex_queue_count(donated) == 0 ||
720 		    fl->fl_owner != LOCK_UNMANAGED,
721 		    ("Lock should be managed if we are going to donate"));
722 		futex_queue_requeue(donated, &fl->fl_writers, UINT_MAX);
723 	} else {
724 		/*
725 		 * This thread cannot deal with the donated threads.
726 		 * Wake up the next thread and let it try it by itself.
727 		 */
728 		futex_queue_wake_up_donate(donated, UINT_MAX);
729 	}
730 
731 	if (error == EBUSY) {
732 		/* Suspend execution if the lock was busy. */
733 		KASSERT(fl->fl_owner != LOCK_UNMANAGED,
734 		    ("Attempted to sleep on an unmanaged lock"));
735 		error = futex_queue_sleep(&fl->fl_writers, fl, &fw, td,
736 		    clock_id, timeout, precision, abstime);
737 		KASSERT((error == 0) == fw.fw_locked,
738 		    ("Should have locked write lock on success"));
739 		KASSERT(futex_queue_count(&fw.fw_donated) == 0,
740 		    ("Lock functions cannot receive threads"));
741 	}
742 	if (error != 0)
743 		futex_lock_unmanage(fl, lock);
744 	return (error);
745 }
746 
747 /*
748  * futex_queue operations.
749  */
750 
751 static cloudabi_tid_t
futex_queue_tid_best(const struct futex_queue * fq)752 futex_queue_tid_best(const struct futex_queue *fq)
753 {
754 
755 	return (STAILQ_FIRST(&fq->fq_list)->fw_tid);
756 }
757 
758 static unsigned int
futex_queue_count(const struct futex_queue * fq)759 futex_queue_count(const struct futex_queue *fq)
760 {
761 
762 	return (fq->fq_count);
763 }
764 
765 static void
futex_queue_init(struct futex_queue * fq)766 futex_queue_init(struct futex_queue *fq)
767 {
768 
769 	STAILQ_INIT(&fq->fq_list);
770 	fq->fq_count = 0;
771 }
772 
773 /* Converts a relative timestamp to an sbintime. */
774 static sbintime_t
futex_queue_convert_timestamp_relative(cloudabi_timestamp_t ts)775 futex_queue_convert_timestamp_relative(cloudabi_timestamp_t ts)
776 {
777 	cloudabi_timestamp_t s, ns;
778 
779 	s = ts / 1000000000;
780 	ns = ts % 1000000000;
781 	if (s > INT32_MAX)
782 		return (INT64_MAX);
783 	return ((s << 32) + (ns << 32) / 1000000000);
784 }
785 
786 /* Converts an absolute timestamp and precision to a pair of sbintime values. */
787 static int
futex_queue_convert_timestamp(struct thread * td,cloudabi_clockid_t clock_id,cloudabi_timestamp_t timeout,cloudabi_timestamp_t precision,sbintime_t * sbttimeout,sbintime_t * sbtprecision,bool abstime)788 futex_queue_convert_timestamp(struct thread *td, cloudabi_clockid_t clock_id,
789     cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision,
790     sbintime_t *sbttimeout, sbintime_t *sbtprecision, bool abstime)
791 {
792 	cloudabi_timestamp_t now;
793 	int error;
794 
795 	if (abstime) {
796 		/* Make the time relative. */
797 		error = cloudabi_clock_time_get(td, clock_id, &now);
798 		if (error != 0)
799 			return (error);
800 		timeout = timeout < now ? 0 : timeout - now;
801 	}
802 
803 	*sbttimeout = futex_queue_convert_timestamp_relative(timeout);
804 	*sbtprecision = futex_queue_convert_timestamp_relative(precision);
805 	return (0);
806 }
807 
808 static int
futex_queue_sleep(struct futex_queue * fq,struct futex_lock * fl,struct futex_waiter * fw,struct thread * td,cloudabi_clockid_t clock_id,cloudabi_timestamp_t timeout,cloudabi_timestamp_t precision,bool abstime)809 futex_queue_sleep(struct futex_queue *fq, struct futex_lock *fl,
810     struct futex_waiter *fw, struct thread *td, cloudabi_clockid_t clock_id,
811     cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision, bool abstime)
812 {
813 	sbintime_t sbttimeout, sbtprecision;
814 	int error;
815 
816 	/* Initialize futex_waiter object. */
817 	fw->fw_tid = td->td_tid;
818 	fw->fw_locked = false;
819 	futex_queue_init(&fw->fw_donated);
820 
821 	if (timeout != UINT64_MAX) {
822 		/* Convert timeout duration. */
823 		error = futex_queue_convert_timestamp(td, clock_id, timeout,
824 		    precision, &sbttimeout, &sbtprecision, abstime);
825 		if (error != 0)
826 			return (error);
827 	}
828 
829 	/* Place object in the queue. */
830 	fw->fw_queue = fq;
831 	STAILQ_INSERT_TAIL(&fq->fq_list, fw, fw_next);
832 	++fq->fq_count;
833 
834 	cv_init(&fw->fw_wait, "futex");
835 	++fl->fl_waitcount;
836 
837 	futex_lock_assert(fl);
838 	if (timeout == UINT64_MAX) {
839 		/* Wait without a timeout. */
840 		error = cv_wait_sig(&fw->fw_wait, &futex_global_lock);
841 	} else {
842 		/* Wait respecting the timeout. */
843 		error = cv_timedwait_sig_sbt(&fw->fw_wait, &futex_global_lock,
844 		    sbttimeout, sbtprecision, 0);
845 		futex_lock_assert(fl);
846 		if (error == EWOULDBLOCK &&
847 		    fw->fw_queue != NULL && fw->fw_queue != fq) {
848 			/*
849 			 * We got signalled on a condition variable, but
850 			 * observed a timeout while waiting to reacquire
851 			 * the lock. In other words, we didn't actually
852 			 * time out. Go back to sleep and wait for the
853 			 * lock to be reacquired.
854 			 */
855 			error = cv_wait_sig(&fw->fw_wait, &futex_global_lock);
856 		}
857 	}
858 	futex_lock_assert(fl);
859 
860 	--fl->fl_waitcount;
861 	cv_destroy(&fw->fw_wait);
862 
863 	fq = fw->fw_queue;
864 	if (fq == NULL) {
865 		/* Thread got dequeued, so we've slept successfully. */
866 		return (0);
867 	}
868 
869 	/* Thread is still enqueued. Remove it. */
870 	KASSERT(error != 0, ("Woken up thread is still enqueued"));
871 	STAILQ_REMOVE(&fq->fq_list, fw, futex_waiter, fw_next);
872 	--fq->fq_count;
873 	return (error == EWOULDBLOCK ? ETIMEDOUT : error);
874 }
875 
876 /* Moves up to nwaiters waiters from one queue to another. */
877 static void
futex_queue_requeue(struct futex_queue * fqfrom,struct futex_queue * fqto,unsigned int nwaiters)878 futex_queue_requeue(struct futex_queue *fqfrom, struct futex_queue *fqto,
879     unsigned int nwaiters)
880 {
881 	struct futex_waiter *fw;
882 
883 	/* Move waiters to the target queue. */
884 	while (nwaiters-- > 0 && !STAILQ_EMPTY(&fqfrom->fq_list)) {
885 		fw = STAILQ_FIRST(&fqfrom->fq_list);
886 		STAILQ_REMOVE_HEAD(&fqfrom->fq_list, fw_next);
887 		--fqfrom->fq_count;
888 
889 		fw->fw_queue = fqto;
890 		STAILQ_INSERT_TAIL(&fqto->fq_list, fw, fw_next);
891 		++fqto->fq_count;
892 	}
893 }
894 
895 /* Wakes up all waiters in a queue. */
896 static void
futex_queue_wake_up_all(struct futex_queue * fq)897 futex_queue_wake_up_all(struct futex_queue *fq)
898 {
899 	struct futex_waiter *fw;
900 
901 	STAILQ_FOREACH(fw, &fq->fq_list, fw_next) {
902 		fw->fw_locked = true;
903 		fw->fw_queue = NULL;
904 		cv_signal(&fw->fw_wait);
905 	}
906 
907 	STAILQ_INIT(&fq->fq_list);
908 	fq->fq_count = 0;
909 }
910 
911 /*
912  * Wakes up the best waiter (i.e., the waiter having the highest
913  * priority) in a queue.
914  */
915 static void
futex_queue_wake_up_best(struct futex_queue * fq)916 futex_queue_wake_up_best(struct futex_queue *fq)
917 {
918 	struct futex_waiter *fw;
919 
920 	fw = STAILQ_FIRST(&fq->fq_list);
921 	fw->fw_locked = true;
922 	fw->fw_queue = NULL;
923 	cv_signal(&fw->fw_wait);
924 
925 	STAILQ_REMOVE_HEAD(&fq->fq_list, fw_next);
926 	--fq->fq_count;
927 }
928 
929 static void
futex_queue_wake_up_donate(struct futex_queue * fq,unsigned int nwaiters)930 futex_queue_wake_up_donate(struct futex_queue *fq, unsigned int nwaiters)
931 {
932 	struct futex_waiter *fw;
933 
934 	fw = STAILQ_FIRST(&fq->fq_list);
935 	if (fw == NULL)
936 		return;
937 	fw->fw_locked = false;
938 	fw->fw_queue = NULL;
939 	cv_signal(&fw->fw_wait);
940 
941 	STAILQ_REMOVE_HEAD(&fq->fq_list, fw_next);
942 	--fq->fq_count;
943 	futex_queue_requeue(fq, &fw->fw_donated, nwaiters);
944 }
945 
946 /*
947  * futex_user operations. Used to adjust values in userspace.
948  */
949 
950 static int
futex_user_load(uint32_t * obj,uint32_t * val)951 futex_user_load(uint32_t *obj, uint32_t *val)
952 {
953 
954 	return (fueword32(obj, val) != 0 ? EFAULT : 0);
955 }
956 
957 static int
futex_user_store(uint32_t * obj,uint32_t val)958 futex_user_store(uint32_t *obj, uint32_t val)
959 {
960 
961 	return (suword32(obj, val) != 0 ? EFAULT : 0);
962 }
963 
964 static int
futex_user_cmpxchg(uint32_t * obj,uint32_t cmp,uint32_t * old,uint32_t new)965 futex_user_cmpxchg(uint32_t *obj, uint32_t cmp, uint32_t *old, uint32_t new)
966 {
967 
968 	return (casueword32(obj, cmp, old, new) != 0 ? EFAULT : 0);
969 }
970 
971 /*
972  * Blocking calls: acquiring locks, waiting on condition variables.
973  */
974 
975 int
cloudabi_futex_condvar_wait(struct thread * td,cloudabi_condvar_t * condvar,cloudabi_scope_t condvar_scope,cloudabi_lock_t * lock,cloudabi_scope_t lock_scope,cloudabi_clockid_t clock_id,cloudabi_timestamp_t timeout,cloudabi_timestamp_t precision,bool abstime)976 cloudabi_futex_condvar_wait(struct thread *td, cloudabi_condvar_t *condvar,
977     cloudabi_scope_t condvar_scope, cloudabi_lock_t *lock,
978     cloudabi_scope_t lock_scope, cloudabi_clockid_t clock_id,
979     cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision, bool abstime)
980 {
981 	struct futex_condvar *fc;
982 	struct futex_lock *fl;
983 	struct futex_waiter fw;
984 	int error, error2;
985 
986 	/* Lookup condition variable object. */
987 	error = futex_condvar_lookup_or_create(td, condvar, condvar_scope, lock,
988 	    lock_scope, &fc);
989 	if (error != 0)
990 		return (error);
991 	fl = fc->fc_lock;
992 
993 	/*
994 	 * Set the condition variable to something other than
995 	 * CLOUDABI_CONDVAR_HAS_NO_WAITERS to make userspace threads
996 	 * call into the kernel to perform wakeups.
997 	 */
998 	error = futex_user_store(condvar, ~CLOUDABI_CONDVAR_HAS_NO_WAITERS);
999 	if (error != 0) {
1000 		futex_condvar_release(fc);
1001 		return (error);
1002 	}
1003 
1004 	/* Drop the lock. */
1005 	error = futex_lock_unlock(fl, td, lock);
1006 	if (error != 0) {
1007 		futex_condvar_unmanage(fc, condvar);
1008 		futex_condvar_release(fc);
1009 		return (error);
1010 	}
1011 
1012 	/* Go to sleep. */
1013 	++fc->fc_waitcount;
1014 	error = futex_queue_sleep(&fc->fc_waiters, fc->fc_lock, &fw, td,
1015 	    clock_id, timeout, precision, abstime);
1016 	if (fw.fw_locked) {
1017 		/* Waited and got the lock assigned to us. */
1018 		KASSERT(futex_queue_count(&fw.fw_donated) == 0,
1019 		    ("Received threads while being locked"));
1020 	} else if (error == 0 || error == ETIMEDOUT) {
1021 		if (error != 0)
1022 			futex_condvar_unmanage(fc, condvar);
1023 		/*
1024 		 * Got woken up without having the lock assigned to us.
1025 		 * This can happen in two cases:
1026 		 *
1027 		 * 1. We observed a timeout on a condition variable.
1028 		 * 2. We got signalled on a condition variable while the
1029 		 *    associated lock is unlocked. We are the first
1030 		 *    thread that gets woken up. This thread is
1031 		 *    responsible for reacquiring the userspace lock.
1032 		 */
1033 		error2 = futex_lock_wrlock(fl, td, lock,
1034 		    CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0, abstime,
1035 		    &fw.fw_donated);
1036 		if (error2 != 0)
1037 			error = error2;
1038 	} else {
1039 		KASSERT(futex_queue_count(&fw.fw_donated) == 0,
1040 		    ("Received threads on error"));
1041 		futex_condvar_unmanage(fc, condvar);
1042 		futex_lock_unmanage(fl, lock);
1043 	}
1044 	--fc->fc_waitcount;
1045 	futex_condvar_release(fc);
1046 	return (error);
1047 }
1048 
1049 int
cloudabi_futex_lock_rdlock(struct thread * td,cloudabi_lock_t * lock,cloudabi_scope_t scope,cloudabi_clockid_t clock_id,cloudabi_timestamp_t timeout,cloudabi_timestamp_t precision,bool abstime)1050 cloudabi_futex_lock_rdlock(struct thread *td, cloudabi_lock_t *lock,
1051     cloudabi_scope_t scope, cloudabi_clockid_t clock_id,
1052     cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision, bool abstime)
1053 {
1054 	struct futex_lock *fl;
1055 	int error;
1056 
1057 	/* Look up lock object. */
1058 	error = futex_lock_lookup(td, lock, scope, &fl);
1059 	if (error != 0)
1060 		return (error);
1061 
1062 	error = futex_lock_rdlock(fl, td, lock, clock_id, timeout,
1063 	    precision, abstime);
1064 	futex_lock_release(fl);
1065 	return (error);
1066 }
1067 
1068 int
cloudabi_futex_lock_wrlock(struct thread * td,cloudabi_lock_t * lock,cloudabi_scope_t scope,cloudabi_clockid_t clock_id,cloudabi_timestamp_t timeout,cloudabi_timestamp_t precision,bool abstime)1069 cloudabi_futex_lock_wrlock(struct thread *td, cloudabi_lock_t *lock,
1070     cloudabi_scope_t scope, cloudabi_clockid_t clock_id,
1071     cloudabi_timestamp_t timeout, cloudabi_timestamp_t precision, bool abstime)
1072 {
1073 	struct futex_lock *fl;
1074 	struct futex_queue fq;
1075 	int error;
1076 
1077 	/* Look up lock object. */
1078 	error = futex_lock_lookup(td, lock, scope, &fl);
1079 	if (error != 0)
1080 		return (error);
1081 
1082 	futex_queue_init(&fq);
1083 	error = futex_lock_wrlock(fl, td, lock, clock_id, timeout,
1084 	    precision, abstime, &fq);
1085 	futex_lock_release(fl);
1086 	return (error);
1087 }
1088 
1089 /*
1090  * Non-blocking calls: releasing locks, signalling condition variables.
1091  */
1092 
1093 int
cloudabi_sys_condvar_signal(struct thread * td,struct cloudabi_sys_condvar_signal_args * uap)1094 cloudabi_sys_condvar_signal(struct thread *td,
1095     struct cloudabi_sys_condvar_signal_args *uap)
1096 {
1097 	struct futex_condvar *fc;
1098 	struct futex_lock *fl;
1099 	cloudabi_nthreads_t nwaiters;
1100 	int error;
1101 
1102 	nwaiters = uap->nwaiters;
1103 	if (nwaiters == 0) {
1104 		/* No threads to wake up. */
1105 		return (0);
1106 	}
1107 
1108 	/* Look up futex object. */
1109 	error = futex_condvar_lookup(td, uap->condvar, uap->scope, &fc);
1110 	if (error != 0) {
1111 		/* Race condition: condition variable with no waiters. */
1112 		return (error == ENOENT ? 0 : error);
1113 	}
1114 	fl = fc->fc_lock;
1115 
1116 	if (fl->fl_owner == LOCK_UNMANAGED) {
1117 		/*
1118 		 * The lock is currently not managed by the kernel,
1119 		 * meaning we must attempt to acquire the userspace lock
1120 		 * first. We cannot requeue threads to an unmanaged lock,
1121 		 * as these threads will then never be scheduled.
1122 		 *
1123 		 * Unfortunately, the memory address of the lock is
1124 		 * unknown from this context, meaning that we cannot
1125 		 * acquire the lock on behalf of the first thread to be
1126 		 * scheduled. The lock may even not be mapped within the
1127 		 * address space of the current thread.
1128 		 *
1129 		 * To solve this, wake up a single waiter that will
1130 		 * attempt to acquire the lock. Donate all of the other
1131 		 * waiters that need to be woken up to this waiter, so
1132 		 * it can requeue them after acquiring the lock.
1133 		 */
1134 		futex_queue_wake_up_donate(&fc->fc_waiters, nwaiters - 1);
1135 	} else {
1136 		/*
1137 		 * Lock is already managed by the kernel. This makes it
1138 		 * easy, as we can requeue the threads from the
1139 		 * condition variable directly to the associated lock.
1140 		 */
1141 		futex_queue_requeue(&fc->fc_waiters, &fl->fl_writers, nwaiters);
1142 	}
1143 
1144 	/* Clear userspace condition variable if all waiters are gone. */
1145 	error = futex_condvar_unmanage(fc, uap->condvar);
1146 	futex_condvar_release(fc);
1147 	return (error);
1148 }
1149 
1150 int
cloudabi_sys_lock_unlock(struct thread * td,struct cloudabi_sys_lock_unlock_args * uap)1151 cloudabi_sys_lock_unlock(struct thread *td,
1152     struct cloudabi_sys_lock_unlock_args *uap)
1153 {
1154 	struct futex_lock *fl;
1155 	int error;
1156 
1157 	error = futex_lock_lookup(td, uap->lock, uap->scope, &fl);
1158 	if (error != 0)
1159 		return (error);
1160 	error = futex_lock_unlock(fl, td, uap->lock);
1161 	futex_lock_release(fl);
1162 	return (error);
1163 }
1164