1 /*
2 * Copyright (c) 2015-2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * @OSF_FREE_COPYRIGHT@
30 */
31 /*
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or [email protected]
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56
57 #include <kern/ast.h>
58 #include <kern/backtrace.h>
59 #include <kern/kern_types.h>
60 #include <kern/mach_param.h>
61 #include <kern/percpu.h>
62 #include <kern/queue.h>
63 #include <kern/sched_prim.h>
64 #include <kern/simple_lock.h>
65 #include <kern/spl.h>
66 #include <kern/waitq.h>
67 #include <kern/zalloc.h>
68 #include <kern/policy_internal.h>
69 #include <kern/turnstile.h>
70
71 #include <os/hash.h>
72 #include <libkern/section_keywords.h>
73 #include <mach/sync_policy.h>
74 #include <vm/vm_kern.h>
75
76 #include <sys/kdebug.h>
77
78 /*!
79 * @const waitq_set_unlink_batch
80 *
81 * @brief
82 * How many links are unhooked under a single set lock hold.
83 *
84 * @discussion
85 * Holding a waitq set lock for too long can cause
86 * extreme contention (when a set is being torn down concurrently
87 * to messages being sent to ports who used to belong to that set).
88 *
89 * In order to fight this, large wait queue sets will drop
90 * and reacquire their lock for each unlinking batch.
91 */
92 static TUNABLE(uint32_t, waitq_set_unlink_batch, "waitq_set_unlink_batch", 64);
93
94 /*!
95 * @const WQL_PREPOST_MARKER
96 *
97 * @brief
98 * Marker set in the @c wql_wqs field of wait queue linkages to denote that
99 * this linkage has preposted to its wait queue set already.
100 *
101 * @discussion
102 * This bit is manipulated under both the wait queue and the wait queue set
103 * locks, and is used for two purposes:
104 *
105 * - for port set queues, it denotes in which circle queue the linkage
106 * is queued on (@c waitq_set::wqset_links or @c waitq_set::wqset_preposts)
107 *
108 * - as an optimization during pre-post to not walk sets this link already
109 * preposted to.
110 */
111 #define WQL_PREPOST_MARKER 1ul
112
113 #if __LP64__
114 /*!
115 * @struct waitq_link_hdr
116 *
117 * @brief
118 * Common "header" between all linkages, in order to find the waitq_set
119 * of this linkage.
120 *
121 * @discussion
122 * Due to unfortunate alignment constraints on @c queue_chain_t,
123 * this is wildly different for LP64 and ILP32.
124 *
125 * Do note that `wql
126 */
127 struct waitq_link_hdr {
128 uintptr_t wql_wqs;
129 };
130
131 /*!
132 * @struct waitq_sellink
133 *
134 * @brief
135 * Linkages used for select waitq queues to select wait queue sets.
136 *
137 * @discussion
138 * Select linkages are one way (queue to set) for two reasons:
139 *
140 * 1. select doesn't use the wait queue subsystem to discover which file
141 * descriptor woke up the set (it will instead scan all fds again),
142 *
143 * 2. all linkages are unhooked on each syscall return, so we minimize
144 * work to be done to be as quick as possible, using a fast invalidation
145 * scheme based on unique identifiers and sequestering
146 * (see @c select_set_nextid()).
147 */
148 struct waitq_sellink {
149 uintptr_t wql_wqs;
150 struct waitq_link_list_entry wql_next;
151 uint64_t wql_setid;
152 };
153
154 /*!
155 * @struct waitq_link
156 *
157 * @brief
158 * Linkages used for port wait queues and port-set wait queue sets.
159 *
160 * @discussion
161 * Those linkages go both ways so that receiving messages through a port-set
162 * can quickly find ports that preposted to the set.
163 *
164 * It also means that unhooking linkages cannot be lazy.
165 */
166 struct waitq_link {
167 uintptr_t wql_wqs; /**< wait queue set for this link */
168 queue_chain_t wql_qlink; /**< linkage through the waitq list */
169 queue_chain_t wql_slink; /**< linkage through the wqset list */
170 struct waitq *wql_wq; /**< wait queue for this link */
171 };
172 #else
173 struct waitq_link_hdr {
174 uint64_t __wql_padding;
175 uintptr_t wql_wqs;
176 };
177
178 struct waitq_sellink {
179 struct waitq_link_list_entry wql_next;
180 uintptr_t __wql_padding;
181 uintptr_t wql_wqs;
182 uint64_t wql_setid;
183 };
184
185 struct waitq_link {
186 queue_chain_t wql_qlink;
187 uintptr_t wql_wqs;
188 struct waitq *wql_wq;
189 queue_chain_t wql_slink;
190 };
191 #endif
192
193 static_assert(offsetof(struct waitq_link_hdr, wql_wqs) ==
194 offsetof(struct waitq_sellink, wql_wqs));
195 static_assert(offsetof(struct waitq_link_hdr, wql_wqs) ==
196 offsetof(struct waitq_link, wql_wqs));
197 static_assert(sizeof(struct waitq) <= WQ_OPAQUE_SIZE, "waitq structure size mismatch");
198 static_assert(__alignof(struct waitq) == WQ_OPAQUE_ALIGN, "waitq structure alignment mismatch");
199
200 static KALLOC_TYPE_DEFINE(waitq_sellink_zone, struct waitq_sellink, KT_PRIV_ACCT);
201 static KALLOC_TYPE_DEFINE(waitq_link_zone, struct waitq_link, KT_PRIV_ACCT);
202 ZONE_DEFINE_ID(ZONE_ID_SELECT_SET, "select_set", struct select_set,
203 ZC_SEQUESTER | ZC_KASAN_NOQUARANTINE | ZC_ZFREE_CLEARMEM);
204
205 static LCK_GRP_DECLARE(waitq_lck_grp, "waitq");
206
207 static uint64_t PERCPU_DATA(select_setid);
208 struct waitq select_conflict_queue;
209
210 #pragma mark waitq links
211
212 static inline bool
waitq_is_sellink(waitq_type_t type)213 waitq_is_sellink(waitq_type_t type)
214 {
215 return type == WQT_SELECT || type == WQT_SELECT_SET;
216 }
217
218 static inline bool
wql_sellink_valid(struct select_set * selset,struct waitq_sellink * link)219 wql_sellink_valid(struct select_set *selset, struct waitq_sellink *link)
220 {
221 return waitq_valid(selset) && selset->selset_id == link->wql_setid;
222 }
223
224 static waitq_t
wql_wqs(waitq_link_t link)225 wql_wqs(waitq_link_t link)
226 {
227 return (waitq_t){ (void *)(link.wqlh->wql_wqs & ~WQL_PREPOST_MARKER) };
228 }
229
230 static bool
wql_wqs_preposted(waitq_link_t link)231 wql_wqs_preposted(waitq_link_t link)
232 {
233 return link.wqlh->wql_wqs & WQL_PREPOST_MARKER;
234 }
235
236 static void
wql_wqs_mark_preposted(waitq_link_t link)237 wql_wqs_mark_preposted(waitq_link_t link)
238 {
239 assert(!wql_wqs_preposted(link));
240 link.wqlh->wql_wqs |= WQL_PREPOST_MARKER;
241 }
242
243 static void
wql_wqs_clear_preposted(waitq_link_t link)244 wql_wqs_clear_preposted(waitq_link_t link)
245 {
246 assert(wql_wqs_preposted(link));
247 link.wqlh->wql_wqs &= ~WQL_PREPOST_MARKER;
248 }
249
250 static circle_queue_t
wql_wqs_queue(struct waitq_set * wqs,struct waitq_link * link)251 wql_wqs_queue(struct waitq_set *wqs, struct waitq_link *link)
252 {
253 return wql_wqs_preposted(link) ? &wqs->wqset_preposts : &wqs->wqset_links;
254 }
255
256 static void
wql_list_push(waitq_link_list_t * list,waitq_link_t link)257 wql_list_push(waitq_link_list_t *list, waitq_link_t link)
258 {
259 link.wqls->wql_next.next = list->next;
260 list->next = &link.wqls->wql_next;
261 }
262
263 static inline struct waitq_sellink *
wql_list_elem(struct waitq_link_list_entry * e)264 wql_list_elem(struct waitq_link_list_entry *e)
265 {
266 return e ? __container_of(e, struct waitq_sellink, wql_next) : NULL;
267 }
268
269 /*!
270 * @function wql_list_next()
271 *
272 * @brief
273 * Helper function to implement wait queue link list enumeration.
274 *
275 * @param e in: pointer to the current element,
276 * out: pointer to the next element or NULL
277 * @param end which element to stop enumeration at (NULL for lists,
278 * or the first element enumerated for circle queues).
279 * @returns true (makes writing for(;;) based enumerators easier).
280 */
281 static inline bool
wql_list_next(struct waitq_link_list_entry ** e,struct waitq_link_list_entry * end)282 wql_list_next(struct waitq_link_list_entry **e, struct waitq_link_list_entry *end)
283 {
284 if (*e == NULL || (*e)->next == end) {
285 *e = NULL;
286 } else {
287 *e = (*e)->next;
288 }
289 return true;
290 }
291
292 #define __wql_list_foreach(it, head, end) \
293 for (struct waitq_link_list_entry *__it = (head)->next, *__end = end; \
294 ((it) = wql_list_elem(__it)); wql_list_next(&__it, __end))
295
296 #define wql_list_foreach(it, head) \
297 __wql_list_foreach(it, head, NULL)
298
299 #define wql_list_foreach_safe(it, head) \
300 for (struct waitq_link_list_entry *__it = (head)->next; \
301 ((it) = wql_list_elem(__it)) && wql_list_next(&__it, NULL); )
302
303 /*
304 * Gross hack: passing `__it` to `__wql_list_foreach` makes it stop whether
305 * we circle back to the first element or NULL (whichever comes first).
306 *
307 * This allows to have a single enumeration function oblivious to whether
308 * we enumerate a circle queue or a sellink list.
309 */
310 #define waitq_link_foreach(link, waitq) \
311 __wql_list_foreach((link).wqls, &(waitq).wq_q->waitq_sellinks, __it)
312
313 static_assert(offsetof(struct waitq, waitq_sellinks) ==
314 offsetof(struct waitq, waitq_links));
315 static_assert(offsetof(struct waitq_sellink, wql_next) ==
316 offsetof(struct waitq_link, wql_qlink.next));
317
318 static struct waitq_link *
wql_find(struct waitq * waitq,waitq_t wqset)319 wql_find(struct waitq *waitq, waitq_t wqset)
320 {
321 struct waitq_link *link;
322
323 cqe_foreach_element(link, &waitq->waitq_links, wql_qlink) {
324 if (waitq_same(wql_wqs(link), wqset)) {
325 return link;
326 }
327 }
328
329 return NULL;
330 }
331
332 waitq_link_t
waitq_link_alloc(waitq_type_t type)333 waitq_link_alloc(waitq_type_t type)
334 {
335 waitq_link_t link;
336
337 if (waitq_is_sellink(type)) {
338 link.wqls = zalloc_flags(waitq_sellink_zone, Z_WAITOK | Z_ZERO);
339 } else {
340 link.wqll = zalloc_flags(waitq_link_zone, Z_WAITOK | Z_ZERO);
341 }
342 return link;
343 }
344
345 void
waitq_link_free(waitq_type_t type,waitq_link_t link)346 waitq_link_free(waitq_type_t type, waitq_link_t link)
347 {
348 if (waitq_is_sellink(type)) {
349 return zfree(waitq_sellink_zone, link.wqls);
350 } else {
351 return zfree(waitq_link_zone, link.wqll);
352 }
353 }
354
355 void
waitq_link_free_list(waitq_type_t type,waitq_link_list_t * free_l)356 waitq_link_free_list(waitq_type_t type, waitq_link_list_t *free_l)
357 {
358 waitq_link_t link;
359
360 wql_list_foreach_safe(link.wqls, free_l) {
361 waitq_link_free(type, link);
362 }
363
364 free_l->next = NULL;
365 }
366
367
368 #pragma mark global wait queues
369
370 static __startup_data struct waitq g_boot_waitq;
371 static SECURITY_READ_ONLY_LATE(struct waitq *) global_waitqs = &g_boot_waitq;
372 static SECURITY_READ_ONLY_LATE(uint32_t) g_num_waitqs = 1;
373
374 /*
375 * Zero out the used MSBs of the event.
376 */
377 #define _CAST_TO_EVENT_MASK(event) \
378 ((waitq_flags_t)(uintptr_t)(event) & ((1ul << _EVENT_MASK_BITS) - 1ul))
379
380 static inline uint32_t
waitq_hash(char * key,size_t length)381 waitq_hash(char *key, size_t length)
382 {
383 return os_hash_jenkins(key, length) & (g_num_waitqs - 1);
384 }
385
386 /* return a global waitq pointer corresponding to the given event */
387 struct waitq *
_global_eventq(char * event,size_t event_length)388 _global_eventq(char *event, size_t event_length)
389 {
390 return &global_waitqs[waitq_hash(event, event_length)];
391 }
392
393 bool
waitq_is_valid(waitq_t waitq)394 waitq_is_valid(waitq_t waitq)
395 {
396 return waitq_valid(waitq);
397 }
398
399 static inline bool
waitq_is_global(waitq_t waitq)400 waitq_is_global(waitq_t waitq)
401 {
402 if (waitq_type(waitq) != WQT_QUEUE) {
403 return false;
404 }
405 return waitq.wq_q >= global_waitqs && waitq.wq_q < global_waitqs + g_num_waitqs;
406 }
407
408 static inline bool
waitq_empty(waitq_t wq)409 waitq_empty(waitq_t wq)
410 {
411 struct turnstile *ts;
412
413 switch (waitq_type(wq)) {
414 case WQT_TURNSTILE:
415 return priority_queue_empty(&wq.wq_q->waitq_prio_queue);
416 case WQT_PORT:
417 ts = wq.wq_q->waitq_ts;
418 return ts == TURNSTILE_NULL ||
419 priority_queue_empty(&ts->ts_waitq.waitq_prio_queue);
420 case WQT_QUEUE:
421 case WQT_SELECT:
422 case WQT_PORT_SET:
423 case WQT_SELECT_SET:
424 return circle_queue_empty(&wq.wq_q->waitq_queue);
425
426 default:
427 return true;
428 }
429 }
430
431 #if CONFIG_WAITQ_STATS
432 #define NWAITQ_BTFRAMES 5
433
434 struct wq_stats {
435 uint64_t waits;
436 uint64_t wakeups;
437 uint64_t clears;
438 uint64_t failed_wakeups;
439
440 uintptr_t last_wait[NWAITQ_BTFRAMES];
441 uintptr_t last_wakeup[NWAITQ_BTFRAMES];
442 uintptr_t last_failed_wakeup[NWAITQ_BTFRAMES];
443 };
444
445 /* this global is for lldb */
446 const uint32_t g_nwaitq_btframes = NWAITQ_BTFRAMES;
447 struct wq_stats g_boot_stats;
448 struct wq_stats *g_waitq_stats = &g_boot_stats;
449
450 static __inline__ void
waitq_grab_backtrace(uintptr_t bt[NWAITQ_BTFRAMES],unsigned skip)451 waitq_grab_backtrace(uintptr_t bt[NWAITQ_BTFRAMES], unsigned skip)
452 {
453 uintptr_t buf[NWAITQ_BTFRAMES + skip];
454
455 memset(buf, 0, (NWAITQ_BTFRAMES + skip) * sizeof(uintptr_t));
456 backtrace(buf, g_nwaitq_btframes + skip, NULL, NULL);
457 memcpy(&bt[0], &buf[skip], NWAITQ_BTFRAMES * sizeof(uintptr_t));
458 }
459
460 static __inline__ struct wq_stats *
waitq_global_stats(waitq_t waitq)461 waitq_global_stats(waitq_t waitq)
462 {
463 struct wq_stats *wqs;
464 uint32_t idx;
465
466 if (!waitq_is_global(waitq)) {
467 return NULL;
468 }
469
470 idx = (uint32_t)(waitq.wq_q - global_waitqs);
471 assert(idx < g_num_waitqs);
472 wqs = &g_waitq_stats[idx];
473 return wqs;
474 }
475
476 static __inline__ void
waitq_stats_count_wait(waitq_t waitq)477 waitq_stats_count_wait(waitq_t waitq)
478 {
479 struct wq_stats *wqs = waitq_global_stats(waitq);
480 if (wqs != NULL) {
481 wqs->waits++;
482 waitq_grab_backtrace(wqs->last_wait, 2);
483 }
484 }
485
486 static __inline__ void
waitq_stats_count_wakeup(waitq_t waitq,int n)487 waitq_stats_count_wakeup(waitq_t waitq, int n)
488 {
489 struct wq_stats *wqs = waitq_global_stats(waitq);
490 if (wqs != NULL) {
491 if (n > 0) {
492 wqs->wakeups += n;
493 waitq_grab_backtrace(wqs->last_wakeup, 2);
494 } else {
495 wqs->failed_wakeups++;
496 waitq_grab_backtrace(wqs->last_failed_wakeup, 2);
497 }
498 }
499 }
500
501 static __inline__ void
waitq_stats_count_clear_wakeup(waitq_t waitq)502 waitq_stats_count_clear_wakeup(waitq_t waitq)
503 {
504 struct wq_stats *wqs = waitq_global_stats(waitq);
505 if (wqs != NULL) {
506 wqs->wakeups++;
507 wqs->clears++;
508 waitq_grab_backtrace(wqs->last_wakeup, 2);
509 }
510 }
511 #else /* !CONFIG_WAITQ_STATS */
512 #define waitq_stats_count_wait(q) do { } while (0)
513 #define waitq_stats_count_wakeup(q, n) do { } while (0)
514 #define waitq_stats_count_clear_wakeup(q) do { } while (0)
515 #endif
516
517 static struct waitq *
waitq_get_safeq(waitq_t waitq)518 waitq_get_safeq(waitq_t waitq)
519 {
520 if (waitq_type(waitq) == WQT_PORT) {
521 struct turnstile *ts = waitq.wq_q->waitq_ts;
522 return ts ? &ts->ts_waitq : NULL;
523 }
524
525 uint32_t hash = os_hash_kernel_pointer(waitq.wq_q);
526 return &global_waitqs[hash & (g_num_waitqs - 1)];
527 }
528
529 /*
530 * Since the priority ordered waitq uses basepri as the
531 * ordering key assert that this value fits in a uint8_t.
532 */
533 static_assert(MAXPRI <= UINT8_MAX);
534
535 static inline void
waitq_thread_insert(struct waitq * safeq,thread_t thread,waitq_t wq,event64_t event)536 waitq_thread_insert(struct waitq *safeq, thread_t thread,
537 waitq_t wq, event64_t event)
538 {
539 if (waitq_type(safeq) == WQT_TURNSTILE) {
540 turnstile_stats_update(0, TSU_TURNSTILE_BLOCK_COUNT, NULL);
541 turnstile_waitq_add_thread_priority_queue(safeq, thread);
542 } else {
543 turnstile_stats_update(0, TSU_REGULAR_WAITQ_BLOCK_COUNT, NULL);
544 /*
545 * This is the extent to which we currently take scheduling
546 * attributes into account:
547 *
548 * - If the thread is vm privileged, we stick it at the front
549 * of the queue, later, these queues will honor the policy
550 * value set at waitq_init time.
551 *
552 * - Realtime threads get priority for wait queue placements.
553 * This allows wait_queue_wakeup_one to prefer a waiting
554 * realtime thread, similar in principle to performing
555 * a wait_queue_wakeup_all and allowing scheduler
556 * prioritization to run the realtime thread, but without
557 * causing the lock contention of that scenario.
558 */
559 if (thread->sched_pri >= BASEPRI_REALTIME ||
560 !safeq->waitq_fifo ||
561 (thread->options & TH_OPT_VMPRIV)) {
562 circle_enqueue_head(&safeq->waitq_queue, &thread->wait_links);
563 } else {
564 circle_enqueue_tail(&safeq->waitq_queue, &thread->wait_links);
565 }
566 }
567
568 /* mark the event and real waitq, even if enqueued on a global safeq */
569 thread->wait_event = event;
570 thread->waitq = wq;
571 }
572
573 /**
574 * clear the thread-related waitq state
575 *
576 * Conditions:
577 * 'thread' is locked
578 */
579 static inline void
thread_clear_waitq_state(thread_t thread)580 thread_clear_waitq_state(thread_t thread)
581 {
582 thread->waitq.wq_q = NULL;
583 thread->wait_event = NO_EVENT64;
584 thread->at_safe_point = FALSE;
585 }
586
587 static inline void
waitq_thread_remove(waitq_t wq,thread_t thread)588 waitq_thread_remove(waitq_t wq, thread_t thread)
589 {
590 if (waitq_type(wq) == WQT_TURNSTILE) {
591 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
592 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS,
593 (THREAD_REMOVED_FROM_TURNSTILE_WAITQ))) | DBG_FUNC_NONE,
594 VM_KERNEL_UNSLIDE_OR_PERM(waitq_to_turnstile(wq.wq_q)),
595 thread_tid(thread), 0, 0, 0);
596 priority_queue_remove(&wq.wq_q->waitq_prio_queue,
597 &thread->wait_prioq_links);
598 } else {
599 circle_dequeue(&wq.wq_q->waitq_queue, &thread->wait_links);
600 if (waitq_is_global(wq) && waitq_empty(wq)) {
601 wq.wq_q->waitq_eventmask = 0;
602 }
603 }
604
605 thread_clear_waitq_state(thread);
606 }
607
608 __startup_func
609 static void
waitq_bootstrap(void)610 waitq_bootstrap(void)
611 {
612 const uint32_t qsz = sizeof(struct waitq);
613 vm_offset_t whsize;
614 int cpu = 0;
615
616 /*
617 * Determine the amount of memory we're willing to reserve for
618 * the waitqueue hash table
619 */
620 if (!PE_parse_boot_argn("wqsize", &whsize, sizeof(whsize))) {
621 whsize = round_page(thread_max * qsz / 5);
622 }
623
624 /*
625 * Determine the number of waitqueues we can fit.
626 * The hash algorithm requires that this be a power of 2.
627 */
628 g_num_waitqs = 0x80000000u >> __builtin_clzl(whsize / qsz);
629 assert(g_num_waitqs > 0);
630 whsize = round_page(g_num_waitqs * qsz);
631
632 kmem_alloc(kernel_map, (vm_offset_t *)&global_waitqs, whsize,
633 KMA_NOFAIL | KMA_KOBJECT | KMA_NOPAGEWAIT | KMA_PERMANENT,
634 VM_KERN_MEMORY_WAITQ);
635
636 #if CONFIG_WAITQ_STATS
637 whsize = round_page(g_num_waitqs * sizeof(struct wq_stats));
638 kmem_alloc(kernel_map, (vm_offset_t *)&g_waitq_stats, whsize,
639 KMA_NOFAIL | KMA_KOBJECT | KMA_NOPAGEWAIT | KMA_ZERO | KMA_PERMANENT,
640 VM_KERN_MEMORY_WAITQ);
641 #endif
642
643 for (uint32_t i = 0; i < g_num_waitqs; i++) {
644 waitq_init(&global_waitqs[i], WQT_QUEUE, SYNC_POLICY_FIFO);
645 }
646
647 waitq_init(&select_conflict_queue, WQT_SELECT, SYNC_POLICY_FIFO);
648
649 percpu_foreach(setid, select_setid) {
650 /* is not cpu_number() but CPUs haven't been numbered yet */
651 *setid = cpu++;
652 }
653 }
654 STARTUP(MACH_IPC, STARTUP_RANK_FIRST, waitq_bootstrap);
655
656
657 #pragma mark locking
658
659 static hw_spin_timeout_status_t
waitq_timeout_handler(void * _lock,hw_spin_timeout_t to,hw_spin_state_t st)660 waitq_timeout_handler(void *_lock, hw_spin_timeout_t to, hw_spin_state_t st)
661 {
662 lck_spinlock_to_info_t lsti;
663 hw_lck_ticket_t tmp;
664 struct waitq *wq = _lock;
665
666 if (machine_timeout_suspended()) {
667 return HW_LOCK_TIMEOUT_CONTINUE;
668 }
669
670 lsti = lck_spinlock_timeout_hit(&wq->waitq_interlock, 0);
671 tmp.tcurnext = os_atomic_load(&wq->waitq_interlock.tcurnext, relaxed);
672
673 panic("waitq(%p) lock " HW_SPIN_TIMEOUT_FMT "; cpu=%d, "
674 "cticket: 0x%x, nticket: 0x%x, waiting for 0x%x, "
675 HW_SPIN_TIMEOUT_DETAILS_FMT,
676 wq, HW_SPIN_TIMEOUT_ARG(to, st), cpu_number(),
677 tmp.cticket, tmp.nticket, lsti->extra,
678 HW_SPIN_TIMEOUT_DETAILS_ARG(to, st));
679 }
680
681 static const struct hw_spin_policy waitq_spin_policy = {
682 .hwsp_name = "waitq",
683 #if defined(__i386__) || defined(__x86_64__)
684 .hwsp_timeout = &LockTimeOutTSC,
685 #else
686 .hwsp_timeout_atomic = &LockTimeOut,
687 #endif
688 /*
689 * Double the standard lock timeout, because wait queues tend
690 * to iterate over a number of threads - locking each. If there is
691 * a problem with a thread lock, it normally times out at the wait
692 * queue level first, hiding the real problem.
693 */
694 .hwsp_timeout_shift = 1,
695 .hwsp_lock_offset = offsetof(struct waitq, waitq_interlock),
696 .hwsp_op_timeout = waitq_timeout_handler,
697 };
698
699 void
waitq_invalidate(waitq_t waitq)700 waitq_invalidate(waitq_t waitq)
701 {
702 hw_lck_ticket_invalidate(&waitq.wq_q->waitq_interlock);
703 }
704
705 bool
waitq_held(waitq_t wq)706 waitq_held(waitq_t wq)
707 {
708 return hw_lck_ticket_held(&wq.wq_q->waitq_interlock);
709 }
710
711 void
waitq_lock(waitq_t wq)712 waitq_lock(waitq_t wq)
713 {
714 (void)hw_lck_ticket_lock_to(&wq.wq_q->waitq_interlock,
715 &waitq_spin_policy, &waitq_lck_grp);
716 #if defined(__x86_64__)
717 pltrace(FALSE);
718 #endif
719 }
720
721 bool
waitq_lock_try(waitq_t wq)722 waitq_lock_try(waitq_t wq)
723 {
724 bool rc = hw_lck_ticket_lock_try(&wq.wq_q->waitq_interlock, &waitq_lck_grp);
725
726 #if defined(__x86_64__)
727 if (rc) {
728 pltrace(FALSE);
729 }
730 #endif
731 return rc;
732 }
733
734 bool
waitq_lock_reserve(waitq_t wq,uint32_t * ticket)735 waitq_lock_reserve(waitq_t wq, uint32_t *ticket)
736 {
737 return hw_lck_ticket_reserve(&wq.wq_q->waitq_interlock, ticket, &waitq_lck_grp);
738 }
739
740 static hw_lock_status_t
waitq_lock_reserve_allow_invalid(waitq_t wq,uint32_t * ticket)741 waitq_lock_reserve_allow_invalid(waitq_t wq, uint32_t *ticket)
742 {
743 return hw_lck_ticket_reserve_allow_invalid(&wq.wq_q->waitq_interlock,
744 ticket, &waitq_lck_grp);
745 }
746
747 void
waitq_lock_wait(waitq_t wq,uint32_t ticket)748 waitq_lock_wait(waitq_t wq, uint32_t ticket)
749 {
750 (void)hw_lck_ticket_wait(&wq.wq_q->waitq_interlock, ticket,
751 &waitq_spin_policy, &waitq_lck_grp);
752 #if defined(__x86_64__)
753 pltrace(FALSE);
754 #endif
755 }
756
757 bool
waitq_lock_allow_invalid(waitq_t wq)758 waitq_lock_allow_invalid(waitq_t wq)
759 {
760 hw_lock_status_t rc;
761
762 rc = hw_lck_ticket_lock_allow_invalid(&wq.wq_q->waitq_interlock,
763 &waitq_spin_policy, &waitq_lck_grp);
764
765 #if defined(__x86_64__)
766 if (rc == HW_LOCK_ACQUIRED) {
767 pltrace(FALSE);
768 }
769 #endif
770 return rc == HW_LOCK_ACQUIRED;
771 }
772
773 void
waitq_unlock(waitq_t wq)774 waitq_unlock(waitq_t wq)
775 {
776 assert(waitq_held(wq));
777 #if defined(__x86_64__)
778 pltrace(TRUE);
779 #endif
780 hw_lck_ticket_unlock(&wq.wq_q->waitq_interlock);
781 }
782
783
784 #pragma mark assert_wait / wakeup
785
786 struct waitq_select_args {
787 /* input parameters */
788 event64_t event;
789 wait_result_t result;
790 waitq_wakeup_flags_t flags;
791
792 /* output parameters */
793 uint32_t max_threads;
794 uint32_t nthreads;
795 spl_t spl;
796 circle_queue_head_t threadq;
797 };
798
799 static inline void
maybe_adjust_thread_pri(thread_t thread,waitq_wakeup_flags_t flags,__kdebug_only waitq_t waitq)800 maybe_adjust_thread_pri(
801 thread_t thread,
802 waitq_wakeup_flags_t flags,
803 __kdebug_only waitq_t waitq)
804 {
805 /*
806 * If the caller is requesting the waitq subsystem to promote the
807 * priority of the awoken thread, then boost the thread's priority to
808 * the default WAITQ_BOOST_PRIORITY (if it's not already equal or
809 * higher priority). This boost must be removed via a call to
810 * waitq_clear_promotion_locked before the thread waits again.
811 *
812 * WAITQ_PROMOTE_PRIORITY is -2.
813 * Anything above 0 represents a mutex promotion.
814 * The default 'no action' value is -1.
815 * TODO: define this in a header
816 */
817 if (flags & WAITQ_PROMOTE_PRIORITY) {
818 uintptr_t trace_waitq = 0;
819 if (__improbable(kdebug_enable)) {
820 trace_waitq = VM_KERNEL_UNSLIDE_OR_PERM(waitq.wq_q);
821 }
822
823 sched_thread_promote_reason(thread, TH_SFLAG_WAITQ_PROMOTED, trace_waitq);
824 }
825 }
826
827 #if SCHED_HYGIENE_DEBUG
828
829 TUNABLE_DEV_WRITEABLE(uint32_t, waitq_flush_excess_threads, "waitq_flush_excess_threads", 20);
830 TUNABLE_DEV_WRITEABLE(uint32_t, waitq_flush_excess_time_mt, "waitq_flush_excess_time_mt", 7200); /* 300us */
831
832 #endif /* SCHED_HYGIENE_DEBUG */
833
834 static void
waitq_select_queue_flush(waitq_t waitq,struct waitq_select_args * args)835 waitq_select_queue_flush(waitq_t waitq, struct waitq_select_args *args)
836 {
837 thread_t thread = THREAD_NULL;
838 __assert_only kern_return_t kr;
839
840 int flushed_threads = 0;
841
842 #if SCHED_HYGIENE_DEBUG
843 uint64_t start_time = ml_get_sched_hygiene_timebase();
844 /* Preemption is enabled on the final unlock */
845 disable_preemption();
846 #endif /* SCHED_HYGIENE_DEBUG */
847
848 cqe_foreach_element_safe(thread, &args->threadq, wait_links) {
849 circle_dequeue(&args->threadq, &thread->wait_links);
850 maybe_adjust_thread_pri(thread, args->flags, waitq);
851 kr = thread_go(thread, args->result, args->flags & WAITQ_HANDOFF);
852 assert(kr == KERN_SUCCESS);
853 thread_unlock(thread);
854
855 flushed_threads++;
856 }
857
858 #if SCHED_HYGIENE_DEBUG
859 uint64_t end_time = ml_get_sched_hygiene_timebase();
860
861 /*
862 * Check for a combination of excess threads and long time,
863 * so that a single thread wakeup that gets stuck is still caught
864 */
865 if (waitq_flush_excess_threads && waitq_flush_excess_time_mt &&
866 flushed_threads > waitq_flush_excess_threads &&
867 (end_time - start_time) > waitq_flush_excess_time_mt) {
868 /*
869 * Hack alert:
870 *
871 * The current way that waitq_wakeup_all is structured
872 * requires all of the threads to be moved from
873 * the waitq to the runq in one giant interrupts
874 * disabled region. With enough threads and lock
875 * contention, it can take Too Long to get through
876 * all the threads, leading to the interrupt watchdog
877 * going off.
878 *
879 * While we are working on a change to break up this
880 * giant glob of work into smaller chunks, remove this
881 * time region from the watchdog's memory to avoid
882 * unit tests that wake up hundreds of threads on
883 * one semaphore from causing this to blow up.
884 *
885 * We only trigger this when seeing a combination of
886 * excess threads and long time, so that a single
887 * thread wakeup that gets stuck is still caught.
888 *
889 * This will be removed with
890 * rdar://90325140 (Break up large waitq_select_queue_flush operations to reduce time spent with interrupts disabled)
891 */
892 ml_spin_debug_reset(current_thread());
893 ml_irq_debug_abandon();
894 abandon_preemption_disable_measurement();
895
896 KDBG(MACHDBG_CODE(DBG_MACH_SCHED, MACH_INT_MASKED_RESET), flushed_threads, end_time - start_time);
897 }
898
899 enable_preemption();
900
901 #endif /* SCHED_HYGIENE_DEBUG */
902 }
903
904 /**
905 * Routine to iterate over the waitq for non-priority ordered waitqs
906 *
907 * Conditions:
908 * args->waitq (and the posted waitq) is locked
909 *
910 * Notes:
911 * Uses the optional select callback function to refine the selection
912 * of one or more threads from a waitq. The select callback is invoked
913 * once for every thread that is found to be waiting on the input args->waitq.
914 *
915 * If one or more threads are selected, this may disable interrupts.
916 * The previous interrupt state is returned in args->spl and should
917 * be used in a call to splx() if threads are returned to the caller.
918 */
919 static waitq_flags_t
waitq_queue_iterate_locked(struct waitq * safeq,struct waitq * waitq,struct waitq_select_args * args)920 waitq_queue_iterate_locked(struct waitq *safeq, struct waitq *waitq,
921 struct waitq_select_args *args)
922 {
923 thread_t thread = THREAD_NULL;
924 waitq_flags_t eventmask = 0;
925
926 cqe_foreach_element_safe(thread, &safeq->waitq_queue, wait_links) {
927 assert_thread_magic(thread);
928
929 /*
930 * For non-priority ordered waitqs, we allow multiple events to be
931 * mux'ed into the same waitq. Also safeqs may contain threads from
932 * multiple waitqs. Only pick threads that match the
933 * requested wait event.
934 */
935 if (waitq_same(thread->waitq, waitq) && thread->wait_event == args->event) {
936 if (args->nthreads == 0 && safeq == waitq) {
937 args->spl = splsched();
938 }
939 thread_lock(thread);
940 thread_clear_waitq_state(thread);
941 circle_dequeue(&safeq->waitq_queue, &thread->wait_links);
942 circle_enqueue_tail(&args->threadq, &thread->wait_links);
943
944 if (++args->nthreads >= args->max_threads) {
945 break;
946 }
947 } else {
948 /* thread wasn't selected so track its event */
949 eventmask |= waitq_same(thread->waitq, safeq)
950 ? _CAST_TO_EVENT_MASK(thread->wait_event)
951 : _CAST_TO_EVENT_MASK(thread->waitq.wq_q);
952 }
953 }
954
955 return eventmask;
956 }
957
958 /**
959 * Routine to iterate and remove threads from priority ordered waitqs
960 *
961 * Conditions:
962 * args->waitq (and the posted waitq) is locked
963 *
964 * Notes:
965 * The priority ordered waitqs only support maximum priority element removal.
966 *
967 * Also, the implementation makes sure that all threads in a priority ordered
968 * waitq are waiting on the same wait event. This is not necessarily true for
969 * non-priority ordered waitqs. If one or more threads are selected, this may
970 * disable interrupts. The previous interrupt state is returned in args->spl
971 * and should be used in a call to splx() if threads are returned to the caller.
972 */
973 static void
waitq_prioq_iterate_locked(struct waitq * ts_wq,struct waitq * waitq,struct waitq_select_args * args)974 waitq_prioq_iterate_locked(
975 struct waitq *ts_wq,
976 struct waitq *waitq,
977 struct waitq_select_args *args)
978 {
979 struct turnstile *ts = waitq_to_turnstile(ts_wq);
980 bool update_inheritor = (args->flags & WAITQ_UPDATE_INHERITOR);
981
982 if (update_inheritor && args->max_threads == UINT32_MAX) {
983 /*
984 * If we are going to wake up all threads,
985 * go ahead and set the inheritor to NULL.
986 */
987 turnstile_kernel_update_inheritor_on_wake_locked(ts,
988 TURNSTILE_INHERITOR_NULL, TURNSTILE_INHERITOR_THREAD);
989 update_inheritor = false;
990 }
991
992 while (!priority_queue_empty(&ts_wq->waitq_prio_queue)) {
993 thread_t thread;
994
995 thread = priority_queue_remove_max(&ts_wq->waitq_prio_queue,
996 struct thread, wait_prioq_links);
997
998 /*
999 * Ensure the wait event matches since priority ordered waitqs do not
1000 * support multiple events in the same waitq.
1001 */
1002 assert(waitq_same(thread->waitq, waitq) && (thread->wait_event == args->event));
1003
1004 if (update_inheritor) {
1005 turnstile_inheritor_t inheritor = thread;
1006
1007 if (priority_queue_empty(&ts_wq->waitq_prio_queue)) {
1008 inheritor = TURNSTILE_INHERITOR_NULL;
1009 }
1010 turnstile_kernel_update_inheritor_on_wake_locked(ts,
1011 inheritor, TURNSTILE_INHERITOR_THREAD);
1012 update_inheritor = false;
1013 }
1014
1015 if (args->nthreads == 0 && ts_wq == waitq) {
1016 args->spl = splsched();
1017 }
1018 thread_lock(thread);
1019 thread_clear_waitq_state(thread);
1020 circle_enqueue_tail(&args->threadq, &thread->wait_links);
1021
1022 if (++args->nthreads >= args->max_threads) {
1023 break;
1024 }
1025 }
1026 }
1027
1028 /**
1029 * @function do_waitq_select_n_locked_queue
1030 *
1031 * @brief
1032 * Selects threads waiting on a wait queue.
1033 *
1034 * @discussion
1035 * @c waitq is locked.
1036 * If @c waitq is a set, then the wait queue posting to it is locked too.
1037 *
1038 * Uses the optional select callback function to refine the selection
1039 * of one or more threads from a waitq.
1040 *
1041 * The select callback is invoked once for every thread that
1042 * is found to be waiting on the input args->waitq.
1043 *
1044 * If one or more threads are selected, this may disable interrupts.
1045 * The previous interrupt state is returned in args->spl and should
1046 * be used in a call to splx() if threads are returned to the caller.
1047 */
1048 static void
do_waitq_select_n_locked_queue(waitq_t waitq,struct waitq_select_args * args)1049 do_waitq_select_n_locked_queue(waitq_t waitq, struct waitq_select_args *args)
1050 {
1051 struct waitq *safeq;
1052 waitq_flags_t eventmask, remaining_eventmask;
1053
1054 if (waitq_irq_safe(waitq)) {
1055 eventmask = _CAST_TO_EVENT_MASK(args->event);
1056 safeq = waitq.wq_q;
1057 } else {
1058 /* JMM - add flag to waitq to avoid global lookup if no waiters */
1059 eventmask = _CAST_TO_EVENT_MASK(waitq.wq_q);
1060 safeq = waitq_get_safeq(waitq);
1061 if (safeq == NULL) {
1062 return;
1063 }
1064
1065 if (args->nthreads == 0) {
1066 args->spl = splsched();
1067 }
1068 waitq_lock(safeq);
1069 }
1070
1071 /*
1072 * If the safeq doesn't have an eventmask (not global) or the event
1073 * we're looking for IS set in its eventmask, then scan the threads
1074 * in that queue for ones that match the original <waitq,event> pair.
1075 */
1076 if (waitq_type(safeq) == WQT_TURNSTILE) {
1077 waitq_prioq_iterate_locked(safeq, waitq.wq_q, args);
1078 } else if (!waitq_is_global(safeq)) {
1079 waitq_queue_iterate_locked(safeq, waitq.wq_q, args);
1080 } else if ((safeq->waitq_eventmask & eventmask) == eventmask) {
1081 remaining_eventmask = waitq_queue_iterate_locked(safeq,
1082 waitq.wq_q, args);
1083
1084 /*
1085 * Update the eventmask of global queues we just scanned:
1086 * - If we selected all the threads in the queue,
1087 * we can clear its eventmask.
1088 *
1089 * - If we didn't find enough threads to fill our needs,
1090 * then we can assume we looked at every thread in the queue
1091 * and the mask we computed is complete - so reset it.
1092 */
1093 if (waitq_empty(safeq)) {
1094 safeq->waitq_eventmask = 0;
1095 } else if (args->nthreads < args->max_threads) {
1096 safeq->waitq_eventmask = remaining_eventmask;
1097 }
1098 }
1099
1100 /* unlock the safe queue if we locked one above */
1101 if (!waitq_same(waitq, safeq)) {
1102 waitq_unlock(safeq);
1103 if (args->nthreads == 0) {
1104 splx(args->spl);
1105 args->spl = 0;
1106 }
1107 }
1108 }
1109
1110 /**
1111 * @function do_waitq_link_select_n_locked()
1112 *
1113 * @brief
1114 * Selects threads waiting on any set a wait queue belongs to,
1115 * or preposts the wait queue onto them.
1116 *
1117 * @discussion
1118 * @c waitq is locked.
1119 */
1120 __attribute__((noinline))
1121 static void
do_waitq_select_n_locked_sets(waitq_t waitq,struct waitq_select_args * args)1122 do_waitq_select_n_locked_sets(waitq_t waitq, struct waitq_select_args *args)
1123 {
1124 waitq_type_t wq_type = waitq_type(waitq);
1125 waitq_link_t link;
1126 hw_lock_status_t st;
1127 uint32_t ticket;
1128
1129 assert(args->event == NO_EVENT64);
1130 assert(waitq_preposts(waitq));
1131
1132 waitq_link_foreach(link, waitq) {
1133 waitq_t wqset = wql_wqs(link);
1134
1135 if (wql_wqs_preposted(link)) {
1136 /*
1137 * The wql_wqs_preposted() bit is cleared
1138 * under both the wq/wqset lock.
1139 *
1140 * If the wqset is still preposted,
1141 * we really won't find threads there.
1142 *
1143 * Just mark the waitq as preposted and move on.
1144 */
1145 if (wq_type == WQT_PORT) {
1146 waitq.wq_q->waitq_preposted = true;
1147 }
1148 continue;
1149 }
1150
1151 if (wq_type == WQT_SELECT) {
1152 /*
1153 * If PGZ picked this select set,
1154 * translate it to the real address
1155 *
1156 * If it is still a select set
1157 * (the slot could have been reused),
1158 * then keep using it for the rest of the logic.
1159 *
1160 * Even in the extremely unlikely case where
1161 * the slot was reused for another select_set,
1162 * the `wql_sellink_valid` check below will
1163 * take care of debouncing it. But we must
1164 * forget the original pointer we read
1165 * so that we unlock the proper object.
1166 */
1167 wqset.wqs_sel = pgz_decode_allow_invalid(wqset.wqs_sel,
1168 ZONE_ID_SELECT_SET);
1169 if (!wqset.wqs_sel) {
1170 continue;
1171 }
1172 st = waitq_lock_reserve_allow_invalid(wqset, &ticket);
1173 if (st == HW_LOCK_INVALID) {
1174 continue;
1175 }
1176 } else {
1177 static_assert(HW_LOCK_CONTENDED == 0);
1178 st = waitq_lock_reserve(wqset, &ticket);
1179 }
1180 if (st == HW_LOCK_CONTENDED) {
1181 if (!circle_queue_empty(&args->threadq)) {
1182 /*
1183 * We are holding several thread locks.
1184 *
1185 * If we fail to acquire this waitq set lock,
1186 * it is possible that another core is holding
1187 * that (non IRQ-safe) waitq set lock,
1188 * while an interrupt is trying to grab the
1189 * thread lock of ones of those threads.
1190 *
1191 * In order to avoid deadlocks, flush out
1192 * the queue of threads.
1193 *
1194 * Note: this code will never run for `identify`
1195 * variants (when `max_threads` is 1).
1196 */
1197 assert(args->max_threads > 1);
1198 waitq_select_queue_flush(waitq, args);
1199 }
1200 waitq_lock_wait(wqset, ticket);
1201 }
1202
1203 if (wq_type == WQT_SELECT) {
1204 if (!wql_sellink_valid(wqset.wqs_sel, link.wqls)) {
1205 goto out_unlock;
1206 }
1207 } else if (!waitq_valid(wqset)) {
1208 goto out_unlock;
1209 }
1210
1211 /*
1212 * Find any threads waiting on this wait queue set as a queue.
1213 */
1214 do_waitq_select_n_locked_queue(wqset, args);
1215
1216 if (args->nthreads == 0) {
1217 /* No thread selected: prepost 'waitq' to 'wqset' */
1218 wql_wqs_mark_preposted(link);
1219 if (wq_type == WQT_SELECT) {
1220 wqset.wqs_sel->selset_preposted = true;
1221 } else {
1222 waitq.wq_q->waitq_preposted = true;
1223 circle_dequeue(&wqset.wqs_set->wqset_links,
1224 &link.wqll->wql_slink);
1225 circle_enqueue_tail(&wqset.wqs_set->wqset_preposts,
1226 &link.wqll->wql_slink);
1227 ipc_pset_prepost(wqset.wqs_set, waitq.wq_q);
1228 }
1229 }
1230
1231 out_unlock:
1232 waitq_unlock(wqset);
1233
1234 if (args->nthreads >= args->max_threads) {
1235 break;
1236 }
1237 }
1238 }
1239
1240 /**
1241 * @function do_waitq_select_n_locked
1242 *
1243 * @brief
1244 * Selects threads waiting on a wait queue, or preposts it.
1245 *
1246 * @discussion
1247 * @c waitq is locked.
1248 *
1249 * Recurses into all sets this wait queue belongs to.
1250 */
1251 static void
do_waitq_select_n_locked(waitq_t waitq,struct waitq_select_args * args)1252 do_waitq_select_n_locked(waitq_t waitq, struct waitq_select_args *args)
1253 {
1254 do_waitq_select_n_locked_queue(waitq, args);
1255
1256 if (args->nthreads >= args->max_threads) {
1257 /* already enough threads found */
1258 return;
1259 }
1260
1261 if (args->event != NO_EVENT64 || !waitq_preposts(waitq)) {
1262 /* this wakeup should not recurse into sets */
1263 return;
1264 }
1265
1266 do_waitq_select_n_locked_sets(waitq, args);
1267 }
1268
1269 static inline bool
waitq_is_preposted_set(waitq_t waitq)1270 waitq_is_preposted_set(waitq_t waitq)
1271 {
1272 switch (waitq_type(waitq)) {
1273 case WQT_PORT_SET:
1274 return waitq_set_first_prepost(waitq.wqs_set, WQS_PREPOST_PEEK) != NULL;
1275
1276 case WQT_SELECT_SET:
1277 return waitq.wqs_sel->selset_preposted;
1278
1279 default:
1280 return false;
1281 }
1282 }
1283
1284 wait_result_t
waitq_assert_wait64_locked(waitq_t waitq,event64_t wait_event,wait_interrupt_t interruptible,wait_timeout_urgency_t urgency,uint64_t deadline,uint64_t leeway,thread_t thread)1285 waitq_assert_wait64_locked(waitq_t waitq,
1286 event64_t wait_event,
1287 wait_interrupt_t interruptible,
1288 wait_timeout_urgency_t urgency,
1289 uint64_t deadline,
1290 uint64_t leeway,
1291 thread_t thread)
1292 {
1293 wait_result_t wait_result;
1294 struct waitq *safeq;
1295 uintptr_t eventmask;
1296 spl_t s;
1297
1298 switch (waitq_type(waitq)) {
1299 case WQT_PORT:
1300 case WQT_SELECT:
1301 case WQT_PORT_SET:
1302 case WQT_SELECT_SET:
1303 assert(wait_event == NO_EVENT64);
1304 break;
1305 default:
1306 assert(wait_event != NO_EVENT64);
1307 break;
1308 }
1309
1310 /*
1311 * Warning: Do _not_ place debugging print statements here.
1312 * The waitq is locked!
1313 */
1314 assert(!thread->started || thread == current_thread());
1315
1316 if (!waitq_wait_possible(thread)) {
1317 panic("thread already waiting on %p", thread->waitq.wq_q);
1318 }
1319
1320 s = splsched();
1321
1322 /*
1323 * early-out if the thread is waiting on a wait queue set
1324 * that has already been pre-posted.
1325 *
1326 * Note: waitq_is_preposted_set() may unlock the waitq-set
1327 */
1328 if (waitq_is_preposted_set(waitq)) {
1329 thread_lock(thread);
1330 thread->wait_result = THREAD_AWAKENED;
1331 thread_unlock(thread);
1332 splx(s);
1333 return THREAD_AWAKENED;
1334 }
1335
1336 /*
1337 * If already dealing with an irq safe wait queue, we are all set.
1338 * Otherwise, determine a global queue to use and lock it.
1339 */
1340 if (waitq_irq_safe(waitq)) {
1341 safeq = waitq.wq_q;
1342 eventmask = _CAST_TO_EVENT_MASK(wait_event);
1343 } else {
1344 safeq = waitq_get_safeq(waitq);
1345 if (__improbable(safeq == NULL)) {
1346 panic("Trying to assert_wait on a turnstile proxy "
1347 "that hasn't been donated one (waitq: %p)", waitq.wq_q);
1348 }
1349 eventmask = _CAST_TO_EVENT_MASK(waitq.wq_q);
1350 waitq_lock(safeq);
1351 }
1352
1353 /* lock the thread now that we have the irq-safe waitq locked */
1354 thread_lock(thread);
1355
1356 wait_result = thread_mark_wait_locked(thread, interruptible);
1357 /* thread->wait_result has been set */
1358 if (wait_result == THREAD_WAITING) {
1359 waitq_thread_insert(safeq, thread, waitq, wait_event);
1360
1361 if (deadline != 0) {
1362 boolean_t act;
1363
1364 act = timer_call_enter_with_leeway(thread->wait_timer,
1365 NULL,
1366 deadline, leeway,
1367 urgency, FALSE);
1368 if (!act) {
1369 thread->wait_timer_active++;
1370 }
1371 thread->wait_timer_is_set = TRUE;
1372 }
1373
1374 if (waitq_is_global(safeq)) {
1375 safeq->waitq_eventmask |= (waitq_flags_t)eventmask;
1376 }
1377
1378 waitq_stats_count_wait(waitq);
1379 }
1380
1381 /* unlock the thread */
1382 thread_unlock(thread);
1383
1384 /* update the inheritor's thread priority if the waitq is embedded in turnstile */
1385 if (waitq_type(safeq) == WQT_TURNSTILE && wait_result == THREAD_WAITING) {
1386 turnstile_recompute_priority_locked(waitq_to_turnstile(safeq));
1387 turnstile_update_inheritor_locked(waitq_to_turnstile(safeq));
1388 }
1389
1390 /* unlock the safeq if we locked it here */
1391 if (!waitq_same(waitq, safeq)) {
1392 waitq_unlock(safeq);
1393 }
1394
1395 splx(s);
1396
1397 return wait_result;
1398 }
1399
1400 bool
waitq_pull_thread_locked(waitq_t waitq,thread_t thread)1401 waitq_pull_thread_locked(waitq_t waitq, thread_t thread)
1402 {
1403 struct waitq *safeq;
1404 uint32_t ticket;
1405
1406 assert_thread_magic(thread);
1407
1408 /* Find the interrupts disabled queue thread is waiting on */
1409 if (waitq_irq_safe(waitq)) {
1410 safeq = waitq.wq_q;
1411 } else {
1412 safeq = waitq_get_safeq(waitq);
1413 if (__improbable(safeq == NULL)) {
1414 panic("Trying to clear_wait on a turnstile proxy "
1415 "that hasn't been donated one (waitq: %p)", waitq.wq_q);
1416 }
1417 }
1418
1419 /*
1420 * thread is already locked so have to try for the waitq lock.
1421 *
1422 * We can't wait for the waitq lock under the thread lock,
1423 * however we can reserve our slot in the lock queue,
1424 * and if that reservation requires waiting, we are guaranteed
1425 * that this waitq can't die until we got our turn!
1426 */
1427 if (!waitq_lock_reserve(safeq, &ticket)) {
1428 thread_unlock(thread);
1429 waitq_lock_wait(safeq, ticket);
1430 thread_lock(thread);
1431
1432 if (!waitq_same(waitq, thread->waitq)) {
1433 /*
1434 * While we were waiting for our reservation the thread
1435 * stopped waiting on this waitq, bail out.
1436 */
1437 waitq_unlock(safeq);
1438 return false;
1439 }
1440 }
1441
1442 waitq_thread_remove(safeq, thread);
1443 waitq_stats_count_clear_wakeup(waitq);
1444 waitq_unlock(safeq);
1445 return true;
1446 }
1447
1448
1449 void
waitq_clear_promotion_locked(waitq_t waitq,thread_t thread)1450 waitq_clear_promotion_locked(waitq_t waitq, thread_t thread)
1451 {
1452 spl_t s = 0;
1453
1454 assert(waitq_held(waitq));
1455 assert(thread != THREAD_NULL);
1456 assert(thread == current_thread());
1457
1458 /* This flag is only cleared by the thread itself, so safe to check outside lock */
1459 if ((thread->sched_flags & TH_SFLAG_WAITQ_PROMOTED) != TH_SFLAG_WAITQ_PROMOTED) {
1460 return;
1461 }
1462
1463 if (!waitq_irq_safe(waitq)) {
1464 s = splsched();
1465 }
1466 thread_lock(thread);
1467
1468 sched_thread_unpromote_reason(thread, TH_SFLAG_WAITQ_PROMOTED, 0);
1469
1470 thread_unlock(thread);
1471 if (!waitq_irq_safe(waitq)) {
1472 splx(s);
1473 }
1474 }
1475
1476 static inline bool
waitq_should_unlock(waitq_wakeup_flags_t flags)1477 waitq_should_unlock(waitq_wakeup_flags_t flags)
1478 {
1479 return (flags & (WAITQ_UNLOCK | WAITQ_KEEP_LOCKED)) == WAITQ_UNLOCK;
1480 }
1481
1482 kern_return_t
waitq_wakeup64_all_locked(waitq_t waitq,event64_t wake_event,wait_result_t result,waitq_wakeup_flags_t flags)1483 waitq_wakeup64_all_locked(
1484 waitq_t waitq,
1485 event64_t wake_event,
1486 wait_result_t result,
1487 waitq_wakeup_flags_t flags)
1488 {
1489 struct waitq_select_args args = {
1490 .event = wake_event,
1491 .result = result,
1492 .flags = flags & ~WAITQ_HANDOFF,
1493 .max_threads = UINT32_MAX,
1494 };
1495
1496 assert(waitq_held(waitq));
1497
1498 do_waitq_select_n_locked(waitq, &args);
1499 waitq_stats_count_wakeup(waitq, args.nthreads);
1500
1501 if (waitq_should_unlock(flags)) {
1502 waitq_unlock(waitq);
1503 }
1504
1505 waitq_select_queue_flush(waitq, &args);
1506
1507 if (args.nthreads > 0) {
1508 splx(args.spl);
1509 return KERN_SUCCESS;
1510 }
1511
1512 return KERN_NOT_WAITING;
1513 }
1514
1515 kern_return_t
waitq_wakeup64_one_locked(waitq_t waitq,event64_t wake_event,wait_result_t result,waitq_wakeup_flags_t flags)1516 waitq_wakeup64_one_locked(
1517 waitq_t waitq,
1518 event64_t wake_event,
1519 wait_result_t result,
1520 waitq_wakeup_flags_t flags)
1521 {
1522 struct waitq_select_args args = {
1523 .event = wake_event,
1524 .result = result,
1525 .flags = flags,
1526 .max_threads = 1,
1527 };
1528
1529 assert(waitq_held(waitq));
1530
1531 do_waitq_select_n_locked(waitq, &args);
1532 waitq_stats_count_wakeup(waitq, args.nthreads);
1533
1534 if (waitq_should_unlock(flags)) {
1535 waitq_unlock(waitq);
1536 }
1537
1538 waitq_select_queue_flush(waitq, &args);
1539
1540 if (args.nthreads > 0) {
1541 splx(args.spl);
1542 return KERN_SUCCESS;
1543 }
1544
1545 return KERN_NOT_WAITING;
1546 }
1547
1548 thread_t
waitq_wakeup64_identify_locked(waitq_t waitq,event64_t wake_event,wait_result_t result,waitq_wakeup_flags_t flags,spl_t * spl)1549 waitq_wakeup64_identify_locked(
1550 waitq_t waitq,
1551 event64_t wake_event,
1552 wait_result_t result,
1553 waitq_wakeup_flags_t flags,
1554 spl_t *spl)
1555 {
1556 struct waitq_select_args args = {
1557 .event = wake_event,
1558 .result = result,
1559 .flags = flags,
1560 .max_threads = 1,
1561 };
1562 thread_t thread = THREAD_NULL;
1563
1564 assert(waitq_held(waitq));
1565
1566 do_waitq_select_n_locked(waitq, &args);
1567 waitq_stats_count_wakeup(waitq, args.nthreads);
1568
1569 if (waitq_should_unlock(flags)) {
1570 waitq_unlock(waitq);
1571 }
1572
1573 if (args.nthreads > 0) {
1574 kern_return_t __assert_only ret;
1575
1576 thread = cqe_dequeue_head(&args.threadq, struct thread, wait_links);
1577 assert(args.nthreads == 1 && circle_queue_empty(&args.threadq));
1578
1579 maybe_adjust_thread_pri(thread, flags, waitq);
1580 ret = thread_go(thread, result, (flags & WAITQ_HANDOFF));
1581 assert(ret == KERN_SUCCESS);
1582 *spl = args.spl;
1583 }
1584
1585 return thread; /* locked if not NULL (caller responsible for spl) */
1586 }
1587
1588 kern_return_t
waitq_wakeup64_thread_and_unlock(struct waitq * waitq,event64_t event,thread_t thread,wait_result_t result)1589 waitq_wakeup64_thread_and_unlock(
1590 struct waitq *waitq,
1591 event64_t event,
1592 thread_t thread,
1593 wait_result_t result)
1594 {
1595 kern_return_t ret = KERN_NOT_WAITING;
1596
1597 assert(waitq_irq_safe(waitq));
1598 assert(waitq_held(waitq));
1599 assert_thread_magic(thread);
1600
1601 /*
1602 * See if the thread was still waiting there. If so, it got
1603 * dequeued and returned locked.
1604 */
1605 thread_lock(thread);
1606
1607 if (waitq_same(thread->waitq, waitq) && thread->wait_event == event) {
1608 waitq_thread_remove(waitq, thread);
1609 ret = KERN_SUCCESS;
1610 }
1611 waitq_stats_count_wakeup(waitq, ret == KERN_SUCCESS ? 1 : 0);
1612
1613 waitq_unlock(waitq);
1614
1615 if (ret == KERN_SUCCESS) {
1616 ret = thread_go(thread, result, /* handoff */ false);
1617 assert(ret == KERN_SUCCESS);
1618 }
1619
1620 thread_unlock(thread);
1621
1622 return ret;
1623 }
1624
1625
1626 #pragma mark waitq
1627
1628 __attribute__((always_inline))
1629 void
waitq_init(waitq_t waitq,waitq_type_t type,int policy)1630 waitq_init(waitq_t waitq, waitq_type_t type, int policy)
1631 {
1632 assert((policy & SYNC_POLICY_FIXED_PRIORITY) == 0);
1633
1634 *waitq.wq_q = (struct waitq){
1635 .waitq_type = type,
1636 .waitq_fifo = ((policy & SYNC_POLICY_REVERSED) == 0),
1637 };
1638
1639 switch (type) {
1640 case WQT_INVALID:
1641 __builtin_trap();
1642
1643 case WQT_TURNSTILE:
1644 /* For turnstile, initialize it as a priority queue */
1645 priority_queue_init(&waitq.wq_q->waitq_prio_queue);
1646 assert(waitq.wq_q->waitq_fifo == 0);
1647 break;
1648
1649 case WQT_PORT:
1650 waitq.wq_q->waitq_ts = TURNSTILE_NULL;
1651 break;
1652
1653 case WQT_PORT_SET:
1654 circle_queue_init(&waitq.wqs_set->wqset_preposts);
1655 OS_FALLTHROUGH;
1656 case WQT_SELECT_SET:
1657 case WQT_QUEUE:
1658 case WQT_SELECT:
1659 circle_queue_init(&waitq.wq_q->waitq_queue);
1660 break;
1661 }
1662
1663 if (policy & SYNC_POLICY_INIT_LOCKED) {
1664 hw_lck_ticket_init_locked(&waitq.wq_q->waitq_interlock, &waitq_lck_grp);
1665 } else {
1666 hw_lck_ticket_init(&waitq.wq_q->waitq_interlock, &waitq_lck_grp);
1667 }
1668 }
1669
1670 void
waitq_deinit(waitq_t waitq)1671 waitq_deinit(waitq_t waitq)
1672 {
1673 waitq_type_t type = waitq_type(waitq);
1674
1675 switch (type) {
1676 case WQT_QUEUE:
1677 assert(circle_queue_empty(&waitq.wq_q->waitq_queue));
1678 waitq_invalidate(waitq);
1679 break;
1680
1681 case WQT_TURNSTILE:
1682 assert(priority_queue_empty(&waitq.wq_q->waitq_prio_queue));
1683 assert(waitq.wq_q->waitq_inheritor == TURNSTILE_INHERITOR_NULL);
1684 waitq_invalidate(waitq);
1685 break;
1686
1687 case WQT_PORT:
1688 assert(waitq.wq_q->waitq_ts == TURNSTILE_NULL);
1689 assert(circle_queue_empty(&waitq.wq_q->waitq_links));
1690 break;
1691
1692 case WQT_SELECT:
1693 assert(waitq.wq_q->waitq_sellinks.next == NULL);
1694 assert(circle_queue_empty(&waitq.wqs_set->wqset_queue));
1695 break;
1696
1697 case WQT_PORT_SET:
1698 assert(circle_queue_empty(&waitq.wqs_set->wqset_queue));
1699 assert(circle_queue_empty(&waitq.wqs_set->wqset_links));
1700 assert(circle_queue_empty(&waitq.wqs_set->wqset_preposts));
1701 break;
1702
1703 default:
1704 panic("invalid wait type: %p/%d", waitq.wq_q, type);
1705 }
1706
1707 /*
1708 * The waitq must have been invalidated, or hw_lck_ticket_destroy()
1709 * below won't wait for reservations from waitq_lock_reserve(),
1710 * waitq_lock_reserve_allow_invalid() or waitq_lock_allow_invalid().
1711 */
1712 assert(!waitq_valid(waitq.wqs_set));
1713 hw_lck_ticket_destroy(&waitq.wq_q->waitq_interlock, &waitq_lck_grp);
1714 }
1715
1716
1717 #pragma mark port-set sets
1718
1719 void
waitq_set_unlink_all_locked(struct waitq_set * wqset,waitq_link_list_t * free_l)1720 waitq_set_unlink_all_locked(struct waitq_set *wqset, waitq_link_list_t *free_l)
1721 {
1722 uint32_t batch = waitq_set_unlink_batch;
1723
1724 waitq_invalidate(wqset);
1725
1726 for (;;) {
1727 struct waitq_link *link;
1728 queue_entry_t elt;
1729 circle_queue_t q;
1730 struct waitq *wq;
1731 uint32_t ticket;
1732 bool stable = true;
1733
1734 if (!circle_queue_empty(&wqset->wqset_links)) {
1735 q = &wqset->wqset_links;
1736 } else if (!circle_queue_empty(&wqset->wqset_preposts)) {
1737 q = &wqset->wqset_preposts;
1738 } else {
1739 break;
1740 }
1741
1742 if (batch-- == 0) {
1743 waitq_unlock(wqset);
1744 waitq_lock(wqset);
1745 batch = waitq_set_unlink_batch;
1746 continue;
1747 }
1748
1749 elt = circle_queue_first(q);
1750 link = cqe_element(elt, struct waitq_link, wql_slink);
1751 wq = link->wql_wq;
1752
1753 if (__improbable(!waitq_lock_reserve(wq, &ticket))) {
1754 waitq_unlock(wqset);
1755 waitq_lock_wait(wq, ticket);
1756 waitq_lock(wqset);
1757 stable = (elt == circle_queue_first(q) && link->wql_wq == wq);
1758 }
1759
1760 if (stable) {
1761 circle_dequeue(q, &link->wql_slink);
1762 circle_dequeue(&wq->waitq_links, &link->wql_qlink);
1763 wql_list_push(free_l, link);
1764 }
1765
1766 waitq_unlock(wq);
1767 }
1768 }
1769
1770 void
waitq_clear_prepost_locked(struct waitq * waitq)1771 waitq_clear_prepost_locked(struct waitq *waitq)
1772 {
1773 assert(waitq_type(waitq) == WQT_PORT);
1774 waitq->waitq_preposted = false;
1775 }
1776
1777 void
1778 waitq_set_foreach_member_locked(struct waitq_set *wqs, void (^cb)(struct waitq *))
1779 {
1780 struct waitq_link *link;
1781
1782 cqe_foreach_element(link, &wqs->wqset_links, wql_slink) {
1783 cb(link->wql_wq);
1784 }
1785
1786 cqe_foreach_element(link, &wqs->wqset_preposts, wql_slink) {
1787 cb(link->wql_wq);
1788 }
1789 }
1790
1791 __abortlike
1792 static void
__waitq_link_arguments_panic(struct waitq * waitq,struct waitq_set * wqset)1793 __waitq_link_arguments_panic(struct waitq *waitq, struct waitq_set *wqset)
1794 {
1795 if (!waitq_valid(waitq)) {
1796 panic("Invalid waitq: %p", waitq);
1797 }
1798 if (waitq_type(waitq) != WQT_PORT) {
1799 panic("Invalid waitq type: %p:%d", waitq, waitq->waitq_type);
1800 }
1801 panic("Invalid waitq-set: %p", wqset);
1802 }
1803
1804 static inline void
__waitq_link_arguments_validate(struct waitq * waitq,struct waitq_set * wqset)1805 __waitq_link_arguments_validate(struct waitq *waitq, struct waitq_set *wqset)
1806 {
1807 if (!waitq_valid(waitq) ||
1808 waitq_type(waitq) != WQT_PORT ||
1809 waitq_type(wqset) != WQT_PORT_SET) {
1810 __waitq_link_arguments_panic(waitq, wqset);
1811 }
1812 }
1813
1814 __abortlike
1815 static void
__waitq_invalid_panic(waitq_t waitq)1816 __waitq_invalid_panic(waitq_t waitq)
1817 {
1818 panic("Invalid waitq: %p", waitq.wq_q);
1819 }
1820
1821 static void
__waitq_validate(waitq_t waitq)1822 __waitq_validate(waitq_t waitq)
1823 {
1824 if (!waitq_valid(waitq)) {
1825 __waitq_invalid_panic(waitq);
1826 }
1827 }
1828
1829 kern_return_t
waitq_link_locked(struct waitq * waitq,struct waitq_set * wqset,waitq_link_t * linkp)1830 waitq_link_locked(struct waitq *waitq, struct waitq_set *wqset,
1831 waitq_link_t *linkp)
1832 {
1833 assert(linkp->wqlh);
1834
1835 __waitq_link_arguments_validate(waitq, wqset);
1836
1837 if (wql_find(waitq, wqset)) {
1838 return KERN_ALREADY_IN_SET;
1839 }
1840
1841 linkp->wqll->wql_wq = waitq;
1842 linkp->wqll->wql_wqs = (uintptr_t)wqset;
1843
1844 if (waitq_valid(wqset)) {
1845 circle_enqueue_tail(&wqset->wqset_links, &linkp->wqll->wql_slink);
1846 circle_enqueue_tail(&waitq->waitq_links, &linkp->wqll->wql_qlink);
1847 *linkp = WQL_NULL;
1848 }
1849
1850 return KERN_SUCCESS;
1851 }
1852
1853 kern_return_t
waitq_link_prepost_locked(struct waitq * waitq,struct waitq_set * wqset)1854 waitq_link_prepost_locked(struct waitq *waitq, struct waitq_set *wqset)
1855 {
1856 struct waitq_link *link;
1857
1858 __waitq_link_arguments_validate(waitq, wqset);
1859
1860 link = wql_find(waitq, wqset);
1861 if (link == NULL) {
1862 return KERN_NOT_IN_SET;
1863 }
1864
1865 if (!wql_wqs_preposted(link)) {
1866 wql_wqs_mark_preposted(link);
1867 waitq->waitq_preposted = true;
1868 circle_dequeue(&wqset->wqset_links, &link->wql_slink);
1869 circle_enqueue_tail(&wqset->wqset_preposts, &link->wql_slink);
1870 ipc_pset_prepost(wqset, waitq);
1871 }
1872
1873 return KERN_SUCCESS;
1874 }
1875
1876 waitq_link_t
waitq_unlink_locked(struct waitq * waitq,struct waitq_set * wqset)1877 waitq_unlink_locked(struct waitq *waitq, struct waitq_set *wqset)
1878 {
1879 struct waitq_link *link;
1880
1881 __waitq_link_arguments_validate(waitq, wqset);
1882
1883 link = wql_find(waitq, wqset);
1884 if (link) {
1885 circle_dequeue(wql_wqs_queue(wqset, link), &link->wql_slink);
1886 circle_dequeue(&waitq->waitq_links, &link->wql_qlink);
1887 }
1888
1889 return (waitq_link_t){ .wqll = link };
1890 }
1891
1892 void
waitq_unlink_all_locked(struct waitq * waitq,struct waitq_set * except_wqset,waitq_link_list_t * free_l)1893 waitq_unlink_all_locked(struct waitq *waitq, struct waitq_set *except_wqset,
1894 waitq_link_list_t *free_l)
1895 {
1896 struct waitq_link *kept_link = NULL;
1897 struct waitq_link *link;
1898
1899 assert(waitq_type(waitq) == WQT_PORT);
1900
1901 cqe_foreach_element_safe(link, &waitq->waitq_links, wql_qlink) {
1902 waitq_t wqs = wql_wqs(link);
1903
1904 if (wqs.wqs_set == except_wqset) {
1905 kept_link = link;
1906 continue;
1907 }
1908
1909 waitq_lock(wqs);
1910 circle_dequeue(wql_wqs_queue(wqs.wqs_set, link),
1911 &link->wql_slink);
1912 wql_list_push(free_l, link);
1913 waitq_unlock(wqs);
1914 }
1915
1916 circle_queue_init(&waitq->waitq_links);
1917 if (kept_link) {
1918 circle_enqueue_tail(&waitq->waitq_links, &kept_link->wql_qlink);
1919 }
1920 }
1921
1922 struct waitq *
waitq_set_first_prepost(struct waitq_set * wqset,wqs_prepost_flags_t flags)1923 waitq_set_first_prepost(struct waitq_set *wqset, wqs_prepost_flags_t flags)
1924 {
1925 circle_queue_t q = &wqset->wqset_preposts;
1926 queue_entry_t elt;
1927 struct waitq_link *link;
1928 struct waitq *wq;
1929 uint32_t ticket;
1930
1931 if (__improbable(!waitq_valid(wqset))) {
1932 return NULL;
1933 }
1934
1935 while (!circle_queue_empty(q)) {
1936 elt = circle_queue_first(q);
1937 link = cqe_element(elt, struct waitq_link, wql_slink);
1938 wq = link->wql_wq;
1939
1940 if (__improbable(!waitq_lock_reserve(wq, &ticket))) {
1941 waitq_unlock(wqset);
1942 waitq_lock_wait(wq, ticket);
1943 waitq_lock(wqset);
1944 if (!waitq_valid(wqset)) {
1945 waitq_unlock(wq);
1946 return NULL;
1947 }
1948
1949 if (elt != circle_queue_first(q) || link->wql_wq != wq) {
1950 waitq_unlock(wq);
1951 continue;
1952 }
1953 }
1954
1955 if (wq->waitq_preposted) {
1956 if ((flags & WQS_PREPOST_PEEK) == 0) {
1957 circle_queue_rotate_head_forward(q);
1958 }
1959 if ((flags & WQS_PREPOST_LOCK) == 0) {
1960 waitq_unlock(wq);
1961 }
1962 return wq;
1963 }
1964
1965 /*
1966 * We found a link that is no longer preposted,
1967 * someone must have called waitq_clear_prepost_locked()
1968 * and this set just only noticed.
1969 */
1970 wql_wqs_clear_preposted(link);
1971 waitq_unlock(wq);
1972
1973 circle_dequeue(q, &link->wql_slink);
1974 circle_enqueue_tail(&wqset->wqset_links, &link->wql_slink);
1975 }
1976
1977 return NULL;
1978 }
1979
1980
1981 #pragma mark select sets
1982
1983 /**
1984 * @function select_set_nextid()
1985 *
1986 * @brief
1987 * Generate a unique ID for a select set "generation"
1988 *
1989 * @discussion
1990 * This mixes the CPU number with a monotonic clock
1991 * (in order to avoid contention on a global atomic).
1992 *
1993 * In order for select sets to be invalidated very quickly,
1994 * they do not have backward linkages to their member queues.
1995 *
1996 * Instead, each time a new @c select() "pass" is initiated,
1997 * a new ID is generated, which is copied onto the @c waitq_sellink
1998 * links at the time of link.
1999 *
2000 * The zone for select sets is sequestered, which allows for select
2001 * wait queues to speculatively lock their set during prepost
2002 * and use this ID to debounce wakeups and avoid spurious wakeups
2003 * (as an "optimization" because select recovers from spurious wakeups,
2004 * we just want those to be very rare).
2005 */
2006 __attribute__((always_inline))
2007 static inline uint64_t
select_set_nextid(bool preemption_enabled)2008 select_set_nextid(bool preemption_enabled)
2009 {
2010 /* waitq_bootstrap() set the low byte to a unique value per CPU */
2011 static_assert(MAX_CPUS <= 256);
2012 const uint64_t inc = 256;
2013 uint64_t id;
2014
2015 #ifdef __x86_64__
2016 /* uncontended atomics are slower than disabling preemption on Intel */
2017 if (preemption_enabled) {
2018 disable_preemption();
2019 }
2020 id = (*PERCPU_GET(select_setid) += inc);
2021 if (preemption_enabled) {
2022 enable_preemption();
2023 }
2024 #else
2025 /*
2026 * if preemption is enabled this might update another CPU's
2027 * setid, which will be rare but is acceptable, it still
2028 * produces a unique select ID.
2029 *
2030 * We chose this because the uncontended atomics on !intel
2031 * are faster than disabling/reenabling preemption.
2032 */
2033 (void)preemption_enabled;
2034 id = os_atomic_add(PERCPU_GET(select_setid), inc, relaxed);
2035 #endif
2036
2037 return id;
2038 }
2039
2040 struct select_set *
select_set_alloc(void)2041 select_set_alloc(void)
2042 {
2043 struct select_set *selset;
2044 selset = zalloc_id(ZONE_ID_SELECT_SET, Z_ZERO | Z_WAITOK | Z_NOFAIL);
2045
2046 waitq_init(selset, WQT_SELECT_SET, SYNC_POLICY_FIFO);
2047 selset->selset_id = select_set_nextid(true);
2048
2049 return selset;
2050 }
2051
2052 __abortlike
2053 static void
__select_set_link_arguments_panic(struct waitq * waitq,struct select_set * set)2054 __select_set_link_arguments_panic(struct waitq *waitq, struct select_set *set)
2055 {
2056 if (!waitq_valid(waitq)) {
2057 panic("Invalid waitq: %p", waitq);
2058 }
2059 if (waitq_type(waitq) != WQT_SELECT) {
2060 panic("Invalid waitq type: %p:%d", waitq, waitq->waitq_type);
2061 }
2062 panic("Invalid waitq-set: %p", set);
2063 }
2064
2065 static inline void
__select_set_link_arguments_validate(struct waitq * waitq,struct select_set * set)2066 __select_set_link_arguments_validate(struct waitq *waitq, struct select_set *set)
2067 {
2068 if (!waitq_valid(waitq) ||
2069 waitq_type(waitq) != WQT_SELECT ||
2070 waitq_type(set) != WQT_SELECT_SET) {
2071 __select_set_link_arguments_panic(waitq, set);
2072 }
2073 }
2074
2075 void
select_set_link(struct waitq * waitq,struct select_set * set,waitq_link_t * linkp)2076 select_set_link(struct waitq *waitq, struct select_set *set,
2077 waitq_link_t *linkp)
2078 {
2079 struct waitq_sellink *link;
2080
2081 __select_set_link_arguments_validate(waitq, set);
2082
2083 waitq_lock(waitq);
2084
2085 if (waitq == &select_conflict_queue) {
2086 waitq_lock(set);
2087 set->selset_conflict = true;
2088 waitq_unlock(set);
2089 }
2090
2091 wql_list_foreach(link, &waitq->waitq_sellinks) {
2092 if (waitq_same(wql_wqs(link), set)) {
2093 goto found;
2094 }
2095 }
2096
2097 link = linkp->wqls;
2098 *linkp = WQL_NULL;
2099 wql_list_push(&waitq->waitq_sellinks, link);
2100
2101 found:
2102 link->wql_wqs = (uintptr_t)set;
2103 link->wql_setid = set->selset_id;
2104 waitq_unlock(waitq);
2105 }
2106
2107 static void
select_set_unlink_conflict_queue(struct select_set * set)2108 select_set_unlink_conflict_queue(struct select_set *set)
2109 {
2110 struct waitq_link_list_entry **prev;
2111 struct waitq_sellink *link;
2112
2113 waitq_lock(&select_conflict_queue);
2114
2115 /*
2116 * We know the conflict queue is hooked,
2117 * so find the linkage and free it.
2118 */
2119 prev = &select_conflict_queue.waitq_sellinks.next;
2120 for (;;) {
2121 assert(*prev);
2122 link = wql_list_elem(*prev);
2123 if (waitq_same(wql_wqs(link), set)) {
2124 *prev = link->wql_next.next;
2125 break;
2126 }
2127 prev = &link->wql_next.next;
2128 }
2129
2130 waitq_unlock(&select_conflict_queue);
2131
2132 waitq_link_free(WQT_SELECT_SET, link);
2133 }
2134
2135 static void
__select_set_reset(struct select_set * set,bool invalidate)2136 __select_set_reset(struct select_set *set, bool invalidate)
2137 {
2138 if (set->selset_conflict) {
2139 select_set_unlink_conflict_queue(set);
2140 }
2141
2142 waitq_lock(set);
2143 if (invalidate) {
2144 waitq_invalidate(set);
2145 }
2146 set->selset_id = select_set_nextid(false);
2147 set->selset_preposted = 0;
2148 set->selset_conflict = 0;
2149 waitq_unlock(set);
2150 }
2151
2152 void
select_set_reset(struct select_set * set)2153 select_set_reset(struct select_set *set)
2154 {
2155 __select_set_reset(set, false);
2156 }
2157
2158 void
select_set_free(struct select_set * set)2159 select_set_free(struct select_set *set)
2160 {
2161 __select_set_reset(set, true);
2162 hw_lck_ticket_destroy(&set->selset_interlock, &waitq_lck_grp);
2163 zfree_id(ZONE_ID_SELECT_SET, set);
2164 }
2165
2166 void
select_waitq_wakeup_and_deinit(struct waitq * waitq,event64_t wake_event,wait_result_t result)2167 select_waitq_wakeup_and_deinit(
2168 struct waitq *waitq,
2169 event64_t wake_event,
2170 wait_result_t result)
2171 {
2172 waitq_link_list_t free_l = { };
2173
2174 if (waitq_is_valid(waitq)) {
2175 assert(waitq_type(waitq) == WQT_SELECT);
2176
2177 waitq_lock(waitq);
2178
2179 waitq_wakeup64_all_locked(waitq, wake_event, result,
2180 WAITQ_KEEP_LOCKED);
2181
2182 waitq_invalidate(waitq);
2183 free_l = waitq->waitq_sellinks;
2184 waitq->waitq_sellinks.next = NULL;
2185
2186 waitq_unlock(waitq);
2187
2188 waitq_link_free_list(WQT_SELECT, &free_l);
2189
2190 waitq_deinit(waitq);
2191 }
2192 }
2193
2194 #pragma mark assert_wait / wakeup (high level)
2195
2196 wait_result_t
waitq_assert_wait64(struct waitq * waitq,event64_t wait_event,wait_interrupt_t interruptible,uint64_t deadline)2197 waitq_assert_wait64(struct waitq *waitq,
2198 event64_t wait_event,
2199 wait_interrupt_t interruptible,
2200 uint64_t deadline)
2201 {
2202 thread_t thread = current_thread();
2203 wait_result_t ret;
2204 spl_t s = 0;
2205
2206 __waitq_validate(waitq);
2207
2208 if (waitq_irq_safe(waitq)) {
2209 s = splsched();
2210 }
2211 waitq_lock(waitq);
2212
2213 ret = waitq_assert_wait64_locked(waitq, wait_event, interruptible,
2214 TIMEOUT_URGENCY_SYS_NORMAL, deadline, TIMEOUT_NO_LEEWAY, thread);
2215
2216 waitq_unlock(waitq);
2217 if (waitq_irq_safe(waitq)) {
2218 splx(s);
2219 }
2220
2221 return ret;
2222 }
2223
2224 wait_result_t
waitq_assert_wait64_leeway(struct waitq * waitq,event64_t wait_event,wait_interrupt_t interruptible,wait_timeout_urgency_t urgency,uint64_t deadline,uint64_t leeway)2225 waitq_assert_wait64_leeway(struct waitq *waitq,
2226 event64_t wait_event,
2227 wait_interrupt_t interruptible,
2228 wait_timeout_urgency_t urgency,
2229 uint64_t deadline,
2230 uint64_t leeway)
2231 {
2232 wait_result_t ret;
2233 thread_t thread = current_thread();
2234 spl_t s = 0;
2235
2236 __waitq_validate(waitq);
2237
2238 if (waitq_irq_safe(waitq)) {
2239 s = splsched();
2240 }
2241 waitq_lock(waitq);
2242
2243 ret = waitq_assert_wait64_locked(waitq, wait_event, interruptible,
2244 urgency, deadline, leeway, thread);
2245
2246 waitq_unlock(waitq);
2247 if (waitq_irq_safe(waitq)) {
2248 splx(s);
2249 }
2250
2251 return ret;
2252 }
2253
2254 kern_return_t
waitq_wakeup64_one(waitq_t waitq,event64_t wake_event,wait_result_t result,waitq_wakeup_flags_t flags)2255 waitq_wakeup64_one(
2256 waitq_t waitq,
2257 event64_t wake_event,
2258 wait_result_t result,
2259 waitq_wakeup_flags_t flags)
2260 {
2261 kern_return_t kr;
2262 spl_t spl = 0;
2263
2264 __waitq_validate(waitq);
2265
2266 if (waitq_irq_safe(waitq)) {
2267 spl = splsched();
2268 }
2269 waitq_lock(waitq);
2270
2271 /* waitq is locked upon return */
2272 kr = waitq_wakeup64_one_locked(waitq, wake_event, result,
2273 flags | WAITQ_UNLOCK);
2274
2275 if (waitq_irq_safe(waitq)) {
2276 splx(spl);
2277 }
2278
2279 return kr;
2280 }
2281
2282 kern_return_t
waitq_wakeup64_all(waitq_t waitq,event64_t wake_event,wait_result_t result,waitq_wakeup_flags_t flags)2283 waitq_wakeup64_all(
2284 waitq_t waitq,
2285 event64_t wake_event,
2286 wait_result_t result,
2287 waitq_wakeup_flags_t flags)
2288 {
2289 kern_return_t ret;
2290 spl_t spl = 0;
2291
2292 __waitq_validate(waitq);
2293
2294 if (waitq_irq_safe(waitq)) {
2295 spl = splsched();
2296 }
2297 waitq_lock(waitq);
2298
2299 ret = waitq_wakeup64_all_locked(waitq, wake_event, result,
2300 flags | WAITQ_UNLOCK);
2301
2302 if (waitq_irq_safe(waitq)) {
2303 splx(spl);
2304 }
2305
2306 return ret;
2307 }
2308
2309 kern_return_t
waitq_wakeup64_thread(struct waitq * waitq,event64_t event,thread_t thread,wait_result_t result)2310 waitq_wakeup64_thread(
2311 struct waitq *waitq,
2312 event64_t event,
2313 thread_t thread,
2314 wait_result_t result)
2315 {
2316 spl_t s = splsched();
2317 kern_return_t ret;
2318
2319 __waitq_validate(waitq);
2320 assert(waitq_irq_safe(waitq));
2321 waitq_lock(waitq);
2322
2323 ret = waitq_wakeup64_thread_and_unlock(waitq, event, thread, result);
2324
2325 splx(s);
2326
2327 return ret;
2328 }
2329
2330 thread_t
waitq_wakeup64_identify(waitq_t waitq,event64_t wake_event,wait_result_t result,waitq_wakeup_flags_t flags)2331 waitq_wakeup64_identify(
2332 waitq_t waitq,
2333 event64_t wake_event,
2334 wait_result_t result,
2335 waitq_wakeup_flags_t flags)
2336 {
2337 spl_t thread_spl = 0;
2338 thread_t thread;
2339 spl_t spl = 0;
2340
2341 __waitq_validate(waitq);
2342
2343 if (waitq_irq_safe(waitq)) {
2344 spl = splsched();
2345 }
2346 waitq_lock(waitq);
2347
2348 thread = waitq_wakeup64_identify_locked(waitq, wake_event, result,
2349 flags | WAITQ_UNLOCK, &thread_spl);
2350 /* waitq is unlocked, thread is locked */
2351
2352 if (thread != THREAD_NULL) {
2353 thread_reference(thread);
2354 thread_unlock(thread);
2355 splx(thread_spl);
2356 }
2357
2358 if (waitq_irq_safe(waitq)) {
2359 splx(spl);
2360 }
2361
2362 /* returns +1 ref to running thread or THREAD_NULL */
2363 return thread;
2364 }
2365
2366
2367 #pragma mark tests
2368 #if DEBUG || DEVELOPMENT
2369
2370 #include <ipc/ipc_pset.h>
2371 #include <sys/errno.h>
2372
2373 #define MAX_GLOBAL_TEST_QUEUES 64
2374 static struct waitq wqt_waitq_array[MAX_GLOBAL_TEST_QUEUES];
2375 static bool wqt_running;
2376 static bool wqt_init;
2377
2378 static bool
wqt_start(const char * test,int64_t * out)2379 wqt_start(const char *test, int64_t *out)
2380 {
2381 if (os_atomic_xchg(&wqt_running, true, acquire)) {
2382 *out = 0;
2383 return false;
2384 }
2385
2386 if (!wqt_init) {
2387 wqt_init = true;
2388 for (int i = 0; i < MAX_GLOBAL_TEST_QUEUES; i++) {
2389 waitq_init(&wqt_waitq_array[i], WQT_PORT, SYNC_POLICY_FIFO);
2390 }
2391 }
2392
2393 printf("[WQ] starting %s\n", test);
2394 return true;
2395 }
2396
2397 static int
wqt_end(const char * test,int64_t * out)2398 wqt_end(const char *test, int64_t *out)
2399 {
2400 os_atomic_store(&wqt_running, false, release);
2401 printf("[WQ] done %s\n", test);
2402 *out = 1;
2403 return 0;
2404 }
2405
2406 static struct waitq *
wqt_wq(uint32_t index)2407 wqt_wq(uint32_t index)
2408 {
2409 return &wqt_waitq_array[index];
2410 }
2411
2412 static uint32_t
wqt_idx(struct waitq * waitq)2413 wqt_idx(struct waitq *waitq)
2414 {
2415 assert(waitq >= wqt_waitq_array &&
2416 waitq < wqt_waitq_array + MAX_GLOBAL_TEST_QUEUES);
2417 return (uint32_t)(waitq - wqt_waitq_array);
2418 }
2419
2420 __attribute__((overloadable))
2421 static uint64_t
wqt_bit(uint32_t index)2422 wqt_bit(uint32_t index)
2423 {
2424 return 1ull << index;
2425 }
2426
2427 __attribute__((overloadable))
2428 static uint64_t
wqt_bit(struct waitq * waitq)2429 wqt_bit(struct waitq *waitq)
2430 {
2431 return wqt_bit(wqt_idx(waitq));
2432 }
2433
2434 static struct waitq_set *
wqt_wqset_create(void)2435 wqt_wqset_create(void)
2436 {
2437 struct waitq_set *wqset;
2438
2439 wqset = &ipc_pset_alloc_special(ipc_space_kernel)->ips_wqset;
2440 printf("[WQ]: created waitq set %p\n", wqset);
2441 return wqset;
2442 }
2443
2444 static void
wqt_wqset_free(struct waitq_set * wqset)2445 wqt_wqset_free(struct waitq_set *wqset)
2446 {
2447 printf("[WQ]: destroying waitq set %p\n", wqset);
2448 waitq_lock(wqset);
2449 ipc_pset_destroy(ipc_space_kernel,
2450 __container_of(wqset, struct ipc_pset, ips_wqset));
2451 }
2452
2453 static void
wqt_link(uint32_t index,struct waitq_set * wqset,kern_return_t want)2454 wqt_link(uint32_t index, struct waitq_set *wqset, kern_return_t want)
2455 {
2456 struct waitq *waitq = wqt_wq(index);
2457 waitq_link_t link = waitq_link_alloc(WQT_PORT_SET);
2458 kern_return_t kr;
2459
2460 printf("[WQ]: linking waitq [%d] to global wqset (%p)\n", index, wqset);
2461
2462 waitq_lock(waitq);
2463 waitq_lock(wqset);
2464 kr = waitq_link_locked(waitq, wqset, &link);
2465 waitq_unlock(wqset);
2466 waitq_unlock(waitq);
2467
2468 if (link.wqlh) {
2469 waitq_link_free(WQT_PORT_SET, link);
2470 }
2471
2472 printf("[WQ]:\tkr=%d\texpected=%d\n", kr, want);
2473 assert(kr == want);
2474 }
2475
2476 static void
wqt_unlink(uint32_t index,struct waitq_set * wqset,kern_return_t want)2477 wqt_unlink(uint32_t index, struct waitq_set *wqset, kern_return_t want)
2478 {
2479 struct waitq *waitq = wqt_wq(index);
2480 waitq_link_t link;
2481 kern_return_t kr;
2482
2483 printf("[WQ]: unlinking waitq [%d] from global wqset (%p)\n",
2484 index, wqset);
2485
2486 waitq_lock(waitq);
2487 waitq_lock(wqset);
2488 link = waitq_unlink_locked(waitq, wqset);
2489 waitq_unlock(wqset);
2490 waitq_unlock(waitq);
2491
2492 if (link.wqlh) {
2493 waitq_link_free(WQT_PORT_SET, link);
2494 kr = KERN_SUCCESS;
2495 } else {
2496 kr = KERN_NOT_IN_SET;
2497 }
2498
2499 printf("[WQ]: \tkr=%d\n", kr);
2500 assert(kr == want);
2501 }
2502
2503 static void
wqt_wakeup_one(uint32_t index,event64_t event64,kern_return_t want)2504 wqt_wakeup_one(uint32_t index, event64_t event64, kern_return_t want)
2505 {
2506 kern_return_t kr;
2507
2508 printf("[WQ]: Waking one thread on waitq [%d] event:0x%llx\n",
2509 index, event64);
2510 kr = waitq_wakeup64_one(wqt_wq(index), event64,
2511 THREAD_AWAKENED, WAITQ_WAKEUP_DEFAULT);
2512 printf("[WQ]: \tkr=%d\n", kr);
2513 assert(kr == want);
2514 }
2515
2516 static void
wqt_clear_preposts(uint32_t idx)2517 wqt_clear_preposts(uint32_t idx)
2518 {
2519 waitq_lock(wqt_wq(idx));
2520 (void)waitq_clear_prepost_locked(wqt_wq(idx));
2521 waitq_unlock(wqt_wq(idx));
2522 }
2523
2524 static void
wqt_preposts_gc_locked(struct waitq_set * wqset)2525 wqt_preposts_gc_locked(struct waitq_set *wqset)
2526 {
2527 circle_queue_t q = &wqset->wqset_preposts;
2528 struct waitq_link *link;
2529 uint32_t ticket;
2530
2531 again:
2532 cqe_foreach_element_safe(link, q, wql_slink) {
2533 struct waitq *wq = link->wql_wq;
2534
2535 if (!waitq_lock_reserve(wq, &ticket)) {
2536 waitq_unlock(wqset);
2537 waitq_lock_wait(wq, ticket);
2538 waitq_lock(wqset);
2539 waitq_unlock(wq);
2540 /* the list was possibly mutated, restart */
2541 goto again;
2542 }
2543
2544 if (!wq->waitq_preposted) {
2545 wql_wqs_clear_preposted(link);
2546 circle_dequeue(q, &link->wql_slink);
2547 circle_enqueue_tail(&wqset->wqset_links, &link->wql_slink);
2548 }
2549
2550 waitq_unlock(wq);
2551 }
2552 }
2553
2554 static void
wqt_expect_preposts(struct waitq_set * wqset,uint64_t preposts)2555 wqt_expect_preposts(struct waitq_set *wqset, uint64_t preposts)
2556 {
2557 struct waitq_link *link;
2558 uint64_t found = 0;
2559
2560 waitq_lock(wqset);
2561
2562 wqt_preposts_gc_locked(wqset);
2563
2564 cqe_foreach_element(link, &wqset->wqset_preposts, wql_slink) {
2565 struct waitq *waitq = link->wql_wq;
2566
2567 printf("[WQ]: found prepost %d\n", wqt_idx(waitq));
2568 assertf((found & wqt_bit(waitq)) == 0,
2569 "found waitq %d twice", wqt_idx(waitq));
2570 found |= wqt_bit(waitq);
2571 }
2572
2573 waitq_unlock(wqset);
2574
2575 assertf(found == preposts, "preposts expected 0x%llx, but got 0x%llx",
2576 preposts, found);
2577 }
2578
2579 static int
waitq_basic_test(__unused int64_t in,int64_t * out)2580 waitq_basic_test(__unused int64_t in, int64_t *out)
2581 {
2582 struct waitq_set *wqset;
2583
2584 if (!wqt_start(__func__, out)) {
2585 return EBUSY;
2586 }
2587
2588 wqset = wqt_wqset_create();
2589 wqt_link(10, wqset, KERN_SUCCESS);
2590 wqt_link(10, wqset, KERN_ALREADY_IN_SET);
2591 wqt_link(11, wqset, KERN_SUCCESS);
2592 wqt_link(11, wqset, KERN_ALREADY_IN_SET);
2593 wqt_link(12, wqset, KERN_SUCCESS);
2594 wqt_link(12, wqset, KERN_ALREADY_IN_SET);
2595
2596 wqt_wakeup_one(10, NO_EVENT64, KERN_NOT_WAITING);
2597 wqt_wakeup_one(12, NO_EVENT64, KERN_NOT_WAITING);
2598
2599 wqt_expect_preposts(wqset, wqt_bit(10) | wqt_bit(12));
2600 wqt_clear_preposts(10);
2601
2602 wqt_expect_preposts(wqset, wqt_bit(12));
2603 wqt_clear_preposts(12);
2604
2605 wqt_expect_preposts(wqset, 0);
2606
2607 wqt_unlink(12, wqset, KERN_SUCCESS);
2608 wqt_unlink(12, wqset, KERN_NOT_IN_SET);
2609 wqt_unlink(11, wqset, KERN_SUCCESS);
2610 wqt_unlink(10, wqset, KERN_SUCCESS);
2611 wqt_wqset_free(wqset);
2612
2613 return wqt_end(__func__, out);
2614 }
2615 SYSCTL_TEST_REGISTER(waitq_basic, waitq_basic_test);
2616 #endif /* DEBUG || DEVELOPMENT */
2617