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