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