1 /*
2 * Copyright (c) 2017-2020 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 #include <kern/turnstile.h>
30 #include <kern/cpu_data.h>
31 #include <kern/mach_param.h>
32 #include <kern/kern_types.h>
33 #include <kern/assert.h>
34 #include <kern/kalloc.h>
35 #include <kern/thread.h>
36 #include <kern/clock.h>
37 #include <kern/policy_internal.h>
38 #include <kern/task.h>
39 #include <kern/waitq.h>
40 #include <kern/sched_prim.h>
41 #include <kern/zalloc.h>
42 #include <kern/debug.h>
43 #include <kern/compact_id.h>
44 #include <machine/limits.h>
45 #include <machine/atomic.h>
46
47 #include <pexpert/pexpert.h>
48 #include <os/hash.h>
49 #include <libkern/section_keywords.h>
50
51 static TUNABLE(int, turnstile_max_hop, "turnstile_max_hop", TURNSTILE_MAX_HOP_DEFAULT);
52 COMPACT_ID_TABLE_DEFINE(static, ctsid_table);
53 static_assert(CTSID_MAX <= COMPACT_ID_MAX);
54 static SECURITY_READ_ONLY_LATE(uint32_t) ctsid_nonce;
55
56 ZONE_DEFINE_ID(ZONE_ID_TURNSTILE, "turnstiles", struct turnstile,
57 ZC_ZFREE_CLEARMEM);
58
59 /* Global table for turnstile promote policy for all type of turnstiles */
60 static const turnstile_promote_policy_t turnstile_promote_policy[TURNSTILE_TOTAL_TYPES] = {
61 [TURNSTILE_NONE] = TURNSTILE_PROMOTE_NONE,
62 [TURNSTILE_KERNEL_MUTEX] = TURNSTILE_KERNEL_PROMOTE,
63 [TURNSTILE_ULOCK] = TURNSTILE_USER_PROMOTE,
64 [TURNSTILE_PTHREAD_MUTEX] = TURNSTILE_USER_PROMOTE,
65 [TURNSTILE_SYNC_IPC] = TURNSTILE_USER_IPC_PROMOTE,
66 [TURNSTILE_WORKLOOPS] = TURNSTILE_USER_IPC_PROMOTE,
67 [TURNSTILE_WORKQS] = TURNSTILE_USER_IPC_PROMOTE,
68 [TURNSTILE_KNOTE] = TURNSTILE_USER_IPC_PROMOTE,
69 [TURNSTILE_SLEEP_INHERITOR] = TURNSTILE_KERNEL_PROMOTE,
70 [TURNSTILE_EPOCH_KERNEL] = TURNSTILE_KERNEL_PROMOTE,
71 [TURNSTILE_EPOCH_USER] = TURNSTILE_USER_PROMOTE,
72 };
73
74 /* Global table for turnstile hash lock policy for all type of turnstiles */
75 static const turnstile_hash_lock_policy_t turnstile_hash_lock_policy[TURNSTILE_TOTAL_TYPES] = {
76 [TURNSTILE_NONE] = TURNSTILE_HASH_LOCK_POLICY_NONE,
77 [TURNSTILE_KERNEL_MUTEX] = TURNSTILE_HASH_LOCK_POLICY_NONE,
78 [TURNSTILE_ULOCK] = TURNSTILE_HASH_LOCK_POLICY_NONE,
79 [TURNSTILE_PTHREAD_MUTEX] = TURNSTILE_HASH_LOCK_POLICY_NONE,
80 [TURNSTILE_SYNC_IPC] = TURNSTILE_HASH_LOCK_POLICY_NONE,
81 [TURNSTILE_WORKLOOPS] = TURNSTILE_HASH_LOCK_POLICY_NONE,
82 [TURNSTILE_WORKQS] = TURNSTILE_HASH_LOCK_POLICY_NONE,
83 [TURNSTILE_KNOTE] = TURNSTILE_HASH_LOCK_POLICY_NONE,
84 [TURNSTILE_SLEEP_INHERITOR] = (TURNSTILE_IRQ_UNSAFE_HASH | TURNSTILE_LOCKED_HASH),
85 [TURNSTILE_EPOCH_KERNEL] = TURNSTILE_HASH_LOCK_POLICY_NONE,
86 [TURNSTILE_EPOCH_USER] = TURNSTILE_HASH_LOCK_POLICY_NONE,
87 };
88
89 os_refgrp_decl(static, turnstile_refgrp, "turnstile", NULL);
90
91 #if DEVELOPMENT || DEBUG
92 static LCK_GRP_DECLARE(turnstiles_dev_lock_grp, "turnstile_dev_lock");
93
94 /* Array to store stats for multi-hop boosting */
95 static struct turnstile_stats turnstile_boost_stats[TURNSTILE_MAX_HOP_DEFAULT] = {};
96 static struct turnstile_stats turnstile_unboost_stats[TURNSTILE_MAX_HOP_DEFAULT] = {};
97 uint64_t thread_block_on_turnstile_count;
98 uint64_t thread_block_on_regular_waitq_count;
99 #endif /* DEVELOPMENT || DEBUG */
100
101 #ifndef max
102 #define max(a, b) (((a) > (b)) ? (a) : (b))
103 #endif /* max */
104
105 /* Static function declarations */
106 static turnstile_type_t
107 turnstile_get_type(struct turnstile *turnstile);
108 static bool
109 turnstile_is_send_turnstile(struct turnstile *turnstile);
110 static uint32_t
111 turnstile_get_gencount(struct turnstile *turnstile);
112 static void
113 turnstile_set_type_and_increment_gencount(struct turnstile *turnstile, turnstile_type_t type);
114 static void
115 turnstile_destroy(struct turnstile *turnstile);
116 static void
117 turnstile_update_inheritor_workq_priority_chain(struct turnstile *in_turnstile, spl_t s);
118 static void
119 turnstile_update_inheritor_thread_priority_chain(struct turnstile **in_turnstile,
120 thread_t *out_thread, int total_hop, turnstile_stats_update_flags_t tsu_flags);
121 static void
122 turnstile_update_inheritor_turnstile_priority_chain(struct turnstile **in_out_turnstile,
123 int total_hop, turnstile_stats_update_flags_t tsu_flags);
124 static void
125 thread_update_waiting_turnstile_priority_chain(thread_t *in_thread,
126 struct turnstile **out_turnstile, int thread_hop, int total_hop,
127 turnstile_stats_update_flags_t tsu_flags);
128 static boolean_t
129 turnstile_update_turnstile_promotion_locked(struct turnstile *dst_turnstile,
130 struct turnstile *src_turnstile);
131 static boolean_t
132 turnstile_update_turnstile_promotion(struct turnstile *dst_turnstile,
133 struct turnstile *src_turnstile);
134 static boolean_t
135 turnstile_need_turnstile_promotion_update(struct turnstile *dst_turnstile,
136 struct turnstile *src_turnstile);
137 static boolean_t
138 turnstile_add_turnstile_promotion(struct turnstile *dst_turnstile,
139 struct turnstile *src_turnstile);
140 static boolean_t
141 turnstile_remove_turnstile_promotion(struct turnstile *dst_turnstile,
142 struct turnstile *src_turnstile);
143 static boolean_t
144 turnstile_update_thread_promotion_locked(struct turnstile *dst_turnstile,
145 thread_t thread);
146 static boolean_t
147 turnstile_need_thread_promotion_update(struct turnstile *dst_turnstile,
148 thread_t thread);
149 static boolean_t
150 thread_add_turnstile_promotion(
151 thread_t thread, struct turnstile *turnstile);
152 static boolean_t
153 thread_remove_turnstile_promotion(
154 thread_t thread, struct turnstile *turnstile);
155 static boolean_t
156 thread_needs_turnstile_promotion_update(thread_t thread,
157 struct turnstile *turnstile);
158 static boolean_t
159 thread_update_turnstile_promotion(
160 thread_t thread, struct turnstile *turnstile);
161 static boolean_t
162 thread_update_turnstile_promotion_locked(
163 thread_t thread, struct turnstile *turnstile);
164 static boolean_t
165 workq_add_turnstile_promotion(
166 struct workqueue *wq_inheritor, struct turnstile *turnstile);
167 static turnstile_stats_update_flags_t
168 thread_get_update_flags_for_turnstile_propagation_stoppage(thread_t thread);
169 static turnstile_stats_update_flags_t
170 turnstile_get_update_flags_for_above_UI_pri_change(struct turnstile *turnstile);
171 static void turnstile_stash_inheritor(turnstile_inheritor_t new_inheritor,
172 turnstile_update_flags_t flags);
173 static int turnstile_compute_thread_push(struct turnstile *turnstile, thread_t thread);
174
175 #if DEVELOPMENT || DEBUG
176 /* Test primitives and interfaces for testing turnstiles */
177 struct tstile_test_prim {
178 struct turnstile *ttprim_turnstile;
179 thread_t ttprim_owner;
180 lck_spin_t ttprim_interlock;
181 uint32_t tt_prim_waiters;
182 };
183
184 struct tstile_test_prim test_prim_ts_inline;
185 struct tstile_test_prim test_prim_global_htable;
186 struct tstile_test_prim test_prim_global_ts_kernel;
187 struct tstile_test_prim test_prim_global_ts_kernel_hash;
188
189 static void
190 tstile_test_prim_init(struct tstile_test_prim *test_prim_ptr);
191 #endif
192
193 union turnstile_type_gencount {
194 uint32_t value;
195 struct {
196 uint32_t ts_type:(8 * sizeof(turnstile_type_t)),
197 ts_gencount: (8 * (sizeof(uint32_t) - sizeof(turnstile_type_t)));
198 };
199 };
200
201 static turnstile_type_t
turnstile_get_type(struct turnstile * turnstile)202 turnstile_get_type(struct turnstile *turnstile)
203 {
204 union turnstile_type_gencount type_and_gencount;
205
206 type_and_gencount.value = atomic_load_explicit(&turnstile->ts_type_gencount, memory_order_relaxed);
207 return (turnstile_type_t) type_and_gencount.ts_type;
208 }
209
210 /* Only safe to be called from stackshot context */
211 static bool
turnstile_is_send_turnstile(struct turnstile * turnstile)212 turnstile_is_send_turnstile(struct turnstile *turnstile)
213 {
214 if (not_in_kdp) {
215 panic("turnstile_is_send_turnstile() called outside of kernel debugger context");
216 }
217
218 if (turnstile_get_type(turnstile) == TURNSTILE_SYNC_IPC) {
219 ipc_port_t port = (ipc_port_t) turnstile->ts_proprietor;
220
221 return port_send_turnstile(port) == turnstile;
222 }
223
224 return false;
225 }
226
227 /* Only safe to be called from stackshot context */
228 static bool
turnstile_is_receive_turnstile(struct turnstile * turnstile)229 turnstile_is_receive_turnstile(struct turnstile *turnstile)
230 {
231 if (not_in_kdp) {
232 panic("turnstile_is_receive_turnstile() called outside of kernel debugger context");
233 }
234
235 if (turnstile_get_type(turnstile) == TURNSTILE_SYNC_IPC) {
236 ipc_port_t port = (ipc_port_t) turnstile->ts_proprietor;
237
238 return *port_rcv_turnstile_address(port) == turnstile;
239 }
240
241 return false;
242 }
243
244 static uint32_t
turnstile_get_gencount(struct turnstile * turnstile)245 turnstile_get_gencount(struct turnstile *turnstile)
246 {
247 union turnstile_type_gencount type_and_gencount;
248
249 type_and_gencount.value = atomic_load_explicit(&turnstile->ts_type_gencount, memory_order_relaxed);
250 return (uint32_t) type_and_gencount.ts_gencount;
251 }
252
253 static void
turnstile_set_type_and_increment_gencount(struct turnstile * turnstile,turnstile_type_t type)254 turnstile_set_type_and_increment_gencount(struct turnstile *turnstile, turnstile_type_t type)
255 {
256 union turnstile_type_gencount type_and_gencount;
257
258 /* No need to compare exchange since the store happens under interlock of the primitive */
259 type_and_gencount.value = atomic_load_explicit(&turnstile->ts_type_gencount, memory_order_relaxed);
260 type_and_gencount.ts_type = type;
261 type_and_gencount.ts_gencount++;
262 atomic_store_explicit(&turnstile->ts_type_gencount, type_and_gencount.value, memory_order_relaxed);
263 }
264
265
266 /* Turnstile hashtable Implementation */
267
268 /*
269 * Maximum number of buckets in the turnstile hashtable. This number affects the
270 * performance of the hashtable since it determines the hash collision
271 * rate. To experiment with the number of buckets in this hashtable use the
272 * "ts_htable_buckets" boot-arg.
273 */
274 #define TURNSTILE_HTABLE_BUCKETS_DEFAULT 32
275 #define TURNSTILE_HTABLE_BUCKETS_MAX 1024
276
277 SLIST_HEAD(turnstile_hashlist, turnstile);
278
279 struct turnstile_htable_bucket {
280 lck_spin_t ts_ht_bucket_lock;
281 struct turnstile_hashlist ts_ht_bucket_list;
282 };
283
284 SECURITY_READ_ONLY_LATE(static uint32_t) ts_htable_buckets;
285 /* Global hashtable for turnstiles managed with interrupts disabled */
286 SECURITY_READ_ONLY_LATE(static struct turnstile_htable_bucket *)turnstile_htable_irq_safe;
287 /* Global hashtable for turnstiles managed with interrupts enabled */
288 SECURITY_READ_ONLY_LATE(static struct turnstile_htable_bucket *)turnstile_htable;
289
290
291 /* Bucket locks for turnstile hashtable */
292 LCK_GRP_DECLARE(turnstiles_htable_lock_grp, "turnstiles_htable_locks");
293
294 #define turnstile_bucket_lock_init(bucket) \
295 lck_spin_init(&bucket->ts_ht_bucket_lock, &turnstiles_htable_lock_grp, LCK_ATTR_NULL)
296 #define turnstile_bucket_lock(bucket) \
297 lck_spin_lock_grp(&bucket->ts_ht_bucket_lock, &turnstiles_htable_lock_grp)
298 #define turnstile_bucket_unlock(bucket) \
299 lck_spin_unlock(&bucket->ts_ht_bucket_lock)
300
301 #define kdp_turnstile_bucket_is_locked(bucket) \
302 kdp_lck_spin_is_acquired(&bucket->ts_ht_bucket_lock)
303
304 /*
305 * Name: turnstiles_hashtable_init
306 *
307 * Description: Initializes the global turnstile hash table.
308 *
309 * Args:
310 * None
311 *
312 * Returns:
313 * None
314 */
315 static void
turnstiles_hashtable_init(void)316 turnstiles_hashtable_init(void)
317 {
318 /* Initialize number of buckets in the hashtable */
319 if (PE_parse_boot_argn("ts_htable_buckets", &ts_htable_buckets, sizeof(ts_htable_buckets)) != TRUE) {
320 ts_htable_buckets = TURNSTILE_HTABLE_BUCKETS_DEFAULT;
321 }
322
323 assert(ts_htable_buckets <= TURNSTILE_HTABLE_BUCKETS_MAX);
324 uint32_t ts_htable_size = ts_htable_buckets * sizeof(struct turnstile_htable_bucket);
325 turnstile_htable_irq_safe = zalloc_permanent(ts_htable_size, ZALIGN_PTR);
326 if (turnstile_htable_irq_safe == NULL) {
327 panic("Turnstiles hash table memory allocation failed!");
328 }
329
330 turnstile_htable = zalloc_permanent(ts_htable_size, ZALIGN_PTR);
331 if (turnstile_htable == NULL) {
332 panic("Turnstiles hash table memory allocation failed!");
333 }
334
335 /* Initialize all the buckets of the hashtables */
336 for (uint32_t i = 0; i < ts_htable_buckets; i++) {
337 struct turnstile_htable_bucket *ts_bucket = &(turnstile_htable_irq_safe[i]);
338 turnstile_bucket_lock_init(ts_bucket);
339 SLIST_INIT(&ts_bucket->ts_ht_bucket_list);
340
341 ts_bucket = &(turnstile_htable[i]);
342 turnstile_bucket_lock_init(ts_bucket);
343 SLIST_INIT(&ts_bucket->ts_ht_bucket_list);
344 }
345 }
346
347 /*
348 * Name: turnstile_freelist_empty
349 *
350 * Description: Checks if the turnstile's freelist is empty
351 * Should be called with the primitive IL held.
352 *
353 * Args:
354 * Arg1: turnstile
355 *
356 * Returns:
357 * true if freelist is empty; false otherwise
358 */
359 static inline boolean_t
turnstile_freelist_empty(struct turnstile * ts)360 turnstile_freelist_empty(
361 struct turnstile *ts)
362 {
363 return SLIST_EMPTY(&ts->ts_free_turnstiles);
364 }
365
366
367 /*
368 * Name: turnstile_freelist_insert
369 *
370 * Description: Inserts the turnstile into the freelist of another turnstile
371 * Should be called with the primitive IL held.
372 *
373 * Args:
374 * Arg1: primitive turnstile
375 * Arg2: turnstile to add to the freelist
376 *
377 * Returns:
378 * None
379 */
380 static void
turnstile_freelist_insert(struct turnstile * dst_ts,struct turnstile * free_ts)381 turnstile_freelist_insert(
382 struct turnstile *dst_ts,
383 struct turnstile *free_ts)
384 {
385 assert(turnstile_get_type(dst_ts) == turnstile_get_type(free_ts));
386 assert(dst_ts->ts_proprietor == free_ts->ts_proprietor);
387 turnstile_state_add(free_ts, TURNSTILE_STATE_FREELIST);
388 SLIST_INSERT_HEAD(&dst_ts->ts_free_turnstiles, free_ts, ts_free_elm);
389 }
390
391 /*
392 * Name: turnstile_freelist_remove
393 *
394 * Description: Removes a turnstile from the freelist of a turnstile
395 * Should be called with the primitive IL held.
396 *
397 * Args:
398 * Arg1: primitive turnstile
399 *
400 * Returns:
401 * turnstile removed from the freelist
402 */
403 static struct turnstile *
turnstile_freelist_remove(struct turnstile * ts)404 turnstile_freelist_remove(
405 struct turnstile *ts)
406 {
407 struct turnstile *ret_turnstile = TURNSTILE_NULL;
408 assert(!SLIST_EMPTY(&ts->ts_free_turnstiles));
409 ret_turnstile = SLIST_FIRST(&ts->ts_free_turnstiles);
410 SLIST_REMOVE_HEAD(&ts->ts_free_turnstiles, ts_free_elm);
411 assert(ret_turnstile != TURNSTILE_NULL);
412 turnstile_state_remove(ret_turnstile, TURNSTILE_STATE_FREELIST);
413 /* Need to initialize the list again, since head and elm are in union */
414 SLIST_INIT(&ret_turnstile->ts_free_turnstiles);
415 return ret_turnstile;
416 }
417
418 /*
419 * Name: turnstile_hash
420 *
421 * Description: Calculates the hash bucket index for a given proprietor
422 *
423 * Args:
424 * Arg1: proprietor (key) for hashing
425 *
426 * Returns:
427 * hash table bucket index for provided proprietor
428 */
429 static inline uint32_t
turnstile_hash(uintptr_t proprietor)430 turnstile_hash(uintptr_t proprietor)
431 {
432 uint32_t hash = os_hash_kernel_pointer((void *)proprietor);
433 return hash & (ts_htable_buckets - 1);
434 }
435
436 static inline struct turnstile_htable_bucket *
turnstile_get_bucket(uint32_t index,turnstile_type_t type)437 turnstile_get_bucket(uint32_t index, turnstile_type_t type)
438 {
439 struct turnstile_htable_bucket *ts_bucket;
440 int hash_policy = turnstile_hash_lock_policy[type];
441
442 if (hash_policy & TURNSTILE_IRQ_UNSAFE_HASH) {
443 ts_bucket = &(turnstile_htable[index]);
444 } else {
445 ts_bucket = &(turnstile_htable_irq_safe[index]);
446 }
447
448 return ts_bucket;
449 }
450
451 /*
452 * Name: turnstile_hash_bucket_lock
453 *
454 * Description: locks the spinlock associated with proprietor's bucket.
455 * if proprietor is specified the index for the hash will be
456 * recomputed and returned in index_proprietor,
457 * otherwise the value save in index_proprietor is used as index.
458 *
459 * Args:
460 * Arg1: proprietor (key) for hashing
461 * Arg2: index for proprietor in the hash
462 * Arg3: turnstile type
463 *
464 * Returns: old value of irq if irq were disabled before acquiring the lock.
465 */
466 unsigned
turnstile_hash_bucket_lock(uintptr_t proprietor,uint32_t * index_proprietor,turnstile_type_t type)467 turnstile_hash_bucket_lock(uintptr_t proprietor, uint32_t *index_proprietor, turnstile_type_t type)
468 {
469 struct turnstile_htable_bucket *ts_bucket;
470 int hash_policy = turnstile_hash_lock_policy[type];
471 bool irq_safe = !(hash_policy & TURNSTILE_IRQ_UNSAFE_HASH);
472 spl_t ret = 0;
473 uint32_t index;
474
475 /*
476 * If the proprietor is specified, the caller doesn't know
477 * the index in the hash, so compute it.
478 * Otherwise use the value of index provided.
479 */
480 if (proprietor) {
481 index = turnstile_hash(proprietor);
482 *index_proprietor = index;
483 } else {
484 index = *index_proprietor;
485 }
486
487 ts_bucket = turnstile_get_bucket(index, type);
488
489 if (irq_safe) {
490 ret = splsched();
491 }
492
493 turnstile_bucket_lock(ts_bucket);
494
495 return ret;
496 }
497
498 /*
499 * Name: turnstile_hash_bucket_unlock
500 *
501 * Description: unlocks the spinlock associated with proprietor's bucket.
502 * if proprietor is specified the index for the hash will be
503 * recomputed and returned in index_proprietor,
504 * otherwise the value save in index_proprietor is used as index.
505 *
506 * Args:
507 * Arg1: proprietor (key) for hashing
508 * Arg2: index for proprietor in the hash
509 * Arg3: turnstile type
510 * Arg4: irq value returned by turnstile_hash_bucket_lock
511 *
512 */
513 void
turnstile_hash_bucket_unlock(uintptr_t proprietor,uint32_t * index_proprietor,turnstile_type_t type,unsigned s)514 turnstile_hash_bucket_unlock(uintptr_t proprietor, uint32_t *index_proprietor, turnstile_type_t type, unsigned s)
515 {
516 struct turnstile_htable_bucket *ts_bucket;
517 int hash_policy = turnstile_hash_lock_policy[type];
518 bool irq_safe = !(hash_policy & TURNSTILE_IRQ_UNSAFE_HASH);
519 uint32_t index;
520
521 /*
522 * If the proprietor is specified, the caller doesn't know
523 * the index in the hash, so compute it.
524 * Otherwise use the value of index provided.
525 */
526 if (proprietor) {
527 index = turnstile_hash(proprietor);
528 *index_proprietor = index;
529 } else {
530 index = *index_proprietor;
531 }
532 ts_bucket = turnstile_get_bucket(index, type);
533
534 turnstile_bucket_unlock(ts_bucket);
535 if (irq_safe) {
536 splx(s);
537 }
538 }
539
540 /*
541 * Name: turnstile_htable_lookup_add
542 *
543 * Description: Lookup the proprietor in the global turnstile hash table.
544 * If an entry is present, add the new turnstile to the entry's freelist.
545 * Otherwise add the passed in turnstile for that proprietor.
546 * The routine assumes that the turnstile->proprietor does not change
547 * while the turnstile is in the global hash table.
548 *
549 * Args:
550 * Arg1: proprietor
551 * Arg2: new turnstile for primitive
552 * Arg3: turnstile_type_t type
553 *
554 * Returns:
555 * Previous turnstile for proprietor in the hash table
556 */
557 static struct turnstile *
turnstile_htable_lookup_add(uintptr_t proprietor,struct turnstile * new_turnstile,turnstile_type_t type)558 turnstile_htable_lookup_add(
559 uintptr_t proprietor,
560 struct turnstile *new_turnstile,
561 turnstile_type_t type)
562 {
563 uint32_t index = turnstile_hash(proprietor);
564 assert(index < ts_htable_buckets);
565 struct turnstile_htable_bucket *ts_bucket;
566 int hash_policy = turnstile_hash_lock_policy[type];
567 bool needs_lock = !(hash_policy & TURNSTILE_LOCKED_HASH);
568 bool irq_safe = !(hash_policy & TURNSTILE_IRQ_UNSAFE_HASH);
569 spl_t s;
570
571 ts_bucket = turnstile_get_bucket(index, type);
572
573 if (needs_lock) {
574 if (irq_safe) {
575 s = splsched();
576 }
577 turnstile_bucket_lock(ts_bucket);
578 }
579
580 struct turnstile *ts;
581
582 SLIST_FOREACH(ts, &ts_bucket->ts_ht_bucket_list, ts_htable_link) {
583 if (ts->ts_proprietor == proprietor) {
584 /*
585 * Found an entry in the hashtable for this proprietor; add thread turnstile to freelist
586 * and return this turnstile
587 */
588 if (needs_lock) {
589 turnstile_bucket_unlock(ts_bucket);
590 if (irq_safe) {
591 splx(s);
592 }
593 }
594 turnstile_freelist_insert(ts, new_turnstile);
595 return ts;
596 }
597 }
598
599 /* No entry for this proprietor; add the new turnstile in the hash table */
600 SLIST_INSERT_HEAD(&ts_bucket->ts_ht_bucket_list, new_turnstile, ts_htable_link);
601 turnstile_state_add(new_turnstile, TURNSTILE_STATE_HASHTABLE);
602 if (needs_lock) {
603 turnstile_bucket_unlock(ts_bucket);
604 if (irq_safe) {
605 splx(s);
606 }
607 }
608 /* Since there was no previous entry for this proprietor, return TURNSTILE_NULL */
609 return TURNSTILE_NULL;
610 }
611
612 /*
613 * Name: turnstable_htable_lookup_remove
614 *
615 * Description: Lookup the proprietor in the global turnstile hash table.
616 * For the turnstile in the hash table, if the freelist has turnstiles on it
617 * return one of them from the freelist. Otherwise remove the turnstile from
618 * the hashtable and return that.
619 * The routine assumes that the turnstile->proprietor does not change
620 * while the turnstile is in the global hash table.
621 *
622 * Args:
623 * Arg1: proprietor
624 * Arg2: free turnstile to be returned
625 * Arg3: turnstile_type_t type
626 *
627 * Returns:
628 * turnstile for this proprietor in the hashtable after the removal
629 */
630 static struct turnstile *
turnstable_htable_lookup_remove(uintptr_t proprietor,struct turnstile ** free_turnstile,turnstile_type_t type)631 turnstable_htable_lookup_remove(
632 uintptr_t proprietor,
633 struct turnstile **free_turnstile,
634 turnstile_type_t type)
635 {
636 uint32_t index = turnstile_hash(proprietor);
637 assert(index < ts_htable_buckets);
638 struct turnstile_htable_bucket *ts_bucket;
639 struct turnstile *ret_turnstile = TURNSTILE_NULL;
640 int hash_policy = turnstile_hash_lock_policy[type];
641 bool needs_lock = !(hash_policy & TURNSTILE_LOCKED_HASH);
642 bool irq_safe = !(hash_policy & TURNSTILE_IRQ_UNSAFE_HASH);
643 spl_t s;
644
645 ts_bucket = turnstile_get_bucket(index, type);
646
647 if (needs_lock) {
648 if (irq_safe) {
649 s = splsched();
650 }
651 turnstile_bucket_lock(ts_bucket);
652 }
653
654 struct turnstile *ts, **prev_tslink;
655 /* Find the turnstile for the given proprietor in the hashtable */
656 SLIST_FOREACH_PREVPTR(ts, prev_tslink, &ts_bucket->ts_ht_bucket_list, ts_htable_link) {
657 if (ts->ts_proprietor == proprietor) {
658 ret_turnstile = ts;
659 break;
660 }
661 }
662 assert(ret_turnstile != TURNSTILE_NULL);
663
664 /* Check if the turnstile has any turnstiles on its freelist */
665 if (turnstile_freelist_empty(ret_turnstile)) {
666 /* No turnstiles on the freelist; remove the turnstile from the hashtable and mark it freed */
667 *prev_tslink = SLIST_NEXT(ret_turnstile, ts_htable_link);
668 turnstile_state_remove(ret_turnstile, TURNSTILE_STATE_HASHTABLE);
669 if (needs_lock) {
670 turnstile_bucket_unlock(ts_bucket);
671 if (irq_safe) {
672 splx(s);
673 }
674 }
675 *free_turnstile = ret_turnstile;
676 return TURNSTILE_NULL;
677 } else {
678 /*
679 * Turnstile has free turnstiles on its list; leave the hashtable unchanged
680 * and return the first turnstile in the freelist as the free turnstile
681 */
682 if (needs_lock) {
683 turnstile_bucket_unlock(ts_bucket);
684 if (irq_safe) {
685 splx(s);
686 }
687 }
688 *free_turnstile = turnstile_freelist_remove(ret_turnstile);
689 return ret_turnstile;
690 }
691 }
692
693 /*
694 * Name: turnstile_htable_lookup
695 *
696 * Description: Lookup the proprietor in the global turnstile hash table.
697 * The routine assumes that the turnstile->proprietor does not change
698 * while the turnstile is in the global hash table.
699 *
700 * Args:
701 * Arg1: proprietor
702 * Arg2: turnstile_type_t type
703 *
704 * Returns:
705 * Turnstile for proprietor in the hash table
706 */
707 static struct turnstile *
turnstile_htable_lookup(uintptr_t proprietor,turnstile_type_t type)708 turnstile_htable_lookup(
709 uintptr_t proprietor,
710 turnstile_type_t type)
711 {
712 uint32_t index = turnstile_hash(proprietor);
713 assert(index < ts_htable_buckets);
714 bool kdp_ctx = !not_in_kdp;
715 struct turnstile_htable_bucket *ts_bucket = turnstile_get_bucket(index, type);
716 int hash_policy = turnstile_hash_lock_policy[type];
717 bool needs_lock = !(hash_policy & TURNSTILE_LOCKED_HASH);
718 bool irq_safe = !(hash_policy & TURNSTILE_IRQ_UNSAFE_HASH);
719 spl_t s;
720
721 if (needs_lock) {
722 if (irq_safe && !kdp_ctx) {
723 s = splsched();
724 }
725
726 if (kdp_ctx) {
727 if (kdp_turnstile_bucket_is_locked(ts_bucket)) {
728 /* This should move to TURNSTILE_BUSY once 51725781 is in the build */
729 return TURNSTILE_NULL;
730 }
731 } else {
732 turnstile_bucket_lock(ts_bucket);
733 }
734 }
735 struct turnstile *ts = TURNSTILE_NULL;
736 struct turnstile *ret_turnstile = TURNSTILE_NULL;
737
738 SLIST_FOREACH(ts, &ts_bucket->ts_ht_bucket_list, ts_htable_link) {
739 if (ts->ts_proprietor == proprietor) {
740 /* Found an entry in the hashtable for this proprietor */
741 ret_turnstile = ts;
742 break;
743 }
744 }
745
746 if (needs_lock && !kdp_ctx) {
747 turnstile_bucket_unlock(ts_bucket);
748 if (irq_safe) {
749 splx(s);
750 }
751 }
752
753 return ret_turnstile;
754 }
755
756 /*
757 * Name: turnstiles_init
758 *
759 * Description: Initialize turnstile sub system.
760 *
761 * Args: None.
762 *
763 * Returns: None.
764 */
765 void
turnstiles_init(void)766 turnstiles_init(void)
767 {
768 ctsid_nonce = (uint32_t)early_random() & CTSID_MASK;
769 turnstiles_hashtable_init();
770
771 #if DEVELOPMENT || DEBUG
772 /* Initialize turnstile test primitive */
773 tstile_test_prim_init(&test_prim_ts_inline);
774 tstile_test_prim_init(&test_prim_global_htable);
775 tstile_test_prim_init(&test_prim_global_ts_kernel);
776 tstile_test_prim_init(&test_prim_global_ts_kernel_hash);
777 #endif
778 }
779
780 /*
781 * Name: turnstile_alloc
782 *
783 * Description: Allocate a turnstile.
784 *
785 * Args: None.
786 *
787 * Returns:
788 * turnstile on Success.
789 */
790 struct turnstile *
turnstile_alloc(void)791 turnstile_alloc(void)
792 {
793 struct turnstile *turnstile = TURNSTILE_NULL;
794
795 turnstile = zalloc_id(ZONE_ID_TURNSTILE, Z_WAITOK | Z_ZERO | Z_NOFAIL);
796
797 /* Initialize the waitq */
798 waitq_init(&turnstile->ts_waitq, WQT_TURNSTILE,
799 SYNC_POLICY_REVERSED | SYNC_POLICY_INIT_LOCKED);
800
801 SLIST_INIT(&turnstile->ts_free_turnstiles);
802
803 turnstile_set_type_and_increment_gencount(turnstile, TURNSTILE_NONE);
804 turnstile_state_init(turnstile, TURNSTILE_STATE_THREAD);
805 os_ref_init_raw(&turnstile->ts_refcount, &turnstile_refgrp);
806 priority_queue_init(&turnstile->ts_inheritor_queue);
807
808 #if DEVELOPMENT || DEBUG
809 turnstile->ts_thread = current_thread();
810 #endif
811
812 waitq_unlock(&turnstile->ts_waitq);
813
814 return turnstile;
815 }
816
817 /*
818 * Name: turnstile_reference
819 *
820 * Description: Take a reference on the turnstile.
821 *
822 * Arg1: turnstile
823 *
824 * Returns: None.
825 */
826 void
turnstile_reference(struct turnstile * turnstile)827 turnstile_reference(struct turnstile *turnstile)
828 {
829 if (turnstile == TURNSTILE_NULL) {
830 return;
831 }
832
833 zone_id_require(ZONE_ID_TURNSTILE, sizeof(struct turnstile), turnstile);
834
835 os_ref_retain_raw(&turnstile->ts_refcount, &turnstile_refgrp);
836 }
837
838 /*
839 * Name: turnstile_deallocate
840 *
841 * Description: Drop a reference on the turnstile.
842 * Destroy the turnstile if the last ref.
843 *
844 * Arg1: turnstile
845 *
846 * Returns: None.
847 */
848 void
turnstile_deallocate(struct turnstile * turnstile)849 turnstile_deallocate(struct turnstile *turnstile)
850 {
851 if (turnstile == TURNSTILE_NULL) {
852 return;
853 }
854
855 if (__improbable(os_ref_release_raw(&turnstile->ts_refcount,
856 &turnstile_refgrp) == 0)) {
857 assert(SLIST_EMPTY(&turnstile->ts_free_turnstiles));
858 turnstile_destroy(turnstile);
859 }
860 }
861
862 /*
863 * Name: turnstile_destroy
864 *
865 * Description: Deallocates the turnstile.
866 *
867 * Args:
868 * Arg1: turnstile
869 *
870 * Returns: None.
871 */
872 static void
turnstile_destroy(struct turnstile * turnstile)873 turnstile_destroy(struct turnstile *turnstile)
874 {
875 assert3u(turnstile->ts_compact_id, ==, 0);
876 assert3u(turnstile->ts_prim_count, ==, 0);
877 assert3p(turnstile->ts_inheritor, ==, TURNSTILE_INHERITOR_NULL);
878 assert3u(turnstile->ts_state, ==, TURNSTILE_STATE_THREAD);
879
880 waitq_deinit(&turnstile->ts_waitq);
881
882 zfree_id(ZONE_ID_TURNSTILE, turnstile);
883 }
884
885 static uint32_t
ctsid_mangle(compact_id_t cid)886 ctsid_mangle(compact_id_t cid)
887 {
888 return (cid == ctsid_nonce ? CTSID_MASK : cid) ^ ctsid_nonce;
889 }
890
891 static compact_id_t
ctsid_unmangle(uint32_t ctsid)892 ctsid_unmangle(uint32_t ctsid)
893 {
894 ctsid ^= ctsid_nonce;
895 return ctsid == CTSID_MASK ? ctsid_nonce : ctsid;
896 }
897
898 uint32_t
turnstile_compact_id_get(void)899 turnstile_compact_id_get(void)
900 {
901 compact_id_t cid;
902
903 cid = compact_id_get(&ctsid_table, CTSID_MAX, NULL);
904 return ctsid_mangle(cid);
905 }
906
907 void
turnstile_compact_id_put(uint32_t ctsid)908 turnstile_compact_id_put(uint32_t ctsid)
909 {
910 assert3u(ctsid, !=, 0);
911 compact_id_put(&ctsid_table, ctsid_unmangle(ctsid));
912 }
913
914 /*
915 * Name: turnstile_get_by_id
916 *
917 * Description: Resolve a turnstile by compact ID
918 *
919 * Args:
920 * Arg1: turnstile compact ID
921 *
922 * Returns: a turnstile
923 */
924 struct turnstile *
turnstile_get_by_id(uint32_t tsid)925 turnstile_get_by_id(uint32_t tsid)
926 {
927 struct turnstile *ts = TURNSTILE_NULL;
928
929 if (tsid) {
930 ts = *compact_id_resolve(&ctsid_table, ctsid_unmangle(tsid));
931 assert(ts && ts->ts_compact_id == tsid);
932 }
933 return ts;
934 }
935
936 /*
937 * Name: turnstile_prepare variants
938 *
939 * Description: Transfer current thread's turnstile to primitive or it's free turnstile list.
940 * Function is called holding the interlock (spinlock) of the primitive.
941 */
942 static struct turnstile *
turnstile_prepare_common(uintptr_t proprietor,thread_t thread,struct turnstile * incoming,turnstile_type_t type)943 turnstile_prepare_common(
944 uintptr_t proprietor,
945 thread_t thread,
946 struct turnstile *incoming,
947 turnstile_type_t type)
948 {
949 struct turnstile *thread_turnstile = incoming;
950
951 /* Get the thread's turnstile if no turnstile provided */
952 if (thread_turnstile == TURNSTILE_NULL) {
953 thread_turnstile = thread->turnstile;
954 assert(thread_turnstile != TURNSTILE_NULL);
955 assert(thread->inheritor == NULL);
956 thread->turnstile = TURNSTILE_NULL;
957 }
958
959 /* Prepare the thread turnstile to be the primitive turnstile */
960 SLIST_INIT(&thread_turnstile->ts_free_turnstiles);
961 turnstile_set_type_and_increment_gencount(thread_turnstile, type);
962 thread_turnstile->ts_inheritor = TURNSTILE_INHERITOR_NULL;
963 thread_turnstile->ts_proprietor = proprietor;
964 turnstile_state_remove(thread_turnstile, TURNSTILE_STATE_THREAD);
965
966 thread_turnstile->ts_priority = 0;
967 #if DEVELOPMENT || DEBUG
968 thread_turnstile->ts_prev_thread = thread_turnstile->ts_thread;
969 thread_turnstile->ts_thread = NULL;
970 #endif
971
972 return thread_turnstile;
973 }
974
975 struct turnstile *
turnstile_prepare(uintptr_t proprietor,struct turnstile ** tstore,struct turnstile * turnstile,turnstile_type_t type)976 turnstile_prepare(
977 uintptr_t proprietor,
978 struct turnstile **tstore,
979 struct turnstile *turnstile,
980 turnstile_type_t type)
981 {
982 thread_t thread = current_thread();
983 struct turnstile *ret_turnstile = TURNSTILE_NULL;
984 struct turnstile *thread_turnstile;
985
986 thread_turnstile = turnstile_prepare_common(proprietor, thread,
987 turnstile, type);
988
989 /*
990 * If the primitive stores the turnstile,
991 * If there is already a turnstile, put the thread_turnstile if the primitive currently does not have a
992 * turnstile.
993 * Else, add the thread turnstile to freelist of the primitive turnstile.
994 */
995 ret_turnstile = *tstore;
996 if (*tstore == TURNSTILE_NULL) {
997 turnstile_state_add(thread_turnstile, TURNSTILE_STATE_PROPRIETOR);
998 *tstore = thread_turnstile;
999 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1000 (TURNSTILE_CODE(TURNSTILE_FREELIST_OPERATIONS, (TURNSTILE_PREPARE))) | DBG_FUNC_NONE,
1001 VM_KERNEL_UNSLIDE_OR_PERM(thread_turnstile),
1002 VM_KERNEL_UNSLIDE_OR_PERM(proprietor),
1003 turnstile_get_type(thread_turnstile), 0, 0);
1004 } else {
1005 turnstile_freelist_insert(ret_turnstile, thread_turnstile);
1006 }
1007 ret_turnstile = *tstore;
1008
1009 return ret_turnstile;
1010 }
1011
1012 struct turnstile *
turnstile_prepare_compact_id(uintptr_t proprietor,uint32_t compact_id,turnstile_type_t type)1013 turnstile_prepare_compact_id(
1014 uintptr_t proprietor,
1015 uint32_t compact_id,
1016 turnstile_type_t type)
1017 {
1018 thread_t thread = current_thread();
1019 struct turnstile *ret_turnstile = TURNSTILE_NULL;
1020 struct turnstile *thread_turnstile;
1021 uint32_t ctsid = thread->ctsid;
1022
1023 thread_turnstile = turnstile_prepare_common(proprietor, thread,
1024 TURNSTILE_NULL, type);
1025
1026 assert3u(ctsid, !=, 0);
1027 thread_turnstile->ts_compact_id = ctsid;
1028 thread->ctsid = 0;
1029
1030 /*
1031 * If the primitive stores the turnstile,
1032 * If there is already a turnstile, put the thread_turnstile if the primitive currently does not have a
1033 * turnstile.
1034 * Else, add the thread turnstile to freelist of the primitive turnstile.
1035 */
1036 if (compact_id == 0) {
1037 turnstile_state_add(thread_turnstile, TURNSTILE_STATE_PROPRIETOR);
1038 ret_turnstile = thread_turnstile;
1039 *compact_id_resolve(&ctsid_table, ctsid_unmangle(ctsid)) = ret_turnstile;
1040 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1041 (TURNSTILE_CODE(TURNSTILE_FREELIST_OPERATIONS, (TURNSTILE_PREPARE))) | DBG_FUNC_NONE,
1042 VM_KERNEL_UNSLIDE_OR_PERM(thread_turnstile),
1043 VM_KERNEL_UNSLIDE_OR_PERM(proprietor),
1044 turnstile_get_type(thread_turnstile), 0, 0);
1045 } else {
1046 ret_turnstile = turnstile_get_by_id(compact_id);
1047 turnstile_freelist_insert(ret_turnstile, thread_turnstile);
1048 }
1049
1050 return ret_turnstile;
1051 }
1052
1053 struct turnstile *
turnstile_prepare_hash(uintptr_t proprietor,turnstile_type_t type)1054 turnstile_prepare_hash(
1055 uintptr_t proprietor,
1056 turnstile_type_t type)
1057 {
1058 thread_t thread = current_thread();
1059 struct turnstile *ret_turnstile = TURNSTILE_NULL;
1060 struct turnstile *thread_turnstile;
1061
1062 thread_turnstile = turnstile_prepare_common(proprietor, thread,
1063 TURNSTILE_NULL, type);
1064
1065 /*
1066 * Lookup the primitive in the turnstile hash table and see if it already has an entry.
1067 */
1068 ret_turnstile = turnstile_htable_lookup_add(proprietor, thread_turnstile, type);
1069 if (ret_turnstile == NULL) {
1070 ret_turnstile = thread_turnstile;
1071 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1072 (TURNSTILE_CODE(TURNSTILE_FREELIST_OPERATIONS, (TURNSTILE_PREPARE))) | DBG_FUNC_NONE,
1073 VM_KERNEL_UNSLIDE_OR_PERM(thread_turnstile),
1074 VM_KERNEL_UNSLIDE_OR_PERM(proprietor),
1075 turnstile_get_type(thread_turnstile), 0, 0);
1076 }
1077
1078 return ret_turnstile;
1079 }
1080
1081 /*
1082 * Name: turnstile_complete variants
1083 *
1084 * Description: Transfer the primitive's turnstile or from it's freelist to current thread.
1085 * Current thread will have a turnstile attached to it after this call.
1086 */
1087 static void
turnstile_complete_common(uintptr_t proprietor __kdebug_only,thread_t thread,struct turnstile * thread_turnstile,struct turnstile * primitive_turnstile,struct turnstile ** out_turnstile)1088 turnstile_complete_common(
1089 uintptr_t proprietor __kdebug_only,
1090 thread_t thread,
1091 struct turnstile *thread_turnstile,
1092 struct turnstile *primitive_turnstile,
1093 struct turnstile **out_turnstile)
1094 {
1095 if (primitive_turnstile == NULL) {
1096 /*
1097 * Primitive no longer has a turnstile associated with it, thread_turnstile
1098 * was the last turnstile attached to primitive, clear out the inheritor and
1099 * set the old inheritor for turnstile cleanup.
1100 */
1101 if (thread_turnstile->ts_inheritor != TURNSTILE_INHERITOR_NULL) {
1102 turnstile_update_inheritor(thread_turnstile, TURNSTILE_INHERITOR_NULL,
1103 (TURNSTILE_IMMEDIATE_UPDATE | TURNSTILE_INHERITOR_THREAD));
1104 /*
1105 * old inheritor is set in curret thread and its priority propagation
1106 * will happen in turnstile cleanup call
1107 */
1108 }
1109 thread_turnstile->ts_proprietor = TURNSTILE_PROPRIETOR_NULL;
1110 assert(thread_turnstile->ts_inheritor == TURNSTILE_INHERITOR_NULL);
1111
1112 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1113 (TURNSTILE_CODE(TURNSTILE_FREELIST_OPERATIONS, (TURNSTILE_COMPLETE))) | DBG_FUNC_NONE,
1114 VM_KERNEL_UNSLIDE_OR_PERM(thread_turnstile),
1115 VM_KERNEL_UNSLIDE_OR_PERM(proprietor),
1116 turnstile_get_type(thread_turnstile), 0, 0);
1117 } else {
1118 /* If primitive's turnstile needs priority update, set it up for turnstile cleanup */
1119 if (turnstile_recompute_priority(primitive_turnstile)) {
1120 turnstile_reference(primitive_turnstile);
1121 thread->inheritor = primitive_turnstile;
1122 thread->inheritor_flags = (TURNSTILE_INHERITOR_TURNSTILE |
1123 TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE);
1124 }
1125 }
1126
1127 turnstile_set_type_and_increment_gencount(thread_turnstile, TURNSTILE_NONE);
1128 #if DEVELOPMENT || DEBUG
1129 thread_turnstile->ts_prev_thread = NULL;
1130 thread_turnstile->ts_thread = thread;
1131 #endif
1132
1133 turnstile_state_add(thread_turnstile, TURNSTILE_STATE_THREAD);
1134 if (out_turnstile == NULL) {
1135 /* Prepare the turnstile to become the thread's turnstile */
1136 thread->turnstile = thread_turnstile;
1137 } else {
1138 *out_turnstile = thread_turnstile;
1139 }
1140 }
1141
1142 void
turnstile_complete(uintptr_t proprietor,struct turnstile ** tstore,struct turnstile ** out_turnstile,turnstile_type_t type __unused)1143 turnstile_complete(
1144 uintptr_t proprietor,
1145 struct turnstile **tstore,
1146 struct turnstile **out_turnstile,
1147 turnstile_type_t type __unused)
1148 {
1149 thread_t thread = current_thread();
1150 struct turnstile *primitive_turnstile = TURNSTILE_NULL;
1151 struct turnstile *thread_turnstile = TURNSTILE_NULL;
1152
1153 assert(thread->inheritor == NULL);
1154
1155 /*
1156 * If the primitive stores the turnstile, check if the primitive turnstile
1157 * has any turnstiles on its freelist.
1158 */
1159 assert(*tstore != TURNSTILE_NULL);
1160 if (turnstile_freelist_empty(*tstore)) {
1161 /* Last turnstile scenario; remove the primitive->turnstile */
1162 thread_turnstile = *tstore;
1163 *tstore = TURNSTILE_NULL;
1164 turnstile_state_remove(thread_turnstile, TURNSTILE_STATE_PROPRIETOR);
1165 } else {
1166 /* Freelist has turnstiles; remove one from the freelist */
1167 thread_turnstile = turnstile_freelist_remove(*tstore);
1168 }
1169 primitive_turnstile = *tstore;
1170
1171 turnstile_complete_common(proprietor, thread, thread_turnstile,
1172 primitive_turnstile, out_turnstile);
1173 }
1174
1175 bool
turnstile_complete_compact_id(uintptr_t proprietor,struct turnstile * primitive_turnstile,turnstile_type_t type __unused)1176 turnstile_complete_compact_id(
1177 uintptr_t proprietor,
1178 struct turnstile *primitive_turnstile,
1179 turnstile_type_t type __unused)
1180 {
1181 thread_t thread = current_thread();
1182 struct turnstile *thread_turnstile = TURNSTILE_NULL;
1183 bool was_last = turnstile_freelist_empty(primitive_turnstile);
1184 uint32_t ctsid;
1185
1186 assert(thread->inheritor == NULL);
1187
1188 assert(primitive_turnstile != TURNSTILE_NULL);
1189 if (was_last) {
1190 /* Last turnstile scenario; remove the primitive->turnstile */
1191 thread_turnstile = primitive_turnstile;
1192 turnstile_state_remove(thread_turnstile, TURNSTILE_STATE_PROPRIETOR);
1193 primitive_turnstile = TURNSTILE_NULL;
1194 } else {
1195 /* Freelist has turnstiles; remove one from the freelist */
1196 thread_turnstile = turnstile_freelist_remove(primitive_turnstile);
1197 }
1198
1199 ctsid = thread_turnstile->ts_compact_id;
1200 assert3u(ctsid, !=, 0);
1201 thread_turnstile->ts_compact_id = 0;
1202 thread->ctsid = ctsid;
1203 if (was_last) {
1204 *compact_id_resolve(&ctsid_table, ctsid_unmangle(ctsid)) = NULL;
1205 }
1206
1207 turnstile_complete_common(proprietor, thread, thread_turnstile,
1208 primitive_turnstile, NULL);
1209
1210 return was_last;
1211 }
1212
1213 void
turnstile_complete_hash(uintptr_t proprietor,turnstile_type_t type)1214 turnstile_complete_hash(
1215 uintptr_t proprietor,
1216 turnstile_type_t type)
1217 {
1218 thread_t thread = current_thread();
1219 struct turnstile *primitive_turnstile = TURNSTILE_NULL;
1220 struct turnstile *thread_turnstile = TURNSTILE_NULL;
1221
1222 assert(thread->inheritor == NULL);
1223
1224 /* Use the global hash to find and remove a turnstile */
1225 primitive_turnstile = turnstable_htable_lookup_remove(proprietor, &thread_turnstile, type);
1226
1227 turnstile_complete_common(proprietor, thread, thread_turnstile,
1228 primitive_turnstile, NULL);
1229 }
1230
1231 /*
1232 * Name: turnstile_kernel_update_inheritor_on_wake_locked
1233 *
1234 * Description: Set thread as the inheritor of the turnstile and
1235 * boost the inheritor.
1236 * Args:
1237 * Arg1: turnstile
1238 * Arg2: new_inheritor
1239 * Arg3: flags
1240 *
1241 * Called with turnstile locked
1242 */
1243 void
turnstile_kernel_update_inheritor_on_wake_locked(struct turnstile * turnstile,turnstile_inheritor_t new_inheritor,turnstile_update_flags_t flags __assert_only)1244 turnstile_kernel_update_inheritor_on_wake_locked(
1245 struct turnstile *turnstile,
1246 turnstile_inheritor_t new_inheritor,
1247 turnstile_update_flags_t flags __assert_only)
1248 {
1249 /* for now only kernel primitives are allowed to call this function */
1250 __assert_only turnstile_promote_policy_t policy =
1251 turnstile_promote_policy[turnstile_get_type(turnstile)];
1252
1253 assert(flags & TURNSTILE_INHERITOR_THREAD);
1254 assert(policy == TURNSTILE_KERNEL_PROMOTE || policy == TURNSTILE_USER_PROMOTE);
1255
1256 turnstile_stash_inheritor((thread_t)new_inheritor, TURNSTILE_INHERITOR_THREAD);
1257 /*
1258 * new_inheritor has just been removed from the turnstile waitq,
1259 * the turnstile new priority needs to be recomputed so that
1260 * when new_inheritor will become this turnstile inheritor can
1261 * inherit the correct priority.
1262 */
1263 turnstile_recompute_priority_locked(turnstile);
1264 turnstile_update_inheritor_locked(turnstile);
1265 }
1266
1267 /*
1268 * Name: turnstile_update_inheritor_locked
1269 *
1270 * Description: Update the inheritor of the turnstile and boost the
1271 * inheritor, called with turnstile locked.
1272 *
1273 * Args:
1274 * Arg1: turnstile
1275 * Implicit arg: new inheritor value is stashed in current thread's struct
1276 *
1277 * Returns:
1278 * old inheritor reference is returned on current thread's struct.
1279 */
1280 void
turnstile_update_inheritor_locked(struct turnstile * turnstile)1281 turnstile_update_inheritor_locked(
1282 struct turnstile *turnstile)
1283 {
1284 turnstile_inheritor_t old_inheritor = turnstile->ts_inheritor;
1285 turnstile_update_flags_t old_inheritor_flags = turnstile->ts_inheritor_flags;
1286 thread_t thread = current_thread();
1287 boolean_t old_inheritor_needs_update = FALSE;
1288 boolean_t new_inheritor_needs_update = FALSE;
1289 turnstile_stats_update_flags_t tsu_flags =
1290 turnstile_get_update_flags_for_above_UI_pri_change(turnstile);
1291
1292 assert(waitq_held(&turnstile->ts_waitq));
1293
1294 /*
1295 * Get the new inheritor value from current thread's
1296 * struct, the value was stashed by turnstile_update_inheritor
1297 */
1298 turnstile_inheritor_t new_inheritor = thread->inheritor;
1299 turnstile_update_flags_t new_inheritor_flags = thread->inheritor_flags;
1300
1301 switch (turnstile_promote_policy[turnstile_get_type(turnstile)]) {
1302 case TURNSTILE_USER_PROMOTE:
1303 case TURNSTILE_USER_IPC_PROMOTE:
1304 break;
1305 case TURNSTILE_KERNEL_PROMOTE:
1306 /* some sanity checks, turnstile kernel can push just between threads */
1307 if (old_inheritor) {
1308 assert(old_inheritor_flags & TURNSTILE_INHERITOR_THREAD);
1309 }
1310
1311 if (new_inheritor) {
1312 assert(new_inheritor_flags & TURNSTILE_INHERITOR_THREAD);
1313 }
1314
1315 break;
1316 default:
1317 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile));
1318 }
1319
1320 /* Check if update is needed */
1321 if (old_inheritor == new_inheritor && old_inheritor == NULL) {
1322 goto done;
1323 }
1324
1325 if (old_inheritor == new_inheritor) {
1326 if (new_inheritor_flags & TURNSTILE_INHERITOR_THREAD) {
1327 thread_t thread_inheritor = (thread_t)new_inheritor;
1328
1329 assert(old_inheritor_flags & TURNSTILE_INHERITOR_THREAD);
1330
1331 /* adjust turnstile position in the thread's inheritor list */
1332 new_inheritor_needs_update = thread_update_turnstile_promotion(
1333 thread_inheritor, turnstile);
1334 } else if (new_inheritor_flags & TURNSTILE_INHERITOR_TURNSTILE) {
1335 struct turnstile *inheritor_turnstile = new_inheritor;
1336
1337 assert(old_inheritor_flags & TURNSTILE_INHERITOR_TURNSTILE);
1338
1339 new_inheritor_needs_update = turnstile_update_turnstile_promotion(
1340 inheritor_turnstile, turnstile);
1341 } else if (new_inheritor_flags & TURNSTILE_INHERITOR_WORKQ) {
1342 /*
1343 * When we are still picking "WORKQ" then possible racing
1344 * updates will call redrive through their own propagation
1345 * and we don't need to update anything here.
1346 */
1347 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED |
1348 TSU_TURNSTILE_ARG | TSU_BOOST_ARG, turnstile);
1349 } else {
1350 panic("Inheritor flags lost along the way");
1351 }
1352
1353 /* Update turnstile stats */
1354 if (!new_inheritor_needs_update) {
1355 turnstile_stats_update(1, TSU_PRI_PROPAGATION |
1356 TSU_TURNSTILE_ARG | TSU_BOOST_ARG | tsu_flags, turnstile);
1357 }
1358 goto done;
1359 }
1360
1361 if (old_inheritor != NULL) {
1362 if (old_inheritor_flags & TURNSTILE_INHERITOR_THREAD) {
1363 thread_t thread_inheritor = (thread_t)old_inheritor;
1364
1365 /* remove turnstile from thread's inheritor list */
1366 old_inheritor_needs_update = thread_remove_turnstile_promotion(thread_inheritor, turnstile);
1367 } else if (old_inheritor_flags & TURNSTILE_INHERITOR_TURNSTILE) {
1368 struct turnstile *old_turnstile = old_inheritor;
1369
1370 old_inheritor_needs_update = turnstile_remove_turnstile_promotion(
1371 old_turnstile, turnstile);
1372 } else if (old_inheritor_flags & TURNSTILE_INHERITOR_WORKQ) {
1373 /*
1374 * We don't need to do anything when the push was WORKQ
1375 * because nothing is pushed on in the first place.
1376 */
1377 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED |
1378 TSU_TURNSTILE_ARG, turnstile);
1379 } else {
1380 panic("Inheritor flags lost along the way");
1381 }
1382 /* Update turnstile stats */
1383 if (!old_inheritor_needs_update) {
1384 turnstile_stats_update(1, TSU_PRI_PROPAGATION | TSU_TURNSTILE_ARG,
1385 turnstile);
1386 }
1387 }
1388
1389 if (new_inheritor != NULL) {
1390 if (new_inheritor_flags & TURNSTILE_INHERITOR_THREAD) {
1391 thread_t thread_inheritor = (thread_t)new_inheritor;
1392
1393 assert(new_inheritor_flags & TURNSTILE_INHERITOR_THREAD);
1394 /* add turnstile to thread's inheritor list */
1395 new_inheritor_needs_update = thread_add_turnstile_promotion(
1396 thread_inheritor, turnstile);
1397 } else if (new_inheritor_flags & TURNSTILE_INHERITOR_TURNSTILE) {
1398 struct turnstile *new_turnstile = new_inheritor;
1399
1400 new_inheritor_needs_update = turnstile_add_turnstile_promotion(
1401 new_turnstile, turnstile);
1402 } else if (new_inheritor_flags & TURNSTILE_INHERITOR_WORKQ) {
1403 struct workqueue *wq_inheritor = new_inheritor;
1404
1405 new_inheritor_needs_update = workq_add_turnstile_promotion(
1406 wq_inheritor, turnstile);
1407 if (!new_inheritor_needs_update) {
1408 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED |
1409 TSU_TURNSTILE_ARG | TSU_BOOST_ARG, turnstile);
1410 }
1411 } else {
1412 panic("Inheritor flags lost along the way");
1413 }
1414 /* Update turnstile stats */
1415 if (!new_inheritor_needs_update) {
1416 turnstile_stats_update(1, TSU_PRI_PROPAGATION |
1417 TSU_TURNSTILE_ARG | TSU_BOOST_ARG | tsu_flags, turnstile);
1418 }
1419 }
1420
1421 done:
1422 if (old_inheritor_needs_update) {
1423 old_inheritor_flags |= TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE;
1424 }
1425
1426 /*
1427 * If new inheritor needs priority updated, then set TURNSTILE_NEEDS_PRI_UPDATE
1428 * on the old_inheritor_flags which will be copied to the thread.
1429 */
1430 if (new_inheritor_needs_update) {
1431 old_inheritor_flags |= TURNSTILE_NEEDS_PRI_UPDATE;
1432 }
1433
1434 turnstile->ts_inheritor = new_inheritor;
1435 turnstile->ts_inheritor_flags = new_inheritor_flags;
1436 thread->inheritor = old_inheritor;
1437 thread->inheritor_flags = old_inheritor_flags;
1438 return;
1439 }
1440
1441 /*
1442 * Name: turnstile_stash_inheritor
1443 *
1444 * Description: Save the new inheritor reference of the turnstile on the
1445 * current thread. It will take a thread reference on the inheritor.
1446 * Called with the interlock of the primitive held.
1447 *
1448 * Args:
1449 * Arg1: inheritor
1450 * Arg2: flags
1451 *
1452 * Returns:
1453 * old inheritor reference is stashed on current thread's struct.
1454 */
1455 static void
turnstile_stash_inheritor(turnstile_inheritor_t new_inheritor,turnstile_update_flags_t flags)1456 turnstile_stash_inheritor(
1457 turnstile_inheritor_t new_inheritor,
1458 turnstile_update_flags_t flags)
1459 {
1460 thread_t thread = current_thread();
1461
1462 /*
1463 * Set the inheritor on calling thread struct, no need
1464 * to take the turnstile waitq lock since the inheritor
1465 * is protected by the primitive's interlock
1466 */
1467 assert(thread->inheritor == TURNSTILE_INHERITOR_NULL);
1468 thread->inheritor = new_inheritor;
1469 thread->inheritor_flags = TURNSTILE_UPDATE_FLAGS_NONE;
1470 if (new_inheritor == TURNSTILE_INHERITOR_NULL) {
1471 /* nothing to retain or remember */
1472 } else if (flags & TURNSTILE_INHERITOR_THREAD) {
1473 thread->inheritor_flags |= TURNSTILE_INHERITOR_THREAD;
1474 thread_reference((thread_t)new_inheritor);
1475 } else if (flags & TURNSTILE_INHERITOR_TURNSTILE) {
1476 thread->inheritor_flags |= TURNSTILE_INHERITOR_TURNSTILE;
1477 turnstile_reference((struct turnstile *)new_inheritor);
1478 } else if (flags & TURNSTILE_INHERITOR_WORKQ) {
1479 thread->inheritor_flags |= TURNSTILE_INHERITOR_WORKQ;
1480 workq_reference((struct workqueue *)new_inheritor);
1481 } else {
1482 panic("Missing type in flags (%x) for inheritor (%p)", flags,
1483 new_inheritor);
1484 }
1485 }
1486
1487 /*
1488 * Name: turnstile_update_inheritor
1489 *
1490 * Description: Update the inheritor of the turnstile and boost the
1491 * inheritor. It will take a thread reference on the inheritor.
1492 * Called with the interlock of the primitive held.
1493 *
1494 * Args:
1495 * Arg1: turnstile
1496 * Arg2: inheritor
1497 * Arg3: flags - TURNSTILE_DELAYED_UPDATE - update will happen later in assert_wait
1498 *
1499 * Returns:
1500 * old inheritor reference is stashed on current thread's struct.
1501 */
1502 void
turnstile_update_inheritor(struct turnstile * turnstile,turnstile_inheritor_t new_inheritor,turnstile_update_flags_t flags)1503 turnstile_update_inheritor(
1504 struct turnstile *turnstile,
1505 turnstile_inheritor_t new_inheritor,
1506 turnstile_update_flags_t flags)
1507 {
1508 spl_t spl;
1509
1510 turnstile_stash_inheritor(new_inheritor, flags);
1511
1512 /* Do not perform the update if delayed update is specified */
1513 if (flags & TURNSTILE_DELAYED_UPDATE) {
1514 return;
1515 }
1516
1517 /* lock the turnstile waitq */
1518 spl = splsched();
1519 waitq_lock(&turnstile->ts_waitq);
1520
1521 turnstile_update_inheritor_locked(turnstile);
1522
1523 waitq_unlock(&turnstile->ts_waitq);
1524 splx(spl);
1525
1526 return;
1527 }
1528
1529
1530 /*
1531 * Name: turnstile_need_thread_promotion_update
1532 *
1533 * Description: Check if thread's place in the turnstile waitq needs to be updated.
1534 *
1535 * Arg1: dst turnstile
1536 * Arg2: thread
1537 *
1538 * Returns: TRUE: if turnstile_update_thread_promotion_locked needs to be called.
1539 * FALSE: otherwise.
1540 *
1541 * Condition: thread locked.
1542 */
1543 static boolean_t
turnstile_need_thread_promotion_update(struct turnstile * dst_turnstile,thread_t thread)1544 turnstile_need_thread_promotion_update(
1545 struct turnstile *dst_turnstile,
1546 thread_t thread)
1547 {
1548 int thread_link_priority;
1549 boolean_t needs_update = FALSE;
1550
1551 thread_link_priority = priority_queue_entry_sched_pri(
1552 &dst_turnstile->ts_waitq.waitq_prio_queue,
1553 &thread->wait_prioq_links);
1554
1555 int priority = turnstile_compute_thread_push(dst_turnstile, thread);
1556
1557 needs_update = (thread_link_priority == priority) ? FALSE : TRUE;
1558
1559 return needs_update;
1560 }
1561
1562 /*
1563 * Name: turnstile_priority_queue_update_entry_key
1564 *
1565 * Description: Updates the priority of an entry in a priority queue
1566 *
1567 * Arg1: a turnstile/thread/... priority queue
1568 * Arg2: the element to change the priority of
1569 * Arg3: the new priority
1570 *
1571 * Returns: whether the maximum priority of the queue changed.
1572 */
1573 static boolean_t
turnstile_priority_queue_update_entry_key(struct priority_queue_sched_max * q,priority_queue_entry_sched_t elt,priority_queue_key_t pri)1574 turnstile_priority_queue_update_entry_key(struct priority_queue_sched_max *q,
1575 priority_queue_entry_sched_t elt, priority_queue_key_t pri)
1576 {
1577 priority_queue_key_t old_key = priority_queue_max_sched_pri(q);
1578
1579 if (priority_queue_entry_sched_pri(q, elt) < pri) {
1580 priority_queue_entry_set_sched_pri(q, elt, pri, false);
1581 if (priority_queue_entry_increased(q, elt)) {
1582 return old_key != priority_queue_max_sched_pri(q);
1583 }
1584 } else if (priority_queue_entry_sched_pri(q, elt) > pri) {
1585 priority_queue_entry_set_sched_pri(q, elt, pri, false);
1586 if (priority_queue_entry_decreased(q, elt)) {
1587 return old_key != priority_queue_max_sched_pri(q);
1588 }
1589 }
1590
1591 return FALSE;
1592 }
1593
1594 /*
1595 * Name: turnstile_update_thread_promotion_locked
1596 *
1597 * Description: Update dst turnstile's inheritor link since one of the waiting
1598 * thread's priority has changed.
1599 *
1600 * Arg1: dst turnstile
1601 * Arg2: thread
1602 *
1603 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
1604 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
1605 *
1606 * Condition: dst turnstile and thread are locked.
1607 */
1608 static boolean_t
turnstile_update_thread_promotion_locked(struct turnstile * dst_turnstile,thread_t thread)1609 turnstile_update_thread_promotion_locked(
1610 struct turnstile *dst_turnstile,
1611 thread_t thread)
1612 {
1613 int thread_link_priority;
1614
1615 int priority = turnstile_compute_thread_push(dst_turnstile, thread);
1616
1617 thread_link_priority = priority_queue_entry_sched_pri(
1618 &dst_turnstile->ts_waitq.waitq_prio_queue,
1619 &thread->wait_prioq_links);
1620
1621 if (priority != thread_link_priority) {
1622 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1623 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS, (THREAD_MOVED_IN_TURNSTILE_WAITQ))) | DBG_FUNC_NONE,
1624 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile),
1625 thread_tid(thread),
1626 priority,
1627 thread_link_priority, 0);
1628 }
1629
1630 if (!turnstile_priority_queue_update_entry_key(
1631 &dst_turnstile->ts_waitq.waitq_prio_queue,
1632 &thread->wait_prioq_links, (priority_queue_key_t)priority)) {
1633 return FALSE;
1634 }
1635
1636 /* Update dst turnstile's priority */
1637 return turnstile_recompute_priority_locked(dst_turnstile);
1638 }
1639
1640 /*
1641 * Name: thread_add_turnstile_promotion
1642 *
1643 * Description: Add a turnstile to thread's inheritor list and update thread's priority.
1644 *
1645 * Arg1: thread
1646 * Arg2: turnstile
1647 *
1648 * Returns: TRUE: if the thread's priority has changed and needs propagation.
1649 * FALSE: if the thread's priority did not change or it does not need propagation.
1650 *
1651 * Condition: turnstile locked.
1652 */
1653 static boolean_t
thread_add_turnstile_promotion(thread_t thread,struct turnstile * turnstile)1654 thread_add_turnstile_promotion(
1655 thread_t thread,
1656 struct turnstile *turnstile)
1657 {
1658 boolean_t needs_update = FALSE;
1659
1660 /* Update the pairing heap */
1661 thread_lock(thread);
1662
1663 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1664 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS, (TURNSTILE_ADDED_TO_THREAD_HEAP))) | DBG_FUNC_NONE,
1665 thread_tid(thread),
1666 VM_KERNEL_UNSLIDE_OR_PERM(turnstile),
1667 turnstile->ts_priority, 0, 0);
1668
1669 priority_queue_entry_init(&turnstile->ts_inheritor_links);
1670
1671 switch (turnstile_promote_policy[turnstile_get_type(turnstile)]) {
1672 case TURNSTILE_USER_PROMOTE:
1673 case TURNSTILE_USER_IPC_PROMOTE:
1674
1675 priority_queue_entry_set_sched_pri(&thread->base_inheritor_queue,
1676 &turnstile->ts_inheritor_links, turnstile->ts_priority, false);
1677 if (priority_queue_insert(&thread->base_inheritor_queue,
1678 &turnstile->ts_inheritor_links)) {
1679 needs_update = thread_recompute_user_promotion_locked(thread);
1680 }
1681
1682 break;
1683 case TURNSTILE_KERNEL_PROMOTE:
1684
1685 priority_queue_entry_set_sched_pri(&thread->sched_inheritor_queue,
1686 &turnstile->ts_inheritor_links, turnstile->ts_priority, false);
1687 if (priority_queue_insert(&thread->sched_inheritor_queue,
1688 &turnstile->ts_inheritor_links)) {
1689 needs_update = thread_recompute_kernel_promotion_locked(thread);
1690 }
1691
1692 break;
1693 default:
1694 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile));
1695 }
1696
1697 /* Update turnstile stats */
1698 if (!needs_update) {
1699 turnstile_stats_update(1,
1700 thread_get_update_flags_for_turnstile_propagation_stoppage(thread) |
1701 TSU_TURNSTILE_ARG | TSU_BOOST_ARG,
1702 turnstile);
1703 }
1704
1705 thread_unlock(thread);
1706
1707 return needs_update;
1708 }
1709
1710 /*
1711 * Name: thread_remove_turnstile_promotion
1712 *
1713 * Description: Remove turnstile from thread's inheritor list and update thread's priority.
1714 *
1715 * Arg1: thread
1716 * Arg2: turnstile
1717 *
1718 * Returns: TRUE: if the thread's priority has changed and needs propagation.
1719 * FALSE: if the thread's priority did not change or it does not need propagation.
1720 *
1721 * Condition: turnstile locked.
1722 */
1723 static boolean_t
thread_remove_turnstile_promotion(thread_t thread,struct turnstile * turnstile)1724 thread_remove_turnstile_promotion(
1725 thread_t thread,
1726 struct turnstile *turnstile)
1727 {
1728 boolean_t needs_update = FALSE;
1729
1730 thread_lock(thread);
1731
1732 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1733 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS, (TURNSTILE_REMOVED_FROM_THREAD_HEAP))) | DBG_FUNC_NONE,
1734 thread_tid(thread),
1735 VM_KERNEL_UNSLIDE_OR_PERM(turnstile),
1736 0, 0, 0);
1737
1738 /* Update the pairing heap */
1739
1740 switch (turnstile_promote_policy[turnstile_get_type(turnstile)]) {
1741 case TURNSTILE_USER_PROMOTE:
1742 case TURNSTILE_USER_IPC_PROMOTE:
1743 if (priority_queue_remove(&thread->base_inheritor_queue,
1744 &turnstile->ts_inheritor_links)) {
1745 needs_update = thread_recompute_user_promotion_locked(thread);
1746 }
1747 break;
1748 case TURNSTILE_KERNEL_PROMOTE:
1749 if (priority_queue_remove(&thread->sched_inheritor_queue,
1750 &turnstile->ts_inheritor_links)) {
1751 needs_update = thread_recompute_kernel_promotion_locked(thread);
1752 }
1753 break;
1754 default:
1755 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile));
1756 }
1757
1758 /* Update turnstile stats */
1759 if (!needs_update) {
1760 turnstile_stats_update(1,
1761 thread_get_update_flags_for_turnstile_propagation_stoppage(thread) | TSU_TURNSTILE_ARG,
1762 turnstile);
1763 }
1764
1765 thread_unlock(thread);
1766
1767 return needs_update;
1768 }
1769
1770 /*
1771 * Name: thread_needs_turnstile_promotion_update
1772 *
1773 * Description: Check if turnstile position in thread's inheritor list needs to be updated.
1774 *
1775 * Arg1: thread
1776 * Arg2: turnstile
1777 *
1778 * Returns: TRUE: if thread_update_turnstile_promotion needs to be called.
1779 * FALSE: otherwise.
1780 *
1781 * Condition: turnstile locked.
1782 */
1783 static boolean_t
thread_needs_turnstile_promotion_update(thread_t thread __assert_only,struct turnstile * turnstile)1784 thread_needs_turnstile_promotion_update(
1785 thread_t thread __assert_only,
1786 struct turnstile *turnstile)
1787 {
1788 boolean_t needs_update = FALSE;
1789 int turnstile_link_priority = 0;
1790
1791 switch (turnstile_promote_policy[turnstile_get_type(turnstile)]) {
1792 case TURNSTILE_USER_PROMOTE:
1793 case TURNSTILE_USER_IPC_PROMOTE:
1794 turnstile_link_priority = priority_queue_entry_sched_pri(
1795 &thread->base_inheritor_queue,
1796 &turnstile->ts_inheritor_links);
1797 break;
1798 case TURNSTILE_KERNEL_PROMOTE:
1799 turnstile_link_priority = priority_queue_entry_sched_pri(
1800 &thread->sched_inheritor_queue,
1801 &turnstile->ts_inheritor_links);
1802 break;
1803 default:
1804 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile));
1805 }
1806
1807 needs_update = (turnstile_link_priority == turnstile->ts_priority) ? FALSE : TRUE;
1808 return needs_update;
1809 }
1810
1811 /*
1812 * Name: thread_update_turnstile_promotion_locked
1813 *
1814 * Description: Update turnstile position in thread's inheritor list and update thread's priority.
1815 *
1816 * Arg1: thread
1817 * Arg2: turnstile
1818 *
1819 * Returns: TRUE: if the thread's priority has changed and needs propagation.
1820 * FALSE: if the thread's priority did not change or it does not need propagation.
1821 *
1822 * Condition: turnstile and thread are locked.
1823 */
1824 static boolean_t
thread_update_turnstile_promotion_locked(thread_t thread,struct turnstile * turnstile)1825 thread_update_turnstile_promotion_locked(
1826 thread_t thread,
1827 struct turnstile *turnstile)
1828 {
1829 boolean_t needs_update = FALSE;
1830 int turnstile_link_priority = 0;
1831
1832 switch (turnstile_promote_policy[turnstile_get_type(turnstile)]) {
1833 case TURNSTILE_USER_PROMOTE:
1834 case TURNSTILE_USER_IPC_PROMOTE:
1835 turnstile_link_priority = priority_queue_entry_sched_pri(
1836 &thread->base_inheritor_queue,
1837 &turnstile->ts_inheritor_links);
1838
1839 if (turnstile_priority_queue_update_entry_key(&(thread->base_inheritor_queue),
1840 &turnstile->ts_inheritor_links, turnstile->ts_priority)) {
1841 needs_update = thread_recompute_user_promotion_locked(thread);
1842 }
1843 break;
1844 case TURNSTILE_KERNEL_PROMOTE:
1845 turnstile_link_priority = priority_queue_entry_sched_pri(
1846 &thread->sched_inheritor_queue,
1847 &turnstile->ts_inheritor_links);
1848
1849 if (turnstile_priority_queue_update_entry_key(&(thread->sched_inheritor_queue),
1850 &turnstile->ts_inheritor_links, turnstile->ts_priority)) {
1851 needs_update = thread_recompute_kernel_promotion_locked(thread);
1852 }
1853 break;
1854 default:
1855 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile));
1856 }
1857
1858 if (turnstile->ts_priority != turnstile_link_priority) {
1859 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1860 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS, (TURNSTILE_MOVED_IN_THREAD_HEAP))) | DBG_FUNC_NONE,
1861 thread_tid(thread),
1862 VM_KERNEL_UNSLIDE_OR_PERM(turnstile),
1863 turnstile->ts_priority,
1864 turnstile_link_priority, 0);
1865 }
1866
1867 return needs_update;
1868 }
1869
1870
1871 /*
1872 * Name: thread_update_turnstile_promotion
1873 *
1874 * Description: Update turnstile position in thread's inheritor list and update thread's priority.
1875 *
1876 * Arg1: thread
1877 * Arg2: turnstile
1878 *
1879 * Returns: TRUE: if the thread's priority has changed and needs propagation.
1880 * FALSE: if the thread's priority did not change or it does not need propagation.
1881 *
1882 * Condition: turnstile locked.
1883 */
1884 static boolean_t
thread_update_turnstile_promotion(thread_t thread,struct turnstile * turnstile)1885 thread_update_turnstile_promotion(
1886 thread_t thread,
1887 struct turnstile *turnstile)
1888 {
1889 /* Before grabbing the thread lock, check if update is needed */
1890 boolean_t needs_update = thread_needs_turnstile_promotion_update(thread, turnstile);
1891
1892 if (!needs_update) {
1893 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED |
1894 TSU_TURNSTILE_ARG | TSU_BOOST_ARG, turnstile);
1895 return needs_update;
1896 }
1897
1898 thread_lock(thread);
1899
1900 /* Update the pairing heap */
1901 needs_update = thread_update_turnstile_promotion_locked(thread, turnstile);
1902
1903 /* Update turnstile stats */
1904 if (!needs_update) {
1905 turnstile_stats_update(1,
1906 thread_get_update_flags_for_turnstile_propagation_stoppage(thread) |
1907 TSU_TURNSTILE_ARG | TSU_BOOST_ARG,
1908 turnstile);
1909 }
1910
1911 thread_unlock(thread);
1912
1913 return needs_update;
1914 }
1915
1916
1917 /*
1918 * Name: thread_get_inheritor_turnstile_sched_priority
1919 *
1920 * Description: Get the max sched priority of all the inheritor turnstiles
1921 *
1922 * Arg1: thread
1923 *
1924 * Returns: Max sched priority of all the inheritor turnstiles.
1925 *
1926 * Condition: thread locked
1927 */
1928 int
thread_get_inheritor_turnstile_sched_priority(thread_t thread)1929 thread_get_inheritor_turnstile_sched_priority(thread_t thread)
1930 {
1931 struct turnstile *max_turnstile;
1932
1933 max_turnstile = priority_queue_max(&thread->sched_inheritor_queue,
1934 struct turnstile, ts_inheritor_links);
1935
1936 if (max_turnstile) {
1937 return priority_queue_entry_sched_pri(
1938 &thread->sched_inheritor_queue,
1939 &max_turnstile->ts_inheritor_links);
1940 }
1941
1942 return 0;
1943 }
1944
1945 /*
1946 * Name: thread_get_inheritor_turnstile_base_priority
1947 *
1948 * Description: Get the max base priority of all the inheritor turnstiles
1949 *
1950 * Arg1: thread
1951 *
1952 * Returns: Max base priority of all the inheritor turnstiles.
1953 *
1954 * Condition: thread locked
1955 */
1956 int
thread_get_inheritor_turnstile_base_priority(thread_t thread)1957 thread_get_inheritor_turnstile_base_priority(thread_t thread)
1958 {
1959 struct turnstile *max_turnstile;
1960
1961 max_turnstile = priority_queue_max(&thread->base_inheritor_queue,
1962 struct turnstile, ts_inheritor_links);
1963
1964 if (max_turnstile) {
1965 return priority_queue_entry_sched_pri(
1966 &thread->base_inheritor_queue,
1967 &max_turnstile->ts_inheritor_links);
1968 }
1969
1970 return 0;
1971 }
1972
1973
1974 /*
1975 * Name: thread_get_waiting_turnstile
1976 *
1977 * Description: Get the turnstile if the thread is waiting on a turnstile.
1978 *
1979 * Arg1: thread
1980 *
1981 * Returns: turnstile: if the thread is blocked on a turnstile.
1982 * TURNSTILE_NULL: otherwise.
1983 *
1984 * Condition: thread locked.
1985 */
1986 struct turnstile *
thread_get_waiting_turnstile(thread_t thread)1987 thread_get_waiting_turnstile(thread_t thread)
1988 {
1989 waitq_t waitq = thread->waitq;
1990
1991 /* Check if the thread is on a waitq */
1992 if (waitq_is_null(waitq)) {
1993 return TURNSTILE_NULL;
1994 }
1995
1996 switch (waitq_type(waitq)) {
1997 case WQT_PORT:
1998 return waitq.wq_q->waitq_ts;
1999
2000 case WQT_TURNSTILE:
2001 return waitq_to_turnstile(waitq.wq_q);
2002
2003 default:
2004 return TURNSTILE_NULL;
2005 }
2006 }
2007
2008 /*
2009 * Name: turnstile_lookup_by_proprietor
2010 *
2011 * Description: Get turnstile for a proprietor from global
2012 * turnstile hash.
2013 *
2014 * Arg1: port
2015 * Arg2: turnstile_type_t type
2016 *
2017 * Returns: turnstile: if the proprietor has a turnstile.
2018 * TURNSTILE_NULL: otherwise.
2019 *
2020 * Condition: proprietor interlock held.
2021 */
2022 struct turnstile *
turnstile_lookup_by_proprietor(uintptr_t proprietor,turnstile_type_t type)2023 turnstile_lookup_by_proprietor(uintptr_t proprietor, turnstile_type_t type)
2024 {
2025 return turnstile_htable_lookup(proprietor, type);
2026 }
2027
2028 /*
2029 * Name: thread_get_update_flags_for_turnstile_propagation_stoppage
2030 *
2031 * Description: Get the turnstile stats flags based on the thread wait status.
2032 *
2033 * Arg1: thread
2034 *
2035 * Returns: TSU_THREAD_RUNNABLE: if the thread is runnable.
2036 * TSU_NO_TURNSTILE: if thread waiting on a regular waitq.
2037 * TSU_NO_PRI_CHANGE_NEEDED: otherwise.
2038 *
2039 * Condition: thread locked.
2040 */
2041 static turnstile_stats_update_flags_t
thread_get_update_flags_for_turnstile_propagation_stoppage(thread_t thread)2042 thread_get_update_flags_for_turnstile_propagation_stoppage(thread_t thread)
2043 {
2044 waitq_t waitq = thread->waitq;
2045
2046 /* Check if the thread is on a waitq */
2047 if (waitq_is_null(waitq)) {
2048 return TSU_THREAD_RUNNABLE;
2049 }
2050
2051 /* Get the safeq if the waitq is a port queue */
2052 switch (waitq_type(waitq)) {
2053 case WQT_PORT:
2054 if (waitq.wq_q->waitq_ts) {
2055 return TSU_NO_PRI_CHANGE_NEEDED;
2056 }
2057 return TSU_NO_TURNSTILE;
2058
2059 case WQT_TURNSTILE:
2060 /* Thread blocked on turnstile waitq but no propagation needed */
2061 return TSU_NO_PRI_CHANGE_NEEDED;
2062
2063 default:
2064 return TSU_NO_TURNSTILE;
2065 }
2066 }
2067
2068
2069 /*
2070 * Name: turnstile_get_update_flags_for_above_UI_pri_change
2071 *
2072 * Description: Get the turnstile stats flags based on the turnstile priority.
2073 *
2074 * Arg1: turnstile
2075 *
2076 * Returns: TSU_ABOVE_UI_PRI_CHANGE: if turnstile priority is above 47 and it is not an ulock.
2077 * TSU_FLAGS_NONE: otherwise.
2078 *
2079 * Condition: turnstile locked.
2080 */
2081 static turnstile_stats_update_flags_t
turnstile_get_update_flags_for_above_UI_pri_change(struct turnstile * turnstile)2082 turnstile_get_update_flags_for_above_UI_pri_change(struct turnstile *turnstile)
2083 {
2084 if (turnstile->ts_priority >
2085 (thread_qos_policy_params.qos_pri[THREAD_QOS_USER_INTERACTIVE] + 1) &&
2086 turnstile_get_type(turnstile) != TURNSTILE_ULOCK) {
2087 return TSU_ABOVE_UI_PRI_CHANGE;
2088 }
2089
2090 return TSU_FLAGS_NONE;
2091 }
2092
2093
2094 /*
2095 * Name: workq_add_turnstile_promotion
2096 *
2097 * Description: Connect the workqueue turnstile to the workqueue as a fake
2098 * inheritor
2099 *
2100 * Arg1: workqueue
2101 * Arg2: turnstile
2102 *
2103 * Condition: turnstile locked.
2104 */
2105 static boolean_t
workq_add_turnstile_promotion(struct workqueue * wq_inheritor __unused,struct turnstile * turnstile)2106 workq_add_turnstile_promotion(
2107 struct workqueue *wq_inheritor __unused,
2108 struct turnstile *turnstile)
2109 {
2110 /*
2111 * If the push is higher than MAXPRI_THROTTLE then the workqueue should
2112 * bring up a thread.
2113 */
2114 return turnstile->ts_priority > MAXPRI_THROTTLE;
2115 }
2116
2117 /*
2118 * Name: turnstile_need_turnstile_promotion_update
2119 *
2120 * Description: Check if turnstile position in turnstile's inheritor list needs to be updated.
2121 *
2122 * Arg1: dst turnstile
2123 * Arg2: src turnstile
2124 *
2125 * Returns: TRUE: if turnstile_update_turnstile_promotion needs to be called.
2126 * FALSE: otherwise.
2127 *
2128 * Condition: src turnstile locked.
2129 */
2130 static boolean_t
turnstile_need_turnstile_promotion_update(struct turnstile * dst_turnstile __assert_only,struct turnstile * src_turnstile)2131 turnstile_need_turnstile_promotion_update(
2132 struct turnstile *dst_turnstile __assert_only,
2133 struct turnstile *src_turnstile)
2134 {
2135 int src_turnstile_link_priority;
2136 boolean_t needs_update = FALSE;
2137
2138 src_turnstile_link_priority = priority_queue_entry_sched_pri(
2139 &dst_turnstile->ts_inheritor_queue,
2140 &src_turnstile->ts_inheritor_links);
2141
2142 needs_update = (src_turnstile_link_priority == src_turnstile->ts_priority) ? FALSE : TRUE;
2143 return needs_update;
2144 }
2145
2146 /*
2147 * Name: turnstile_update_turnstile_promotion_locked
2148 *
2149 * Description: Update dst turnstile's inheritor link since src turnstile's
2150 * promote priority has changed.
2151 *
2152 * Arg1: dst turnstile
2153 * Arg2: src turnstile
2154 *
2155 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2156 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2157 *
2158 * Condition: src and dst turnstile locked.
2159 */
2160 static boolean_t
turnstile_update_turnstile_promotion_locked(struct turnstile * dst_turnstile,struct turnstile * src_turnstile)2161 turnstile_update_turnstile_promotion_locked(
2162 struct turnstile *dst_turnstile,
2163 struct turnstile *src_turnstile)
2164 {
2165 int src_turnstile_link_priority;
2166 src_turnstile_link_priority = priority_queue_entry_sched_pri(
2167 &dst_turnstile->ts_inheritor_queue,
2168 &src_turnstile->ts_inheritor_links);
2169
2170 if (src_turnstile->ts_priority != src_turnstile_link_priority) {
2171 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
2172 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS, (TURNSTILE_MOVED_IN_TURNSTILE_HEAP))) | DBG_FUNC_NONE,
2173 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile),
2174 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile),
2175 src_turnstile->ts_priority, src_turnstile_link_priority, 0);
2176 }
2177
2178 if (!turnstile_priority_queue_update_entry_key(
2179 &dst_turnstile->ts_inheritor_queue, &src_turnstile->ts_inheritor_links,
2180 src_turnstile->ts_priority)) {
2181 return FALSE;
2182 }
2183
2184 /* Update dst turnstile's priority */
2185 return turnstile_recompute_priority_locked(dst_turnstile);
2186 }
2187
2188 /*
2189 * Name: turnstile_update_turnstile_promotion
2190 *
2191 * Description: Update dst turnstile's inheritor link since src turnstile's
2192 * promote priority has changed.
2193 *
2194 * Arg1: dst turnstile
2195 * Arg2: src turnstile
2196 *
2197 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2198 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2199 *
2200 * Condition: src turnstile locked.
2201 */
2202 static boolean_t
turnstile_update_turnstile_promotion(struct turnstile * dst_turnstile,struct turnstile * src_turnstile)2203 turnstile_update_turnstile_promotion(
2204 struct turnstile *dst_turnstile,
2205 struct turnstile *src_turnstile)
2206 {
2207 /* Check if update is needed before grabbing the src turnstile lock */
2208 boolean_t needs_update = turnstile_need_turnstile_promotion_update(dst_turnstile, src_turnstile);
2209 if (!needs_update) {
2210 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED |
2211 TSU_TURNSTILE_ARG | TSU_BOOST_ARG,
2212 src_turnstile);
2213 return needs_update;
2214 }
2215
2216 /* Update the pairing heap */
2217 waitq_lock(&dst_turnstile->ts_waitq);
2218 needs_update = turnstile_update_turnstile_promotion_locked(dst_turnstile, src_turnstile);
2219
2220 /* Update turnstile stats */
2221 if (!needs_update) {
2222 turnstile_stats_update(1,
2223 (dst_turnstile->ts_inheritor ? TSU_NO_PRI_CHANGE_NEEDED : TSU_NO_INHERITOR) |
2224 TSU_TURNSTILE_ARG | TSU_BOOST_ARG, src_turnstile);
2225 }
2226 waitq_unlock(&dst_turnstile->ts_waitq);
2227 return needs_update;
2228 }
2229
2230 /*
2231 * Name: turnstile_add_turnstile_promotion
2232 *
2233 * Description: Add src turnstile to dst turnstile's inheritor link
2234 * and update dst turnstile's priority.
2235 *
2236 * Arg1: dst turnstile
2237 * Arg2: src turnstile
2238 *
2239 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2240 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2241 *
2242 * Condition: src turnstile locked.
2243 */
2244 static boolean_t
turnstile_add_turnstile_promotion(struct turnstile * dst_turnstile,struct turnstile * src_turnstile)2245 turnstile_add_turnstile_promotion(
2246 struct turnstile *dst_turnstile,
2247 struct turnstile *src_turnstile)
2248 {
2249 boolean_t needs_update = FALSE;
2250
2251 /* Update the pairing heap */
2252 waitq_lock(&dst_turnstile->ts_waitq);
2253
2254 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
2255 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS, (TURNSTILE_ADDED_TO_TURNSTILE_HEAP))) | DBG_FUNC_NONE,
2256 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile),
2257 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile),
2258 src_turnstile->ts_priority, 0, 0);
2259
2260 priority_queue_entry_init(&src_turnstile->ts_inheritor_links);
2261 priority_queue_entry_set_sched_pri(&dst_turnstile->ts_inheritor_queue,
2262 &src_turnstile->ts_inheritor_links, src_turnstile->ts_priority, false);
2263 if (priority_queue_insert(&dst_turnstile->ts_inheritor_queue,
2264 &src_turnstile->ts_inheritor_links)) {
2265 /* Update dst turnstile priority */
2266 needs_update = turnstile_recompute_priority_locked(dst_turnstile);
2267 }
2268
2269 /* Update turnstile stats */
2270 if (!needs_update) {
2271 turnstile_stats_update(1,
2272 (dst_turnstile->ts_inheritor ? TSU_NO_PRI_CHANGE_NEEDED : TSU_NO_INHERITOR) |
2273 TSU_TURNSTILE_ARG | TSU_BOOST_ARG, src_turnstile);
2274 }
2275
2276 waitq_unlock(&dst_turnstile->ts_waitq);
2277 return needs_update;
2278 }
2279
2280 /*
2281 * Name: turnstile_remove_turnstile_promotion
2282 *
2283 * Description: Remove src turnstile from dst turnstile's inheritor link
2284 * and update dst turnstile's priority.
2285 *
2286 * Arg1: dst turnstile
2287 * Arg2: src turnstile
2288 *
2289 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2290 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2291 *
2292 * Condition: src turnstile locked.
2293 */
2294 static boolean_t
turnstile_remove_turnstile_promotion(struct turnstile * dst_turnstile,struct turnstile * src_turnstile)2295 turnstile_remove_turnstile_promotion(
2296 struct turnstile *dst_turnstile,
2297 struct turnstile *src_turnstile)
2298 {
2299 boolean_t needs_update = FALSE;
2300
2301 /* Update the pairing heap */
2302 waitq_lock(&dst_turnstile->ts_waitq);
2303
2304 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
2305 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS, (TURNSTILE_REMOVED_FROM_TURNSTILE_HEAP))) | DBG_FUNC_NONE,
2306 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile),
2307 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile),
2308 0, 0, 0);
2309
2310 if (priority_queue_remove(&dst_turnstile->ts_inheritor_queue,
2311 &src_turnstile->ts_inheritor_links)) {
2312 /* Update dst turnstile priority */
2313 needs_update = turnstile_recompute_priority_locked(dst_turnstile);
2314 }
2315
2316 /* Update turnstile stats */
2317 if (!needs_update) {
2318 turnstile_stats_update(1,
2319 (dst_turnstile->ts_inheritor ? TSU_NO_PRI_CHANGE_NEEDED : TSU_NO_INHERITOR) |
2320 TSU_TURNSTILE_ARG, src_turnstile);
2321 }
2322
2323 waitq_unlock(&dst_turnstile->ts_waitq);
2324 return needs_update;
2325 }
2326
2327 /*
2328 * Name: turnstile_compute_thread_push
2329 *
2330 * Description: Compute the priority at which the thread will push
2331 * on the turnstile.
2332 *
2333 * Arg1: turnstile
2334 * Arg2: thread
2335 *
2336 * Condition: wq locked
2337 */
2338 static int
turnstile_compute_thread_push(struct turnstile * turnstile,thread_t thread)2339 turnstile_compute_thread_push(
2340 struct turnstile *turnstile,
2341 thread_t thread)
2342 {
2343 int priority = 0;
2344 switch (turnstile_promote_policy[turnstile_get_type(turnstile)]) {
2345 case TURNSTILE_USER_PROMOTE:
2346 case TURNSTILE_USER_IPC_PROMOTE:
2347 priority = thread->base_pri;
2348 break;
2349 case TURNSTILE_KERNEL_PROMOTE:
2350 /*
2351 * Ideally this should be policy based
2352 * according to the turnstile type.
2353 *
2354 * The priority with which each thread pushes on
2355 * a primitive should be primitive dependent.
2356 */
2357 priority = thread->sched_pri;
2358 priority = MAX(priority, thread->base_pri);
2359 priority = MAX(priority, BASEPRI_DEFAULT);
2360 priority = MIN(priority, MAXPRI_PROMOTE);
2361 break;
2362 default:
2363 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile));
2364 }
2365
2366 return priority;
2367 }
2368
2369 /*
2370 * Name: turnstile_waitq_add_thread_priority_queue
2371 *
2372 * Description: add thread to the turnstile wq
2373 *
2374 * Arg1: turnstile wq
2375 * Arg2: thread to add
2376 *
2377 * Condition: wq locked
2378 */
2379 void
turnstile_waitq_add_thread_priority_queue(struct waitq * wq,thread_t thread)2380 turnstile_waitq_add_thread_priority_queue(
2381 struct waitq *wq,
2382 thread_t thread)
2383 {
2384 struct turnstile *turnstile = waitq_to_turnstile(wq);
2385 int priority = turnstile_compute_thread_push(turnstile, thread);
2386
2387 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
2388 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS, (THREAD_ADDED_TO_TURNSTILE_WAITQ))) | DBG_FUNC_NONE,
2389 VM_KERNEL_UNSLIDE_OR_PERM(turnstile),
2390 thread_tid(thread),
2391 priority, 0, 0);
2392 /*
2393 * For turnstile queues (which use priority queues),
2394 * insert the thread in the heap based on its priority.
2395 * Note that the priority queue implementation
2396 * is currently not stable, so does not maintain fifo for
2397 * threads at the same pri. Also, if the pri
2398 * of the thread changes while its blocked in the waitq,
2399 * the thread position should be updated in the priority
2400 * queue by calling priority queue increase/decrease
2401 * operations.
2402 */
2403 priority_queue_entry_init(&thread->wait_prioq_links);
2404 priority_queue_entry_set_sched_pri(&wq->waitq_prio_queue,
2405 &thread->wait_prioq_links, priority, false);
2406 priority_queue_insert(&wq->waitq_prio_queue,
2407 &thread->wait_prioq_links);
2408 }
2409
2410 /*
2411 * Name: turnstile_recompute_priority_locked
2412 *
2413 * Description: Update turnstile priority based
2414 * on highest waiter thread and highest blocking
2415 * turnstile.
2416 *
2417 * Args: turnstile
2418 *
2419 * Returns: TRUE: if the turnstile priority changed and needs propagation.
2420 * FALSE: if the turnstile priority did not change or it does not need propagation.
2421 *
2422 * Condition: turnstile locked
2423 */
2424 boolean_t
turnstile_recompute_priority_locked(struct turnstile * turnstile)2425 turnstile_recompute_priority_locked(
2426 struct turnstile *turnstile)
2427 {
2428 int old_priority;
2429 int new_priority;
2430 boolean_t needs_priority_update = FALSE;
2431 thread_t max_thread = THREAD_NULL;
2432 struct turnstile *max_turnstile;
2433 int thread_max_pri = 0;
2434 int turnstile_max_pri = 0;
2435
2436 switch (turnstile_promote_policy[turnstile_get_type(turnstile)]) {
2437 case TURNSTILE_USER_PROMOTE:
2438 case TURNSTILE_USER_IPC_PROMOTE:
2439 case TURNSTILE_KERNEL_PROMOTE:
2440
2441 old_priority = turnstile->ts_priority;
2442
2443 max_thread = priority_queue_max(&turnstile->ts_waitq.waitq_prio_queue,
2444 struct thread, wait_prioq_links);
2445
2446 if (max_thread) {
2447 thread_max_pri = priority_queue_entry_sched_pri(
2448 &turnstile->ts_waitq.waitq_prio_queue,
2449 &max_thread->wait_prioq_links);
2450 }
2451
2452 max_turnstile = priority_queue_max(&turnstile->ts_inheritor_queue,
2453 struct turnstile, ts_inheritor_links);
2454
2455 if (max_turnstile) {
2456 assert(turnstile_promote_policy[turnstile_get_type(turnstile)] != TURNSTILE_KERNEL_PROMOTE);
2457 turnstile_max_pri = priority_queue_entry_sched_pri(
2458 &turnstile->ts_inheritor_queue,
2459 &max_turnstile->ts_inheritor_links);
2460 }
2461
2462 new_priority = max(thread_max_pri, turnstile_max_pri);
2463 turnstile->ts_priority = (uint8_t)new_priority;
2464
2465 if (old_priority != new_priority) {
2466 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
2467 (TURNSTILE_CODE(TURNSTILE_PRIORITY_OPERATIONS,
2468 (TURNSTILE_PRIORITY_CHANGE))) | DBG_FUNC_NONE,
2469 VM_KERNEL_UNSLIDE_OR_PERM(turnstile),
2470 new_priority,
2471 old_priority,
2472 0, 0);
2473 }
2474 needs_priority_update = (!(old_priority == new_priority)) &&
2475 (turnstile->ts_inheritor != NULL);
2476 break;
2477
2478 case TURNSTILE_PROMOTE_NONE:
2479 /* The turnstile was repurposed, do nothing */
2480 break;
2481
2482 default:
2483
2484 panic("Needs implementation for turnstile_recompute_priority");
2485 break;
2486 }
2487 return needs_priority_update;
2488 }
2489
2490
2491 /*
2492 * Name: turnstile_recompute_priority
2493 *
2494 * Description: Update turnstile priority based
2495 * on highest waiter thread and highest blocking
2496 * turnstile.
2497 *
2498 * Args: turnstile
2499 *
2500 * Returns: TRUE: if the turnstile priority changed and needs propagation.
2501 * FALSE: if the turnstile priority did not change or it does not need propagation.
2502 */
2503 boolean_t
turnstile_recompute_priority(struct turnstile * turnstile)2504 turnstile_recompute_priority(
2505 struct turnstile *turnstile)
2506 {
2507 boolean_t needs_priority_update = FALSE;
2508 spl_t s = splsched();
2509
2510 waitq_lock(&turnstile->ts_waitq);
2511
2512 needs_priority_update = turnstile_recompute_priority_locked(turnstile);
2513
2514 waitq_unlock(&turnstile->ts_waitq);
2515 splx(s);
2516 return needs_priority_update;
2517 }
2518
2519
2520 /*
2521 * Name: turnstile_workq_proprietor_of_max_turnstile
2522 *
2523 * Description: Returns the highest priority and proprietor of a turnstile
2524 * pushing on a workqueue turnstile.
2525 *
2526 * This will not return waiters that are at priority
2527 * MAXPRI_THROTTLE or lower.
2528 *
2529 * Args: turnstile
2530 *
2531 * Returns:
2532 * Priority of the max entry, or 0
2533 * Pointer to the max entry proprietor
2534 */
2535 int
turnstile_workq_proprietor_of_max_turnstile(struct turnstile * turnstile,uintptr_t * proprietor_out)2536 turnstile_workq_proprietor_of_max_turnstile(
2537 struct turnstile *turnstile,
2538 uintptr_t *proprietor_out)
2539 {
2540 struct turnstile *max_turnstile;
2541 int max_priority = 0;
2542 uintptr_t proprietor = 0;
2543
2544 assert(turnstile_get_type(turnstile) == TURNSTILE_WORKQS);
2545
2546 spl_t s = splsched();
2547
2548 waitq_lock(&turnstile->ts_waitq);
2549
2550 max_turnstile = priority_queue_max(&turnstile->ts_inheritor_queue,
2551 struct turnstile, ts_inheritor_links);
2552 if (max_turnstile) {
2553 max_priority = priority_queue_entry_sched_pri(
2554 &turnstile->ts_inheritor_queue,
2555 &max_turnstile->ts_inheritor_links);
2556 proprietor = max_turnstile->ts_proprietor;
2557 }
2558
2559 waitq_unlock(&turnstile->ts_waitq);
2560 splx(s);
2561
2562 if (max_priority <= MAXPRI_THROTTLE) {
2563 max_priority = 0;
2564 proprietor = 0;
2565 }
2566 if (proprietor_out) {
2567 *proprietor_out = proprietor;
2568 }
2569 return max_priority;
2570 }
2571
2572 /*
2573 * Name: turnstile_workloop_pusher_info
2574 *
2575 * Description: Returns the priority of the turnstile push for a workloop,
2576 * and the thread or knote responsible for this push.
2577 *
2578 * Args: workloop turnstile
2579 *
2580 * Returns:
2581 * Priority of the push or 0
2582 * Thread (with a +1 reference) with that push or THREAD_NULL.
2583 * Port (with a +1 reference) with that push, or IP_NULL.
2584 * Sync IPC knote with the highest push (or NULL)
2585 */
2586 int
turnstile_workloop_pusher_info(struct turnstile * turnstile,thread_t * thread_out,ipc_port_t * port_out,struct knote ** knote_out)2587 turnstile_workloop_pusher_info(
2588 struct turnstile *turnstile,
2589 thread_t *thread_out,
2590 ipc_port_t *port_out,
2591 struct knote **knote_out)
2592 {
2593 struct turnstile *max_ts;
2594 thread_t max_thread;
2595 int max_thread_pri = 0;
2596 int max_ts_pri = 0;
2597 ipc_port_t port;
2598
2599 assert(turnstile_get_type(turnstile) == TURNSTILE_WORKLOOPS);
2600
2601 spl_t s = splsched();
2602 waitq_lock(&turnstile->ts_waitq);
2603
2604 max_thread = priority_queue_max(&turnstile->ts_waitq.waitq_prio_queue,
2605 struct thread, wait_prioq_links);
2606 if (max_thread) {
2607 max_thread_pri = priority_queue_entry_sched_pri(
2608 &turnstile->ts_waitq.waitq_prio_queue,
2609 &max_thread->wait_prioq_links);
2610 }
2611
2612 max_ts = priority_queue_max(&turnstile->ts_inheritor_queue,
2613 struct turnstile, ts_inheritor_links);
2614 if (max_ts) {
2615 max_ts_pri = priority_queue_entry_sched_pri(
2616 &turnstile->ts_inheritor_queue,
2617 &max_ts->ts_inheritor_links);
2618 }
2619
2620 /*
2621 * Reasons to push on a workloop turnstile are:
2622 *
2623 * 1. threads in dispatch sync
2624 *
2625 * 2. sync IPC pushes, which in turn have 4 sub-cases:
2626 *
2627 * 2.a. special reply port or receive right pushing through a knote
2628 * turnstile,
2629 *
2630 * 2.b. special reply port stashed on a knote, pushing on the workloop
2631 * directly,
2632 *
2633 * 2.c. receive right stashed on a knote, pushing on the workloop
2634 * directly,
2635 *
2636 * 2.d. a receive right monitored by a knote, pushing on the workloop
2637 * directly.
2638 *
2639 * See ipc_port_send_update_inheritor(), ipc_port_recv_update_inheritor().
2640 *
2641 * Note: dereferencing the knote in the caller is safe provided this
2642 * function i scalled under the proper interlocks (the filt_wllock + req
2643 * lock) which serializes with the knote going away.
2644 */
2645 if (max_thread_pri > max_ts_pri) {
2646 thread_reference(max_thread);
2647 *thread_out = max_thread;
2648 *port_out = NULL;
2649 *knote_out = NULL;
2650 } else if (max_ts_pri) {
2651 switch (turnstile_get_type(max_ts)) {
2652 case TURNSTILE_KNOTE:
2653 /* 2.a. */
2654 *thread_out = THREAD_NULL;
2655 *port_out = IP_NULL;
2656 *knote_out = (struct knote *)max_ts->ts_proprietor;
2657 break;
2658
2659 case TURNSTILE_SYNC_IPC:
2660 /* 2.[bcd] */
2661 port = (ipc_port_t)max_ts->ts_proprietor;
2662 ip_reference(port);
2663 *thread_out = THREAD_NULL;
2664 *port_out = port;
2665 *knote_out = NULL;
2666 break;
2667
2668 default:
2669 panic("Unexpected type for turnstile %p", max_ts);
2670 }
2671 } else {
2672 *thread_out = THREAD_NULL;
2673 *port_out = IP_NULL;
2674 *knote_out = NULL;
2675 }
2676
2677 waitq_unlock(&turnstile->ts_waitq);
2678 splx(s);
2679
2680 return max(max_thread_pri, max_ts_pri);
2681 }
2682
2683 /*
2684 * Name: turnstile_has_waiters
2685 *
2686 * Description: returns if there are waiters on the turnstile
2687 *
2688 * Arg1: turnstile: turnstile
2689 *
2690 * Returns: TRUE if there are waiters, FALSE otherwise.
2691 */
2692
2693 boolean_t
turnstile_has_waiters(struct turnstile * turnstile)2694 turnstile_has_waiters(struct turnstile *turnstile)
2695 {
2696 boolean_t ret;
2697
2698 spl_t s = splsched();
2699 waitq_lock(&turnstile->ts_waitq);
2700 ret = !priority_queue_empty(&turnstile->ts_waitq.waitq_prio_queue);
2701 waitq_unlock(&turnstile->ts_waitq);
2702 splx(s);
2703
2704 return ret;
2705 }
2706
2707 /*
2708 * Name: turnstile_update_inheritor_priority_chain
2709 *
2710 * Description: Update turnstile inheritor's priority and propagate
2711 * the priority if the inheritor is blocked on a turnstile.
2712 *
2713 * Arg1: inheritor
2714 * Arg2: inheritor flags
2715 *
2716 * Returns: None.
2717 */
2718 static void
turnstile_update_inheritor_priority_chain(turnstile_inheritor_t inheritor,turnstile_update_flags_t turnstile_flags)2719 turnstile_update_inheritor_priority_chain(
2720 turnstile_inheritor_t inheritor,
2721 turnstile_update_flags_t turnstile_flags)
2722 {
2723 struct turnstile *turnstile = TURNSTILE_NULL;
2724 thread_t thread = THREAD_NULL;
2725 int total_hop = 0, thread_hop = 0;
2726 spl_t s;
2727 turnstile_stats_update_flags_t tsu_flags = ((turnstile_flags & TURNSTILE_UPDATE_BOOST) ?
2728 TSU_BOOST_ARG : TSU_FLAGS_NONE) | TSU_PRI_PROPAGATION;
2729
2730 if (inheritor == NULL) {
2731 return;
2732 }
2733
2734 s = splsched();
2735
2736 if (turnstile_flags & TURNSTILE_INHERITOR_THREAD) {
2737 thread = inheritor;
2738 thread_lock(thread);
2739 thread_recompute_user_promotion_locked(thread);
2740 thread_recompute_kernel_promotion_locked(thread);
2741 } else if (turnstile_flags & TURNSTILE_INHERITOR_TURNSTILE) {
2742 turnstile = inheritor;
2743 waitq_lock(&turnstile->ts_waitq);
2744 turnstile_recompute_priority_locked(turnstile);
2745 tsu_flags |= turnstile_get_update_flags_for_above_UI_pri_change(turnstile);
2746 } else {
2747 /*
2748 * we should never call turnstile_update_inheritor_priority_chain()
2749 * for a workqueue, they have no "chain" after them.
2750 */
2751 assert((turnstile_flags & TURNSTILE_INHERITOR_WORKQ) == 0);
2752 }
2753
2754 while (turnstile != TURNSTILE_NULL || thread != THREAD_NULL) {
2755 if (turnstile != TURNSTILE_NULL) {
2756 if (turnstile->ts_inheritor == NULL) {
2757 turnstile_stats_update(total_hop + 1, TSU_NO_INHERITOR |
2758 TSU_TURNSTILE_ARG | tsu_flags,
2759 turnstile);
2760 waitq_unlock(&turnstile->ts_waitq);
2761 turnstile = TURNSTILE_NULL;
2762 break;
2763 }
2764 if (turnstile->ts_inheritor_flags & TURNSTILE_INHERITOR_THREAD) {
2765 turnstile_update_inheritor_thread_priority_chain(&turnstile, &thread,
2766 total_hop, tsu_flags);
2767 } else if (turnstile->ts_inheritor_flags & TURNSTILE_INHERITOR_TURNSTILE) {
2768 turnstile_update_inheritor_turnstile_priority_chain(&turnstile,
2769 total_hop, tsu_flags);
2770 } else if (turnstile->ts_inheritor_flags & TURNSTILE_INHERITOR_WORKQ) {
2771 turnstile_update_inheritor_workq_priority_chain(turnstile, s);
2772 turnstile_stats_update(total_hop + 1, TSU_NO_PRI_CHANGE_NEEDED | tsu_flags,
2773 NULL);
2774 return;
2775 } else {
2776 panic("Inheritor flags not passed in turnstile_update_inheritor");
2777 }
2778 } else if (thread != THREAD_NULL) {
2779 thread_update_waiting_turnstile_priority_chain(&thread, &turnstile,
2780 thread_hop, total_hop, tsu_flags);
2781 thread_hop++;
2782 }
2783 total_hop++;
2784 }
2785
2786 splx(s);
2787 return;
2788 }
2789
2790 /*
2791 * Name: turnstile_update_inheritor_complete
2792 *
2793 * Description: Update turnstile inheritor's priority and propagate the
2794 * priority if the inheritor is blocked on a turnstile.
2795 * Consumes thread ref of old inheritor returned by
2796 * turnstile_update_inheritor. Recursive priority update
2797 * will only happen when called with interlock dropped.
2798 *
2799 * Args:
2800 * Arg1: turnstile
2801 * Arg2: interlock held
2802 *
2803 * Returns: None.
2804 */
2805 void
turnstile_update_inheritor_complete(struct turnstile * turnstile,turnstile_update_complete_flags_t flags __unused)2806 turnstile_update_inheritor_complete(
2807 struct turnstile *turnstile,
2808 turnstile_update_complete_flags_t flags __unused)
2809 {
2810 thread_t thread = current_thread();
2811
2812 turnstile_update_flags_t inheritor_flags = thread->inheritor_flags;
2813
2814 turnstile_cleanup();
2815
2816 /* Perform priority update for new inheritor */
2817 if (inheritor_flags & TURNSTILE_NEEDS_PRI_UPDATE) {
2818 turnstile_update_inheritor_priority_chain(turnstile,
2819 TURNSTILE_INHERITOR_TURNSTILE | TURNSTILE_UPDATE_BOOST);
2820 }
2821 }
2822
2823 /*
2824 * Name: turnstile_cleanup
2825 *
2826 * Description: Update priority of a turnstile inheritor
2827 * if needed.
2828 *
2829 * Args: inheritor and flags passed on thread struct.
2830 *
2831 * Returns: None.
2832 */
2833 void
turnstile_cleanup(void)2834 turnstile_cleanup(void)
2835 {
2836 thread_t thread = current_thread();
2837
2838 /* Get the old inheritor from calling thread struct */
2839 turnstile_inheritor_t old_inheritor = thread->inheritor;
2840 turnstile_update_flags_t inheritor_flags = thread->inheritor_flags;
2841 thread->inheritor = THREAD_NULL;
2842 thread->inheritor_flags = TURNSTILE_UPDATE_FLAGS_NONE;
2843
2844 if (old_inheritor == TURNSTILE_INHERITOR_NULL) {
2845 /* no cleanup to do */
2846 return;
2847 }
2848
2849 /* Perform priority demotion for old inheritor */
2850 if (inheritor_flags & TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE) {
2851 turnstile_update_inheritor_priority_chain(old_inheritor,
2852 inheritor_flags);
2853 }
2854
2855 /* Drop thread reference for old inheritor */
2856 if (inheritor_flags & TURNSTILE_INHERITOR_THREAD) {
2857 thread_deallocate_safe(old_inheritor);
2858 } else if (inheritor_flags & TURNSTILE_INHERITOR_TURNSTILE) {
2859 turnstile_deallocate((struct turnstile *)old_inheritor);
2860 } else if (inheritor_flags & TURNSTILE_INHERITOR_WORKQ) {
2861 workq_deallocate_safe((struct workqueue *)old_inheritor);
2862 } else {
2863 panic("Inheritor flags lost along the way");
2864 }
2865 }
2866
2867 /*
2868 * Name: turnstile_update_thread_priority_chain
2869 *
2870 * Description: Priority of a thread blocked on a turnstile
2871 * has changed, update the turnstile priority.
2872 *
2873 * Arg1: thread: thread whose priority has changed.
2874 *
2875 * Returns: None.
2876 */
2877 void
turnstile_update_thread_priority_chain(thread_t thread)2878 turnstile_update_thread_priority_chain(thread_t thread)
2879 {
2880 turnstile_update_inheritor_priority_chain(thread,
2881 TURNSTILE_INHERITOR_THREAD | TURNSTILE_UPDATE_BOOST);
2882 }
2883
2884 /*
2885 * Name: turnstile_update_inheritor_workq_priority_chain
2886 *
2887 * Description: Helper function to update turnstile's inheritor(workq)
2888 * priority and possibly redrive thread creation
2889 *
2890 * Arg1: turnstile: turnstile
2891 * Arg2: s: whether iterrupts are disabled.
2892 *
2893 * Condition: turnstile is locked on entry, it is unlocked on exit,
2894 * and interrupts re-enabled.
2895 */
2896 static void
turnstile_update_inheritor_workq_priority_chain(struct turnstile * turnstile,spl_t s)2897 turnstile_update_inheritor_workq_priority_chain(struct turnstile *turnstile, spl_t s)
2898 {
2899 struct workqueue *wq = turnstile->ts_inheritor;
2900 bool workq_lock_held = workq_is_current_thread_updating_turnstile(wq);
2901
2902 if (__improbable(turnstile->ts_priority <= MAXPRI_THROTTLE)) {
2903 waitq_unlock(&turnstile->ts_waitq);
2904 splx(s);
2905 return;
2906 }
2907
2908 if (!workq_lock_held) {
2909 workq_reference(wq);
2910 disable_preemption();
2911 }
2912 waitq_unlock(&turnstile->ts_waitq);
2913 splx(s);
2914
2915 workq_schedule_creator_turnstile_redrive(wq, workq_lock_held);
2916
2917 if (!workq_lock_held) {
2918 enable_preemption();
2919 workq_deallocate_safe(wq);
2920 }
2921 }
2922
2923 /*
2924 * Name: turnstile_update_inheritor_thread_priority_chain
2925 *
2926 * Description: Helper function to update turnstile's inheritor(thread)
2927 * priority.
2928 *
2929 * Arg1: in_turnstile: address to turnstile
2930 * Arg2: out_thread: address to return the thread inheritor
2931 * Arg3: thread_hop: number to thread hop in propagation chain
2932 * Arg4: tsu_flags: turnstile update flags
2933 *
2934 * Returns: Implicit returns locked thread in out_thread if it needs
2935 * further propagation.
2936 *
2937 * Condition: *in_turnstile is locked on entry, it is unlocked on exit and
2938 * *in_turnstile is set to NULL.
2939 */
2940 static void
turnstile_update_inheritor_thread_priority_chain(struct turnstile ** in_turnstile,thread_t * out_thread,int total_hop,turnstile_stats_update_flags_t tsu_flags)2941 turnstile_update_inheritor_thread_priority_chain(
2942 struct turnstile **in_turnstile,
2943 thread_t *out_thread,
2944 int total_hop,
2945 turnstile_stats_update_flags_t tsu_flags)
2946 {
2947 boolean_t needs_update = FALSE;
2948 struct turnstile *turnstile = *in_turnstile;
2949 thread_t thread_inheritor = turnstile->ts_inheritor;
2950 boolean_t first_update = !total_hop;
2951
2952 assert(turnstile->ts_inheritor_flags & TURNSTILE_INHERITOR_THREAD);
2953 *in_turnstile = TURNSTILE_NULL;
2954
2955 /* Check if update is needed before grabbing the thread lock */
2956 needs_update = thread_needs_turnstile_promotion_update(thread_inheritor, turnstile);
2957 if (!needs_update && !first_update) {
2958 turnstile_stats_update(total_hop + 1, TSU_NO_PRI_CHANGE_NEEDED |
2959 TSU_TURNSTILE_ARG | tsu_flags, turnstile);
2960 waitq_unlock(&turnstile->ts_waitq);
2961 return;
2962 }
2963
2964 thread_lock(thread_inheritor);
2965
2966 /* adjust turnstile position in the thread's inheritor list */
2967 needs_update = thread_update_turnstile_promotion_locked(
2968 thread_inheritor, turnstile);
2969
2970 /*
2971 * Check if thread needs further priority propagation,
2972 * since the first hop priority update was done in
2973 * turnstile_update_inheritor, do not bailout if it is
2974 * the first update as needs_update flag would evaluate to
2975 * false for that case.
2976 */
2977 if (!needs_update && !first_update) {
2978 /* Update turnstile stats before returning */
2979 turnstile_stats_update(total_hop + 1,
2980 (thread_get_update_flags_for_turnstile_propagation_stoppage(thread_inheritor)) |
2981 TSU_TURNSTILE_ARG | tsu_flags,
2982 turnstile);
2983 thread_unlock(thread_inheritor);
2984 waitq_unlock(&turnstile->ts_waitq);
2985 return;
2986 }
2987
2988 /* Unlock the turnstile and update the thread */
2989 waitq_unlock(&turnstile->ts_waitq);
2990 *out_thread = thread_inheritor;
2991 return;
2992 }
2993
2994 /*
2995 * Name: turnstile_update_inheritor_turnstile_priority_chain
2996 *
2997 * Description: Helper function to update turnstile's inheritor(turnstile)
2998 * priority.
2999 *
3000 * Arg1: in_out_turnstile: address to turnstile
3001 * Arg2: thread_hop: number of thread hop in propagation chain
3002 * Arg3: tsu_flags: turnstile update flags
3003 *
3004 * Returns: Implicit returns locked turnstile in in_out_turnstile if it needs
3005 * further propagation.
3006 *
3007 * Condition: *in_out_turnstile is locked on entry, *in_out_turnstile on exit,
3008 * but the value of *in_out_turnstile might change and turnstile lock
3009 * will be dropped for old value and will be acquired for the new value.
3010 */
3011 static void
turnstile_update_inheritor_turnstile_priority_chain(struct turnstile ** in_out_turnstile,int total_hop,turnstile_stats_update_flags_t tsu_flags)3012 turnstile_update_inheritor_turnstile_priority_chain(
3013 struct turnstile **in_out_turnstile,
3014 int total_hop,
3015 turnstile_stats_update_flags_t tsu_flags)
3016 {
3017 boolean_t needs_update = FALSE;
3018 struct turnstile *turnstile = *in_out_turnstile;
3019 struct turnstile *inheritor_turnstile = turnstile->ts_inheritor;
3020 boolean_t first_update = !total_hop;
3021
3022 assert(turnstile->ts_inheritor_flags & TURNSTILE_INHERITOR_TURNSTILE);
3023 *in_out_turnstile = TURNSTILE_NULL;
3024
3025 /* Check if the inheritor turnstile needs to be updated before grabbing the lock */
3026 needs_update = turnstile_need_turnstile_promotion_update(inheritor_turnstile, turnstile);
3027 if (!needs_update && !first_update) {
3028 turnstile_stats_update(total_hop + 1, TSU_NO_PRI_CHANGE_NEEDED |
3029 TSU_TURNSTILE_ARG | tsu_flags,
3030 turnstile);
3031 waitq_unlock(&turnstile->ts_waitq);
3032 return;
3033 }
3034
3035 waitq_lock(&inheritor_turnstile->ts_waitq);
3036
3037 needs_update = turnstile_update_turnstile_promotion_locked(
3038 inheritor_turnstile, turnstile);
3039
3040 /*
3041 * Check if turnstile needs further priority propagation,
3042 * since the first hop priority update was done in
3043 * turnstile_update_inheritor, do not bailout if it is
3044 * the first update as needs_update flag would evaluate to
3045 * false for that case.
3046 */
3047 if (!needs_update && !first_update) {
3048 /* Update turnstile stats before returning */
3049 turnstile_stats_update(total_hop + 1,
3050 (inheritor_turnstile->ts_inheritor ? TSU_NO_PRI_CHANGE_NEEDED : TSU_NO_INHERITOR) |
3051 TSU_TURNSTILE_ARG | tsu_flags,
3052 turnstile);
3053 waitq_unlock(&inheritor_turnstile->ts_waitq);
3054 waitq_unlock(&turnstile->ts_waitq);
3055 return;
3056 }
3057
3058 /* Unlock the outer turnstile and update the inner turnstile */
3059 waitq_unlock(&turnstile->ts_waitq);
3060 *in_out_turnstile = inheritor_turnstile;
3061 return;
3062 }
3063
3064 /*
3065 * Name: thread_update_waiting_turnstile_priority_chain
3066 *
3067 * Description: Helper function to update thread's waiting
3068 * turnstile priority.
3069 *
3070 * Arg1: in_thread: pointer to thread
3071 * Arg2: out_turnstile: pointer to turnstile to return to caller
3072 * Arg3: thread_hop: Number of thread hops visited
3073 * Arg4: total_hop: total hops visited
3074 * Arg5: tsu_flags: turnstile update flags
3075 *
3076 * Returns: *out_turnstile returns the inheritor if it needs further propagation.
3077 *
3078 * Condition: *in_thread locked on entry, unlocked on exit and set to NULL.
3079 */
3080 static void
thread_update_waiting_turnstile_priority_chain(thread_t * in_thread,struct turnstile ** out_turnstile,int thread_hop,int total_hop,turnstile_stats_update_flags_t tsu_flags)3081 thread_update_waiting_turnstile_priority_chain(
3082 thread_t *in_thread,
3083 struct turnstile **out_turnstile,
3084 int thread_hop,
3085 int total_hop,
3086 turnstile_stats_update_flags_t tsu_flags)
3087 {
3088 boolean_t needs_update = FALSE;
3089 thread_t thread = *in_thread;
3090 struct turnstile *waiting_turnstile = TURNSTILE_NULL;
3091 uint32_t turnstile_gencount;
3092 boolean_t first_update = !total_hop;
3093 uint32_t ticket;
3094
3095 *in_thread = THREAD_NULL;
3096
3097 /* Check if thread waiting on a turnstile */
3098 waiting_turnstile = thread_get_waiting_turnstile(thread);
3099
3100 if (waiting_turnstile == TURNSTILE_NULL || thread_hop > turnstile_max_hop) {
3101 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
3102 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS,
3103 (waiting_turnstile ? TURNSTILE_UPDATE_STOPPED_BY_LIMIT : THREAD_NOT_WAITING_ON_TURNSTILE)
3104 )) | DBG_FUNC_NONE,
3105 thread_tid(thread),
3106 turnstile_max_hop,
3107 thread_hop,
3108 VM_KERNEL_UNSLIDE_OR_PERM(waiting_turnstile), 0);
3109 turnstile_stats_update(total_hop + 1, TSU_NO_TURNSTILE |
3110 TSU_THREAD_ARG | tsu_flags, thread);
3111 thread_unlock(thread);
3112 return;
3113 }
3114
3115 /* Check if the thread needs to update the waiting turnstile */
3116 needs_update = turnstile_need_thread_promotion_update(waiting_turnstile, thread);
3117 if (!needs_update && !first_update) {
3118 turnstile_stats_update(total_hop + 1, TSU_NO_PRI_CHANGE_NEEDED |
3119 TSU_THREAD_ARG | tsu_flags, thread);
3120 thread_unlock(thread);
3121 return;
3122 }
3123
3124 /*
3125 * Do not take turnstile reference here because
3126 * 1. The ticket lock guarantees that waiting_turnstile will not be reused by other threads
3127 * 2. We are holding other turnstile lock, and doing turnstile free when we dereference it can cause deadlock
3128 * 3. Performance optimization
3129 */
3130 if (!waitq_lock_reserve(&waiting_turnstile->ts_waitq, &ticket)) {
3131 /* take a reference on thread and snapshot of gencount */
3132 turnstile_gencount = turnstile_get_gencount(waiting_turnstile);
3133 thread_reference(thread);
3134
3135 /* drop the thread lock and wait for turnstile ticket */
3136 thread_unlock(thread);
3137 waitq_lock_wait(&waiting_turnstile->ts_waitq, ticket);
3138 thread_lock(thread);
3139
3140 /* Check if the gencount matches and thread is still waiting on same turnstile */
3141 if (turnstile_gencount != turnstile_get_gencount(waiting_turnstile) ||
3142 waiting_turnstile != thread_get_waiting_turnstile(thread)) {
3143 turnstile_stats_update(total_hop + 1, TSU_NO_PRI_CHANGE_NEEDED |
3144 TSU_THREAD_ARG | tsu_flags, thread);
3145 /* No updates required, bail out */
3146 thread_unlock(thread);
3147 waitq_unlock(&waiting_turnstile->ts_waitq);
3148 thread_deallocate_safe(thread);
3149 return;
3150 }
3151
3152 /*
3153 * The thread is waiting on the waiting_turnstile and we have thread lock,
3154 * we can drop the thread reference since it is on waitq and
3155 * it could not be removed from the waitq without the thread lock.
3156 */
3157 thread_deallocate_safe(thread);
3158 }
3159
3160 /* adjust thread's position on turnstile waitq */
3161 needs_update = turnstile_update_thread_promotion_locked(waiting_turnstile, thread);
3162
3163 /*
3164 * Check if thread needs further priority propagation,
3165 * since the first hop priority update was done in
3166 * turnstile_update_inheritor, do not bailout if it is
3167 * the first update as needs_update flag would evaluate to
3168 * false for that case.
3169 */
3170 if (!needs_update && !first_update) {
3171 turnstile_stats_update(total_hop + 1,
3172 (waiting_turnstile->ts_inheritor ? TSU_NO_PRI_CHANGE_NEEDED : TSU_NO_INHERITOR) |
3173 TSU_THREAD_ARG | tsu_flags, thread);
3174 thread_unlock(thread);
3175 waitq_unlock(&waiting_turnstile->ts_waitq);
3176 return;
3177 }
3178
3179 /* drop the thread lock and update the turnstile */
3180 thread_unlock(thread);
3181 *out_turnstile = waiting_turnstile;
3182 }
3183
3184 /*
3185 * Name: turnstile_stats_update
3186 *
3187 * Description: Function to update turnstile stats for dev kernel.
3188 *
3189 * Arg1: hops : number of thread hops in priority propagation
3190 * Arg2: flags : turnstile stats update flags
3191 * Arg3: inheritor: inheritor
3192 *
3193 * Returns: Nothing
3194 */
3195 void
turnstile_stats_update(int hop __assert_only,turnstile_stats_update_flags_t flags __assert_only,turnstile_inheritor_t inheritor __assert_only)3196 turnstile_stats_update(
3197 int hop __assert_only,
3198 turnstile_stats_update_flags_t flags __assert_only,
3199 turnstile_inheritor_t inheritor __assert_only)
3200 {
3201 #if DEVELOPMENT || DEBUG
3202 if (flags & TSU_TURNSTILE_BLOCK_COUNT) {
3203 os_atomic_inc(&thread_block_on_turnstile_count, relaxed);
3204 }
3205
3206 if (flags & TSU_REGULAR_WAITQ_BLOCK_COUNT) {
3207 os_atomic_inc(&thread_block_on_regular_waitq_count, relaxed);
3208 }
3209
3210 if (hop > TURNSTILE_MAX_HOP_DEFAULT || hop == 0) {
3211 return;
3212 }
3213
3214 assert(hop >= 0);
3215
3216 /*
3217 * Check if turnstile stats needs to be updated.
3218 * Bail out if the turnstile or thread does not
3219 * have any user promotion.
3220 * Bail out if it is the first hop of WQ turnstile
3221 * since WQ's use of a turnstile for the admission check
3222 * introduces a lot of noise due to state changes.
3223 */
3224 if (flags & TSU_TURNSTILE_ARG) {
3225 struct turnstile *ts = (struct turnstile *)inheritor;
3226 if (ts->ts_priority == 0) {
3227 return;
3228 }
3229
3230 if (hop == 1 && turnstile_get_type(ts) == TURNSTILE_WORKQS) {
3231 return;
3232 }
3233 } else if (flags & TSU_THREAD_ARG) {
3234 thread_t thread = (thread_t)inheritor;
3235 if (thread->user_promotion_basepri == 0) {
3236 return;
3237 }
3238 } else {
3239 assert(inheritor == NULL);
3240 }
3241
3242 struct turnstile_stats *turnstile_stats;
3243 if (flags & TSU_BOOST_ARG) {
3244 turnstile_stats = turnstile_boost_stats;
3245 } else {
3246 turnstile_stats = turnstile_unboost_stats;
3247 }
3248
3249 if (flags & TSU_PRI_PROPAGATION) {
3250 os_atomic_inc(&turnstile_stats[hop - 1].ts_priority_propagation, relaxed);
3251 }
3252
3253 if (flags & TSU_NO_INHERITOR) {
3254 os_atomic_inc(&turnstile_stats[hop - 1].ts_no_inheritor, relaxed);
3255 }
3256
3257 if (flags & TSU_NO_TURNSTILE) {
3258 os_atomic_inc(&turnstile_stats[hop - 1].ts_no_turnstile, relaxed);
3259 }
3260
3261 if (flags & TSU_NO_PRI_CHANGE_NEEDED) {
3262 os_atomic_inc(&turnstile_stats[hop - 1].ts_no_priority_change_required, relaxed);
3263 }
3264
3265 if (flags & TSU_THREAD_RUNNABLE) {
3266 os_atomic_inc(&turnstile_stats[hop - 1].ts_thread_runnable, relaxed);
3267 }
3268
3269 if (flags & TSU_ABOVE_UI_PRI_CHANGE) {
3270 os_atomic_inc(&turnstile_stats[hop - 1].ts_above_ui_pri_change, relaxed);
3271 }
3272 #endif
3273 }
3274
3275 #define STACKSHOT_TURNSTILE_FLAGS_MODE(flags) ((flags) & ~STACKSHOT_TURNSTILE_STATUS_PORTFLAGS)
3276 #define STACKSHOT_TURNSTILE_FLAGS_PORT(flags) ((flags) & STACKSHOT_TURNSTILE_STATUS_PORTFLAGS)
3277 #define STACKSHOT_TURNSTILE_FLAGS_WITHPORT(flags, port) \
3278 (STACKSHOT_TURNSTILE_FLAGS_MODE(flags) | STACKSHOT_TURNSTILE_FLAGS_PORT(port))
3279
3280 static uint64_t
kdp_turnstile_traverse_inheritor_chain(struct turnstile * ts,uint64_t * flags,uint8_t * hops,struct ipc_service_port_label ** isplp)3281 kdp_turnstile_traverse_inheritor_chain(struct turnstile *ts, uint64_t *flags, uint8_t *hops, struct ipc_service_port_label **isplp)
3282 {
3283 uint8_t unknown_hops;
3284
3285 if (waitq_held(&ts->ts_waitq)) {
3286 *flags |= STACKSHOT_TURNSTILE_STATUS_LOCKED_WAITQ;
3287 return 0;
3288 }
3289
3290 *hops = *hops + 1;
3291 unknown_hops = *hops;
3292
3293 /*
3294 * If we find a port along the way, include it. Do this first so the
3295 * furthest one wins.
3296 */
3297 if (turnstile_is_send_turnstile(ts)) {
3298 ipc_port_t port = (ipc_port_t)ts->ts_proprietor;
3299
3300 if (port && ip_active(port) && port->ip_service_port && port->ip_splabel != NULL) {
3301 *flags = STACKSHOT_TURNSTILE_FLAGS_WITHPORT(*flags, STACKSHOT_TURNSTILE_STATUS_SENDPORT);
3302 *isplp = (struct ipc_service_port_label *)port->ip_splabel;
3303 }
3304 }
3305 if (turnstile_is_receive_turnstile(ts)) {
3306 ipc_port_t port = (ipc_port_t)ts->ts_proprietor;
3307 if (port && ip_active(port) && port->ip_service_port && port->ip_splabel != NULL) {
3308 *flags = STACKSHOT_TURNSTILE_FLAGS_WITHPORT(*flags, STACKSHOT_TURNSTILE_STATUS_RECEIVEPORT);
3309 *isplp = (struct ipc_service_port_label *)port->ip_splabel;
3310 }
3311 }
3312 /*
3313 * If a turnstile is inheriting our priority, recurse. If we get back *exactly* UNKNOWN,
3314 * continue on, since we may be able to give a more specific answer. To
3315 * give an accurate hops count, we reset *hops, saving the recursive value in
3316 * unknown_hops to use if we can't give a better answer.
3317 */
3318 if (ts->ts_inheritor_flags & TURNSTILE_INHERITOR_TURNSTILE) {
3319 uint8_t pre_hops = *hops;
3320 uint64_t ret = kdp_turnstile_traverse_inheritor_chain(ts->ts_inheritor, flags, hops, isplp);
3321 /*
3322 * Note that while flags is usually |=ed, we're checking with != here to
3323 * make sure we only replace *exactly* UNKNOWN, ignoring the portflags.
3324 */
3325 if (ret != 0 || STACKSHOT_TURNSTILE_FLAGS_MODE(*flags) != STACKSHOT_TURNSTILE_STATUS_UNKNOWN) {
3326 return ret;
3327 }
3328 /* restore original hops value, saving the new one if we fall through to unknown */
3329 unknown_hops = *hops;
3330 *hops = pre_hops;
3331 *flags = STACKSHOT_TURNSTILE_FLAGS_PORT(*flags);
3332 }
3333
3334 if (ts->ts_inheritor_flags & TURNSTILE_INHERITOR_THREAD) {
3335 *flags |= STACKSHOT_TURNSTILE_STATUS_THREAD;
3336 return (uint64_t) thread_tid(ts->ts_inheritor);
3337 }
3338
3339 if (ts->ts_inheritor_flags & TURNSTILE_INHERITOR_WORKQ) {
3340 *flags |= STACKSHOT_TURNSTILE_STATUS_WORKQUEUE;
3341 return VM_KERNEL_UNSLIDE_OR_PERM(ts->ts_inheritor);
3342 }
3343
3344 /*
3345 * If we found a send turnstile, try to get the task that the turnstile's
3346 * port is in the ipc space of
3347 */
3348 if (turnstile_is_send_turnstile(ts)) {
3349 ipc_port_t port = (ipc_port_t)ts->ts_proprietor;
3350
3351 if (port && ip_active(port)) {
3352 if (ip_mq_lock_held_kdp(port)) {
3353 *flags |= STACKSHOT_TURNSTILE_STATUS_HELD_IPLOCK;
3354 return 0;
3355 }
3356 if (port->ip_receiver_name != 0 && port->ip_receiver) {
3357 ipc_space_t space = (ipc_space_t) port->ip_receiver;
3358 task_t dest_task = space->is_task;
3359
3360 if (dest_task != TASK_NULL) {
3361 *flags |= STACKSHOT_TURNSTILE_STATUS_BLOCKED_ON_TASK;
3362 return pid_from_task(dest_task);
3363 }
3364 }
3365 }
3366 }
3367
3368 if (turnstile_is_receive_turnstile(ts)) {
3369 ipc_port_t port = (ipc_port_t)ts->ts_proprietor;
3370 if (port && ip_active(port)) {
3371 if (ip_mq_lock_held_kdp(port)) {
3372 *flags |= STACKSHOT_TURNSTILE_STATUS_HELD_IPLOCK;
3373 return 0;
3374 }
3375 if (port->ip_specialreply) {
3376 /* try getting the pid stored in the port */
3377 uint64_t pid_candidate = ipc_special_reply_get_pid_locked(port);
3378
3379 if (pid_candidate) {
3380 *flags |= STACKSHOT_TURNSTILE_STATUS_BLOCKED_ON_TASK;
3381 return pid_candidate;
3382 }
3383 }
3384 }
3385 }
3386
3387 *hops = unknown_hops;
3388 *flags |= STACKSHOT_TURNSTILE_STATUS_UNKNOWN;
3389 return 0;
3390 }
3391
3392 void
kdp_turnstile_fill_tsinfo(struct turnstile * ts,thread_turnstileinfo_v2_t * tsinfo,struct ipc_service_port_label ** isplp)3393 kdp_turnstile_fill_tsinfo(struct turnstile *ts, thread_turnstileinfo_v2_t *tsinfo, struct ipc_service_port_label **isplp)
3394 {
3395 uint64_t final_inheritor;
3396 uint64_t flags = 0;
3397 uint8_t hops = 0;
3398
3399 tsinfo->turnstile_context = 0;
3400 tsinfo->number_of_hops = 0;
3401 tsinfo->turnstile_priority = 0;
3402
3403 assert(ts != TURNSTILE_NULL);
3404
3405 if (waitq_held(&ts->ts_waitq)) {
3406 tsinfo->turnstile_flags |= STACKSHOT_TURNSTILE_STATUS_LOCKED_WAITQ;
3407 return;
3408 }
3409
3410 final_inheritor = kdp_turnstile_traverse_inheritor_chain(ts, &flags, &hops, isplp);
3411
3412 /* store some metadata about the turnstile itself */
3413 tsinfo->turnstile_flags = flags;
3414 tsinfo->number_of_hops = hops;
3415 tsinfo->turnstile_priority = ts->ts_priority;
3416 tsinfo->turnstile_context = final_inheritor;
3417 }
3418
3419 #if DEVELOPMENT || DEBUG
3420
3421 int sysctl_io_opaque(void *req, void *pValue, size_t valueSize, int *changed);
3422
3423 /*
3424 * Name: turnstile_get_boost_stats_sysctl
3425 *
3426 * Description: Function to get turnstile stats.
3427 *
3428 * Args: req : opaque struct to pass to sysctl_io_opaque
3429 *
3430 * Returns: errorno
3431 */
3432 int
turnstile_get_boost_stats_sysctl(void * req)3433 turnstile_get_boost_stats_sysctl(
3434 void *req)
3435 {
3436 return sysctl_io_opaque(req, turnstile_boost_stats, sizeof(struct turnstile_stats) * TURNSTILE_MAX_HOP_DEFAULT, NULL);
3437 }
3438
3439 /*
3440 * Name: get_turnstile_stats_sysctl
3441 *
3442 * Description: Function to get turnstile stats.
3443 *
3444 * Args: req : opaque struct to pass to sysctl_io_opaque
3445 *
3446 * Returns: errorno
3447 */
3448 int
turnstile_get_unboost_stats_sysctl(void * req)3449 turnstile_get_unboost_stats_sysctl(
3450 void *req)
3451 {
3452 return sysctl_io_opaque(req, turnstile_unboost_stats, sizeof(struct turnstile_stats) * TURNSTILE_MAX_HOP_DEFAULT, NULL);
3453 }
3454
3455 /* Testing interface for Development kernels */
3456 #define tstile_test_prim_lock_interlock(test_prim) \
3457 lck_spin_lock(&test_prim->ttprim_interlock)
3458 #define tstile_test_prim_unlock_interlock(test_prim) \
3459 lck_spin_unlock(&test_prim->ttprim_interlock)
3460
3461 static void
tstile_test_prim_init(struct tstile_test_prim * test_prim)3462 tstile_test_prim_init(struct tstile_test_prim *test_prim)
3463 {
3464 test_prim->ttprim_turnstile = TURNSTILE_NULL;
3465 test_prim->ttprim_owner = NULL;
3466 lck_spin_init(&test_prim->ttprim_interlock, &turnstiles_dev_lock_grp, LCK_ATTR_NULL);
3467 test_prim->tt_prim_waiters = 0;
3468 }
3469
3470 int
tstile_test_prim_lock(int val)3471 tstile_test_prim_lock(int val)
3472 {
3473 struct tstile_test_prim *test_prim;
3474 boolean_t use_hashtable;
3475 turnstile_type_t type;
3476 wait_interrupt_t wait_type;
3477
3478 switch (val) {
3479 case SYSCTL_TURNSTILE_TEST_USER_DEFAULT:
3480 test_prim = &test_prim_ts_inline;
3481 use_hashtable = FALSE;
3482 wait_type = THREAD_ABORTSAFE;
3483 type = TURNSTILE_ULOCK;
3484 break;
3485 case SYSCTL_TURNSTILE_TEST_USER_HASHTABLE:
3486 test_prim = &test_prim_global_htable;
3487 use_hashtable = TRUE;
3488 wait_type = THREAD_ABORTSAFE;
3489 type = TURNSTILE_ULOCK;
3490 break;
3491 case SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT:
3492 test_prim = &test_prim_global_ts_kernel;
3493 use_hashtable = FALSE;
3494 wait_type = THREAD_UNINT | THREAD_WAIT_NOREPORT_USER;
3495 type = TURNSTILE_KERNEL_MUTEX;
3496 break;
3497 case SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE:
3498 test_prim = &test_prim_global_ts_kernel_hash;
3499 use_hashtable = TRUE;
3500 wait_type = THREAD_UNINT | THREAD_WAIT_NOREPORT_USER;
3501 type = TURNSTILE_KERNEL_MUTEX;
3502 break;
3503
3504 default:
3505 return -1;
3506 }
3507
3508 lock_start:
3509
3510 /* take the interlock of the primitive */
3511 tstile_test_prim_lock_interlock(test_prim);
3512
3513 /* Check if the lock is available */
3514 if (test_prim->ttprim_owner == NULL && test_prim->tt_prim_waiters == 0) {
3515 thread_reference(current_thread());
3516 test_prim->ttprim_owner = current_thread();
3517 tstile_test_prim_unlock_interlock(test_prim);
3518 return 0;
3519 }
3520
3521 struct turnstile *prim_turnstile = TURNSTILE_NULL;
3522
3523 /* primitive locked, get a turnstile */
3524 if (use_hashtable) {
3525 prim_turnstile = turnstile_prepare_hash((uintptr_t)test_prim, type);
3526 } else {
3527 prim_turnstile = turnstile_prepare((uintptr_t)test_prim,
3528 &test_prim->ttprim_turnstile, TURNSTILE_NULL, type);
3529 }
3530
3531 assert(prim_turnstile != TURNSTILE_NULL);
3532
3533 /* This is contented acquire case */
3534 if (test_prim->ttprim_owner == NULL) {
3535 thread_reference(current_thread());
3536 test_prim->ttprim_owner = current_thread();
3537
3538 /* Update the turnstile owner */
3539 turnstile_update_inheritor(prim_turnstile,
3540 current_thread(),
3541 (TURNSTILE_IMMEDIATE_UPDATE | TURNSTILE_INHERITOR_THREAD));
3542
3543 turnstile_update_inheritor_complete(prim_turnstile, TURNSTILE_INTERLOCK_HELD);
3544
3545 if (use_hashtable) {
3546 turnstile_complete_hash((uintptr_t)test_prim, type);
3547 } else {
3548 turnstile_complete((uintptr_t)test_prim,
3549 &test_prim->ttprim_turnstile, NULL, type);
3550 }
3551
3552 tstile_test_prim_unlock_interlock(test_prim);
3553
3554 turnstile_cleanup();
3555 return 0;
3556 }
3557
3558 test_prim->tt_prim_waiters++;
3559 turnstile_update_inheritor(prim_turnstile,
3560 test_prim->ttprim_owner,
3561 (TURNSTILE_DELAYED_UPDATE | TURNSTILE_INHERITOR_THREAD));
3562
3563 waitq_assert_wait64(&prim_turnstile->ts_waitq,
3564 CAST_EVENT64_T(test_prim), wait_type,
3565 TIMEOUT_WAIT_FOREVER);
3566
3567 /* drop the interlock */
3568 tstile_test_prim_unlock_interlock(test_prim);
3569
3570 turnstile_update_inheritor_complete(prim_turnstile, TURNSTILE_INTERLOCK_NOT_HELD);
3571
3572 wait_result_t result;
3573 result = thread_block(THREAD_CONTINUE_NULL);
3574
3575 /* re-acquire the interlock to get turnstile back */
3576 tstile_test_prim_lock_interlock(test_prim);
3577 test_prim->tt_prim_waiters--;
3578
3579 if (use_hashtable) {
3580 turnstile_complete_hash((uintptr_t)test_prim, type);
3581 } else {
3582 turnstile_complete((uintptr_t)test_prim,
3583 &test_prim->ttprim_turnstile, NULL, type);
3584 }
3585
3586 tstile_test_prim_unlock_interlock(test_prim);
3587
3588 turnstile_cleanup();
3589
3590 /* Return if thread interrupted */
3591 if (result == THREAD_INTERRUPTED) {
3592 return 1;
3593 }
3594
3595 goto lock_start;
3596 }
3597
3598 int
tstile_test_prim_unlock(int val)3599 tstile_test_prim_unlock(int val)
3600 {
3601 struct tstile_test_prim *test_prim;
3602 boolean_t use_hashtable;
3603 turnstile_type_t type;
3604
3605 switch (val) {
3606 case SYSCTL_TURNSTILE_TEST_USER_DEFAULT:
3607 test_prim = &test_prim_ts_inline;
3608 use_hashtable = FALSE;
3609 type = TURNSTILE_ULOCK;
3610 break;
3611 case SYSCTL_TURNSTILE_TEST_USER_HASHTABLE:
3612 test_prim = &test_prim_global_htable;
3613 use_hashtable = TRUE;
3614 type = TURNSTILE_ULOCK;
3615 break;
3616 case SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT:
3617 test_prim = &test_prim_global_ts_kernel;
3618 use_hashtable = FALSE;
3619 type = TURNSTILE_KERNEL_MUTEX;
3620 break;
3621 case SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE:
3622 test_prim = &test_prim_global_ts_kernel_hash;
3623 use_hashtable = TRUE;
3624 type = TURNSTILE_KERNEL_MUTEX;
3625 break;
3626 default:
3627 return -1;
3628 }
3629
3630 /* take the interlock of the primitive */
3631 tstile_test_prim_lock_interlock(test_prim);
3632
3633 if (test_prim->ttprim_owner == NULL) {
3634 tstile_test_prim_unlock_interlock(test_prim);
3635 return 1;
3636 }
3637
3638 /* Check if the lock is contended */
3639 if (test_prim->ttprim_owner != NULL && test_prim->tt_prim_waiters == 0) {
3640 /* lock is not contended */
3641 thread_t old_owner = test_prim->ttprim_owner;
3642 test_prim->ttprim_owner = NULL;
3643 tstile_test_prim_unlock_interlock(test_prim);
3644
3645 thread_deallocate(old_owner);
3646 return 0;
3647 }
3648
3649 struct turnstile *prim_turnstile = TURNSTILE_NULL;
3650
3651 thread_t old_owner = test_prim->ttprim_owner;
3652 test_prim->ttprim_owner = NULL;
3653
3654 /* primitive locked, get a turnstile */
3655 if (use_hashtable) {
3656 prim_turnstile = turnstile_prepare_hash((uintptr_t)test_prim, type);
3657 } else {
3658 prim_turnstile = turnstile_prepare((uintptr_t)test_prim,
3659 &test_prim->ttprim_turnstile, TURNSTILE_NULL, type);
3660 }
3661
3662 assert(prim_turnstile != TURNSTILE_NULL);
3663
3664 /* Update the turnstile owner */
3665 turnstile_update_inheritor(prim_turnstile,
3666 NULL,
3667 (TURNSTILE_IMMEDIATE_UPDATE | TURNSTILE_INHERITOR_THREAD));
3668
3669 waitq_wakeup64_one(&prim_turnstile->ts_waitq,
3670 CAST_EVENT64_T(test_prim),
3671 THREAD_AWAKENED, WAITQ_WAKEUP_DEFAULT);
3672
3673 turnstile_update_inheritor_complete(prim_turnstile, TURNSTILE_INTERLOCK_HELD);
3674
3675 if (use_hashtable) {
3676 turnstile_complete_hash((uintptr_t)test_prim, type);
3677 } else {
3678 turnstile_complete((uintptr_t)test_prim,
3679 &test_prim->ttprim_turnstile, NULL, type);
3680 }
3681
3682 tstile_test_prim_unlock_interlock(test_prim);
3683
3684 turnstile_cleanup();
3685
3686 if (old_owner) {
3687 /* Changing this to thread_deallocate_safe to exercise thread_deallocate_safe path */
3688 thread_deallocate_safe(old_owner);
3689 }
3690
3691 return 0;
3692 }
3693
3694 #endif
3695