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