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