2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
46 #include <pexpert/pexpert.h>
48 #include <libkern/section_keywords.h>
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)
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
,
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
),
82 os_refgrp_decl(static, turnstile_refgrp
, "turnstile", NULL
);
84 #if DEVELOPMENT || DEBUG
85 static queue_head_t turnstiles_list
;
86 static lck_spin_t global_turnstile_lock
;
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
;
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)
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 */
111 #define max(a, b) (((a) > (b)) ? (a) : (b))
114 /* Static function declarations */
115 static turnstile_type_t
116 turnstile_get_type(struct turnstile
*turnstile
);
118 turnstile_get_gencount(struct turnstile
*turnstile
);
120 turnstile_set_type_and_increment_gencount(struct turnstile
*turnstile
, turnstile_type_t type
);
122 turnstile_init(struct turnstile
*turnstile
);
124 turnstile_update_inheritor_workq_priority_chain(struct turnstile
*in_turnstile
, spl_t s
);
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
);
129 turnstile_update_inheritor_turnstile_priority_chain(struct turnstile
**in_out_turnstile
,
130 int total_hop
, turnstile_stats_update_flags_t tsu_flags
);
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
);
136 turnstile_update_turnstile_promotion_locked(struct turnstile
*dst_turnstile
,
137 struct turnstile
*src_turnstile
);
139 turnstile_update_turnstile_promotion(struct turnstile
*dst_turnstile
,
140 struct turnstile
*src_turnstile
);
142 turnstile_need_turnstile_promotion_update(struct turnstile
*dst_turnstile
,
143 struct turnstile
*src_turnstile
);
145 turnstile_add_turnstile_promotion(struct turnstile
*dst_turnstile
,
146 struct turnstile
*src_turnstile
);
148 turnstile_remove_turnstile_promotion(struct turnstile
*dst_turnstile
,
149 struct turnstile
*src_turnstile
);
151 turnstile_update_thread_promotion_locked(struct turnstile
*dst_turnstile
,
154 turnstile_need_thread_promotion_update(struct turnstile
*dst_turnstile
,
157 thread_add_turnstile_promotion(
158 thread_t thread
, struct turnstile
*turnstile
);
160 thread_remove_turnstile_promotion(
161 thread_t thread
, struct turnstile
*turnstile
);
163 thread_needs_turnstile_promotion_update(thread_t thread
,
164 struct turnstile
*turnstile
);
166 thread_update_turnstile_promotion(
167 thread_t thread
, struct turnstile
*turnstile
);
169 thread_update_turnstile_promotion_locked(
170 thread_t thread
, struct turnstile
*turnstile
);
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
);
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
;
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
;
197 tstile_test_prim_init(struct tstile_test_prim
**test_prim_ptr
);
200 union turnstile_type_gencount
{
203 uint32_t ts_type
:(8 * sizeof(turnstile_type_t
)),
204 ts_gencount
: (8 * (sizeof(uint32_t) - sizeof(turnstile_type_t
)));
208 static turnstile_type_t
209 turnstile_get_type(struct turnstile
*turnstile
)
211 union turnstile_type_gencount type_and_gencount
;
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
;
218 turnstile_get_gencount(struct turnstile
*turnstile
)
220 union turnstile_type_gencount type_and_gencount
;
222 type_and_gencount
.value
= atomic_load_explicit(&turnstile
->ts_type_gencount
, memory_order_relaxed
);
223 return (uint32_t) type_and_gencount
.ts_gencount
;
227 turnstile_set_type_and_increment_gencount(struct turnstile
*turnstile
, turnstile_type_t type
)
229 union turnstile_type_gencount type_and_gencount
;
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
);
239 /* Turnstile hashtable Implementation */
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.
247 #define TURNSTILE_HTABLE_BUCKETS_DEFAULT 32
248 #define TURNSTILE_HTABLE_BUCKETS_MAX 1024
250 SLIST_HEAD(turnstile_hashlist
, turnstile
);
252 struct turnstile_htable_bucket
{
253 lck_spin_t ts_ht_bucket_lock
;
254 struct turnstile_hashlist ts_ht_bucket_list
;
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
;
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
;
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)
276 #define kdp_turnstile_bucket_is_locked(bucket) \
277 kdp_lck_spin_is_acquired(&bucket->ts_ht_bucket_lock)
280 * Name: turnstiles_hashtable_init
282 * Description: Initializes the global turnstile hash table.
291 turnstiles_hashtable_init(void)
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
;
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!");
305 turnstile_htable
= (struct turnstile_htable_bucket
*)kalloc(ts_htable_size
);
306 if (turnstile_htable
== NULL
) {
307 panic("Turnstiles hash table memory allocation failed!");
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
);
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
);
319 ts_bucket
= &(turnstile_htable
[i
]);
320 turnstile_bucket_lock_init(ts_bucket
);
321 SLIST_INIT(&ts_bucket
->ts_ht_bucket_list
);
326 * Name: turnstile_freelist_empty
328 * Description: Checks if the turnstile's freelist is empty
329 * Should be called with the primitive IL held.
335 * true if freelist is empty; false otherwise
337 static inline boolean_t
338 turnstile_freelist_empty(
339 struct turnstile
*ts
)
341 return SLIST_EMPTY(&ts
->ts_free_turnstiles
);
346 * Name: turnstile_freelist_insert
348 * Description: Inserts the turnstile into the freelist of another turnstile
349 * Should be called with the primitive IL held.
352 * Arg1: primitive turnstile
353 * Arg2: turnstile to add to the freelist
359 turnstile_freelist_insert(
360 struct turnstile
*dst_ts
,
361 struct turnstile
*free_ts
)
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
);
370 * Name: turnstile_freelist_remove
372 * Description: Removes a turnstile from the freelist of a turnstile
373 * Should be called with the primitive IL held.
376 * Arg1: primitive turnstile
379 * turnstile removed from the freelist
381 static struct turnstile
*
382 turnstile_freelist_remove(
383 struct turnstile
*ts
)
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
;
397 * Name: turnstile_hash
399 * Description: Calculates the hash bucket index for a given proprietor
402 * Arg1: proprietor (key) for hashing
405 * hash table bucket index for provided proprietor
407 static inline uint32_t
408 turnstile_hash(uintptr_t proprietor
)
410 uint32_t hash
= os_hash_kernel_pointer((void *)proprietor
);
411 return hash
& (ts_htable_buckets
- 1);
414 static inline struct turnstile_htable_bucket
*
415 turnstile_get_bucket(uint32_t index
, turnstile_type_t type
)
417 struct turnstile_htable_bucket
*ts_bucket
;
418 int hash_policy
= turnstile_hash_lock_policy
[type
];
420 if (hash_policy
& TURNSTILE_IRQ_UNSAFE_HASH
) {
421 ts_bucket
= &(turnstile_htable
[index
]);
423 ts_bucket
= &(turnstile_htable_irq_safe
[index
]);
430 * Name: turnstile_hash_bucket_lock
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.
438 * Arg1: proprietor (key) for hashing
439 * Arg2: index for proprietor in the hash
440 * Arg3: turnstile type
442 * Returns: old value of irq if irq were disabled before acquiring the lock.
445 turnstile_hash_bucket_lock(uintptr_t proprietor
, uint32_t *index_proprietor
, turnstile_type_t type
)
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
);
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.
459 index
= turnstile_hash(proprietor
);
460 *index_proprietor
= index
;
462 index
= *index_proprietor
;
465 ts_bucket
= turnstile_get_bucket(index
, type
);
471 turnstile_bucket_lock(ts_bucket
);
477 * Name: turnstile_hash_bucket_unlock
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.
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
492 turnstile_hash_bucket_unlock(uintptr_t proprietor
, uint32_t *index_proprietor
, turnstile_type_t type
, unsigned s
)
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
);
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.
505 index
= turnstile_hash(proprietor
);
506 *index_proprietor
= index
;
508 index
= *index_proprietor
;
510 ts_bucket
= turnstile_get_bucket(index
, type
);
512 turnstile_bucket_unlock(ts_bucket
);
519 * Name: turnstile_htable_lookup_add
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.
529 * Arg2: new turnstile for primitive
530 * Arg3: turnstile_type_t type
533 * Previous turnstile for proprietor in the hash table
535 static struct turnstile
*
536 turnstile_htable_lookup_add(
537 uintptr_t proprietor
,
538 struct turnstile
*new_turnstile
,
539 turnstile_type_t type
)
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
);
549 ts_bucket
= turnstile_get_bucket(index
, type
);
555 turnstile_bucket_lock(ts_bucket
);
558 struct turnstile
*ts
;
560 SLIST_FOREACH(ts
, &ts_bucket
->ts_ht_bucket_list
, ts_htable_link
) {
561 if (ts
->ts_proprietor
== proprietor
) {
563 * Found an entry in the hashtable for this proprietor; add thread turnstile to freelist
564 * and return this turnstile
567 turnstile_bucket_unlock(ts_bucket
);
572 turnstile_freelist_insert(ts
, new_turnstile
);
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
);
581 turnstile_bucket_unlock(ts_bucket
);
586 /* Since there was no previous entry for this proprietor, return TURNSTILE_NULL */
587 return TURNSTILE_NULL
;
591 * Name: turnstable_htable_lookup_remove
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.
602 * Arg2: free turnstile to be returned
603 * Arg3: turnstile_type_t type
606 * turnstile for this proprietor in the hashtable after the removal
608 static struct turnstile
*
609 turnstable_htable_lookup_remove(
610 uintptr_t proprietor
,
611 struct turnstile
**free_turnstile
,
612 turnstile_type_t type
)
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
);
623 ts_bucket
= turnstile_get_bucket(index
, type
);
629 turnstile_bucket_lock(ts_bucket
);
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
) {
640 assert(ret_turnstile
!= TURNSTILE_NULL
);
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
);
648 turnstile_bucket_unlock(ts_bucket
);
653 *free_turnstile
= ret_turnstile
;
654 return TURNSTILE_NULL
;
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
661 turnstile_bucket_unlock(ts_bucket
);
666 *free_turnstile
= turnstile_freelist_remove(ret_turnstile
);
667 return ret_turnstile
;
672 * Name: turnstile_htable_lookup
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.
680 * Arg2: turnstile_type_t type
683 * Turnstile for proprietor in the hash table
685 static struct turnstile
*
686 turnstile_htable_lookup(
687 uintptr_t proprietor
,
688 turnstile_type_t type
)
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
);
700 if (irq_safe
&& !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
;
710 turnstile_bucket_lock(ts_bucket
);
713 struct turnstile
*ts
= TURNSTILE_NULL
;
714 struct turnstile
*ret_turnstile
= TURNSTILE_NULL
;
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 */
724 if (needs_lock
&& !kdp_ctx
) {
725 turnstile_bucket_unlock(ts_bucket
);
731 return ret_turnstile
;
735 * Name: turnstile_deallocate_queue_invoke
737 * Description: invoke function for the asynchronous turnstile deallocation
740 * Arg1: &turnstile_deallocate_queue
741 * Arg2: a pointer to the turnstile ts_deallocate_link member of a tunrstile to
747 turnstile_deallocate_queue_invoke(mpsc_queue_chain_t e
,
748 __assert_only mpsc_daemon_queue_t dq
)
750 struct turnstile
*ts
;
752 ts
= mpsc_queue_element(e
, struct turnstile
, ts_deallocate_link
);
753 assert(dq
== &turnstile_deallocate_queue
);
754 turnstile_destroy(ts
);
758 * Name: turnstiles_init
760 * Description: Initialize turnstile sub system.
767 turnstiles_init(void)
769 turnstiles_zone
= zinit(sizeof(struct turnstile
),
770 MAX_TURNSTILES
* sizeof(struct turnstile
),
771 TURNSTILES_CHUNK
* sizeof(struct turnstile
),
774 if (!PE_parse_boot_argn("turnstile_max_hop", &turnstile_max_hop
, sizeof(turnstile_max_hop
))) {
775 turnstile_max_hop
= TURNSTILE_MAX_HOP_DEFAULT
;
778 turnstiles_hashtable_init();
780 thread_deallocate_daemon_register_queue(&turnstile_deallocate_queue
,
781 turnstile_deallocate_queue_invoke
);
783 #if DEVELOPMENT || DEBUG
784 /* Initialize the global turnstile locks and lock group */
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();
791 queue_init(&turnstiles_list
);
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
);
803 * Name: turnstile_alloc
805 * Description: Allocate a turnstile.
810 * turnstile on Success.
813 turnstile_alloc(void)
815 struct turnstile
*turnstile
= TURNSTILE_NULL
;
817 turnstile
= zalloc(turnstiles_zone
);
818 turnstile_init(turnstile
);
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();
831 * Name: turnstile_init
833 * Description: Initialize the turnstile.
836 * Arg1: turnstile to initialize
841 turnstile_init(struct turnstile
*turnstile
)
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
);
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
);
863 #if DEVELOPMENT || DEBUG
864 turnstile
->ts_thread
= current_thread();
865 turnstile
->ts_prev_thread
= NULL
;
870 * Name: turnstile_reference
872 * Description: Take a reference on the turnstile.
879 turnstile_reference(struct turnstile
*turnstile
)
881 if (turnstile
== TURNSTILE_NULL
) {
884 os_ref_retain(&turnstile
->ts_refcount
);
888 * Name: turnstile_deallocate
890 * Description: Drop a reference on the turnstile.
891 * Destroy the turnstile if the last ref.
898 turnstile_deallocate(struct turnstile
*turnstile
)
900 if (turnstile
== TURNSTILE_NULL
) {
904 if (__improbable(os_ref_release(&turnstile
->ts_refcount
) == 0)) {
905 turnstile_destroy(turnstile
);
910 * Name: turnstile_deallocate_safe
912 * Description: Drop a reference on the turnstile safely without triggering zfree.
919 turnstile_deallocate_safe(struct turnstile
*turnstile
)
921 if (turnstile
== TURNSTILE_NULL
) {
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
);
932 * Name: turnstile_destroy
934 * Description: Deallocates the turnstile.
942 turnstile_destroy(struct turnstile
*turnstile
)
944 /* destroy the waitq */
945 waitq_deinit(&turnstile
->ts_waitq
);
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();
957 zfree(turnstiles_zone
, turnstile
);
961 * Name: turnstile_prepare
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.
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
980 uintptr_t proprietor
,
981 struct turnstile
**tstore
,
982 struct turnstile
*turnstile
,
983 turnstile_type_t type
)
985 thread_t thread
= current_thread();
986 struct turnstile
*ret_turnstile
= TURNSTILE_NULL
;
987 struct turnstile
*thread_turnstile
= turnstile
;
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
;
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
);
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
;
1010 if (tstore
!= NULL
) {
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
1015 * Else, add the thread turnstile to freelist of the primitive turnstile.
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);
1027 turnstile_freelist_insert(ret_turnstile
, thread_turnstile
);
1029 ret_turnstile
= *tstore
;
1032 * Lookup the primitive in the turnstile hash table and see if it already has an entry.
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);
1045 return ret_turnstile
;
1049 * Name: turnstile_complete
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.
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
1065 uintptr_t proprietor
,
1066 struct turnstile
**tstore
,
1067 struct turnstile
**out_turnstile
,
1068 turnstile_type_t type
)
1070 thread_t thread
= current_thread();
1071 struct turnstile
*primitive_turnstile
= TURNSTILE_NULL
;
1072 struct turnstile
*thread_turnstile
= TURNSTILE_NULL
;
1074 assert(thread
->inheritor
== NULL
);
1076 if (tstore
!= NULL
) {
1078 * If the primitive stores the turnstile, check if the primitive turnstile
1079 * has any turnstiles on its freelist.
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
);
1088 /* Freelist has turnstiles; remove one from the freelist */
1089 thread_turnstile
= turnstile_freelist_remove(*tstore
);
1091 primitive_turnstile
= *tstore
;
1093 /* Use the global hash to find and remove a turnstile */
1094 primitive_turnstile
= turnstable_htable_lookup_remove(proprietor
, &thread_turnstile
, type
);
1096 if (primitive_turnstile
== NULL
) {
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.
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
));
1106 * old inheritor is set in curret thread and its priority propagation
1107 * will happen in turnstile cleanup call
1110 assert(thread_turnstile
->ts_inheritor
== TURNSTILE_INHERITOR_NULL
);
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);
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
);
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
;
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
;
1138 *out_turnstile
= thread_turnstile
;
1144 * Name: turnstile_kernel_update_inheritor_on_wake_locked
1146 * Description: Set thread as the inheritor of the turnstile and
1147 * boost the inheritor.
1150 * Arg2: new_inheritor
1153 * Called with turnstile locked
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
)
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
)];
1165 assert(flags
& TURNSTILE_INHERITOR_THREAD
);
1166 assert(policy
== TURNSTILE_KERNEL_PROMOTE
|| policy
== TURNSTILE_USER_PROMOTE
);
1168 turnstile_stash_inheritor((thread_t
)new_inheritor
, TURNSTILE_INHERITOR_THREAD
);
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.
1175 turnstile_recompute_priority_locked(turnstile
);
1176 turnstile_update_inheritor_locked(turnstile
);
1180 * Name: turnstile_update_inheritor_locked
1182 * Description: Update the inheritor of the turnstile and boost the
1183 * inheritor, called with turnstile locked.
1187 * Implicit arg: new inheritor value is stashed in current thread's struct
1190 * old inheritor reference is returned on current thread's struct.
1193 turnstile_update_inheritor_locked(
1194 struct turnstile
*turnstile
)
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
);
1204 assert(waitq_held(&turnstile
->ts_waitq
));
1207 * Get the new inheritor value from current thread's
1208 * struct, the value was stashed by turnstile_update_inheritor
1210 turnstile_inheritor_t new_inheritor
= thread
->inheritor
;
1211 turnstile_update_flags_t new_inheritor_flags
= thread
->inheritor_flags
;
1213 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
1214 case TURNSTILE_USER_PROMOTE
:
1215 case TURNSTILE_USER_IPC_PROMOTE
:
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
);
1223 if (new_inheritor
) {
1224 assert(new_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
1229 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
1232 /* Check if update is needed */
1233 if (old_inheritor
== new_inheritor
&& old_inheritor
== NULL
) {
1237 if (old_inheritor
== new_inheritor
) {
1238 if (new_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
1239 thread_t thread_inheritor
= (thread_t
)new_inheritor
;
1241 assert(old_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
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
;
1249 assert(old_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
);
1251 new_inheritor_needs_update
= turnstile_update_turnstile_promotion(
1252 inheritor_turnstile
, turnstile
);
1253 } else if (new_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
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.
1259 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED
|
1260 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
, turnstile
);
1262 panic("Inheritor flags lost along the way");
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
);
1273 if (old_inheritor
!= NULL
) {
1274 if (old_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
1275 thread_t thread_inheritor
= (thread_t
)old_inheritor
;
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
;
1282 old_inheritor_needs_update
= turnstile_remove_turnstile_promotion(
1283 old_turnstile
, turnstile
);
1284 } else if (old_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
1286 * We don't need to do anything when the push was WORKQ
1287 * because nothing is pushed on in the first place.
1289 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED
|
1290 TSU_TURNSTILE_ARG
, turnstile
);
1292 panic("Inheritor flags lost along the way");
1294 /* Update turnstile stats */
1295 if (!old_inheritor_needs_update
) {
1296 turnstile_stats_update(1, TSU_PRI_PROPAGATION
| TSU_TURNSTILE_ARG
,
1301 if (new_inheritor
!= NULL
) {
1302 if (new_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
1303 thread_t thread_inheritor
= (thread_t
)new_inheritor
;
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
;
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
;
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
);
1324 panic("Inheritor flags lost along the way");
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
);
1334 if (old_inheritor_needs_update
) {
1335 old_inheritor_flags
|= TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE
;
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.
1342 if (new_inheritor_needs_update
) {
1343 old_inheritor_flags
|= TURNSTILE_NEEDS_PRI_UPDATE
;
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
;
1354 * Name: turnstile_stash_inheritor
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.
1365 * old inheritor reference is stashed on current thread's struct.
1368 turnstile_stash_inheritor(
1369 turnstile_inheritor_t new_inheritor
,
1370 turnstile_update_flags_t flags
)
1372 thread_t thread
= current_thread();
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
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
);
1394 panic("Missing type in flags (%x) for inheritor (%p)", flags
,
1400 * Name: turnstile_update_inheritor
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.
1409 * Arg3: flags - TURNSTILE_DELAYED_UPDATE - update will happen later in assert_wait
1412 * old inheritor reference is stashed on current thread's struct.
1415 turnstile_update_inheritor(
1416 struct turnstile
*turnstile
,
1417 turnstile_inheritor_t new_inheritor
,
1418 turnstile_update_flags_t flags
)
1422 turnstile_stash_inheritor(new_inheritor
, flags
);
1424 /* Do not perform the update if delayed update is specified */
1425 if (flags
& TURNSTILE_DELAYED_UPDATE
) {
1429 /* lock the turnstile waitq */
1431 waitq_lock(&turnstile
->ts_waitq
);
1433 turnstile_update_inheritor_locked(turnstile
);
1435 waitq_unlock(&turnstile
->ts_waitq
);
1443 * Name: turnstile_need_thread_promotion_update
1445 * Description: Check if thread's place in the turnstile waitq needs to be updated.
1447 * Arg1: dst turnstile
1450 * Returns: TRUE: if turnstile_update_thread_promotion_locked needs to be called.
1453 * Condition: thread locked.
1456 turnstile_need_thread_promotion_update(
1457 struct turnstile
*dst_turnstile
,
1460 int thread_link_priority
;
1461 boolean_t needs_update
= FALSE
;
1463 thread_link_priority
= priority_queue_entry_key(&(dst_turnstile
->ts_waitq
.waitq_prio_queue
),
1464 &(thread
->wait_prioq_links
));
1466 int priority
= turnstile_compute_thread_push(dst_turnstile
, thread
);
1468 needs_update
= (thread_link_priority
== priority
) ? FALSE
: TRUE
;
1470 return needs_update
;
1474 * Name: turnstile_priority_queue_update_entry_key
1476 * Description: Updates the priority of an entry in a priority queue
1478 * Arg1: a turnstile/thread/... priority queue
1479 * Arg2: the element to change the priority of
1480 * Arg3: the new priority
1482 * Returns: whether the maximum priority of the queue changed.
1485 turnstile_priority_queue_update_entry_key(struct priority_queue
*q
,
1486 priority_queue_entry_t elt
, priority_queue_key_t pri
)
1488 priority_queue_key_t old_key
= priority_queue_max_key(q
);
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
);
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
);
1506 * Name: turnstile_update_thread_promotion_locked
1508 * Description: Update dst turnstile's inheritor link since one of the waiting
1509 * thread's priority has changed.
1511 * Arg1: dst turnstile
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.
1517 * Condition: dst turnstile and thread are locked.
1520 turnstile_update_thread_promotion_locked(
1521 struct turnstile
*dst_turnstile
,
1524 int thread_link_priority
;
1526 int priority
= turnstile_compute_thread_push(dst_turnstile
, thread
);
1528 thread_link_priority
= priority_queue_entry_key(&(dst_turnstile
->ts_waitq
.waitq_prio_queue
),
1529 &(thread
->wait_prioq_links
));
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
),
1537 thread_link_priority
, 0);
1540 if (!turnstile_priority_queue_update_entry_key(
1541 &dst_turnstile
->ts_waitq
.waitq_prio_queue
,
1542 &thread
->wait_prioq_links
, priority
)) {
1546 /* Update dst turnstile's priority */
1547 return turnstile_recompute_priority_locked(dst_turnstile
);
1551 * Name: thread_add_turnstile_promotion
1553 * Description: Add a turnstile to thread's inheritor list and update thread's priority.
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.
1561 * Condition: turnstile locked.
1564 thread_add_turnstile_promotion(
1566 struct turnstile
*turnstile
)
1568 boolean_t needs_update
= FALSE
;
1570 /* Update the pairing heap */
1571 thread_lock(thread
);
1573 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1574 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_ADDED_TO_THREAD_HEAP
))) | DBG_FUNC_NONE
,
1576 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
1577 turnstile
->ts_priority
, 0, 0);
1579 priority_queue_entry_init(&turnstile
->ts_inheritor_links
);
1581 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
1582 case TURNSTILE_USER_PROMOTE
:
1583 case TURNSTILE_USER_IPC_PROMOTE
:
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
);
1592 case TURNSTILE_KERNEL_PROMOTE
:
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
);
1602 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
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
,
1613 thread_unlock(thread
);
1615 return needs_update
;
1619 * Name: thread_remove_turnstile_promotion
1621 * Description: Remove turnstile from thread's inheritor list and update thread's priority.
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.
1629 * Condition: turnstile locked.
1632 thread_remove_turnstile_promotion(
1634 struct turnstile
*turnstile
)
1636 boolean_t needs_update
= FALSE
;
1638 thread_lock(thread
);
1640 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1641 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_REMOVED_FROM_THREAD_HEAP
))) | DBG_FUNC_NONE
,
1643 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
1646 /* Update the pairing heap */
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
);
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
);
1665 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
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
,
1675 thread_unlock(thread
);
1677 return needs_update
;
1681 * Name: thread_needs_turnstile_promotion_update
1683 * Description: Check if turnstile position in thread's inheritor list needs to be updated.
1688 * Returns: TRUE: if thread_update_turnstile_promotion needs to be called.
1691 * Condition: turnstile locked.
1694 thread_needs_turnstile_promotion_update(
1695 thread_t thread __assert_only
,
1696 struct turnstile
*turnstile
)
1698 boolean_t needs_update
= FALSE
;
1699 int turnstile_link_priority
= 0;
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
));
1707 case TURNSTILE_KERNEL_PROMOTE
:
1708 turnstile_link_priority
= priority_queue_entry_key(&(thread
->sched_inheritor_queue
),
1709 &(turnstile
->ts_inheritor_links
));
1712 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
1715 needs_update
= (turnstile_link_priority
== turnstile
->ts_priority
) ? FALSE
: TRUE
;
1716 return needs_update
;
1720 * Name: thread_update_turnstile_promotion_locked
1722 * Description: Update turnstile position in thread's inheritor list and update thread's priority.
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.
1730 * Condition: turnstile and thread are locked.
1733 thread_update_turnstile_promotion_locked(
1735 struct turnstile
*turnstile
)
1737 boolean_t needs_update
= FALSE
;
1738 int turnstile_link_priority
= 0;
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
);
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
);
1750 case TURNSTILE_KERNEL_PROMOTE
:
1751 turnstile_link_priority
= priority_queue_entry_key(&(thread
->sched_inheritor_queue
), &turnstile
->ts_inheritor_links
);
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
);
1759 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
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
,
1766 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
1767 turnstile
->ts_priority
,
1768 turnstile_link_priority
, 0);
1771 return needs_update
;
1776 * Name: thread_update_turnstile_promotion
1778 * Description: Update turnstile position in thread's inheritor list and update thread's priority.
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.
1786 * Condition: turnstile locked.
1789 thread_update_turnstile_promotion(
1791 struct turnstile
*turnstile
)
1793 /* Before grabbing the thread lock, check if update is needed */
1794 boolean_t needs_update
= thread_needs_turnstile_promotion_update(thread
, turnstile
);
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
;
1802 thread_lock(thread
);
1804 /* Update the pairing heap */
1805 needs_update
= thread_update_turnstile_promotion_locked(thread
, turnstile
);
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
,
1815 thread_unlock(thread
);
1817 return needs_update
;
1822 * Name: thread_get_inheritor_turnstile_sched_priority
1824 * Description: Get the max sched priority of all the inheritor turnstiles
1828 * Returns: Max sched priority of all the inheritor turnstiles.
1830 * Condition: thread locked
1833 thread_get_inheritor_turnstile_sched_priority(thread_t thread
)
1835 struct turnstile
*max_turnstile
;
1837 max_turnstile
= priority_queue_max(&thread
->sched_inheritor_queue
,
1838 struct turnstile
, ts_inheritor_links
);
1840 if (max_turnstile
) {
1841 return priority_queue_entry_key(&thread
->sched_inheritor_queue
,
1842 &max_turnstile
->ts_inheritor_links
);
1849 * Name: thread_get_inheritor_turnstile_base_priority
1851 * Description: Get the max base priority of all the inheritor turnstiles
1855 * Returns: Max base priority of all the inheritor turnstiles.
1857 * Condition: thread locked
1860 thread_get_inheritor_turnstile_base_priority(thread_t thread
)
1862 struct turnstile
*max_turnstile
;
1864 max_turnstile
= priority_queue_max(&thread
->base_inheritor_queue
,
1865 struct turnstile
, ts_inheritor_links
);
1867 if (max_turnstile
) {
1868 return priority_queue_entry_key(&thread
->base_inheritor_queue
,
1869 &max_turnstile
->ts_inheritor_links
);
1877 * Name: thread_get_waiting_turnstile
1879 * Description: Get the turnstile if the thread is waiting on a turnstile.
1883 * Returns: turnstile: if the thread is blocked on a turnstile.
1884 * TURNSTILE_NULL: otherwise.
1886 * Condition: thread locked.
1889 thread_get_waiting_turnstile(thread_t thread
)
1891 struct turnstile
*turnstile
= TURNSTILE_NULL
;
1892 struct waitq
*waitq
= thread
->waitq
;
1894 /* Check if the thread is on a waitq */
1895 if (waitq
== NULL
) {
1899 if (waitq_is_turnstile_proxy(waitq
)) {
1900 return waitq
->waitq_ts
;
1903 /* Check if the waitq is a turnstile queue */
1904 if (waitq_is_turnstile_queue(waitq
)) {
1905 turnstile
= waitq_to_turnstile(waitq
);
1911 * Name: turnstile_lookup_by_proprietor
1913 * Description: Get turnstile for a proprietor from global
1917 * Arg2: turnstile_type_t type
1919 * Returns: turnstile: if the proprietor has a turnstile.
1920 * TURNSTILE_NULL: otherwise.
1922 * Condition: proprietor interlock held.
1925 turnstile_lookup_by_proprietor(uintptr_t proprietor
, turnstile_type_t type
)
1927 return turnstile_htable_lookup(proprietor
, type
);
1931 * Name: thread_get_update_flags_for_turnstile_propagation_stoppage
1933 * Description: Get the turnstile stats flags based on the thread wait status.
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.
1941 * Condition: thread locked.
1943 static turnstile_stats_update_flags_t
1944 thread_get_update_flags_for_turnstile_propagation_stoppage(thread_t thread
)
1946 struct waitq
*waitq
= thread
->waitq
;
1948 /* Check if the thread is on a waitq */
1949 if (waitq
== NULL
) {
1950 return TSU_THREAD_RUNNABLE
;
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
;
1958 return TSU_NO_TURNSTILE
;
1961 /* Check if the waitq is a turnstile queue */
1962 if (!waitq_is_turnstile_queue(waitq
)) {
1963 return TSU_NO_TURNSTILE
;
1966 /* Thread blocked on turnstile waitq but no propagation needed */
1967 return TSU_NO_PRI_CHANGE_NEEDED
;
1972 * Name: turnstile_get_update_flags_for_above_UI_pri_change
1974 * Description: Get the turnstile stats flags based on the turnstile priority.
1978 * Returns: TSU_ABOVE_UI_PRI_CHANGE: if turnstile priority is above 47 and it is not an ulock.
1979 * TSU_FLAGS_NONE: otherwise.
1981 * Condition: turnstile locked.
1983 static turnstile_stats_update_flags_t
1984 turnstile_get_update_flags_for_above_UI_pri_change(struct turnstile
*turnstile
)
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
;
1992 return TSU_FLAGS_NONE
;
1997 * Name: workq_add_turnstile_promotion
1999 * Description: Connect the workqueue turnstile to the workqueue as a fake
2005 * Condition: turnstile locked.
2008 workq_add_turnstile_promotion(
2009 struct workqueue
*wq_inheritor __unused
,
2010 struct turnstile
*turnstile
)
2013 * If the push is higher than MAXPRI_THROTTLE then the workqueue should
2014 * bring up a thread.
2016 return turnstile
->ts_priority
> MAXPRI_THROTTLE
;
2020 * Name: turnstile_need_turnstile_promotion_update
2022 * Description: Check if turnstile position in turnstile's inheritor list needs to be updated.
2024 * Arg1: dst turnstile
2025 * Arg2: src turnstile
2027 * Returns: TRUE: if turnstile_update_turnstile_promotion needs to be called.
2030 * Condition: src turnstile locked.
2033 turnstile_need_turnstile_promotion_update(
2034 struct turnstile
*dst_turnstile __assert_only
,
2035 struct turnstile
*src_turnstile
)
2037 int src_turnstile_link_priority
;
2038 boolean_t needs_update
= FALSE
;
2040 src_turnstile_link_priority
= priority_queue_entry_key(&(dst_turnstile
->ts_inheritor_queue
),
2041 &(src_turnstile
->ts_inheritor_links
));
2043 needs_update
= (src_turnstile_link_priority
== src_turnstile
->ts_priority
) ? FALSE
: TRUE
;
2044 return needs_update
;
2048 * Name: turnstile_update_turnstile_promotion_locked
2050 * Description: Update dst turnstile's inheritor link since src turnstile's
2051 * promote priority has changed.
2053 * Arg1: dst turnstile
2054 * Arg2: src turnstile
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.
2059 * Condition: src and dst turnstile locked.
2062 turnstile_update_turnstile_promotion_locked(
2063 struct turnstile
*dst_turnstile
,
2064 struct turnstile
*src_turnstile
)
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
));
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);
2078 if (!turnstile_priority_queue_update_entry_key(
2079 &dst_turnstile
->ts_inheritor_queue
, &src_turnstile
->ts_inheritor_links
,
2080 src_turnstile
->ts_priority
)) {
2084 /* Update dst turnstile's priority */
2085 return turnstile_recompute_priority_locked(dst_turnstile
);
2089 * Name: turnstile_update_turnstile_promotion
2091 * Description: Update dst turnstile's inheritor link since src turnstile's
2092 * promote priority has changed.
2094 * Arg1: dst turnstile
2095 * Arg2: src turnstile
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.
2100 * Condition: src turnstile locked.
2103 turnstile_update_turnstile_promotion(
2104 struct turnstile
*dst_turnstile
,
2105 struct turnstile
*src_turnstile
)
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
,
2113 return needs_update
;
2116 /* Update the pairing heap */
2117 waitq_lock(&dst_turnstile
->ts_waitq
);
2118 needs_update
= turnstile_update_turnstile_promotion_locked(dst_turnstile
, src_turnstile
);
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
);
2126 waitq_unlock(&dst_turnstile
->ts_waitq
);
2127 return needs_update
;
2131 * Name: turnstile_add_turnstile_promotion
2133 * Description: Add src turnstile to dst turnstile's inheritor link
2134 * and update dst turnstile's priority.
2136 * Arg1: dst turnstile
2137 * Arg2: src turnstile
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.
2142 * Condition: src turnstile locked.
2145 turnstile_add_turnstile_promotion(
2146 struct turnstile
*dst_turnstile
,
2147 struct turnstile
*src_turnstile
)
2149 boolean_t needs_update
= FALSE
;
2151 /* Update the pairing heap */
2152 waitq_lock(&dst_turnstile
->ts_waitq
);
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);
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
);
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
);
2175 waitq_unlock(&dst_turnstile
->ts_waitq
);
2176 return needs_update
;
2180 * Name: turnstile_remove_turnstile_promotion
2182 * Description: Remove src turnstile from dst turnstile's inheritor link
2183 * and update dst turnstile's priority.
2185 * Arg1: dst turnstile
2186 * Arg2: src turnstile
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.
2191 * Condition: src turnstile locked.
2194 turnstile_remove_turnstile_promotion(
2195 struct turnstile
*dst_turnstile
,
2196 struct turnstile
*src_turnstile
)
2198 boolean_t needs_update
= FALSE
;
2200 /* Update the pairing heap */
2201 waitq_lock(&dst_turnstile
->ts_waitq
);
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
),
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
);
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
);
2223 waitq_unlock(&dst_turnstile
->ts_waitq
);
2224 return needs_update
;
2228 * Name: turnstile_compute_thread_push
2230 * Description: Compute the priority at which the thread will push
2236 * Condition: wq locked
2239 turnstile_compute_thread_push(
2240 struct turnstile
*turnstile
,
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
;
2249 case TURNSTILE_KERNEL_PROMOTE
:
2251 * Ideally this should be policy based
2252 * according to the turnstile type.
2254 * The priority with which each thread pushes on
2255 * a primitive should be primitive dependent.
2257 priority
= thread
->sched_pri
;
2258 priority
= MAX(priority
, thread
->base_pri
);
2259 priority
= MAX(priority
, BASEPRI_DEFAULT
);
2260 priority
= MIN(priority
, MAXPRI_PROMOTE
);
2263 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
2270 * Name: turnstile_waitq_add_thread_priority_queue
2272 * Description: add thread to the turnstile wq
2274 * Arg1: turnstile wq
2275 * Arg2: thread to add
2277 * Condition: wq locked
2280 turnstile_waitq_add_thread_priority_queue(
2284 struct turnstile
*turnstile
= waitq_to_turnstile(wq
);
2285 int priority
= turnstile_compute_thread_push(turnstile
, thread
);
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
),
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
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
);
2310 * Name: turnstile_recompute_priority_locked
2312 * Description: Update turnstile priority based
2313 * on highest waiter thread and highest blocking
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.
2321 * Condition: turnstile locked
2324 turnstile_recompute_priority_locked(
2325 struct turnstile
*turnstile
)
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;
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
:
2340 old_priority
= turnstile
->ts_priority
;
2342 max_thread
= priority_queue_max(&turnstile
->ts_waitq
.waitq_prio_queue
,
2343 struct thread
, wait_prioq_links
);
2346 thread_max_pri
= priority_queue_entry_key(&turnstile
->ts_waitq
.waitq_prio_queue
,
2347 &max_thread
->wait_prioq_links
);
2350 max_turnstile
= priority_queue_max(&turnstile
->ts_inheritor_queue
,
2351 struct turnstile
, ts_inheritor_links
);
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
);
2359 new_priority
= max(thread_max_pri
, turnstile_max_pri
);
2360 turnstile
->ts_priority
= new_priority
;
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
),
2371 needs_priority_update
= (!(old_priority
== new_priority
)) &&
2372 (turnstile
->ts_inheritor
!= NULL
);
2375 case TURNSTILE_PROMOTE_NONE
:
2376 /* The turnstile was repurposed, do nothing */
2381 panic("Needs implementation for turnstile_recompute_priority");
2384 return needs_priority_update
;
2389 * Name: turnstile_recompute_priority
2391 * Description: Update turnstile priority based
2392 * on highest waiter thread and highest blocking
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.
2401 turnstile_recompute_priority(
2402 struct turnstile
*turnstile
)
2404 boolean_t needs_priority_update
= FALSE
;
2405 spl_t s
= splsched();
2407 waitq_lock(&turnstile
->ts_waitq
);
2409 needs_priority_update
= turnstile_recompute_priority_locked(turnstile
);
2411 waitq_unlock(&turnstile
->ts_waitq
);
2413 return needs_priority_update
;
2418 * Name: turnstile_workq_proprietor_of_max_turnstile
2420 * Description: Returns the highest priority and proprietor of a turnstile
2421 * pushing on a workqueue turnstile.
2423 * This will not return waiters that are at priority
2424 * MAXPRI_THROTTLE or lower.
2429 * Priority of the max entry, or 0
2430 * Pointer to the max entry proprietor
2433 turnstile_workq_proprietor_of_max_turnstile(
2434 struct turnstile
*turnstile
,
2435 uintptr_t *proprietor_out
)
2437 struct turnstile
*max_turnstile
;
2438 int max_priority
= 0;
2439 uintptr_t proprietor
= 0;
2441 assert(turnstile_get_type(turnstile
) == TURNSTILE_WORKQS
);
2443 spl_t s
= splsched();
2445 waitq_lock(&turnstile
->ts_waitq
);
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
;
2455 waitq_unlock(&turnstile
->ts_waitq
);
2458 if (max_priority
<= MAXPRI_THROTTLE
) {
2462 if (proprietor_out
) {
2463 *proprietor_out
= proprietor
;
2465 return max_priority
;
2469 * Name: turnstile_workloop_pusher_info
2471 * Description: Returns the priority of the turnstile push for a workloop,
2472 * and the thread or knote responsible for this push.
2474 * Args: workloop turnstile
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)
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
)
2489 struct turnstile
*max_ts
;
2490 thread_t max_thread
;
2491 int max_thread_pri
= 0;
2495 assert(turnstile_get_type(turnstile
) == TURNSTILE_WORKLOOPS
);
2497 spl_t s
= splsched();
2498 waitq_lock(&turnstile
->ts_waitq
);
2500 max_thread
= priority_queue_max(&turnstile
->ts_waitq
.waitq_prio_queue
,
2501 struct thread
, wait_prioq_links
);
2503 max_thread_pri
= priority_queue_entry_key(
2504 &turnstile
->ts_waitq
.waitq_prio_queue
,
2505 &max_thread
->wait_prioq_links
);
2508 max_ts
= priority_queue_max(&turnstile
->ts_inheritor_queue
,
2509 struct turnstile
, ts_inheritor_links
);
2511 max_ts_pri
= priority_queue_entry_key(&turnstile
->ts_inheritor_queue
,
2512 &max_ts
->ts_inheritor_links
);
2516 * Reasons to push on a workloop turnstile are:
2518 * 1. threads in dispatch sync
2520 * 2. sync IPC pushes, which in turn have 4 sub-cases:
2522 * 2.a. special reply port or receive right pushing through a knote
2525 * 2.b. special reply port stashed on a knote, pushing on the workloop
2528 * 2.c. receive right stashed on a knote, pushing on the workloop
2531 * 2.d. a receive right monitored by a knote, pushing on the workloop
2534 * See ipc_port_send_update_inheritor(), ipc_port_recv_update_inheritor().
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.
2540 if (max_thread_pri
> max_ts_pri
) {
2541 thread_reference(max_thread
);
2542 *thread_out
= max_thread
;
2545 } else if (max_ts_pri
) {
2546 switch (turnstile_get_type(max_ts
)) {
2547 case TURNSTILE_KNOTE
:
2549 *thread_out
= THREAD_NULL
;
2550 *port_out
= IP_NULL
;
2551 *knote_out
= (struct knote
*)max_ts
->ts_proprietor
;
2554 case TURNSTILE_SYNC_IPC
:
2556 port
= (ipc_port_t
)max_ts
->ts_proprietor
;
2558 *thread_out
= THREAD_NULL
;
2564 panic("Unexpected type for turnstile %p", max_ts
);
2567 *thread_out
= THREAD_NULL
;
2568 *port_out
= IP_NULL
;
2572 waitq_unlock(&turnstile
->ts_waitq
);
2575 return max(max_thread_pri
, max_ts_pri
);
2579 * Name: turnstile_has_waiters
2581 * Description: returns if there are waiters on the turnstile
2583 * Arg1: turnstile: turnstile
2585 * Returns: TRUE if there are waiters, FALSE otherwise.
2589 turnstile_has_waiters(struct turnstile
*turnstile
)
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
);
2603 * Name: turnstile_update_inheritor_priority_chain
2605 * Description: Update turnstile inheritor's priority and propagate
2606 * the priority if the inheritor is blocked on a turnstile.
2609 * Arg2: inheritor flags
2614 turnstile_update_inheritor_priority_chain(
2615 turnstile_inheritor_t inheritor
,
2616 turnstile_update_flags_t turnstile_flags
)
2618 struct turnstile
*turnstile
= TURNSTILE_NULL
;
2619 thread_t thread
= THREAD_NULL
;
2620 int total_hop
= 0, thread_hop
= 0;
2622 turnstile_stats_update_flags_t tsu_flags
= ((turnstile_flags
& TURNSTILE_UPDATE_BOOST
) ?
2623 TSU_BOOST_ARG
: TSU_FLAGS_NONE
) | TSU_PRI_PROPAGATION
;
2625 if (inheritor
== NULL
) {
2631 if (turnstile_flags
& TURNSTILE_INHERITOR_THREAD
) {
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
);
2643 * we should never call turnstile_update_inheritor_priority_chain()
2644 * for a workqueue, they have no "chain" after them.
2646 assert((turnstile_flags
& TURNSTILE_INHERITOR_WORKQ
) == 0);
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
,
2655 waitq_unlock(&turnstile
->ts_waitq
);
2656 turnstile
= TURNSTILE_NULL
;
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
,
2671 panic("Inheritor flags not passed in turnstile_update_inheritor");
2673 } else if (thread
!= THREAD_NULL
) {
2674 thread_update_waiting_turnstile_priority_chain(&thread
, &turnstile
,
2675 thread_hop
, total_hop
, tsu_flags
);
2686 * Name: turnstile_update_inheritor_complete
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.
2696 * Arg2: interlock held
2701 turnstile_update_inheritor_complete(
2702 struct turnstile
*turnstile
,
2703 turnstile_update_complete_flags_t flags __unused
)
2705 thread_t thread
= current_thread();
2707 turnstile_update_flags_t inheritor_flags
= thread
->inheritor_flags
;
2709 turnstile_cleanup();
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
);
2719 * Name: turnstile_cleanup
2721 * Description: Update priority of a turnstile inheritor
2724 * Args: inheritor and flags passed on thread struct.
2729 turnstile_cleanup(void)
2731 thread_t thread
= current_thread();
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
;
2739 if (old_inheritor
== TURNSTILE_INHERITOR_NULL
) {
2740 /* no cleanup to do */
2744 /* Perform priority demotion for old inheritor */
2745 if (inheritor_flags
& TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE
) {
2746 turnstile_update_inheritor_priority_chain(old_inheritor
,
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
);
2758 panic("Inheritor flags lost along the way");
2763 * Name: turnstile_update_thread_priority_chain
2765 * Description: Priority of a thread blocked on a turnstile
2766 * has changed, update the turnstile priority.
2768 * Arg1: thread: thread whose priority has changed.
2773 turnstile_update_thread_priority_chain(thread_t thread
)
2775 turnstile_update_inheritor_priority_chain(thread
,
2776 TURNSTILE_INHERITOR_THREAD
| TURNSTILE_UPDATE_BOOST
);
2780 * Name: turnstile_update_inheritor_workq_priority_chain
2782 * Description: Helper function to update turnstile's inheritor(workq)
2783 * priority and possibly redrive thread creation
2785 * Arg1: turnstile: turnstile
2786 * Arg2: s: whether iterrupts are disabled.
2788 * Condition: turnstile is locked on entry, it is unlocked on exit,
2789 * and interrupts re-enabled.
2792 turnstile_update_inheritor_workq_priority_chain(struct turnstile
*turnstile
, spl_t s
)
2794 struct workqueue
*wq
= turnstile
->ts_inheritor
;
2795 bool workq_lock_held
= workq_is_current_thread_updating_turnstile(wq
);
2797 if (__improbable(turnstile
->ts_priority
<= MAXPRI_THROTTLE
)) {
2798 waitq_unlock(&turnstile
->ts_waitq
);
2803 if (!workq_lock_held
) {
2804 workq_reference(wq
);
2805 disable_preemption();
2807 waitq_unlock(&turnstile
->ts_waitq
);
2810 workq_schedule_creator_turnstile_redrive(wq
, workq_lock_held
);
2812 if (!workq_lock_held
) {
2813 enable_preemption();
2814 workq_deallocate_safe(wq
);
2819 * Name: turnstile_update_inheritor_thread_priority_chain
2821 * Description: Helper function to update turnstile's inheritor(thread)
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
2829 * Returns: Implicit returns locked thread in out_thread if it needs
2830 * further propagation.
2832 * Condition: *in_turnstile is locked on entry, it is unlocked on exit and
2833 * *in_turnstile is set to NULL.
2836 turnstile_update_inheritor_thread_priority_chain(
2837 struct turnstile
**in_turnstile
,
2838 thread_t
*out_thread
,
2840 turnstile_stats_update_flags_t tsu_flags
)
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
;
2847 assert(turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
2848 *in_turnstile
= TURNSTILE_NULL
;
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
);
2859 thread_lock(thread_inheritor
);
2861 /* adjust turnstile position in the thread's inheritor list */
2862 needs_update
= thread_update_turnstile_promotion_locked(
2863 thread_inheritor
, turnstile
);
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.
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
,
2878 thread_unlock(thread_inheritor
);
2879 waitq_unlock(&turnstile
->ts_waitq
);
2883 /* Unlock the turnstile and update the thread */
2884 waitq_unlock(&turnstile
->ts_waitq
);
2885 *out_thread
= thread_inheritor
;
2890 * Name: turnstile_update_inheritor_turnstile_priority_chain
2892 * Description: Helper function to update turnstile's inheritor(turnstile)
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
2899 * Returns: Implicit returns locked turnstile in in_out_turnstile if it needs
2900 * further propagation.
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.
2907 turnstile_update_inheritor_turnstile_priority_chain(
2908 struct turnstile
**in_out_turnstile
,
2910 turnstile_stats_update_flags_t tsu_flags
)
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
;
2917 assert(turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
);
2918 *in_out_turnstile
= TURNSTILE_NULL
;
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
,
2926 waitq_unlock(&turnstile
->ts_waitq
);
2930 waitq_lock(&inheritor_turnstile
->ts_waitq
);
2932 needs_update
= turnstile_update_turnstile_promotion_locked(
2933 inheritor_turnstile
, turnstile
);
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.
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
,
2948 waitq_unlock(&inheritor_turnstile
->ts_waitq
);
2949 waitq_unlock(&turnstile
->ts_waitq
);
2953 /* Unlock the outer turnstile and update the inner turnstile */
2954 waitq_unlock(&turnstile
->ts_waitq
);
2955 *in_out_turnstile
= inheritor_turnstile
;
2960 * Name: thread_update_waiting_turnstile_priority_chain
2962 * Description: Helper function to update thread's waiting
2963 * turnstile priority.
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
2971 * Returns: *out_turnstile returns the inheritor if it needs further propagation.
2973 * Condition: *in_thread locked on entry, unlocked on exit and set to NULL.
2976 thread_update_waiting_turnstile_priority_chain(
2977 thread_t
*in_thread
,
2978 struct turnstile
**out_turnstile
,
2981 turnstile_stats_update_flags_t tsu_flags
)
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
;
2989 *in_thread
= THREAD_NULL
;
2991 /* Check if thread waiting on a turnstile */
2992 waiting_turnstile
= thread_get_waiting_turnstile(thread
);
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
)
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
);
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
);
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
);
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
);
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
);
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.
3046 thread_deallocate_safe(thread
);
3047 turnstile_deallocate_safe(waiting_turnstile
);
3049 /* adjust thread's position on turnstile waitq */
3050 needs_update
= turnstile_update_thread_promotion_locked(waiting_turnstile
, thread
);
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.
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
);
3068 /* drop the thread lock and update the turnstile */
3069 thread_unlock(thread
);
3070 *out_turnstile
= waiting_turnstile
;
3074 * Name: turnstile_stats_update
3076 * Description: Function to update turnstile stats for dev kernel.
3078 * Arg1: hops : number of thread hops in priority propagation
3079 * Arg2: flags : turnstile stats update flags
3080 * Arg3: inheritor: inheritor
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
)
3090 #if DEVELOPMENT || DEBUG
3091 if (flags
& TSU_TURNSTILE_BLOCK_COUNT
) {
3092 os_atomic_inc(&thread_block_on_turnstile_count
, relaxed
);
3095 if (flags
& TSU_REGULAR_WAITQ_BLOCK_COUNT
) {
3096 os_atomic_inc(&thread_block_on_regular_waitq_count
, relaxed
);
3099 if (hop
> TURNSTILE_MAX_HOP_DEFAULT
|| hop
== 0) {
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.
3113 if (flags
& TSU_TURNSTILE_ARG
) {
3114 struct turnstile
*ts
= (struct turnstile
*)inheritor
;
3115 if (ts
->ts_priority
== 0) {
3119 if (hop
== 1 && turnstile_get_type(ts
) == TURNSTILE_WORKQS
) {
3122 } else if (flags
& TSU_THREAD_ARG
) {
3123 thread_t thread
= (thread_t
)inheritor
;
3124 if (thread
->user_promotion_basepri
== 0) {
3128 assert(inheritor
== NULL
);
3131 struct turnstile_stats
*turnstile_stats
;
3132 if (flags
& TSU_BOOST_ARG
) {
3133 turnstile_stats
= turnstile_boost_stats
;
3135 turnstile_stats
= turnstile_unboost_stats
;
3138 if (flags
& TSU_PRI_PROPAGATION
) {
3139 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_priority_propagation
, relaxed
);
3142 if (flags
& TSU_NO_INHERITOR
) {
3143 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_inheritor
, relaxed
);
3146 if (flags
& TSU_NO_TURNSTILE
) {
3147 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_turnstile
, relaxed
);
3150 if (flags
& TSU_NO_PRI_CHANGE_NEEDED
) {
3151 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_priority_change_required
, relaxed
);
3154 if (flags
& TSU_THREAD_RUNNABLE
) {
3155 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_thread_runnable
, relaxed
);
3158 if (flags
& TSU_ABOVE_UI_PRI_CHANGE
) {
3159 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_above_ui_pri_change
, relaxed
);
3165 kdp_turnstile_traverse_inheritor_chain(struct turnstile
*ts
, uint64_t *flags
, uint8_t *hops
)
3167 if (waitq_held(&ts
->ts_waitq
)) {
3168 *flags
|= STACKSHOT_TURNSTILE_STATUS_LOCKED_WAITQ
;
3174 if (ts
->ts_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
3175 return kdp_turnstile_traverse_inheritor_chain(ts
->ts_inheritor
, flags
, hops
);
3178 if (ts
->ts_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
3179 *flags
|= STACKSHOT_TURNSTILE_STATUS_THREAD
;
3180 return (uint64_t) thread_tid(ts
->ts_inheritor
);
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
);
3188 *flags
|= STACKSHOT_TURNSTILE_STATUS_UNKNOWN
;
3193 kdp_turnstile_fill_tsinfo(struct turnstile
*ts
, thread_turnstileinfo_t
*tsinfo
)
3195 uint64_t final_inheritor
;
3199 tsinfo
->turnstile_context
= 0;
3200 tsinfo
->number_of_hops
= 0;
3201 tsinfo
->turnstile_priority
= 0;
3203 assert(ts
!= TURNSTILE_NULL
);
3205 if (waitq_held(&ts
->ts_waitq
)) {
3206 tsinfo
->turnstile_flags
|= STACKSHOT_TURNSTILE_STATUS_LOCKED_WAITQ
;
3210 final_inheritor
= kdp_turnstile_traverse_inheritor_chain(ts
, &flags
, &hops
);
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
;
3219 #if DEVELOPMENT || DEBUG
3221 int sysctl_io_opaque(void *req
, void *pValue
, size_t valueSize
, int *changed
);
3224 * Name: turnstile_get_boost_stats_sysctl
3226 * Description: Function to get turnstile stats.
3228 * Args: req : opaque struct to pass to sysctl_io_opaque
3233 turnstile_get_boost_stats_sysctl(
3236 return sysctl_io_opaque(req
, turnstile_boost_stats
, sizeof(struct turnstile_stats
) * TURNSTILE_MAX_HOP_DEFAULT
, NULL
);
3240 * Name: get_turnstile_stats_sysctl
3242 * Description: Function to get turnstile stats.
3244 * Args: req : opaque struct to pass to sysctl_io_opaque
3249 turnstile_get_unboost_stats_sysctl(
3252 return sysctl_io_opaque(req
, turnstile_unboost_stats
, sizeof(struct turnstile_stats
) * TURNSTILE_MAX_HOP_DEFAULT
, NULL
);
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)
3262 tstile_test_prim_init(struct tstile_test_prim
**test_prim_ptr
)
3264 struct tstile_test_prim
*test_prim
= (struct tstile_test_prim
*) kalloc(sizeof(struct tstile_test_prim
));
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;
3271 *test_prim_ptr
= test_prim
;
3276 tstile_test_prim_lock(int val
)
3278 struct tstile_test_prim
*test_prim
;
3279 boolean_t use_hashtable
;
3280 turnstile_type_t type
;
3281 wait_interrupt_t wait_type
;
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
;
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
;
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
;
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
;
3315 /* take the interlock of the primitive */
3316 tstile_test_prim_lock_interlock(test_prim
);
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
);
3326 struct turnstile
*prim_turnstile
= TURNSTILE_NULL
;
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
);
3333 assert(prim_turnstile
!= TURNSTILE_NULL
);
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();
3340 /* Update the turnstile owner */
3341 turnstile_update_inheritor(prim_turnstile
,
3343 (TURNSTILE_IMMEDIATE_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3345 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_HELD
);
3347 turnstile_complete((uintptr_t)test_prim
,
3348 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
, NULL
, type
);
3350 tstile_test_prim_unlock_interlock(test_prim
);
3352 turnstile_cleanup();
3356 test_prim
->tt_prim_waiters
++;
3357 turnstile_update_inheritor(prim_turnstile
,
3358 test_prim
->ttprim_owner
,
3359 (TURNSTILE_DELAYED_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3361 waitq_assert_wait64(&prim_turnstile
->ts_waitq
,
3362 CAST_EVENT64_T(test_prim
), wait_type
,
3363 TIMEOUT_WAIT_FOREVER
);
3365 /* drop the interlock */
3366 tstile_test_prim_unlock_interlock(test_prim
);
3368 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_NOT_HELD
);
3370 wait_result_t result
;
3371 result
= thread_block(THREAD_CONTINUE_NULL
);
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
);
3379 tstile_test_prim_unlock_interlock(test_prim
);
3381 turnstile_cleanup();
3383 /* Return if thread interrupted */
3384 if (result
== THREAD_INTERRUPTED
) {
3392 tstile_test_prim_unlock(int val
)
3394 struct tstile_test_prim
*test_prim
;
3395 boolean_t use_hashtable
;
3396 turnstile_type_t type
;
3399 case SYSCTL_TURNSTILE_TEST_USER_DEFAULT
:
3400 test_prim
= test_prim_ts_inline
;
3401 use_hashtable
= FALSE
;
3402 type
= TURNSTILE_ULOCK
;
3404 case SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
:
3405 test_prim
= test_prim_global_htable
;
3406 use_hashtable
= TRUE
;
3407 type
= TURNSTILE_ULOCK
;
3409 case SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
:
3410 test_prim
= test_prim_global_ts_kernel
;
3411 use_hashtable
= FALSE
;
3412 type
= TURNSTILE_KERNEL_MUTEX
;
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
;
3423 /* take the interlock of the primitive */
3424 tstile_test_prim_lock_interlock(test_prim
);
3426 if (test_prim
->ttprim_owner
== NULL
) {
3427 tstile_test_prim_unlock_interlock(test_prim
);
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
);
3438 thread_deallocate(old_owner
);
3442 struct turnstile
*prim_turnstile
= TURNSTILE_NULL
;
3444 thread_t old_owner
= test_prim
->ttprim_owner
;
3445 test_prim
->ttprim_owner
= NULL
;
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
);
3452 assert(prim_turnstile
!= TURNSTILE_NULL
);
3454 /* Update the turnstile owner */
3455 turnstile_update_inheritor(prim_turnstile
,
3457 (TURNSTILE_IMMEDIATE_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3459 waitq_wakeup64_one(&prim_turnstile
->ts_waitq
,
3460 CAST_EVENT64_T(test_prim
),
3461 THREAD_AWAKENED
, WAITQ_ALL_PRIORITIES
);
3463 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_HELD
);
3465 turnstile_complete((uintptr_t)test_prim
,
3466 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
, NULL
, type
);
3468 tstile_test_prim_unlock_interlock(test_prim
);
3470 turnstile_cleanup();
3473 /* Changing this to thread_deallocate_safe to exercise thread_deallocate_safe path */
3474 thread_deallocate_safe(old_owner
);