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