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