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