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