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