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