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 /* Get the safeq if the waitq is a port queue */
1900 if (waitq_is_port_queue(waitq
)) {
1901 waitq
= waitq_get_safeq(waitq
);
1904 /* Check if the waitq is a turnstile queue */
1905 if (waitq_is_turnstile_queue(waitq
)) {
1906 turnstile
= waitq_to_turnstile(waitq
);
1912 * Name: turnstile_lookup_by_proprietor
1914 * Description: Get turnstile for a proprietor from global
1918 * Arg2: turnstile_type_t type
1920 * Returns: turnstile: if the proprietor has a turnstile.
1921 * TURNSTILE_NULL: otherwise.
1923 * Condition: proprietor interlock held.
1926 turnstile_lookup_by_proprietor(uintptr_t proprietor
, turnstile_type_t type
)
1928 return turnstile_htable_lookup(proprietor
, type
);
1932 * Name: thread_get_update_flags_for_turnstile_propagation_stoppage
1934 * Description: Get the turnstile stats flags based on the thread wait status.
1938 * Returns: TSU_THREAD_RUNNABLE: if the thread is runnable.
1939 * TSU_NO_TURNSTILE: if thread waiting on a regular waitq.
1940 * TSU_NO_PRI_CHANGE_NEEDED: otherwise.
1942 * Condition: thread locked.
1944 static turnstile_stats_update_flags_t
1945 thread_get_update_flags_for_turnstile_propagation_stoppage(thread_t thread
)
1947 struct waitq
*waitq
= thread
->waitq
;
1949 /* Check if the thread is on a waitq */
1950 if (waitq
== NULL
) {
1951 return TSU_THREAD_RUNNABLE
;
1954 /* Get the safeq if the waitq is a port queue */
1955 if (waitq_is_port_queue(waitq
)) {
1956 waitq
= waitq_get_safeq(waitq
);
1959 /* Check if the waitq is a turnstile queue */
1960 if (!waitq_is_turnstile_queue(waitq
)) {
1961 return TSU_NO_TURNSTILE
;
1964 /* Thread blocked on turnstile waitq but no propagation needed */
1965 return TSU_NO_PRI_CHANGE_NEEDED
;
1970 * Name: turnstile_get_update_flags_for_above_UI_pri_change
1972 * Description: Get the turnstile stats flags based on the turnstile priority.
1976 * Returns: TSU_ABOVE_UI_PRI_CHANGE: if turnstile priority is above 47 and it is not an ulock.
1977 * TSU_FLAGS_NONE: otherwise.
1979 * Condition: turnstile locked.
1981 static turnstile_stats_update_flags_t
1982 turnstile_get_update_flags_for_above_UI_pri_change(struct turnstile
*turnstile
)
1984 if (turnstile
->ts_priority
>
1985 (thread_qos_policy_params
.qos_pri
[THREAD_QOS_USER_INTERACTIVE
] + 1) &&
1986 turnstile_get_type(turnstile
) != TURNSTILE_ULOCK
) {
1987 return TSU_ABOVE_UI_PRI_CHANGE
;
1990 return TSU_FLAGS_NONE
;
1995 * Name: workq_add_turnstile_promotion
1997 * Description: Connect the workqueue turnstile to the workqueue as a fake
2003 * Condition: turnstile locked.
2006 workq_add_turnstile_promotion(
2007 struct workqueue
*wq_inheritor __unused
,
2008 struct turnstile
*turnstile
)
2011 * If the push is higher than MAXPRI_THROTTLE then the workqueue should
2012 * bring up a thread.
2014 return turnstile
->ts_priority
> MAXPRI_THROTTLE
;
2018 * Name: turnstile_need_turnstile_promotion_update
2020 * Description: Check if turnstile position in turnstile's inheritor list needs to be updated.
2022 * Arg1: dst turnstile
2023 * Arg2: src turnstile
2025 * Returns: TRUE: if turnstile_update_turnstile_promotion needs to be called.
2028 * Condition: src turnstile locked.
2031 turnstile_need_turnstile_promotion_update(
2032 struct turnstile
*dst_turnstile __assert_only
,
2033 struct turnstile
*src_turnstile
)
2035 int src_turnstile_link_priority
;
2036 boolean_t needs_update
= FALSE
;
2038 src_turnstile_link_priority
= priority_queue_entry_key(&(dst_turnstile
->ts_inheritor_queue
),
2039 &(src_turnstile
->ts_inheritor_links
));
2041 needs_update
= (src_turnstile_link_priority
== src_turnstile
->ts_priority
) ? FALSE
: TRUE
;
2042 return needs_update
;
2046 * Name: turnstile_update_turnstile_promotion_locked
2048 * Description: Update dst turnstile's inheritor link since src turnstile's
2049 * promote priority has changed.
2051 * Arg1: dst turnstile
2052 * Arg2: src turnstile
2054 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2055 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2057 * Condition: src and dst turnstile locked.
2060 turnstile_update_turnstile_promotion_locked(
2061 struct turnstile
*dst_turnstile
,
2062 struct turnstile
*src_turnstile
)
2064 int src_turnstile_link_priority
;
2065 src_turnstile_link_priority
= priority_queue_entry_key(&(dst_turnstile
->ts_inheritor_queue
),
2066 &(src_turnstile
->ts_inheritor_links
));
2068 if (src_turnstile
->ts_priority
!= src_turnstile_link_priority
) {
2069 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2070 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_MOVED_IN_TURNSTILE_HEAP
))) | DBG_FUNC_NONE
,
2071 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile
),
2072 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile
),
2073 src_turnstile
->ts_priority
, src_turnstile_link_priority
, 0);
2076 if (!turnstile_priority_queue_update_entry_key(
2077 &dst_turnstile
->ts_inheritor_queue
, &src_turnstile
->ts_inheritor_links
,
2078 src_turnstile
->ts_priority
)) {
2082 /* Update dst turnstile's priority */
2083 return turnstile_recompute_priority_locked(dst_turnstile
);
2087 * Name: turnstile_update_turnstile_promotion
2089 * Description: Update dst turnstile's inheritor link since src turnstile's
2090 * promote priority has changed.
2092 * Arg1: dst turnstile
2093 * Arg2: src turnstile
2095 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2096 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2098 * Condition: src turnstile locked.
2101 turnstile_update_turnstile_promotion(
2102 struct turnstile
*dst_turnstile
,
2103 struct turnstile
*src_turnstile
)
2105 /* Check if update is needed before grabbing the src turnstile lock */
2106 boolean_t needs_update
= turnstile_need_turnstile_promotion_update(dst_turnstile
, src_turnstile
);
2107 if (!needs_update
) {
2108 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED
|
2109 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
,
2111 return needs_update
;
2114 /* Update the pairing heap */
2115 waitq_lock(&dst_turnstile
->ts_waitq
);
2116 needs_update
= turnstile_update_turnstile_promotion_locked(dst_turnstile
, src_turnstile
);
2118 /* Update turnstile stats */
2119 if (!needs_update
) {
2120 turnstile_stats_update(1,
2121 (dst_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
2122 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
, src_turnstile
);
2124 waitq_unlock(&dst_turnstile
->ts_waitq
);
2125 return needs_update
;
2129 * Name: turnstile_add_turnstile_promotion
2131 * Description: Add src turnstile to dst turnstile's inheritor link
2132 * and update dst turnstile's priority.
2134 * Arg1: dst turnstile
2135 * Arg2: src turnstile
2137 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2138 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2140 * Condition: src turnstile locked.
2143 turnstile_add_turnstile_promotion(
2144 struct turnstile
*dst_turnstile
,
2145 struct turnstile
*src_turnstile
)
2147 boolean_t needs_update
= FALSE
;
2149 /* Update the pairing heap */
2150 waitq_lock(&dst_turnstile
->ts_waitq
);
2152 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2153 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_ADDED_TO_TURNSTILE_HEAP
))) | DBG_FUNC_NONE
,
2154 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile
),
2155 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile
),
2156 src_turnstile
->ts_priority
, 0, 0);
2158 priority_queue_entry_init(&(src_turnstile
->ts_inheritor_links
));
2159 if (priority_queue_insert(&dst_turnstile
->ts_inheritor_queue
,
2160 &src_turnstile
->ts_inheritor_links
, src_turnstile
->ts_priority
,
2161 PRIORITY_QUEUE_SCHED_PRI_MAX_HEAP_COMPARE
)) {
2162 /* Update dst turnstile priority */
2163 needs_update
= turnstile_recompute_priority_locked(dst_turnstile
);
2166 /* Update turnstile stats */
2167 if (!needs_update
) {
2168 turnstile_stats_update(1,
2169 (dst_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
2170 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
, src_turnstile
);
2173 waitq_unlock(&dst_turnstile
->ts_waitq
);
2174 return needs_update
;
2178 * Name: turnstile_remove_turnstile_promotion
2180 * Description: Remove src turnstile from dst turnstile's inheritor link
2181 * and update dst turnstile's priority.
2183 * Arg1: dst turnstile
2184 * Arg2: src turnstile
2186 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2187 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2189 * Condition: src turnstile locked.
2192 turnstile_remove_turnstile_promotion(
2193 struct turnstile
*dst_turnstile
,
2194 struct turnstile
*src_turnstile
)
2196 boolean_t needs_update
= FALSE
;
2198 /* Update the pairing heap */
2199 waitq_lock(&dst_turnstile
->ts_waitq
);
2201 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2202 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_REMOVED_FROM_TURNSTILE_HEAP
))) | DBG_FUNC_NONE
,
2203 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile
),
2204 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile
),
2207 if (priority_queue_remove(&dst_turnstile
->ts_inheritor_queue
,
2208 &src_turnstile
->ts_inheritor_links
,
2209 PRIORITY_QUEUE_SCHED_PRI_MAX_HEAP_COMPARE
)) {
2210 /* Update dst turnstile priority */
2211 needs_update
= turnstile_recompute_priority_locked(dst_turnstile
);
2214 /* Update turnstile stats */
2215 if (!needs_update
) {
2216 turnstile_stats_update(1,
2217 (dst_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
2218 TSU_TURNSTILE_ARG
, src_turnstile
);
2221 waitq_unlock(&dst_turnstile
->ts_waitq
);
2222 return needs_update
;
2226 * Name: turnstile_compute_thread_push
2228 * Description: Compute the priority at which the thread will push
2234 * Condition: wq locked
2237 turnstile_compute_thread_push(
2238 struct turnstile
*turnstile
,
2242 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
2243 case TURNSTILE_USER_PROMOTE
:
2244 case TURNSTILE_USER_IPC_PROMOTE
:
2245 priority
= thread
->base_pri
;
2247 case TURNSTILE_KERNEL_PROMOTE
:
2249 * Ideally this should be policy based
2250 * according to the turnstile type.
2252 * The priority with which each thread pushes on
2253 * a primitive should be primitive dependent.
2255 priority
= thread
->sched_pri
;
2256 priority
= MAX(priority
, thread
->base_pri
);
2257 priority
= MAX(priority
, BASEPRI_DEFAULT
);
2258 priority
= MIN(priority
, MAXPRI_PROMOTE
);
2261 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
2268 * Name: turnstile_waitq_add_thread_priority_queue
2270 * Description: add thread to the turnstile wq
2272 * Arg1: turnstile wq
2273 * Arg2: thread to add
2275 * Condition: wq locked
2278 turnstile_waitq_add_thread_priority_queue(
2282 struct turnstile
*turnstile
= waitq_to_turnstile(wq
);
2283 int priority
= turnstile_compute_thread_push(turnstile
, thread
);
2285 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2286 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (THREAD_ADDED_TO_TURNSTILE_WAITQ
))) | DBG_FUNC_NONE
,
2287 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
2291 * For turnstile queues (which use priority queues),
2292 * insert the thread in the heap based on its priority.
2293 * Note that the priority queue implementation
2294 * is currently not stable, so does not maintain fifo for
2295 * threads at the same pri. Also, if the pri
2296 * of the thread changes while its blocked in the waitq,
2297 * the thread position should be updated in the priority
2298 * queue by calling priority queue increase/decrease
2301 priority_queue_entry_init(&(thread
->wait_prioq_links
));
2302 priority_queue_insert(&wq
->waitq_prio_queue
,
2303 &thread
->wait_prioq_links
, priority
,
2304 PRIORITY_QUEUE_SCHED_PRI_MAX_HEAP_COMPARE
);
2308 * Name: turnstile_recompute_priority_locked
2310 * Description: Update turnstile priority based
2311 * on highest waiter thread and highest blocking
2316 * Returns: TRUE: if the turnstile priority changed and needs propagation.
2317 * FALSE: if the turnstile priority did not change or it does not need propagation.
2319 * Condition: turnstile locked
2322 turnstile_recompute_priority_locked(
2323 struct turnstile
*turnstile
)
2327 boolean_t needs_priority_update
= FALSE
;
2328 thread_t max_thread
= THREAD_NULL
;
2329 struct turnstile
*max_turnstile
;
2330 int thread_max_pri
= 0;
2331 int turnstile_max_pri
= 0;
2333 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
2334 case TURNSTILE_USER_PROMOTE
:
2335 case TURNSTILE_USER_IPC_PROMOTE
:
2336 case TURNSTILE_KERNEL_PROMOTE
:
2338 old_priority
= turnstile
->ts_priority
;
2340 max_thread
= priority_queue_max(&turnstile
->ts_waitq
.waitq_prio_queue
,
2341 struct thread
, wait_prioq_links
);
2344 thread_max_pri
= priority_queue_entry_key(&turnstile
->ts_waitq
.waitq_prio_queue
,
2345 &max_thread
->wait_prioq_links
);
2348 max_turnstile
= priority_queue_max(&turnstile
->ts_inheritor_queue
,
2349 struct turnstile
, ts_inheritor_links
);
2351 if (max_turnstile
) {
2352 assert(turnstile_promote_policy
[turnstile_get_type(turnstile
)] != TURNSTILE_KERNEL_PROMOTE
);
2353 turnstile_max_pri
= priority_queue_entry_key(&turnstile
->ts_inheritor_queue
,
2354 &max_turnstile
->ts_inheritor_links
);
2357 new_priority
= max(thread_max_pri
, turnstile_max_pri
);
2358 turnstile
->ts_priority
= new_priority
;
2360 if (old_priority
!= new_priority
) {
2361 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2362 (TURNSTILE_CODE(TURNSTILE_PRIORITY_OPERATIONS
,
2363 (TURNSTILE_PRIORITY_CHANGE
))) | DBG_FUNC_NONE
,
2364 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
2369 needs_priority_update
= (!(old_priority
== new_priority
)) &&
2370 (turnstile
->ts_inheritor
!= NULL
);
2373 case TURNSTILE_PROMOTE_NONE
:
2374 /* The turnstile was repurposed, do nothing */
2379 panic("Needs implementation for turnstile_recompute_priority");
2382 return needs_priority_update
;
2387 * Name: turnstile_recompute_priority
2389 * Description: Update turnstile priority based
2390 * on highest waiter thread and highest blocking
2395 * Returns: TRUE: if the turnstile priority changed and needs propagation.
2396 * FALSE: if the turnstile priority did not change or it does not need propagation.
2399 turnstile_recompute_priority(
2400 struct turnstile
*turnstile
)
2402 boolean_t needs_priority_update
= FALSE
;
2403 spl_t s
= splsched();
2405 waitq_lock(&turnstile
->ts_waitq
);
2407 needs_priority_update
= turnstile_recompute_priority_locked(turnstile
);
2409 waitq_unlock(&turnstile
->ts_waitq
);
2411 return needs_priority_update
;
2416 * Name: turnstile_workq_proprietor_of_max_turnstile
2418 * Description: Returns the highest priority and proprietor of a turnstile
2419 * pushing on a workqueue turnstile.
2421 * This will not return waiters that are at priority
2422 * MAXPRI_THROTTLE or lower.
2427 * Priority of the max entry, or 0
2428 * Pointer to the max entry proprietor
2431 turnstile_workq_proprietor_of_max_turnstile(
2432 struct turnstile
*turnstile
,
2433 uintptr_t *proprietor_out
)
2435 struct turnstile
*max_turnstile
;
2436 int max_priority
= 0;
2437 uintptr_t proprietor
= 0;
2439 assert(turnstile_get_type(turnstile
) == TURNSTILE_WORKQS
);
2441 spl_t s
= splsched();
2443 waitq_lock(&turnstile
->ts_waitq
);
2445 max_turnstile
= priority_queue_max(&turnstile
->ts_inheritor_queue
,
2446 struct turnstile
, ts_inheritor_links
);
2447 if (max_turnstile
) {
2448 max_priority
= priority_queue_entry_key(&turnstile
->ts_inheritor_queue
,
2449 &max_turnstile
->ts_inheritor_links
);
2450 proprietor
= max_turnstile
->ts_proprietor
;
2453 waitq_unlock(&turnstile
->ts_waitq
);
2456 if (max_priority
<= MAXPRI_THROTTLE
) {
2460 if (proprietor_out
) {
2461 *proprietor_out
= proprietor
;
2463 return max_priority
;
2467 * Name: turnstile_workloop_pusher_info
2469 * Description: Returns the priority of the turnstile push for a workloop,
2470 * and the thread or knote responsible for this push.
2472 * Args: workloop turnstile
2475 * Priority of the push or 0
2476 * Thread (with a +1 reference) with that push or THREAD_NULL.
2477 * Port (with a +1 reference) with that push, or IP_NULL.
2478 * Sync IPC knote with the highest push (or NULL)
2481 turnstile_workloop_pusher_info(
2482 struct turnstile
*turnstile
,
2483 thread_t
*thread_out
,
2484 ipc_port_t
*port_out
,
2485 struct knote
**knote_out
)
2487 struct turnstile
*max_ts
;
2488 thread_t max_thread
;
2489 int max_thread_pri
= 0;
2493 assert(turnstile_get_type(turnstile
) == TURNSTILE_WORKLOOPS
);
2495 spl_t s
= splsched();
2496 waitq_lock(&turnstile
->ts_waitq
);
2498 max_thread
= priority_queue_max(&turnstile
->ts_waitq
.waitq_prio_queue
,
2499 struct thread
, wait_prioq_links
);
2501 max_thread_pri
= priority_queue_entry_key(
2502 &turnstile
->ts_waitq
.waitq_prio_queue
,
2503 &max_thread
->wait_prioq_links
);
2506 max_ts
= priority_queue_max(&turnstile
->ts_inheritor_queue
,
2507 struct turnstile
, ts_inheritor_links
);
2509 max_ts_pri
= priority_queue_entry_key(&turnstile
->ts_inheritor_queue
,
2510 &max_ts
->ts_inheritor_links
);
2514 * Reasons to push on a workloop turnstile are:
2516 * 1. threads in dispatch sync
2518 * 2. sync IPC pushes, which in turn have 4 sub-cases:
2520 * 2.a. special reply port or receive right pushing through a knote
2523 * 2.b. special reply port stashed on a knote, pushing on the workloop
2526 * 2.c. receive right stashed on a knote, pushing on the workloop
2529 * 2.d. a receive right monitored by a knote, pushing on the workloop
2532 * See ipc_port_send_update_inheritor(), ipc_port_recv_update_inheritor().
2534 * Note: dereferencing the knote in the caller is safe provided this
2535 * function i scalled under the proper interlocks (the filt_wllock + req
2536 * lock) which serializes with the knote going away.
2538 if (max_thread_pri
> max_ts_pri
) {
2539 thread_reference(max_thread
);
2540 *thread_out
= max_thread
;
2543 } else if (max_ts_pri
) {
2544 switch (turnstile_get_type(max_ts
)) {
2545 case TURNSTILE_KNOTE
:
2547 *thread_out
= THREAD_NULL
;
2548 *port_out
= IP_NULL
;
2549 *knote_out
= (struct knote
*)max_ts
->ts_proprietor
;
2552 case TURNSTILE_SYNC_IPC
:
2554 port
= (ipc_port_t
)max_ts
->ts_proprietor
;
2556 *thread_out
= THREAD_NULL
;
2562 panic("Unexpected type for turnstile %p", max_ts
);
2565 *thread_out
= THREAD_NULL
;
2566 *port_out
= IP_NULL
;
2570 waitq_unlock(&turnstile
->ts_waitq
);
2573 return max(max_thread_pri
, max_ts_pri
);
2577 * Name: turnstile_has_waiters
2579 * Description: returns if there are waiters on the turnstile
2581 * Arg1: turnstile: turnstile
2583 * Returns: TRUE if there are waiters, FALSE otherwise.
2587 turnstile_has_waiters(struct turnstile
*turnstile
)
2591 spl_t s
= splsched();
2592 waitq_lock(&turnstile
->ts_waitq
);
2593 ret
= !priority_queue_empty(&turnstile
->ts_waitq
.waitq_prio_queue
);
2594 waitq_unlock(&turnstile
->ts_waitq
);
2601 * Name: turnstile_update_inheritor_priority_chain
2603 * Description: Update turnstile inheritor's priority and propagate
2604 * the priority if the inheritor is blocked on a turnstile.
2607 * Arg2: inheritor flags
2612 turnstile_update_inheritor_priority_chain(
2613 turnstile_inheritor_t inheritor
,
2614 turnstile_update_flags_t turnstile_flags
)
2616 struct turnstile
*turnstile
= TURNSTILE_NULL
;
2617 thread_t thread
= THREAD_NULL
;
2618 int total_hop
= 0, thread_hop
= 0;
2620 turnstile_stats_update_flags_t tsu_flags
= ((turnstile_flags
& TURNSTILE_UPDATE_BOOST
) ?
2621 TSU_BOOST_ARG
: TSU_FLAGS_NONE
) | TSU_PRI_PROPAGATION
;
2623 if (inheritor
== NULL
) {
2629 if (turnstile_flags
& TURNSTILE_INHERITOR_THREAD
) {
2631 thread_lock(thread
);
2632 thread_recompute_user_promotion_locked(thread
);
2633 thread_recompute_kernel_promotion_locked(thread
);
2634 } else if (turnstile_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
2635 turnstile
= inheritor
;
2636 waitq_lock(&turnstile
->ts_waitq
);
2637 turnstile_recompute_priority_locked(turnstile
);
2638 tsu_flags
|= turnstile_get_update_flags_for_above_UI_pri_change(turnstile
);
2641 * we should never call turnstile_update_inheritor_priority_chain()
2642 * for a workqueue, they have no "chain" after them.
2644 assert((turnstile_flags
& TURNSTILE_INHERITOR_WORKQ
) == 0);
2647 while (turnstile
!= TURNSTILE_NULL
|| thread
!= THREAD_NULL
) {
2648 if (turnstile
!= TURNSTILE_NULL
) {
2649 if (turnstile
->ts_inheritor
== NULL
) {
2650 turnstile_stats_update(total_hop
+ 1, TSU_NO_INHERITOR
|
2651 TSU_TURNSTILE_ARG
| tsu_flags
,
2653 waitq_unlock(&turnstile
->ts_waitq
);
2654 turnstile
= TURNSTILE_NULL
;
2657 if (turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
2658 turnstile_update_inheritor_thread_priority_chain(&turnstile
, &thread
,
2659 total_hop
, tsu_flags
);
2660 } else if (turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
2661 turnstile_update_inheritor_turnstile_priority_chain(&turnstile
,
2662 total_hop
, tsu_flags
);
2663 } else if (turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
2664 turnstile_update_inheritor_workq_priority_chain(turnstile
, s
);
2665 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
| tsu_flags
,
2669 panic("Inheritor flags not passed in turnstile_update_inheritor");
2671 } else if (thread
!= THREAD_NULL
) {
2672 thread_update_waiting_turnstile_priority_chain(&thread
, &turnstile
,
2673 thread_hop
, total_hop
, tsu_flags
);
2684 * Name: turnstile_update_inheritor_complete
2686 * Description: Update turnstile inheritor's priority and propagate the
2687 * priority if the inheritor is blocked on a turnstile.
2688 * Consumes thread ref of old inheritor returned by
2689 * turnstile_update_inheritor. Recursive priority update
2690 * will only happen when called with interlock dropped.
2694 * Arg2: interlock held
2699 turnstile_update_inheritor_complete(
2700 struct turnstile
*turnstile
,
2701 turnstile_update_complete_flags_t flags __unused
)
2703 thread_t thread
= current_thread();
2705 turnstile_update_flags_t inheritor_flags
= thread
->inheritor_flags
;
2707 turnstile_cleanup();
2709 /* Perform priority update for new inheritor */
2710 if (inheritor_flags
& TURNSTILE_NEEDS_PRI_UPDATE
) {
2711 turnstile_update_inheritor_priority_chain(turnstile
,
2712 TURNSTILE_INHERITOR_TURNSTILE
| TURNSTILE_UPDATE_BOOST
);
2717 * Name: turnstile_cleanup
2719 * Description: Update priority of a turnstile inheritor
2722 * Args: inheritor and flags passed on thread struct.
2727 turnstile_cleanup(void)
2729 thread_t thread
= current_thread();
2731 /* Get the old inheritor from calling thread struct */
2732 turnstile_inheritor_t old_inheritor
= thread
->inheritor
;
2733 turnstile_update_flags_t inheritor_flags
= thread
->inheritor_flags
;
2734 thread
->inheritor
= THREAD_NULL
;
2735 thread
->inheritor_flags
= TURNSTILE_UPDATE_FLAGS_NONE
;
2737 if (old_inheritor
== TURNSTILE_INHERITOR_NULL
) {
2738 /* no cleanup to do */
2742 /* Perform priority demotion for old inheritor */
2743 if (inheritor_flags
& TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE
) {
2744 turnstile_update_inheritor_priority_chain(old_inheritor
,
2748 /* Drop thread reference for old inheritor */
2749 if (inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
2750 thread_deallocate_safe(old_inheritor
);
2751 } else if (inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
2752 turnstile_deallocate_safe((struct turnstile
*)old_inheritor
);
2753 } else if (inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
2754 workq_deallocate_safe((struct workqueue
*)old_inheritor
);
2756 panic("Inheritor flags lost along the way");
2761 * Name: turnstile_update_thread_priority_chain
2763 * Description: Priority of a thread blocked on a turnstile
2764 * has changed, update the turnstile priority.
2766 * Arg1: thread: thread whose priority has changed.
2771 turnstile_update_thread_priority_chain(thread_t thread
)
2773 turnstile_update_inheritor_priority_chain(thread
,
2774 TURNSTILE_INHERITOR_THREAD
| TURNSTILE_UPDATE_BOOST
);
2778 * Name: turnstile_update_inheritor_workq_priority_chain
2780 * Description: Helper function to update turnstile's inheritor(workq)
2781 * priority and possibly redrive thread creation
2783 * Arg1: turnstile: turnstile
2784 * Arg2: s: whether iterrupts are disabled.
2786 * Condition: turnstile is locked on entry, it is unlocked on exit,
2787 * and interrupts re-enabled.
2790 turnstile_update_inheritor_workq_priority_chain(struct turnstile
*turnstile
, spl_t s
)
2792 struct workqueue
*wq
= turnstile
->ts_inheritor
;
2793 bool workq_lock_held
= workq_is_current_thread_updating_turnstile(wq
);
2795 if (__improbable(turnstile
->ts_priority
<= MAXPRI_THROTTLE
)) {
2796 waitq_unlock(&turnstile
->ts_waitq
);
2801 if (!workq_lock_held
) {
2802 workq_reference(wq
);
2803 disable_preemption();
2805 waitq_unlock(&turnstile
->ts_waitq
);
2808 workq_schedule_creator_turnstile_redrive(wq
, workq_lock_held
);
2810 if (!workq_lock_held
) {
2811 enable_preemption();
2812 workq_deallocate_safe(wq
);
2817 * Name: turnstile_update_inheritor_thread_priority_chain
2819 * Description: Helper function to update turnstile's inheritor(thread)
2822 * Arg1: in_turnstile: address to turnstile
2823 * Arg2: out_thread: address to return the thread inheritor
2824 * Arg3: thread_hop: number to thread hop in propagation chain
2825 * Arg4: tsu_flags: turnstile update flags
2827 * Returns: Implicit returns locked thread in out_thread if it needs
2828 * further propagation.
2830 * Condition: *in_turnstile is locked on entry, it is unlocked on exit and
2831 * *in_turnstile is set to NULL.
2834 turnstile_update_inheritor_thread_priority_chain(
2835 struct turnstile
**in_turnstile
,
2836 thread_t
*out_thread
,
2838 turnstile_stats_update_flags_t tsu_flags
)
2840 boolean_t needs_update
= FALSE
;
2841 struct turnstile
*turnstile
= *in_turnstile
;
2842 thread_t thread_inheritor
= turnstile
->ts_inheritor
;
2843 boolean_t first_update
= !total_hop
;
2845 assert(turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
2846 *in_turnstile
= TURNSTILE_NULL
;
2848 /* Check if update is needed before grabbing the thread lock */
2849 needs_update
= thread_needs_turnstile_promotion_update(thread_inheritor
, turnstile
);
2850 if (!needs_update
&& !first_update
) {
2851 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
|
2852 TSU_TURNSTILE_ARG
| tsu_flags
, turnstile
);
2853 waitq_unlock(&turnstile
->ts_waitq
);
2857 thread_lock(thread_inheritor
);
2859 /* adjust turnstile position in the thread's inheritor list */
2860 needs_update
= thread_update_turnstile_promotion_locked(
2861 thread_inheritor
, turnstile
);
2864 * Check if thread needs further priority propagation,
2865 * since the first hop priority update was done in
2866 * turnstile_update_inheritor, do not bailout if it is
2867 * the first update as needs_update flag would evaluate to
2868 * false for that case.
2870 if (!needs_update
&& !first_update
) {
2871 /* Update turnstile stats before returning */
2872 turnstile_stats_update(total_hop
+ 1,
2873 (thread_get_update_flags_for_turnstile_propagation_stoppage(thread_inheritor
)) |
2874 TSU_TURNSTILE_ARG
| tsu_flags
,
2876 thread_unlock(thread_inheritor
);
2877 waitq_unlock(&turnstile
->ts_waitq
);
2881 /* Unlock the turnstile and update the thread */
2882 waitq_unlock(&turnstile
->ts_waitq
);
2883 *out_thread
= thread_inheritor
;
2888 * Name: turnstile_update_inheritor_turnstile_priority_chain
2890 * Description: Helper function to update turnstile's inheritor(turnstile)
2893 * Arg1: in_out_turnstile: address to turnstile
2894 * Arg2: thread_hop: number of thread hop in propagation chain
2895 * Arg3: tsu_flags: turnstile update flags
2897 * Returns: Implicit returns locked turnstile in in_out_turnstile if it needs
2898 * further propagation.
2900 * Condition: *in_out_turnstile is locked on entry, *in_out_turnstile on exit,
2901 * but the value of *in_out_turnstile might change and turnstile lock
2902 * will be dropped for old value and will be acquired for the new value.
2905 turnstile_update_inheritor_turnstile_priority_chain(
2906 struct turnstile
**in_out_turnstile
,
2908 turnstile_stats_update_flags_t tsu_flags
)
2910 boolean_t needs_update
= FALSE
;
2911 struct turnstile
*turnstile
= *in_out_turnstile
;
2912 struct turnstile
*inheritor_turnstile
= turnstile
->ts_inheritor
;
2913 boolean_t first_update
= !total_hop
;
2915 assert(turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
);
2916 *in_out_turnstile
= TURNSTILE_NULL
;
2918 /* Check if the inheritor turnstile needs to be updated before grabbing the lock */
2919 needs_update
= turnstile_need_turnstile_promotion_update(inheritor_turnstile
, turnstile
);
2920 if (!needs_update
&& !first_update
) {
2921 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
|
2922 TSU_TURNSTILE_ARG
| tsu_flags
,
2924 waitq_unlock(&turnstile
->ts_waitq
);
2928 waitq_lock(&inheritor_turnstile
->ts_waitq
);
2930 needs_update
= turnstile_update_turnstile_promotion_locked(
2931 inheritor_turnstile
, turnstile
);
2934 * Check if turnstile needs further priority propagation,
2935 * since the first hop priority update was done in
2936 * turnstile_update_inheritor, do not bailout if it is
2937 * the first update as needs_update flag would evaluate to
2938 * false for that case.
2940 if (!needs_update
&& !first_update
) {
2941 /* Update turnstile stats before returning */
2942 turnstile_stats_update(total_hop
+ 1,
2943 (inheritor_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
2944 TSU_TURNSTILE_ARG
| tsu_flags
,
2946 waitq_unlock(&inheritor_turnstile
->ts_waitq
);
2947 waitq_unlock(&turnstile
->ts_waitq
);
2951 /* Unlock the outer turnstile and update the inner turnstile */
2952 waitq_unlock(&turnstile
->ts_waitq
);
2953 *in_out_turnstile
= inheritor_turnstile
;
2958 * Name: thread_update_waiting_turnstile_priority_chain
2960 * Description: Helper function to update thread's waiting
2961 * turnstile priority.
2963 * Arg1: in_thread: pointer to thread
2964 * Arg2: out_turnstile: pointer to turnstile to return to caller
2965 * Arg3: thread_hop: Number of thread hops visited
2966 * Arg4: total_hop: total hops visited
2967 * Arg5: tsu_flags: turnstile update flags
2969 * Returns: *out_turnstile returns the inheritor if it needs further propagation.
2971 * Condition: *in_thread locked on entry, unlocked on exit and set to NULL.
2974 thread_update_waiting_turnstile_priority_chain(
2975 thread_t
*in_thread
,
2976 struct turnstile
**out_turnstile
,
2979 turnstile_stats_update_flags_t tsu_flags
)
2981 boolean_t needs_update
= FALSE
;
2982 thread_t thread
= *in_thread
;
2983 struct turnstile
*waiting_turnstile
= TURNSTILE_NULL
;
2984 uint32_t turnstile_gencount
;
2985 boolean_t first_update
= !total_hop
;
2987 *in_thread
= THREAD_NULL
;
2989 /* Check if thread waiting on a turnstile */
2990 waiting_turnstile
= thread_get_waiting_turnstile(thread
);
2992 if (waiting_turnstile
== TURNSTILE_NULL
|| thread_hop
> turnstile_max_hop
) {
2993 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2994 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
,
2995 (waiting_turnstile
? TURNSTILE_UPDATE_STOPPED_BY_LIMIT
: THREAD_NOT_WAITING_ON_TURNSTILE
)
3000 VM_KERNEL_UNSLIDE_OR_PERM(waiting_turnstile
), 0);
3001 turnstile_stats_update(total_hop
+ 1, TSU_NO_TURNSTILE
|
3002 TSU_THREAD_ARG
| tsu_flags
, thread
);
3003 thread_unlock(thread
);
3007 /* Check if the thread needs to update the waiting turnstile */
3008 needs_update
= turnstile_need_thread_promotion_update(waiting_turnstile
, thread
);
3009 if (!needs_update
&& !first_update
) {
3010 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
|
3011 TSU_THREAD_ARG
| tsu_flags
, thread
);
3012 thread_unlock(thread
);
3016 /* take a reference on thread, turnstile and snapshot of gencount */
3017 turnstile_gencount
= turnstile_get_gencount(waiting_turnstile
);
3018 turnstile_reference(waiting_turnstile
);
3019 thread_reference(thread
);
3021 /* drop the thread lock and acquire the turnstile lock */
3022 thread_unlock(thread
);
3023 waitq_lock(&waiting_turnstile
->ts_waitq
);
3024 thread_lock(thread
);
3026 /* Check if the gencount matches and thread is still waiting on same turnstile */
3027 if (turnstile_gencount
!= turnstile_get_gencount(waiting_turnstile
) ||
3028 waiting_turnstile
!= thread_get_waiting_turnstile(thread
)) {
3029 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
|
3030 TSU_THREAD_ARG
| tsu_flags
, thread
);
3031 /* No updates required, bail out */
3032 thread_unlock(thread
);
3033 waitq_unlock(&waiting_turnstile
->ts_waitq
);
3034 thread_deallocate_safe(thread
);
3035 turnstile_deallocate_safe(waiting_turnstile
);
3040 * The thread is waiting on the waiting_turnstile and we have thread lock,
3041 * we can drop the thread and turnstile reference since its on waitq and
3042 * it could not be removed from the waitq without the thread lock.
3044 thread_deallocate_safe(thread
);
3045 turnstile_deallocate_safe(waiting_turnstile
);
3047 /* adjust thread's position on turnstile waitq */
3048 needs_update
= turnstile_update_thread_promotion_locked(waiting_turnstile
, thread
);
3051 * Check if thread needs further priority propagation,
3052 * since the first hop priority update was done in
3053 * turnstile_update_inheritor, do not bailout if it is
3054 * the first update as needs_update flag would evaluate to
3055 * false for that case.
3057 if (!needs_update
&& !first_update
) {
3058 turnstile_stats_update(total_hop
+ 1,
3059 (waiting_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
3060 TSU_THREAD_ARG
| tsu_flags
, thread
);
3061 thread_unlock(thread
);
3062 waitq_unlock(&waiting_turnstile
->ts_waitq
);
3066 /* drop the thread lock and update the turnstile */
3067 thread_unlock(thread
);
3068 *out_turnstile
= waiting_turnstile
;
3072 * Name: turnstile_stats_update
3074 * Description: Function to update turnstile stats for dev kernel.
3076 * Arg1: hops : number of thread hops in priority propagation
3077 * Arg2: flags : turnstile stats update flags
3078 * Arg3: inheritor: inheritor
3083 turnstile_stats_update(
3084 int hop __assert_only
,
3085 turnstile_stats_update_flags_t flags __assert_only
,
3086 turnstile_inheritor_t inheritor __assert_only
)
3088 #if DEVELOPMENT || DEBUG
3089 if (flags
& TSU_TURNSTILE_BLOCK_COUNT
) {
3090 os_atomic_inc(&thread_block_on_turnstile_count
, relaxed
);
3093 if (flags
& TSU_REGULAR_WAITQ_BLOCK_COUNT
) {
3094 os_atomic_inc(&thread_block_on_regular_waitq_count
, relaxed
);
3097 if (hop
> TURNSTILE_MAX_HOP_DEFAULT
|| hop
== 0) {
3104 * Check if turnstile stats needs to be updated.
3105 * Bail out if the turnstile or thread does not
3106 * have any user promotion.
3107 * Bail out if it is the first hop of WQ turnstile
3108 * since WQ's use of a turnstile for the admission check
3109 * introduces a lot of noise due to state changes.
3111 if (flags
& TSU_TURNSTILE_ARG
) {
3112 struct turnstile
*ts
= (struct turnstile
*)inheritor
;
3113 if (ts
->ts_priority
== 0) {
3117 if (hop
== 1 && turnstile_get_type(ts
) == TURNSTILE_WORKQS
) {
3120 } else if (flags
& TSU_THREAD_ARG
) {
3121 thread_t thread
= (thread_t
)inheritor
;
3122 if (thread
->user_promotion_basepri
== 0) {
3126 assert(inheritor
== NULL
);
3129 struct turnstile_stats
*turnstile_stats
;
3130 if (flags
& TSU_BOOST_ARG
) {
3131 turnstile_stats
= turnstile_boost_stats
;
3133 turnstile_stats
= turnstile_unboost_stats
;
3136 if (flags
& TSU_PRI_PROPAGATION
) {
3137 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_priority_propagation
, relaxed
);
3140 if (flags
& TSU_NO_INHERITOR
) {
3141 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_inheritor
, relaxed
);
3144 if (flags
& TSU_NO_TURNSTILE
) {
3145 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_turnstile
, relaxed
);
3148 if (flags
& TSU_NO_PRI_CHANGE_NEEDED
) {
3149 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_priority_change_required
, relaxed
);
3152 if (flags
& TSU_THREAD_RUNNABLE
) {
3153 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_thread_runnable
, relaxed
);
3156 if (flags
& TSU_ABOVE_UI_PRI_CHANGE
) {
3157 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_above_ui_pri_change
, relaxed
);
3163 kdp_turnstile_traverse_inheritor_chain(struct turnstile
*ts
, uint64_t *flags
, uint8_t *hops
)
3165 if (waitq_held(&ts
->ts_waitq
)) {
3166 *flags
|= STACKSHOT_TURNSTILE_STATUS_LOCKED_WAITQ
;
3172 if (ts
->ts_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
3173 return kdp_turnstile_traverse_inheritor_chain(ts
->ts_inheritor
, flags
, hops
);
3176 if (ts
->ts_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
3177 *flags
|= STACKSHOT_TURNSTILE_STATUS_THREAD
;
3178 return (uint64_t) thread_tid(ts
->ts_inheritor
);
3181 if (ts
->ts_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
3182 *flags
|= STACKSHOT_TURNSTILE_STATUS_WORKQUEUE
;
3183 return VM_KERNEL_UNSLIDE_OR_PERM(ts
->ts_inheritor
);
3186 *flags
|= STACKSHOT_TURNSTILE_STATUS_UNKNOWN
;
3191 kdp_turnstile_fill_tsinfo(struct turnstile
*ts
, thread_turnstileinfo_t
*tsinfo
)
3193 uint64_t final_inheritor
;
3197 tsinfo
->turnstile_context
= 0;
3198 tsinfo
->number_of_hops
= 0;
3199 tsinfo
->turnstile_priority
= 0;
3201 assert(ts
!= TURNSTILE_NULL
);
3203 if (waitq_held(&ts
->ts_waitq
)) {
3204 tsinfo
->turnstile_flags
|= STACKSHOT_TURNSTILE_STATUS_LOCKED_WAITQ
;
3208 final_inheritor
= kdp_turnstile_traverse_inheritor_chain(ts
, &flags
, &hops
);
3210 /* store some metadata about the turnstile itself */
3211 tsinfo
->turnstile_flags
= flags
;
3212 tsinfo
->number_of_hops
= hops
;
3213 tsinfo
->turnstile_priority
= ts
->ts_priority
;
3214 tsinfo
->turnstile_context
= final_inheritor
;
3217 #if DEVELOPMENT || DEBUG
3219 int sysctl_io_opaque(void *req
, void *pValue
, size_t valueSize
, int *changed
);
3222 * Name: turnstile_get_boost_stats_sysctl
3224 * Description: Function to get turnstile stats.
3226 * Args: req : opaque struct to pass to sysctl_io_opaque
3231 turnstile_get_boost_stats_sysctl(
3234 return sysctl_io_opaque(req
, turnstile_boost_stats
, sizeof(struct turnstile_stats
) * TURNSTILE_MAX_HOP_DEFAULT
, NULL
);
3238 * Name: get_turnstile_stats_sysctl
3240 * Description: Function to get turnstile stats.
3242 * Args: req : opaque struct to pass to sysctl_io_opaque
3247 turnstile_get_unboost_stats_sysctl(
3250 return sysctl_io_opaque(req
, turnstile_unboost_stats
, sizeof(struct turnstile_stats
) * TURNSTILE_MAX_HOP_DEFAULT
, NULL
);
3253 /* Testing interface for Development kernels */
3254 #define tstile_test_prim_lock_interlock(test_prim) \
3255 lck_spin_lock(&test_prim->ttprim_interlock)
3256 #define tstile_test_prim_unlock_interlock(test_prim) \
3257 lck_spin_unlock(&test_prim->ttprim_interlock)
3260 tstile_test_prim_init(struct tstile_test_prim
**test_prim_ptr
)
3262 struct tstile_test_prim
*test_prim
= (struct tstile_test_prim
*) kalloc(sizeof(struct tstile_test_prim
));
3264 test_prim
->ttprim_turnstile
= TURNSTILE_NULL
;
3265 test_prim
->ttprim_owner
= NULL
;
3266 lck_spin_init(&test_prim
->ttprim_interlock
, &turnstiles_dev_lock_grp
, &turnstiles_dev_lock_attr
);
3267 test_prim
->tt_prim_waiters
= 0;
3269 *test_prim_ptr
= test_prim
;
3274 tstile_test_prim_lock(int val
)
3276 struct tstile_test_prim
*test_prim
;
3277 boolean_t use_hashtable
;
3278 turnstile_type_t type
;
3279 wait_interrupt_t wait_type
;
3282 case SYSCTL_TURNSTILE_TEST_USER_DEFAULT
:
3283 test_prim
= test_prim_ts_inline
;
3284 use_hashtable
= FALSE
;
3285 wait_type
= THREAD_ABORTSAFE
;
3286 type
= TURNSTILE_ULOCK
;
3288 case SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
:
3289 test_prim
= test_prim_global_htable
;
3290 use_hashtable
= TRUE
;
3291 wait_type
= THREAD_ABORTSAFE
;
3292 type
= TURNSTILE_ULOCK
;
3294 case SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
:
3295 test_prim
= test_prim_global_ts_kernel
;
3296 use_hashtable
= FALSE
;
3297 wait_type
= THREAD_UNINT
| THREAD_WAIT_NOREPORT_USER
;
3298 type
= TURNSTILE_KERNEL_MUTEX
;
3300 case SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE
:
3301 test_prim
= test_prim_global_ts_kernel_hash
;
3302 use_hashtable
= TRUE
;
3303 wait_type
= THREAD_UNINT
| THREAD_WAIT_NOREPORT_USER
;
3304 type
= TURNSTILE_KERNEL_MUTEX
;
3313 /* take the interlock of the primitive */
3314 tstile_test_prim_lock_interlock(test_prim
);
3316 /* Check if the lock is available */
3317 if (test_prim
->ttprim_owner
== NULL
&& test_prim
->tt_prim_waiters
== 0) {
3318 thread_reference(current_thread());
3319 test_prim
->ttprim_owner
= current_thread();
3320 tstile_test_prim_unlock_interlock(test_prim
);
3324 struct turnstile
*prim_turnstile
= TURNSTILE_NULL
;
3326 /* primitive locked, get a turnstile */
3327 prim_turnstile
= turnstile_prepare((uintptr_t)test_prim
,
3328 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
,
3329 TURNSTILE_NULL
, type
);
3331 assert(prim_turnstile
!= TURNSTILE_NULL
);
3333 /* This is contented acquire case */
3334 if (test_prim
->ttprim_owner
== NULL
) {
3335 thread_reference(current_thread());
3336 test_prim
->ttprim_owner
= current_thread();
3338 /* Update the turnstile owner */
3339 turnstile_update_inheritor(prim_turnstile
,
3341 (TURNSTILE_IMMEDIATE_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3343 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_HELD
);
3345 turnstile_complete((uintptr_t)test_prim
,
3346 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
, NULL
, type
);
3348 tstile_test_prim_unlock_interlock(test_prim
);
3350 turnstile_cleanup();
3354 test_prim
->tt_prim_waiters
++;
3355 turnstile_update_inheritor(prim_turnstile
,
3356 test_prim
->ttprim_owner
,
3357 (TURNSTILE_DELAYED_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3359 waitq_assert_wait64(&prim_turnstile
->ts_waitq
,
3360 CAST_EVENT64_T(test_prim
), wait_type
,
3361 TIMEOUT_WAIT_FOREVER
);
3363 /* drop the interlock */
3364 tstile_test_prim_unlock_interlock(test_prim
);
3366 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_NOT_HELD
);
3368 wait_result_t result
;
3369 result
= thread_block(THREAD_CONTINUE_NULL
);
3371 /* re-acquire the interlock to get turnstile back */
3372 tstile_test_prim_lock_interlock(test_prim
);
3373 test_prim
->tt_prim_waiters
--;
3374 turnstile_complete((uintptr_t)test_prim
,
3375 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
, NULL
, type
);
3377 tstile_test_prim_unlock_interlock(test_prim
);
3379 turnstile_cleanup();
3381 /* Return if thread interrupted */
3382 if (result
== THREAD_INTERRUPTED
) {
3390 tstile_test_prim_unlock(int val
)
3392 struct tstile_test_prim
*test_prim
;
3393 boolean_t use_hashtable
;
3394 turnstile_type_t type
;
3397 case SYSCTL_TURNSTILE_TEST_USER_DEFAULT
:
3398 test_prim
= test_prim_ts_inline
;
3399 use_hashtable
= FALSE
;
3400 type
= TURNSTILE_ULOCK
;
3402 case SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
:
3403 test_prim
= test_prim_global_htable
;
3404 use_hashtable
= TRUE
;
3405 type
= TURNSTILE_ULOCK
;
3407 case SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
:
3408 test_prim
= test_prim_global_ts_kernel
;
3409 use_hashtable
= FALSE
;
3410 type
= TURNSTILE_KERNEL_MUTEX
;
3412 case SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE
:
3413 test_prim
= test_prim_global_ts_kernel_hash
;
3414 use_hashtable
= TRUE
;
3415 type
= TURNSTILE_KERNEL_MUTEX
;
3421 /* take the interlock of the primitive */
3422 tstile_test_prim_lock_interlock(test_prim
);
3424 if (test_prim
->ttprim_owner
== NULL
) {
3425 tstile_test_prim_unlock_interlock(test_prim
);
3429 /* Check if the lock is contended */
3430 if (test_prim
->ttprim_owner
!= NULL
&& test_prim
->tt_prim_waiters
== 0) {
3431 /* lock is not contended */
3432 thread_t old_owner
= test_prim
->ttprim_owner
;
3433 test_prim
->ttprim_owner
= NULL
;
3434 tstile_test_prim_unlock_interlock(test_prim
);
3436 thread_deallocate(old_owner
);
3440 struct turnstile
*prim_turnstile
= TURNSTILE_NULL
;
3442 thread_t old_owner
= test_prim
->ttprim_owner
;
3443 test_prim
->ttprim_owner
= NULL
;
3445 /* primitive locked, get a turnstile */
3446 prim_turnstile
= turnstile_prepare((uintptr_t)test_prim
,
3447 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
,
3448 TURNSTILE_NULL
, type
);
3450 assert(prim_turnstile
!= TURNSTILE_NULL
);
3452 /* Update the turnstile owner */
3453 turnstile_update_inheritor(prim_turnstile
,
3455 (TURNSTILE_IMMEDIATE_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3457 waitq_wakeup64_one(&prim_turnstile
->ts_waitq
,
3458 CAST_EVENT64_T(test_prim
),
3459 THREAD_AWAKENED
, WAITQ_ALL_PRIORITIES
);
3461 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_HELD
);
3463 turnstile_complete((uintptr_t)test_prim
,
3464 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
, NULL
, type
);
3466 tstile_test_prim_unlock_interlock(test_prim
);
3468 turnstile_cleanup();
3471 /* Changing this to thread_deallocate_safe to exercise thread_deallocate_safe path */
3472 thread_deallocate_safe(old_owner
);