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