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