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