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