2 * Copyright (c) 2017-2020 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 TUNABLE(int, turnstile_max_hop
, "turnstile_max_hop", TURNSTILE_MAX_HOP_DEFAULT
);
51 static ZONE_DECLARE(turnstiles_zone
, "turnstiles", sizeof(struct turnstile
), ZC_NONE
);
53 static struct mpsc_daemon_queue turnstile_deallocate_queue
;
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
;
87 static LCK_GRP_DECLARE(turnstiles_dev_lock_grp
, "turnstile_dev_lock");
88 static LCK_SPIN_DECLARE(global_turnstile_lock
, &turnstiles_dev_lock_grp
);
90 #define global_turnstiles_lock() \
91 lck_spin_lock_grp(&global_turnstile_lock, &turnstiles_dev_lock_grp)
92 #define global_turnstiles_lock_try() \
93 lck_spin_try_lock_grp(&global_turnstile_lock, &turnstiles_dev_lock_grp)
94 #define global_turnstiles_unlock() \
95 lck_spin_unlock(&global_turnstile_lock)
97 /* Array to store stats for multi-hop boosting */
98 static struct turnstile_stats turnstile_boost_stats
[TURNSTILE_MAX_HOP_DEFAULT
] = {};
99 static struct turnstile_stats turnstile_unboost_stats
[TURNSTILE_MAX_HOP_DEFAULT
] = {};
100 uint64_t thread_block_on_turnstile_count
;
101 uint64_t thread_block_on_regular_waitq_count
;
102 #endif /* DEVELOPMENT || DEBUG */
105 #define max(a, b) (((a) > (b)) ? (a) : (b))
108 /* Static function declarations */
109 static turnstile_type_t
110 turnstile_get_type(struct turnstile
*turnstile
);
112 turnstile_is_send_turnstile(struct turnstile
*turnstile
);
114 turnstile_get_gencount(struct turnstile
*turnstile
);
116 turnstile_set_type_and_increment_gencount(struct turnstile
*turnstile
, turnstile_type_t type
);
118 turnstile_init(struct turnstile
*turnstile
);
120 turnstile_update_inheritor_workq_priority_chain(struct turnstile
*in_turnstile
, spl_t s
);
122 turnstile_update_inheritor_thread_priority_chain(struct turnstile
**in_turnstile
,
123 thread_t
*out_thread
, int total_hop
, turnstile_stats_update_flags_t tsu_flags
);
125 turnstile_update_inheritor_turnstile_priority_chain(struct turnstile
**in_out_turnstile
,
126 int total_hop
, turnstile_stats_update_flags_t tsu_flags
);
128 thread_update_waiting_turnstile_priority_chain(thread_t
*in_thread
,
129 struct turnstile
**out_turnstile
, int thread_hop
, int total_hop
,
130 turnstile_stats_update_flags_t tsu_flags
);
132 turnstile_update_turnstile_promotion_locked(struct turnstile
*dst_turnstile
,
133 struct turnstile
*src_turnstile
);
135 turnstile_update_turnstile_promotion(struct turnstile
*dst_turnstile
,
136 struct turnstile
*src_turnstile
);
138 turnstile_need_turnstile_promotion_update(struct turnstile
*dst_turnstile
,
139 struct turnstile
*src_turnstile
);
141 turnstile_add_turnstile_promotion(struct turnstile
*dst_turnstile
,
142 struct turnstile
*src_turnstile
);
144 turnstile_remove_turnstile_promotion(struct turnstile
*dst_turnstile
,
145 struct turnstile
*src_turnstile
);
147 turnstile_update_thread_promotion_locked(struct turnstile
*dst_turnstile
,
150 turnstile_need_thread_promotion_update(struct turnstile
*dst_turnstile
,
153 thread_add_turnstile_promotion(
154 thread_t thread
, struct turnstile
*turnstile
);
156 thread_remove_turnstile_promotion(
157 thread_t thread
, struct turnstile
*turnstile
);
159 thread_needs_turnstile_promotion_update(thread_t thread
,
160 struct turnstile
*turnstile
);
162 thread_update_turnstile_promotion(
163 thread_t thread
, struct turnstile
*turnstile
);
165 thread_update_turnstile_promotion_locked(
166 thread_t thread
, struct turnstile
*turnstile
);
168 workq_add_turnstile_promotion(
169 struct workqueue
*wq_inheritor
, struct turnstile
*turnstile
);
170 static turnstile_stats_update_flags_t
171 thread_get_update_flags_for_turnstile_propagation_stoppage(thread_t thread
);
172 static turnstile_stats_update_flags_t
173 turnstile_get_update_flags_for_above_UI_pri_change(struct turnstile
*turnstile
);
174 static void turnstile_stash_inheritor(turnstile_inheritor_t new_inheritor
,
175 turnstile_update_flags_t flags
);
176 static int turnstile_compute_thread_push(struct turnstile
*turnstile
, thread_t thread
);
178 #if DEVELOPMENT || DEBUG
179 /* Test primitives and interfaces for testing turnstiles */
180 struct tstile_test_prim
{
181 struct turnstile
*ttprim_turnstile
;
182 thread_t ttprim_owner
;
183 lck_spin_t ttprim_interlock
;
184 uint32_t tt_prim_waiters
;
187 struct tstile_test_prim
*test_prim_ts_inline
;
188 struct tstile_test_prim
*test_prim_global_htable
;
189 struct tstile_test_prim
*test_prim_global_ts_kernel
;
190 struct tstile_test_prim
*test_prim_global_ts_kernel_hash
;
193 tstile_test_prim_init(struct tstile_test_prim
**test_prim_ptr
);
196 union turnstile_type_gencount
{
199 uint32_t ts_type
:(8 * sizeof(turnstile_type_t
)),
200 ts_gencount
: (8 * (sizeof(uint32_t) - sizeof(turnstile_type_t
)));
204 static turnstile_type_t
205 turnstile_get_type(struct turnstile
*turnstile
)
207 union turnstile_type_gencount type_and_gencount
;
209 type_and_gencount
.value
= atomic_load_explicit(&turnstile
->ts_type_gencount
, memory_order_relaxed
);
210 return (turnstile_type_t
) type_and_gencount
.ts_type
;
213 /* Only safe to be called from stackshot context */
215 turnstile_is_send_turnstile(struct turnstile
*turnstile
)
218 panic("turnstile_is_send_turnstile() called outside of kernel debugger context");
221 if (turnstile_get_type(turnstile
) == TURNSTILE_SYNC_IPC
) {
222 ipc_port_t port
= (ipc_port_t
) turnstile
->ts_proprietor
;
224 return port_send_turnstile(port
) == turnstile
;
230 /* Only safe to be called from stackshot context */
232 turnstile_is_receive_turnstile(struct turnstile
*turnstile
)
235 panic("turnstile_is_receive_turnstile() called outside of kernel debugger context");
238 if (turnstile_get_type(turnstile
) == TURNSTILE_SYNC_IPC
) {
239 ipc_port_t port
= (ipc_port_t
) turnstile
->ts_proprietor
;
241 return *port_rcv_turnstile_address(port
) == turnstile
;
248 turnstile_get_gencount(struct turnstile
*turnstile
)
250 union turnstile_type_gencount type_and_gencount
;
252 type_and_gencount
.value
= atomic_load_explicit(&turnstile
->ts_type_gencount
, memory_order_relaxed
);
253 return (uint32_t) type_and_gencount
.ts_gencount
;
257 turnstile_set_type_and_increment_gencount(struct turnstile
*turnstile
, turnstile_type_t type
)
259 union turnstile_type_gencount type_and_gencount
;
261 /* No need to compare exchange since the store happens under interlock of the primitive */
262 type_and_gencount
.value
= atomic_load_explicit(&turnstile
->ts_type_gencount
, memory_order_relaxed
);
263 type_and_gencount
.ts_type
= type
;
264 type_and_gencount
.ts_gencount
++;
265 atomic_store_explicit(&turnstile
->ts_type_gencount
, type_and_gencount
.value
, memory_order_relaxed
);
269 /* Turnstile hashtable Implementation */
272 * Maximum number of buckets in the turnstile hashtable. This number affects the
273 * performance of the hashtable since it determines the hash collision
274 * rate. To experiment with the number of buckets in this hashtable use the
275 * "ts_htable_buckets" boot-arg.
277 #define TURNSTILE_HTABLE_BUCKETS_DEFAULT 32
278 #define TURNSTILE_HTABLE_BUCKETS_MAX 1024
280 SLIST_HEAD(turnstile_hashlist
, turnstile
);
282 struct turnstile_htable_bucket
{
283 lck_spin_t ts_ht_bucket_lock
;
284 struct turnstile_hashlist ts_ht_bucket_list
;
287 SECURITY_READ_ONLY_LATE(static uint32_t) ts_htable_buckets
;
288 /* Global hashtable for turnstiles managed with interrupts disabled */
289 SECURITY_READ_ONLY_LATE(static struct turnstile_htable_bucket
*)turnstile_htable_irq_safe
;
290 /* Global hashtable for turnstiles managed with interrupts enabled */
291 SECURITY_READ_ONLY_LATE(static struct turnstile_htable_bucket
*)turnstile_htable
;
294 /* Bucket locks for turnstile hashtable */
295 LCK_GRP_DECLARE(turnstiles_htable_lock_grp
, "turnstiles_htable_locks");
297 #define turnstile_bucket_lock_init(bucket) \
298 lck_spin_init(&bucket->ts_ht_bucket_lock, &turnstiles_htable_lock_grp, LCK_ATTR_NULL)
299 #define turnstile_bucket_lock(bucket) \
300 lck_spin_lock_grp(&bucket->ts_ht_bucket_lock, &turnstiles_htable_lock_grp)
301 #define turnstile_bucket_unlock(bucket) \
302 lck_spin_unlock(&bucket->ts_ht_bucket_lock)
304 #define kdp_turnstile_bucket_is_locked(bucket) \
305 kdp_lck_spin_is_acquired(&bucket->ts_ht_bucket_lock)
308 * Name: turnstiles_hashtable_init
310 * Description: Initializes the global turnstile hash table.
319 turnstiles_hashtable_init(void)
321 /* Initialize number of buckets in the hashtable */
322 if (PE_parse_boot_argn("ts_htable_buckets", &ts_htable_buckets
, sizeof(ts_htable_buckets
)) != TRUE
) {
323 ts_htable_buckets
= TURNSTILE_HTABLE_BUCKETS_DEFAULT
;
326 assert(ts_htable_buckets
<= TURNSTILE_HTABLE_BUCKETS_MAX
);
327 uint32_t ts_htable_size
= ts_htable_buckets
* sizeof(struct turnstile_htable_bucket
);
328 turnstile_htable_irq_safe
= zalloc_permanent(ts_htable_size
, ZALIGN_PTR
);
329 if (turnstile_htable_irq_safe
== NULL
) {
330 panic("Turnstiles hash table memory allocation failed!");
333 turnstile_htable
= zalloc_permanent(ts_htable_size
, ZALIGN_PTR
);
334 if (turnstile_htable
== NULL
) {
335 panic("Turnstiles hash table memory allocation failed!");
338 /* Initialize all the buckets of the hashtables */
339 for (uint32_t i
= 0; i
< ts_htable_buckets
; i
++) {
340 struct turnstile_htable_bucket
*ts_bucket
= &(turnstile_htable_irq_safe
[i
]);
341 turnstile_bucket_lock_init(ts_bucket
);
342 SLIST_INIT(&ts_bucket
->ts_ht_bucket_list
);
344 ts_bucket
= &(turnstile_htable
[i
]);
345 turnstile_bucket_lock_init(ts_bucket
);
346 SLIST_INIT(&ts_bucket
->ts_ht_bucket_list
);
351 * Name: turnstile_freelist_empty
353 * Description: Checks if the turnstile's freelist is empty
354 * Should be called with the primitive IL held.
360 * true if freelist is empty; false otherwise
362 static inline boolean_t
363 turnstile_freelist_empty(
364 struct turnstile
*ts
)
366 return SLIST_EMPTY(&ts
->ts_free_turnstiles
);
371 * Name: turnstile_freelist_insert
373 * Description: Inserts the turnstile into the freelist of another turnstile
374 * Should be called with the primitive IL held.
377 * Arg1: primitive turnstile
378 * Arg2: turnstile to add to the freelist
384 turnstile_freelist_insert(
385 struct turnstile
*dst_ts
,
386 struct turnstile
*free_ts
)
388 assert(turnstile_get_type(dst_ts
) == turnstile_get_type(free_ts
));
389 assert(dst_ts
->ts_proprietor
== free_ts
->ts_proprietor
);
390 turnstile_state_add(free_ts
, TURNSTILE_STATE_FREELIST
);
391 SLIST_INSERT_HEAD(&dst_ts
->ts_free_turnstiles
, free_ts
, ts_free_elm
);
395 * Name: turnstile_freelist_remove
397 * Description: Removes a turnstile from the freelist of a turnstile
398 * Should be called with the primitive IL held.
401 * Arg1: primitive turnstile
404 * turnstile removed from the freelist
406 static struct turnstile
*
407 turnstile_freelist_remove(
408 struct turnstile
*ts
)
410 struct turnstile
*ret_turnstile
= TURNSTILE_NULL
;
411 assert(!SLIST_EMPTY(&ts
->ts_free_turnstiles
));
412 ret_turnstile
= SLIST_FIRST(&ts
->ts_free_turnstiles
);
413 SLIST_REMOVE_HEAD(&ts
->ts_free_turnstiles
, ts_free_elm
);
414 assert(ret_turnstile
!= TURNSTILE_NULL
);
415 turnstile_state_remove(ret_turnstile
, TURNSTILE_STATE_FREELIST
);
416 /* Need to initialize the list again, since head and elm are in union */
417 SLIST_INIT(&ret_turnstile
->ts_free_turnstiles
);
418 return ret_turnstile
;
422 * Name: turnstile_hash
424 * Description: Calculates the hash bucket index for a given proprietor
427 * Arg1: proprietor (key) for hashing
430 * hash table bucket index for provided proprietor
432 static inline uint32_t
433 turnstile_hash(uintptr_t proprietor
)
435 uint32_t hash
= os_hash_kernel_pointer((void *)proprietor
);
436 return hash
& (ts_htable_buckets
- 1);
439 static inline struct turnstile_htable_bucket
*
440 turnstile_get_bucket(uint32_t index
, turnstile_type_t type
)
442 struct turnstile_htable_bucket
*ts_bucket
;
443 int hash_policy
= turnstile_hash_lock_policy
[type
];
445 if (hash_policy
& TURNSTILE_IRQ_UNSAFE_HASH
) {
446 ts_bucket
= &(turnstile_htable
[index
]);
448 ts_bucket
= &(turnstile_htable_irq_safe
[index
]);
455 * Name: turnstile_hash_bucket_lock
457 * Description: locks the spinlock associated with proprietor's bucket.
458 * if proprietor is specified the index for the hash will be
459 * recomputed and returned in index_proprietor,
460 * otherwise the value save in index_proprietor is used as index.
463 * Arg1: proprietor (key) for hashing
464 * Arg2: index for proprietor in the hash
465 * Arg3: turnstile type
467 * Returns: old value of irq if irq were disabled before acquiring the lock.
470 turnstile_hash_bucket_lock(uintptr_t proprietor
, uint32_t *index_proprietor
, turnstile_type_t type
)
472 struct turnstile_htable_bucket
*ts_bucket
;
473 int hash_policy
= turnstile_hash_lock_policy
[type
];
474 bool irq_safe
= !(hash_policy
& TURNSTILE_IRQ_UNSAFE_HASH
);
479 * If the proprietor is specified, the caller doesn't know
480 * the index in the hash, so compute it.
481 * Otherwise use the value of index provided.
484 index
= turnstile_hash(proprietor
);
485 *index_proprietor
= index
;
487 index
= *index_proprietor
;
490 ts_bucket
= turnstile_get_bucket(index
, type
);
496 turnstile_bucket_lock(ts_bucket
);
502 * Name: turnstile_hash_bucket_unlock
504 * Description: unlocks the spinlock associated with proprietor's bucket.
505 * if proprietor is specified the index for the hash will be
506 * recomputed and returned in index_proprietor,
507 * otherwise the value save in index_proprietor is used as index.
510 * Arg1: proprietor (key) for hashing
511 * Arg2: index for proprietor in the hash
512 * Arg3: turnstile type
513 * Arg4: irq value returned by turnstile_hash_bucket_lock
517 turnstile_hash_bucket_unlock(uintptr_t proprietor
, uint32_t *index_proprietor
, turnstile_type_t type
, unsigned s
)
519 struct turnstile_htable_bucket
*ts_bucket
;
520 int hash_policy
= turnstile_hash_lock_policy
[type
];
521 bool irq_safe
= !(hash_policy
& TURNSTILE_IRQ_UNSAFE_HASH
);
525 * If the proprietor is specified, the caller doesn't know
526 * the index in the hash, so compute it.
527 * Otherwise use the value of index provided.
530 index
= turnstile_hash(proprietor
);
531 *index_proprietor
= index
;
533 index
= *index_proprietor
;
535 ts_bucket
= turnstile_get_bucket(index
, type
);
537 turnstile_bucket_unlock(ts_bucket
);
544 * Name: turnstile_htable_lookup_add
546 * Description: Lookup the proprietor in the global turnstile hash table.
547 * If an entry is present, add the new turnstile to the entry's freelist.
548 * Otherwise add the passed in turnstile for that proprietor.
549 * The routine assumes that the turnstile->proprietor does not change
550 * while the turnstile is in the global hash table.
554 * Arg2: new turnstile for primitive
555 * Arg3: turnstile_type_t type
558 * Previous turnstile for proprietor in the hash table
560 static struct turnstile
*
561 turnstile_htable_lookup_add(
562 uintptr_t proprietor
,
563 struct turnstile
*new_turnstile
,
564 turnstile_type_t type
)
566 uint32_t index
= turnstile_hash(proprietor
);
567 assert(index
< ts_htable_buckets
);
568 struct turnstile_htable_bucket
*ts_bucket
;
569 int hash_policy
= turnstile_hash_lock_policy
[type
];
570 bool needs_lock
= !(hash_policy
& TURNSTILE_LOCKED_HASH
);
571 bool irq_safe
= !(hash_policy
& TURNSTILE_IRQ_UNSAFE_HASH
);
574 ts_bucket
= turnstile_get_bucket(index
, type
);
580 turnstile_bucket_lock(ts_bucket
);
583 struct turnstile
*ts
;
585 SLIST_FOREACH(ts
, &ts_bucket
->ts_ht_bucket_list
, ts_htable_link
) {
586 if (ts
->ts_proprietor
== proprietor
) {
588 * Found an entry in the hashtable for this proprietor; add thread turnstile to freelist
589 * and return this turnstile
592 turnstile_bucket_unlock(ts_bucket
);
597 turnstile_freelist_insert(ts
, new_turnstile
);
602 /* No entry for this proprietor; add the new turnstile in the hash table */
603 SLIST_INSERT_HEAD(&ts_bucket
->ts_ht_bucket_list
, new_turnstile
, ts_htable_link
);
604 turnstile_state_add(new_turnstile
, TURNSTILE_STATE_HASHTABLE
);
606 turnstile_bucket_unlock(ts_bucket
);
611 /* Since there was no previous entry for this proprietor, return TURNSTILE_NULL */
612 return TURNSTILE_NULL
;
616 * Name: turnstable_htable_lookup_remove
618 * Description: Lookup the proprietor in the global turnstile hash table.
619 * For the turnstile in the hash table, if the freelist has turnstiles on it
620 * return one of them from the freelist. Otherwise remove the turnstile from
621 * the hashtable and return that.
622 * The routine assumes that the turnstile->proprietor does not change
623 * while the turnstile is in the global hash table.
627 * Arg2: free turnstile to be returned
628 * Arg3: turnstile_type_t type
631 * turnstile for this proprietor in the hashtable after the removal
633 static struct turnstile
*
634 turnstable_htable_lookup_remove(
635 uintptr_t proprietor
,
636 struct turnstile
**free_turnstile
,
637 turnstile_type_t type
)
639 uint32_t index
= turnstile_hash(proprietor
);
640 assert(index
< ts_htable_buckets
);
641 struct turnstile_htable_bucket
*ts_bucket
;
642 struct turnstile
*ret_turnstile
= TURNSTILE_NULL
;
643 int hash_policy
= turnstile_hash_lock_policy
[type
];
644 bool needs_lock
= !(hash_policy
& TURNSTILE_LOCKED_HASH
);
645 bool irq_safe
= !(hash_policy
& TURNSTILE_IRQ_UNSAFE_HASH
);
648 ts_bucket
= turnstile_get_bucket(index
, type
);
654 turnstile_bucket_lock(ts_bucket
);
657 struct turnstile
*ts
, **prev_tslink
;
658 /* Find the turnstile for the given proprietor in the hashtable */
659 SLIST_FOREACH_PREVPTR(ts
, prev_tslink
, &ts_bucket
->ts_ht_bucket_list
, ts_htable_link
) {
660 if (ts
->ts_proprietor
== proprietor
) {
665 assert(ret_turnstile
!= TURNSTILE_NULL
);
667 /* Check if the turnstile has any turnstiles on its freelist */
668 if (turnstile_freelist_empty(ret_turnstile
)) {
669 /* No turnstiles on the freelist; remove the turnstile from the hashtable and mark it freed */
670 *prev_tslink
= SLIST_NEXT(ret_turnstile
, ts_htable_link
);
671 turnstile_state_remove(ret_turnstile
, TURNSTILE_STATE_HASHTABLE
);
673 turnstile_bucket_unlock(ts_bucket
);
678 *free_turnstile
= ret_turnstile
;
679 return TURNSTILE_NULL
;
682 * Turnstile has free turnstiles on its list; leave the hashtable unchanged
683 * and return the first turnstile in the freelist as the free turnstile
686 turnstile_bucket_unlock(ts_bucket
);
691 *free_turnstile
= turnstile_freelist_remove(ret_turnstile
);
692 return ret_turnstile
;
697 * Name: turnstile_htable_lookup
699 * Description: Lookup the proprietor in the global turnstile hash table.
700 * The routine assumes that the turnstile->proprietor does not change
701 * while the turnstile is in the global hash table.
705 * Arg2: turnstile_type_t type
708 * Turnstile for proprietor in the hash table
710 static struct turnstile
*
711 turnstile_htable_lookup(
712 uintptr_t proprietor
,
713 turnstile_type_t type
)
715 uint32_t index
= turnstile_hash(proprietor
);
716 assert(index
< ts_htable_buckets
);
717 bool kdp_ctx
= !not_in_kdp
;
718 struct turnstile_htable_bucket
*ts_bucket
= turnstile_get_bucket(index
, type
);
719 int hash_policy
= turnstile_hash_lock_policy
[type
];
720 bool needs_lock
= !(hash_policy
& TURNSTILE_LOCKED_HASH
);
721 bool irq_safe
= !(hash_policy
& TURNSTILE_IRQ_UNSAFE_HASH
);
725 if (irq_safe
&& !kdp_ctx
) {
730 if (kdp_turnstile_bucket_is_locked(ts_bucket
)) {
731 /* This should move to TURNSTILE_BUSY once 51725781 is in the build */
732 return TURNSTILE_NULL
;
735 turnstile_bucket_lock(ts_bucket
);
738 struct turnstile
*ts
= TURNSTILE_NULL
;
739 struct turnstile
*ret_turnstile
= TURNSTILE_NULL
;
741 SLIST_FOREACH(ts
, &ts_bucket
->ts_ht_bucket_list
, ts_htable_link
) {
742 if (ts
->ts_proprietor
== proprietor
) {
743 /* Found an entry in the hashtable for this proprietor */
749 if (needs_lock
&& !kdp_ctx
) {
750 turnstile_bucket_unlock(ts_bucket
);
756 return ret_turnstile
;
760 * Name: turnstile_deallocate_queue_invoke
762 * Description: invoke function for the asynchronous turnstile deallocation
765 * Arg1: &turnstile_deallocate_queue
766 * Arg2: a pointer to the turnstile ts_deallocate_link member of a tunrstile to
772 turnstile_deallocate_queue_invoke(mpsc_queue_chain_t e
,
773 __assert_only mpsc_daemon_queue_t dq
)
775 struct turnstile
*ts
;
777 ts
= mpsc_queue_element(e
, struct turnstile
, ts_deallocate_link
);
778 assert(dq
== &turnstile_deallocate_queue
);
779 turnstile_destroy(ts
);
783 * Name: turnstiles_init
785 * Description: Initialize turnstile sub system.
792 turnstiles_init(void)
794 turnstiles_hashtable_init();
796 thread_deallocate_daemon_register_queue(&turnstile_deallocate_queue
,
797 turnstile_deallocate_queue_invoke
);
799 #if DEVELOPMENT || DEBUG
800 queue_init(&turnstiles_list
);
802 /* Initialize turnstile test primitive */
803 tstile_test_prim_init(&test_prim_ts_inline
);
804 tstile_test_prim_init(&test_prim_global_htable
);
805 tstile_test_prim_init(&test_prim_global_ts_kernel
);
806 tstile_test_prim_init(&test_prim_global_ts_kernel_hash
);
811 * Name: turnstile_alloc
813 * Description: Allocate a turnstile.
818 * turnstile on Success.
821 turnstile_alloc(void)
823 struct turnstile
*turnstile
= TURNSTILE_NULL
;
825 turnstile
= zalloc(turnstiles_zone
);
826 turnstile_init(turnstile
);
828 #if DEVELOPMENT || DEBUG
829 /* Add turnstile to global list */
830 global_turnstiles_lock();
831 queue_enter(&turnstiles_list
, turnstile
,
832 struct turnstile
*, ts_global_elm
);
833 global_turnstiles_unlock();
839 * Name: turnstile_init
841 * Description: Initialize the turnstile.
844 * Arg1: turnstile to initialize
849 turnstile_init(struct turnstile
*turnstile
)
853 /* Initialize the waitq */
854 kret
= waitq_init(&turnstile
->ts_waitq
, SYNC_POLICY_DISABLE_IRQ
| SYNC_POLICY_REVERSED
|
855 SYNC_POLICY_TURNSTILE
);
856 assert(kret
== KERN_SUCCESS
);
858 turnstile
->ts_inheritor
= TURNSTILE_INHERITOR_NULL
;
859 SLIST_INIT(&turnstile
->ts_free_turnstiles
);
860 os_atomic_init(&turnstile
->ts_type_gencount
, 0);
861 turnstile_set_type_and_increment_gencount(turnstile
, TURNSTILE_NONE
);
862 turnstile_state_init(turnstile
, TURNSTILE_STATE_THREAD
);
863 os_ref_init_count(&turnstile
->ts_refcount
, &turnstile_refgrp
, 1);
864 turnstile
->ts_proprietor
= TURNSTILE_PROPRIETOR_NULL
;
865 turnstile
->ts_priority
= 0;
866 turnstile
->ts_inheritor_flags
= TURNSTILE_UPDATE_FLAGS_NONE
;
867 turnstile
->ts_port_ref
= 0;
868 priority_queue_init(&turnstile
->ts_inheritor_queue
);
870 #if DEVELOPMENT || DEBUG
871 turnstile
->ts_thread
= current_thread();
872 turnstile
->ts_prev_thread
= NULL
;
877 * Name: turnstile_reference
879 * Description: Take a reference on the turnstile.
886 turnstile_reference(struct turnstile
*turnstile
)
888 if (turnstile
== TURNSTILE_NULL
) {
891 os_ref_retain(&turnstile
->ts_refcount
);
895 * Name: turnstile_deallocate
897 * Description: Drop a reference on the turnstile.
898 * Destroy the turnstile if the last ref.
905 turnstile_deallocate(struct turnstile
*turnstile
)
907 if (turnstile
== TURNSTILE_NULL
) {
911 if (__improbable(os_ref_release(&turnstile
->ts_refcount
) == 0)) {
912 turnstile_destroy(turnstile
);
917 * Name: turnstile_deallocate_safe
919 * Description: Drop a reference on the turnstile safely without triggering zfree.
926 turnstile_deallocate_safe(struct turnstile
*turnstile
)
928 if (turnstile
== TURNSTILE_NULL
) {
932 if (__improbable(os_ref_release(&turnstile
->ts_refcount
) == 0)) {
933 mpsc_daemon_enqueue(&turnstile_deallocate_queue
,
934 &turnstile
->ts_deallocate_link
, MPSC_QUEUE_DISABLE_PREEMPTION
);
939 * Name: turnstile_destroy
941 * Description: Deallocates the turnstile.
949 turnstile_destroy(struct turnstile
*turnstile
)
951 /* destroy the waitq */
952 waitq_deinit(&turnstile
->ts_waitq
);
954 assert(turnstile
->ts_inheritor
== TURNSTILE_INHERITOR_NULL
);
955 assert(SLIST_EMPTY(&turnstile
->ts_free_turnstiles
));
956 assert(turnstile
->ts_state
& TURNSTILE_STATE_THREAD
);
957 #if DEVELOPMENT || DEBUG
958 /* Remove turnstile from global list */
959 global_turnstiles_lock();
960 queue_remove(&turnstiles_list
, turnstile
,
961 struct turnstile
*, ts_global_elm
);
962 global_turnstiles_unlock();
964 zfree(turnstiles_zone
, turnstile
);
968 * Name: turnstile_prepare
970 * Description: Transfer current thread's turnstile to primitive or it's free turnstile list.
971 * Function is called holding the interlock (spinlock) of the primitive.
972 * The turnstile returned by this function is safe to use untill the thread calls turnstile_complete.
973 * When no turnstile is provided explicitly, the calling thread will not have a turnstile attached to
974 * it untill it calls turnstile_complete.
978 * Arg2: pointer in primitive struct to store turnstile
979 * Arg3: turnstile to use instead of taking it from thread.
980 * Arg4: type of primitive
987 uintptr_t proprietor
,
988 struct turnstile
**tstore
,
989 struct turnstile
*turnstile
,
990 turnstile_type_t type
)
992 thread_t thread
= current_thread();
993 struct turnstile
*ret_turnstile
= TURNSTILE_NULL
;
994 struct turnstile
*thread_turnstile
= turnstile
;
996 /* Get the thread's turnstile if no turnstile provided */
997 if (thread_turnstile
== TURNSTILE_NULL
) {
998 thread_turnstile
= thread
->turnstile
;
999 assert(thread_turnstile
!= TURNSTILE_NULL
);
1000 assert(thread
->inheritor
== NULL
);
1001 thread
->turnstile
= TURNSTILE_NULL
;
1004 /* Prepare the thread turnstile to be the primitive turnstile */
1005 SLIST_INIT(&thread_turnstile
->ts_free_turnstiles
);
1006 turnstile_set_type_and_increment_gencount(thread_turnstile
, type
);
1007 thread_turnstile
->ts_inheritor
= TURNSTILE_INHERITOR_NULL
;
1008 thread_turnstile
->ts_proprietor
= proprietor
;
1009 turnstile_state_remove(thread_turnstile
, TURNSTILE_STATE_THREAD
);
1011 thread_turnstile
->ts_priority
= 0;
1012 #if DEVELOPMENT || DEBUG
1013 thread_turnstile
->ts_prev_thread
= thread_turnstile
->ts_thread
;
1014 thread_turnstile
->ts_thread
= NULL
;
1017 if (tstore
!= NULL
) {
1019 * If the primitive stores the turnstile,
1020 * If there is already a turnstile, put the thread_turnstile if the primitive currently does not have a
1022 * Else, add the thread turnstile to freelist of the primitive turnstile.
1024 ret_turnstile
= *tstore
;
1025 if (*tstore
== TURNSTILE_NULL
) {
1026 turnstile_state_add(thread_turnstile
, TURNSTILE_STATE_PROPRIETOR
);
1027 *tstore
= thread_turnstile
;
1028 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1029 (TURNSTILE_CODE(TURNSTILE_FREELIST_OPERATIONS
, (TURNSTILE_PREPARE
))) | DBG_FUNC_NONE
,
1030 VM_KERNEL_UNSLIDE_OR_PERM(thread_turnstile
),
1031 VM_KERNEL_UNSLIDE_OR_PERM(proprietor
),
1032 turnstile_get_type(thread_turnstile
), 0, 0);
1034 turnstile_freelist_insert(ret_turnstile
, thread_turnstile
);
1036 ret_turnstile
= *tstore
;
1039 * Lookup the primitive in the turnstile hash table and see if it already has an entry.
1041 ret_turnstile
= turnstile_htable_lookup_add(proprietor
, thread_turnstile
, type
);
1042 if (ret_turnstile
== NULL
) {
1043 ret_turnstile
= thread_turnstile
;
1044 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1045 (TURNSTILE_CODE(TURNSTILE_FREELIST_OPERATIONS
, (TURNSTILE_PREPARE
))) | DBG_FUNC_NONE
,
1046 VM_KERNEL_UNSLIDE_OR_PERM(thread_turnstile
),
1047 VM_KERNEL_UNSLIDE_OR_PERM(proprietor
),
1048 turnstile_get_type(thread_turnstile
), 0, 0);
1052 return ret_turnstile
;
1056 * Name: turnstile_complete
1058 * Description: Transfer the primitive's turnstile or from it's freelist to current thread.
1059 * Current thread will have a turnstile attached to it after this call.
1063 * Arg2: pointer in primitive struct to update turnstile
1064 * Arg3: pointer to store the returned turnstile instead of attaching it to thread
1065 * Arg4: type of primitive
1072 uintptr_t proprietor
,
1073 struct turnstile
**tstore
,
1074 struct turnstile
**out_turnstile
,
1075 turnstile_type_t type
)
1077 thread_t thread
= current_thread();
1078 struct turnstile
*primitive_turnstile
= TURNSTILE_NULL
;
1079 struct turnstile
*thread_turnstile
= TURNSTILE_NULL
;
1081 assert(thread
->inheritor
== NULL
);
1083 if (tstore
!= NULL
) {
1085 * If the primitive stores the turnstile, check if the primitive turnstile
1086 * has any turnstiles on its freelist.
1088 assert(*tstore
!= TURNSTILE_NULL
);
1089 if (turnstile_freelist_empty(*tstore
)) {
1090 /* Last turnstile scenario; remove the primitive->turnstile */
1091 thread_turnstile
= *tstore
;
1092 *tstore
= TURNSTILE_NULL
;
1093 turnstile_state_remove(thread_turnstile
, TURNSTILE_STATE_PROPRIETOR
);
1095 /* Freelist has turnstiles; remove one from the freelist */
1096 thread_turnstile
= turnstile_freelist_remove(*tstore
);
1098 primitive_turnstile
= *tstore
;
1100 /* Use the global hash to find and remove a turnstile */
1101 primitive_turnstile
= turnstable_htable_lookup_remove(proprietor
, &thread_turnstile
, type
);
1103 if (primitive_turnstile
== NULL
) {
1105 * Primitive no longer has a turnstile associated with it, thread_turnstile
1106 * was the last turnstile attached to primitive, clear out the inheritor and
1107 * set the old inheritor for turnstile cleanup.
1109 if (thread_turnstile
->ts_inheritor
!= TURNSTILE_INHERITOR_NULL
) {
1110 turnstile_update_inheritor(thread_turnstile
, TURNSTILE_INHERITOR_NULL
,
1111 (TURNSTILE_IMMEDIATE_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
1113 * old inheritor is set in curret thread and its priority propagation
1114 * will happen in turnstile cleanup call
1117 assert(thread_turnstile
->ts_inheritor
== TURNSTILE_INHERITOR_NULL
);
1119 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1120 (TURNSTILE_CODE(TURNSTILE_FREELIST_OPERATIONS
, (TURNSTILE_COMPLETE
))) | DBG_FUNC_NONE
,
1121 VM_KERNEL_UNSLIDE_OR_PERM(thread_turnstile
),
1122 VM_KERNEL_UNSLIDE_OR_PERM(proprietor
),
1123 turnstile_get_type(thread_turnstile
), 0, 0);
1125 /* If primitive's turnstile needs priority update, set it up for turnstile cleanup */
1126 if (turnstile_recompute_priority(primitive_turnstile
)) {
1127 turnstile_reference(primitive_turnstile
);
1128 thread
->inheritor
= primitive_turnstile
;
1129 thread
->inheritor_flags
= (TURNSTILE_INHERITOR_TURNSTILE
|
1130 TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE
);
1134 turnstile_set_type_and_increment_gencount(thread_turnstile
, TURNSTILE_NONE
);
1135 #if DEVELOPMENT || DEBUG
1136 thread_turnstile
->ts_prev_thread
= NULL
;
1137 thread_turnstile
->ts_thread
= thread
;
1140 turnstile_state_add(thread_turnstile
, TURNSTILE_STATE_THREAD
);
1141 if (out_turnstile
== NULL
) {
1142 /* Prepare the turnstile to become the thread's turnstile */
1143 thread
->turnstile
= thread_turnstile
;
1145 *out_turnstile
= thread_turnstile
;
1151 * Name: turnstile_kernel_update_inheritor_on_wake_locked
1153 * Description: Set thread as the inheritor of the turnstile and
1154 * boost the inheritor.
1157 * Arg2: new_inheritor
1160 * Called with turnstile locked
1163 turnstile_kernel_update_inheritor_on_wake_locked(
1164 struct turnstile
*turnstile
,
1165 turnstile_inheritor_t new_inheritor
,
1166 turnstile_update_flags_t flags __assert_only
)
1168 /* for now only kernel primitives are allowed to call this function */
1169 __assert_only turnstile_promote_policy_t policy
=
1170 turnstile_promote_policy
[turnstile_get_type(turnstile
)];
1172 assert(flags
& TURNSTILE_INHERITOR_THREAD
);
1173 assert(policy
== TURNSTILE_KERNEL_PROMOTE
|| policy
== TURNSTILE_USER_PROMOTE
);
1175 turnstile_stash_inheritor((thread_t
)new_inheritor
, TURNSTILE_INHERITOR_THREAD
);
1177 * new_inheritor has just been removed from the turnstile waitq,
1178 * the turnstile new priority needs to be recomputed so that
1179 * when new_inheritor will become this turnstile inheritor can
1180 * inherit the correct priority.
1182 turnstile_recompute_priority_locked(turnstile
);
1183 turnstile_update_inheritor_locked(turnstile
);
1187 * Name: turnstile_update_inheritor_locked
1189 * Description: Update the inheritor of the turnstile and boost the
1190 * inheritor, called with turnstile locked.
1194 * Implicit arg: new inheritor value is stashed in current thread's struct
1197 * old inheritor reference is returned on current thread's struct.
1200 turnstile_update_inheritor_locked(
1201 struct turnstile
*turnstile
)
1203 turnstile_inheritor_t old_inheritor
= turnstile
->ts_inheritor
;
1204 turnstile_update_flags_t old_inheritor_flags
= turnstile
->ts_inheritor_flags
;
1205 thread_t thread
= current_thread();
1206 boolean_t old_inheritor_needs_update
= FALSE
;
1207 boolean_t new_inheritor_needs_update
= FALSE
;
1208 turnstile_stats_update_flags_t tsu_flags
=
1209 turnstile_get_update_flags_for_above_UI_pri_change(turnstile
);
1211 assert(waitq_held(&turnstile
->ts_waitq
));
1214 * Get the new inheritor value from current thread's
1215 * struct, the value was stashed by turnstile_update_inheritor
1217 turnstile_inheritor_t new_inheritor
= thread
->inheritor
;
1218 turnstile_update_flags_t new_inheritor_flags
= thread
->inheritor_flags
;
1220 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
1221 case TURNSTILE_USER_PROMOTE
:
1222 case TURNSTILE_USER_IPC_PROMOTE
:
1224 case TURNSTILE_KERNEL_PROMOTE
:
1225 /* some sanity checks, turnstile kernel can push just between threads */
1226 if (old_inheritor
) {
1227 assert(old_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
1230 if (new_inheritor
) {
1231 assert(new_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
1236 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
1239 /* Check if update is needed */
1240 if (old_inheritor
== new_inheritor
&& old_inheritor
== NULL
) {
1244 if (old_inheritor
== new_inheritor
) {
1245 if (new_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
1246 thread_t thread_inheritor
= (thread_t
)new_inheritor
;
1248 assert(old_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
1250 /* adjust turnstile position in the thread's inheritor list */
1251 new_inheritor_needs_update
= thread_update_turnstile_promotion(
1252 thread_inheritor
, turnstile
);
1253 } else if (new_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
1254 struct turnstile
*inheritor_turnstile
= new_inheritor
;
1256 assert(old_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
);
1258 new_inheritor_needs_update
= turnstile_update_turnstile_promotion(
1259 inheritor_turnstile
, turnstile
);
1260 } else if (new_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
1262 * When we are still picking "WORKQ" then possible racing
1263 * updates will call redrive through their own propagation
1264 * and we don't need to update anything here.
1266 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED
|
1267 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
, turnstile
);
1269 panic("Inheritor flags lost along the way");
1272 /* Update turnstile stats */
1273 if (!new_inheritor_needs_update
) {
1274 turnstile_stats_update(1, TSU_PRI_PROPAGATION
|
1275 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
| tsu_flags
, turnstile
);
1280 if (old_inheritor
!= NULL
) {
1281 if (old_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
1282 thread_t thread_inheritor
= (thread_t
)old_inheritor
;
1284 /* remove turnstile from thread's inheritor list */
1285 old_inheritor_needs_update
= thread_remove_turnstile_promotion(thread_inheritor
, turnstile
);
1286 } else if (old_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
1287 struct turnstile
*old_turnstile
= old_inheritor
;
1289 old_inheritor_needs_update
= turnstile_remove_turnstile_promotion(
1290 old_turnstile
, turnstile
);
1291 } else if (old_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
1293 * We don't need to do anything when the push was WORKQ
1294 * because nothing is pushed on in the first place.
1296 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED
|
1297 TSU_TURNSTILE_ARG
, turnstile
);
1299 panic("Inheritor flags lost along the way");
1301 /* Update turnstile stats */
1302 if (!old_inheritor_needs_update
) {
1303 turnstile_stats_update(1, TSU_PRI_PROPAGATION
| TSU_TURNSTILE_ARG
,
1308 if (new_inheritor
!= NULL
) {
1309 if (new_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
1310 thread_t thread_inheritor
= (thread_t
)new_inheritor
;
1312 assert(new_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
1313 /* add turnstile to thread's inheritor list */
1314 new_inheritor_needs_update
= thread_add_turnstile_promotion(
1315 thread_inheritor
, turnstile
);
1316 } else if (new_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
1317 struct turnstile
*new_turnstile
= new_inheritor
;
1319 new_inheritor_needs_update
= turnstile_add_turnstile_promotion(
1320 new_turnstile
, turnstile
);
1321 } else if (new_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
1322 struct workqueue
*wq_inheritor
= new_inheritor
;
1324 new_inheritor_needs_update
= workq_add_turnstile_promotion(
1325 wq_inheritor
, turnstile
);
1326 if (!new_inheritor_needs_update
) {
1327 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED
|
1328 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
, turnstile
);
1331 panic("Inheritor flags lost along the way");
1333 /* Update turnstile stats */
1334 if (!new_inheritor_needs_update
) {
1335 turnstile_stats_update(1, TSU_PRI_PROPAGATION
|
1336 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
| tsu_flags
, turnstile
);
1341 if (old_inheritor_needs_update
) {
1342 old_inheritor_flags
|= TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE
;
1346 * If new inheritor needs priority updated, then set TURNSTILE_NEEDS_PRI_UPDATE
1347 * on the old_inheritor_flags which will be copied to the thread.
1349 if (new_inheritor_needs_update
) {
1350 old_inheritor_flags
|= TURNSTILE_NEEDS_PRI_UPDATE
;
1353 turnstile
->ts_inheritor
= new_inheritor
;
1354 turnstile
->ts_inheritor_flags
= new_inheritor_flags
;
1355 thread
->inheritor
= old_inheritor
;
1356 thread
->inheritor_flags
= old_inheritor_flags
;
1361 * Name: turnstile_stash_inheritor
1363 * Description: Save the new inheritor reference of the turnstile on the
1364 * current thread. It will take a thread reference on the inheritor.
1365 * Called with the interlock of the primitive held.
1372 * old inheritor reference is stashed on current thread's struct.
1375 turnstile_stash_inheritor(
1376 turnstile_inheritor_t new_inheritor
,
1377 turnstile_update_flags_t flags
)
1379 thread_t thread
= current_thread();
1382 * Set the inheritor on calling thread struct, no need
1383 * to take the turnstile waitq lock since the inheritor
1384 * is protected by the primitive's interlock
1386 assert(thread
->inheritor
== TURNSTILE_INHERITOR_NULL
);
1387 thread
->inheritor
= new_inheritor
;
1388 thread
->inheritor_flags
= TURNSTILE_UPDATE_FLAGS_NONE
;
1389 if (new_inheritor
== TURNSTILE_INHERITOR_NULL
) {
1390 /* nothing to retain or remember */
1391 } else if (flags
& TURNSTILE_INHERITOR_THREAD
) {
1392 thread
->inheritor_flags
|= TURNSTILE_INHERITOR_THREAD
;
1393 thread_reference((thread_t
)new_inheritor
);
1394 } else if (flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
1395 thread
->inheritor_flags
|= TURNSTILE_INHERITOR_TURNSTILE
;
1396 turnstile_reference((struct turnstile
*)new_inheritor
);
1397 } else if (flags
& TURNSTILE_INHERITOR_WORKQ
) {
1398 thread
->inheritor_flags
|= TURNSTILE_INHERITOR_WORKQ
;
1399 workq_reference((struct workqueue
*)new_inheritor
);
1401 panic("Missing type in flags (%x) for inheritor (%p)", flags
,
1407 * Name: turnstile_update_inheritor
1409 * Description: Update the inheritor of the turnstile and boost the
1410 * inheritor. It will take a thread reference on the inheritor.
1411 * Called with the interlock of the primitive held.
1416 * Arg3: flags - TURNSTILE_DELAYED_UPDATE - update will happen later in assert_wait
1419 * old inheritor reference is stashed on current thread's struct.
1422 turnstile_update_inheritor(
1423 struct turnstile
*turnstile
,
1424 turnstile_inheritor_t new_inheritor
,
1425 turnstile_update_flags_t flags
)
1429 turnstile_stash_inheritor(new_inheritor
, flags
);
1431 /* Do not perform the update if delayed update is specified */
1432 if (flags
& TURNSTILE_DELAYED_UPDATE
) {
1436 /* lock the turnstile waitq */
1438 waitq_lock(&turnstile
->ts_waitq
);
1440 turnstile_update_inheritor_locked(turnstile
);
1442 waitq_unlock(&turnstile
->ts_waitq
);
1450 * Name: turnstile_need_thread_promotion_update
1452 * Description: Check if thread's place in the turnstile waitq needs to be updated.
1454 * Arg1: dst turnstile
1457 * Returns: TRUE: if turnstile_update_thread_promotion_locked needs to be called.
1460 * Condition: thread locked.
1463 turnstile_need_thread_promotion_update(
1464 struct turnstile
*dst_turnstile
,
1467 int thread_link_priority
;
1468 boolean_t needs_update
= FALSE
;
1470 thread_link_priority
= priority_queue_entry_sched_pri(
1471 &dst_turnstile
->ts_waitq
.waitq_prio_queue
,
1472 &thread
->wait_prioq_links
);
1474 int priority
= turnstile_compute_thread_push(dst_turnstile
, thread
);
1476 needs_update
= (thread_link_priority
== priority
) ? FALSE
: TRUE
;
1478 return needs_update
;
1482 * Name: turnstile_priority_queue_update_entry_key
1484 * Description: Updates the priority of an entry in a priority queue
1486 * Arg1: a turnstile/thread/... priority queue
1487 * Arg2: the element to change the priority of
1488 * Arg3: the new priority
1490 * Returns: whether the maximum priority of the queue changed.
1493 turnstile_priority_queue_update_entry_key(struct priority_queue_sched_max
*q
,
1494 priority_queue_entry_sched_t elt
, priority_queue_key_t pri
)
1496 priority_queue_key_t old_key
= priority_queue_max_sched_pri(q
);
1498 if (priority_queue_entry_sched_pri(q
, elt
) < pri
) {
1499 priority_queue_entry_set_sched_pri(q
, elt
, pri
, false);
1500 if (priority_queue_entry_increased(q
, elt
)) {
1501 return old_key
!= priority_queue_max_sched_pri(q
);
1503 } else if (priority_queue_entry_sched_pri(q
, elt
) > pri
) {
1504 priority_queue_entry_set_sched_pri(q
, elt
, pri
, false);
1505 if (priority_queue_entry_decreased(q
, elt
)) {
1506 return old_key
!= priority_queue_max_sched_pri(q
);
1514 * Name: turnstile_update_thread_promotion_locked
1516 * Description: Update dst turnstile's inheritor link since one of the waiting
1517 * thread's priority has changed.
1519 * Arg1: dst turnstile
1522 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
1523 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
1525 * Condition: dst turnstile and thread are locked.
1528 turnstile_update_thread_promotion_locked(
1529 struct turnstile
*dst_turnstile
,
1532 int thread_link_priority
;
1534 int priority
= turnstile_compute_thread_push(dst_turnstile
, thread
);
1536 thread_link_priority
= priority_queue_entry_sched_pri(
1537 &dst_turnstile
->ts_waitq
.waitq_prio_queue
,
1538 &thread
->wait_prioq_links
);
1540 if (priority
!= thread_link_priority
) {
1541 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1542 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (THREAD_MOVED_IN_TURNSTILE_WAITQ
))) | DBG_FUNC_NONE
,
1543 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile
),
1546 thread_link_priority
, 0);
1549 if (!turnstile_priority_queue_update_entry_key(
1550 &dst_turnstile
->ts_waitq
.waitq_prio_queue
,
1551 &thread
->wait_prioq_links
, (priority_queue_key_t
)priority
)) {
1555 /* Update dst turnstile's priority */
1556 return turnstile_recompute_priority_locked(dst_turnstile
);
1560 * Name: thread_add_turnstile_promotion
1562 * Description: Add a turnstile to thread's inheritor list and update thread's priority.
1567 * Returns: TRUE: if the thread's priority has changed and needs propagation.
1568 * FALSE: if the thread's priority did not change or it does not need propagation.
1570 * Condition: turnstile locked.
1573 thread_add_turnstile_promotion(
1575 struct turnstile
*turnstile
)
1577 boolean_t needs_update
= FALSE
;
1579 /* Update the pairing heap */
1580 thread_lock(thread
);
1582 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1583 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_ADDED_TO_THREAD_HEAP
))) | DBG_FUNC_NONE
,
1585 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
1586 turnstile
->ts_priority
, 0, 0);
1588 priority_queue_entry_init(&turnstile
->ts_inheritor_links
);
1590 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
1591 case TURNSTILE_USER_PROMOTE
:
1592 case TURNSTILE_USER_IPC_PROMOTE
:
1594 priority_queue_entry_set_sched_pri(&thread
->base_inheritor_queue
,
1595 &turnstile
->ts_inheritor_links
, turnstile
->ts_priority
, false);
1596 if (priority_queue_insert(&thread
->base_inheritor_queue
,
1597 &turnstile
->ts_inheritor_links
)) {
1598 needs_update
= thread_recompute_user_promotion_locked(thread
);
1602 case TURNSTILE_KERNEL_PROMOTE
:
1604 priority_queue_entry_set_sched_pri(&thread
->sched_inheritor_queue
,
1605 &turnstile
->ts_inheritor_links
, turnstile
->ts_priority
, false);
1606 if (priority_queue_insert(&thread
->sched_inheritor_queue
,
1607 &turnstile
->ts_inheritor_links
)) {
1608 needs_update
= thread_recompute_kernel_promotion_locked(thread
);
1613 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
1616 /* Update turnstile stats */
1617 if (!needs_update
) {
1618 turnstile_stats_update(1,
1619 thread_get_update_flags_for_turnstile_propagation_stoppage(thread
) |
1620 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
,
1624 thread_unlock(thread
);
1626 return needs_update
;
1630 * Name: thread_remove_turnstile_promotion
1632 * Description: Remove turnstile from thread's inheritor list and update thread's priority.
1637 * Returns: TRUE: if the thread's priority has changed and needs propagation.
1638 * FALSE: if the thread's priority did not change or it does not need propagation.
1640 * Condition: turnstile locked.
1643 thread_remove_turnstile_promotion(
1645 struct turnstile
*turnstile
)
1647 boolean_t needs_update
= FALSE
;
1649 thread_lock(thread
);
1651 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1652 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_REMOVED_FROM_THREAD_HEAP
))) | DBG_FUNC_NONE
,
1654 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
1657 /* Update the pairing heap */
1659 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
1660 case TURNSTILE_USER_PROMOTE
:
1661 case TURNSTILE_USER_IPC_PROMOTE
:
1662 if (priority_queue_remove(&thread
->base_inheritor_queue
,
1663 &turnstile
->ts_inheritor_links
)) {
1664 needs_update
= thread_recompute_user_promotion_locked(thread
);
1667 case TURNSTILE_KERNEL_PROMOTE
:
1668 if (priority_queue_remove(&thread
->sched_inheritor_queue
,
1669 &turnstile
->ts_inheritor_links
)) {
1670 needs_update
= thread_recompute_kernel_promotion_locked(thread
);
1674 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
1677 /* Update turnstile stats */
1678 if (!needs_update
) {
1679 turnstile_stats_update(1,
1680 thread_get_update_flags_for_turnstile_propagation_stoppage(thread
) | TSU_TURNSTILE_ARG
,
1684 thread_unlock(thread
);
1686 return needs_update
;
1690 * Name: thread_needs_turnstile_promotion_update
1692 * Description: Check if turnstile position in thread's inheritor list needs to be updated.
1697 * Returns: TRUE: if thread_update_turnstile_promotion needs to be called.
1700 * Condition: turnstile locked.
1703 thread_needs_turnstile_promotion_update(
1704 thread_t thread __assert_only
,
1705 struct turnstile
*turnstile
)
1707 boolean_t needs_update
= FALSE
;
1708 int turnstile_link_priority
= 0;
1710 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
1711 case TURNSTILE_USER_PROMOTE
:
1712 case TURNSTILE_USER_IPC_PROMOTE
:
1713 turnstile_link_priority
= priority_queue_entry_sched_pri(
1714 &thread
->base_inheritor_queue
,
1715 &turnstile
->ts_inheritor_links
);
1717 case TURNSTILE_KERNEL_PROMOTE
:
1718 turnstile_link_priority
= priority_queue_entry_sched_pri(
1719 &thread
->sched_inheritor_queue
,
1720 &turnstile
->ts_inheritor_links
);
1723 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
1726 needs_update
= (turnstile_link_priority
== turnstile
->ts_priority
) ? FALSE
: TRUE
;
1727 return needs_update
;
1731 * Name: thread_update_turnstile_promotion_locked
1733 * Description: Update turnstile position in thread's inheritor list and update thread's priority.
1738 * Returns: TRUE: if the thread's priority has changed and needs propagation.
1739 * FALSE: if the thread's priority did not change or it does not need propagation.
1741 * Condition: turnstile and thread are locked.
1744 thread_update_turnstile_promotion_locked(
1746 struct turnstile
*turnstile
)
1748 boolean_t needs_update
= FALSE
;
1749 int turnstile_link_priority
= 0;
1751 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
1752 case TURNSTILE_USER_PROMOTE
:
1753 case TURNSTILE_USER_IPC_PROMOTE
:
1754 turnstile_link_priority
= priority_queue_entry_sched_pri(
1755 &thread
->base_inheritor_queue
,
1756 &turnstile
->ts_inheritor_links
);
1758 if (turnstile_priority_queue_update_entry_key(&(thread
->base_inheritor_queue
),
1759 &turnstile
->ts_inheritor_links
, turnstile
->ts_priority
)) {
1760 needs_update
= thread_recompute_user_promotion_locked(thread
);
1763 case TURNSTILE_KERNEL_PROMOTE
:
1764 turnstile_link_priority
= priority_queue_entry_sched_pri(
1765 &thread
->sched_inheritor_queue
,
1766 &turnstile
->ts_inheritor_links
);
1768 if (turnstile_priority_queue_update_entry_key(&(thread
->sched_inheritor_queue
),
1769 &turnstile
->ts_inheritor_links
, turnstile
->ts_priority
)) {
1770 needs_update
= thread_recompute_kernel_promotion_locked(thread
);
1774 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
1777 if (turnstile
->ts_priority
!= turnstile_link_priority
) {
1778 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1779 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_MOVED_IN_THREAD_HEAP
))) | DBG_FUNC_NONE
,
1781 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
1782 turnstile
->ts_priority
,
1783 turnstile_link_priority
, 0);
1786 return needs_update
;
1791 * Name: thread_update_turnstile_promotion
1793 * Description: Update turnstile position in thread's inheritor list and update thread's priority.
1798 * Returns: TRUE: if the thread's priority has changed and needs propagation.
1799 * FALSE: if the thread's priority did not change or it does not need propagation.
1801 * Condition: turnstile locked.
1804 thread_update_turnstile_promotion(
1806 struct turnstile
*turnstile
)
1808 /* Before grabbing the thread lock, check if update is needed */
1809 boolean_t needs_update
= thread_needs_turnstile_promotion_update(thread
, turnstile
);
1811 if (!needs_update
) {
1812 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED
|
1813 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
, turnstile
);
1814 return needs_update
;
1817 thread_lock(thread
);
1819 /* Update the pairing heap */
1820 needs_update
= thread_update_turnstile_promotion_locked(thread
, turnstile
);
1822 /* Update turnstile stats */
1823 if (!needs_update
) {
1824 turnstile_stats_update(1,
1825 thread_get_update_flags_for_turnstile_propagation_stoppage(thread
) |
1826 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
,
1830 thread_unlock(thread
);
1832 return needs_update
;
1837 * Name: thread_get_inheritor_turnstile_sched_priority
1839 * Description: Get the max sched priority of all the inheritor turnstiles
1843 * Returns: Max sched priority of all the inheritor turnstiles.
1845 * Condition: thread locked
1848 thread_get_inheritor_turnstile_sched_priority(thread_t thread
)
1850 struct turnstile
*max_turnstile
;
1852 max_turnstile
= priority_queue_max(&thread
->sched_inheritor_queue
,
1853 struct turnstile
, ts_inheritor_links
);
1855 if (max_turnstile
) {
1856 return priority_queue_entry_sched_pri(
1857 &thread
->sched_inheritor_queue
,
1858 &max_turnstile
->ts_inheritor_links
);
1865 * Name: thread_get_inheritor_turnstile_base_priority
1867 * Description: Get the max base priority of all the inheritor turnstiles
1871 * Returns: Max base priority of all the inheritor turnstiles.
1873 * Condition: thread locked
1876 thread_get_inheritor_turnstile_base_priority(thread_t thread
)
1878 struct turnstile
*max_turnstile
;
1880 max_turnstile
= priority_queue_max(&thread
->base_inheritor_queue
,
1881 struct turnstile
, ts_inheritor_links
);
1883 if (max_turnstile
) {
1884 return priority_queue_entry_sched_pri(
1885 &thread
->base_inheritor_queue
,
1886 &max_turnstile
->ts_inheritor_links
);
1894 * Name: thread_get_waiting_turnstile
1896 * Description: Get the turnstile if the thread is waiting on a turnstile.
1900 * Returns: turnstile: if the thread is blocked on a turnstile.
1901 * TURNSTILE_NULL: otherwise.
1903 * Condition: thread locked.
1906 thread_get_waiting_turnstile(thread_t thread
)
1908 struct turnstile
*turnstile
= TURNSTILE_NULL
;
1909 struct waitq
*waitq
= thread
->waitq
;
1911 /* Check if the thread is on a waitq */
1912 if (waitq
== NULL
) {
1916 if (waitq_is_turnstile_proxy(waitq
)) {
1917 return waitq
->waitq_ts
;
1920 /* Check if the waitq is a turnstile queue */
1921 if (waitq_is_turnstile_queue(waitq
)) {
1922 turnstile
= waitq_to_turnstile(waitq
);
1928 * Name: turnstile_lookup_by_proprietor
1930 * Description: Get turnstile for a proprietor from global
1934 * Arg2: turnstile_type_t type
1936 * Returns: turnstile: if the proprietor has a turnstile.
1937 * TURNSTILE_NULL: otherwise.
1939 * Condition: proprietor interlock held.
1942 turnstile_lookup_by_proprietor(uintptr_t proprietor
, turnstile_type_t type
)
1944 return turnstile_htable_lookup(proprietor
, type
);
1948 * Name: thread_get_update_flags_for_turnstile_propagation_stoppage
1950 * Description: Get the turnstile stats flags based on the thread wait status.
1954 * Returns: TSU_THREAD_RUNNABLE: if the thread is runnable.
1955 * TSU_NO_TURNSTILE: if thread waiting on a regular waitq.
1956 * TSU_NO_PRI_CHANGE_NEEDED: otherwise.
1958 * Condition: thread locked.
1960 static turnstile_stats_update_flags_t
1961 thread_get_update_flags_for_turnstile_propagation_stoppage(thread_t thread
)
1963 struct waitq
*waitq
= thread
->waitq
;
1965 /* Check if the thread is on a waitq */
1966 if (waitq
== NULL
) {
1967 return TSU_THREAD_RUNNABLE
;
1970 /* Get the safeq if the waitq is a port queue */
1971 if (waitq_is_turnstile_proxy(waitq
)) {
1972 if (waitq
->waitq_ts
) {
1973 return TSU_NO_PRI_CHANGE_NEEDED
;
1975 return TSU_NO_TURNSTILE
;
1978 /* Check if the waitq is a turnstile queue */
1979 if (!waitq_is_turnstile_queue(waitq
)) {
1980 return TSU_NO_TURNSTILE
;
1983 /* Thread blocked on turnstile waitq but no propagation needed */
1984 return TSU_NO_PRI_CHANGE_NEEDED
;
1989 * Name: turnstile_get_update_flags_for_above_UI_pri_change
1991 * Description: Get the turnstile stats flags based on the turnstile priority.
1995 * Returns: TSU_ABOVE_UI_PRI_CHANGE: if turnstile priority is above 47 and it is not an ulock.
1996 * TSU_FLAGS_NONE: otherwise.
1998 * Condition: turnstile locked.
2000 static turnstile_stats_update_flags_t
2001 turnstile_get_update_flags_for_above_UI_pri_change(struct turnstile
*turnstile
)
2003 if (turnstile
->ts_priority
>
2004 (thread_qos_policy_params
.qos_pri
[THREAD_QOS_USER_INTERACTIVE
] + 1) &&
2005 turnstile_get_type(turnstile
) != TURNSTILE_ULOCK
) {
2006 return TSU_ABOVE_UI_PRI_CHANGE
;
2009 return TSU_FLAGS_NONE
;
2014 * Name: workq_add_turnstile_promotion
2016 * Description: Connect the workqueue turnstile to the workqueue as a fake
2022 * Condition: turnstile locked.
2025 workq_add_turnstile_promotion(
2026 struct workqueue
*wq_inheritor __unused
,
2027 struct turnstile
*turnstile
)
2030 * If the push is higher than MAXPRI_THROTTLE then the workqueue should
2031 * bring up a thread.
2033 return turnstile
->ts_priority
> MAXPRI_THROTTLE
;
2037 * Name: turnstile_need_turnstile_promotion_update
2039 * Description: Check if turnstile position in turnstile's inheritor list needs to be updated.
2041 * Arg1: dst turnstile
2042 * Arg2: src turnstile
2044 * Returns: TRUE: if turnstile_update_turnstile_promotion needs to be called.
2047 * Condition: src turnstile locked.
2050 turnstile_need_turnstile_promotion_update(
2051 struct turnstile
*dst_turnstile __assert_only
,
2052 struct turnstile
*src_turnstile
)
2054 int src_turnstile_link_priority
;
2055 boolean_t needs_update
= FALSE
;
2057 src_turnstile_link_priority
= priority_queue_entry_sched_pri(
2058 &dst_turnstile
->ts_inheritor_queue
,
2059 &src_turnstile
->ts_inheritor_links
);
2061 needs_update
= (src_turnstile_link_priority
== src_turnstile
->ts_priority
) ? FALSE
: TRUE
;
2062 return needs_update
;
2066 * Name: turnstile_update_turnstile_promotion_locked
2068 * Description: Update dst turnstile's inheritor link since src turnstile's
2069 * promote priority has changed.
2071 * Arg1: dst turnstile
2072 * Arg2: src turnstile
2074 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2075 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2077 * Condition: src and dst turnstile locked.
2080 turnstile_update_turnstile_promotion_locked(
2081 struct turnstile
*dst_turnstile
,
2082 struct turnstile
*src_turnstile
)
2084 int src_turnstile_link_priority
;
2085 src_turnstile_link_priority
= priority_queue_entry_sched_pri(
2086 &dst_turnstile
->ts_inheritor_queue
,
2087 &src_turnstile
->ts_inheritor_links
);
2089 if (src_turnstile
->ts_priority
!= src_turnstile_link_priority
) {
2090 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2091 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_MOVED_IN_TURNSTILE_HEAP
))) | DBG_FUNC_NONE
,
2092 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile
),
2093 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile
),
2094 src_turnstile
->ts_priority
, src_turnstile_link_priority
, 0);
2097 if (!turnstile_priority_queue_update_entry_key(
2098 &dst_turnstile
->ts_inheritor_queue
, &src_turnstile
->ts_inheritor_links
,
2099 src_turnstile
->ts_priority
)) {
2103 /* Update dst turnstile's priority */
2104 return turnstile_recompute_priority_locked(dst_turnstile
);
2108 * Name: turnstile_update_turnstile_promotion
2110 * Description: Update dst turnstile's inheritor link since src turnstile's
2111 * promote priority has changed.
2113 * Arg1: dst turnstile
2114 * Arg2: src turnstile
2116 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2117 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2119 * Condition: src turnstile locked.
2122 turnstile_update_turnstile_promotion(
2123 struct turnstile
*dst_turnstile
,
2124 struct turnstile
*src_turnstile
)
2126 /* Check if update is needed before grabbing the src turnstile lock */
2127 boolean_t needs_update
= turnstile_need_turnstile_promotion_update(dst_turnstile
, src_turnstile
);
2128 if (!needs_update
) {
2129 turnstile_stats_update(1, TSU_NO_PRI_CHANGE_NEEDED
|
2130 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
,
2132 return needs_update
;
2135 /* Update the pairing heap */
2136 waitq_lock(&dst_turnstile
->ts_waitq
);
2137 needs_update
= turnstile_update_turnstile_promotion_locked(dst_turnstile
, src_turnstile
);
2139 /* Update turnstile stats */
2140 if (!needs_update
) {
2141 turnstile_stats_update(1,
2142 (dst_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
2143 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
, src_turnstile
);
2145 waitq_unlock(&dst_turnstile
->ts_waitq
);
2146 return needs_update
;
2150 * Name: turnstile_add_turnstile_promotion
2152 * Description: Add src turnstile to dst turnstile's inheritor link
2153 * and update dst turnstile's priority.
2155 * Arg1: dst turnstile
2156 * Arg2: src turnstile
2158 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2159 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2161 * Condition: src turnstile locked.
2164 turnstile_add_turnstile_promotion(
2165 struct turnstile
*dst_turnstile
,
2166 struct turnstile
*src_turnstile
)
2168 boolean_t needs_update
= FALSE
;
2170 /* Update the pairing heap */
2171 waitq_lock(&dst_turnstile
->ts_waitq
);
2173 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2174 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_ADDED_TO_TURNSTILE_HEAP
))) | DBG_FUNC_NONE
,
2175 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile
),
2176 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile
),
2177 src_turnstile
->ts_priority
, 0, 0);
2179 priority_queue_entry_init(&src_turnstile
->ts_inheritor_links
);
2180 priority_queue_entry_set_sched_pri(&dst_turnstile
->ts_inheritor_queue
,
2181 &src_turnstile
->ts_inheritor_links
, src_turnstile
->ts_priority
, false);
2182 if (priority_queue_insert(&dst_turnstile
->ts_inheritor_queue
,
2183 &src_turnstile
->ts_inheritor_links
)) {
2184 /* Update dst turnstile priority */
2185 needs_update
= turnstile_recompute_priority_locked(dst_turnstile
);
2188 /* Update turnstile stats */
2189 if (!needs_update
) {
2190 turnstile_stats_update(1,
2191 (dst_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
2192 TSU_TURNSTILE_ARG
| TSU_BOOST_ARG
, src_turnstile
);
2195 waitq_unlock(&dst_turnstile
->ts_waitq
);
2196 return needs_update
;
2200 * Name: turnstile_remove_turnstile_promotion
2202 * Description: Remove src turnstile from dst turnstile's inheritor link
2203 * and update dst turnstile's priority.
2205 * Arg1: dst turnstile
2206 * Arg2: src turnstile
2208 * Returns: TRUE: if the dst turnstile priority has changed and needs propagation.
2209 * FALSE: if the dst turnstile priority did not change or it does not need propagation.
2211 * Condition: src turnstile locked.
2214 turnstile_remove_turnstile_promotion(
2215 struct turnstile
*dst_turnstile
,
2216 struct turnstile
*src_turnstile
)
2218 boolean_t needs_update
= FALSE
;
2220 /* Update the pairing heap */
2221 waitq_lock(&dst_turnstile
->ts_waitq
);
2223 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2224 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (TURNSTILE_REMOVED_FROM_TURNSTILE_HEAP
))) | DBG_FUNC_NONE
,
2225 VM_KERNEL_UNSLIDE_OR_PERM(dst_turnstile
),
2226 VM_KERNEL_UNSLIDE_OR_PERM(src_turnstile
),
2229 if (priority_queue_remove(&dst_turnstile
->ts_inheritor_queue
,
2230 &src_turnstile
->ts_inheritor_links
)) {
2231 /* Update dst turnstile priority */
2232 needs_update
= turnstile_recompute_priority_locked(dst_turnstile
);
2235 /* Update turnstile stats */
2236 if (!needs_update
) {
2237 turnstile_stats_update(1,
2238 (dst_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
2239 TSU_TURNSTILE_ARG
, src_turnstile
);
2242 waitq_unlock(&dst_turnstile
->ts_waitq
);
2243 return needs_update
;
2247 * Name: turnstile_compute_thread_push
2249 * Description: Compute the priority at which the thread will push
2255 * Condition: wq locked
2258 turnstile_compute_thread_push(
2259 struct turnstile
*turnstile
,
2263 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
2264 case TURNSTILE_USER_PROMOTE
:
2265 case TURNSTILE_USER_IPC_PROMOTE
:
2266 priority
= thread
->base_pri
;
2268 case TURNSTILE_KERNEL_PROMOTE
:
2270 * Ideally this should be policy based
2271 * according to the turnstile type.
2273 * The priority with which each thread pushes on
2274 * a primitive should be primitive dependent.
2276 priority
= thread
->sched_pri
;
2277 priority
= MAX(priority
, thread
->base_pri
);
2278 priority
= MAX(priority
, BASEPRI_DEFAULT
);
2279 priority
= MIN(priority
, MAXPRI_PROMOTE
);
2282 panic("turnstile promotion for type %d not yet implemented", turnstile_get_type(turnstile
));
2289 * Name: turnstile_waitq_add_thread_priority_queue
2291 * Description: add thread to the turnstile wq
2293 * Arg1: turnstile wq
2294 * Arg2: thread to add
2296 * Condition: wq locked
2299 turnstile_waitq_add_thread_priority_queue(
2303 struct turnstile
*turnstile
= waitq_to_turnstile(wq
);
2304 int priority
= turnstile_compute_thread_push(turnstile
, thread
);
2306 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2307 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
, (THREAD_ADDED_TO_TURNSTILE_WAITQ
))) | DBG_FUNC_NONE
,
2308 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
2312 * For turnstile queues (which use priority queues),
2313 * insert the thread in the heap based on its priority.
2314 * Note that the priority queue implementation
2315 * is currently not stable, so does not maintain fifo for
2316 * threads at the same pri. Also, if the pri
2317 * of the thread changes while its blocked in the waitq,
2318 * the thread position should be updated in the priority
2319 * queue by calling priority queue increase/decrease
2322 priority_queue_entry_init(&thread
->wait_prioq_links
);
2323 priority_queue_entry_set_sched_pri(&wq
->waitq_prio_queue
,
2324 &thread
->wait_prioq_links
, priority
, false);
2325 priority_queue_insert(&wq
->waitq_prio_queue
,
2326 &thread
->wait_prioq_links
);
2330 * Name: turnstile_recompute_priority_locked
2332 * Description: Update turnstile priority based
2333 * on highest waiter thread and highest blocking
2338 * Returns: TRUE: if the turnstile priority changed and needs propagation.
2339 * FALSE: if the turnstile priority did not change or it does not need propagation.
2341 * Condition: turnstile locked
2344 turnstile_recompute_priority_locked(
2345 struct turnstile
*turnstile
)
2349 boolean_t needs_priority_update
= FALSE
;
2350 thread_t max_thread
= THREAD_NULL
;
2351 struct turnstile
*max_turnstile
;
2352 int thread_max_pri
= 0;
2353 int turnstile_max_pri
= 0;
2355 switch (turnstile_promote_policy
[turnstile_get_type(turnstile
)]) {
2356 case TURNSTILE_USER_PROMOTE
:
2357 case TURNSTILE_USER_IPC_PROMOTE
:
2358 case TURNSTILE_KERNEL_PROMOTE
:
2360 old_priority
= turnstile
->ts_priority
;
2362 max_thread
= priority_queue_max(&turnstile
->ts_waitq
.waitq_prio_queue
,
2363 struct thread
, wait_prioq_links
);
2366 thread_max_pri
= priority_queue_entry_sched_pri(
2367 &turnstile
->ts_waitq
.waitq_prio_queue
,
2368 &max_thread
->wait_prioq_links
);
2371 max_turnstile
= priority_queue_max(&turnstile
->ts_inheritor_queue
,
2372 struct turnstile
, ts_inheritor_links
);
2374 if (max_turnstile
) {
2375 assert(turnstile_promote_policy
[turnstile_get_type(turnstile
)] != TURNSTILE_KERNEL_PROMOTE
);
2376 turnstile_max_pri
= priority_queue_entry_sched_pri(
2377 &turnstile
->ts_inheritor_queue
,
2378 &max_turnstile
->ts_inheritor_links
);
2381 new_priority
= max(thread_max_pri
, turnstile_max_pri
);
2382 turnstile
->ts_priority
= (uint8_t)new_priority
;
2384 if (old_priority
!= new_priority
) {
2385 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
2386 (TURNSTILE_CODE(TURNSTILE_PRIORITY_OPERATIONS
,
2387 (TURNSTILE_PRIORITY_CHANGE
))) | DBG_FUNC_NONE
,
2388 VM_KERNEL_UNSLIDE_OR_PERM(turnstile
),
2393 needs_priority_update
= (!(old_priority
== new_priority
)) &&
2394 (turnstile
->ts_inheritor
!= NULL
);
2397 case TURNSTILE_PROMOTE_NONE
:
2398 /* The turnstile was repurposed, do nothing */
2403 panic("Needs implementation for turnstile_recompute_priority");
2406 return needs_priority_update
;
2411 * Name: turnstile_recompute_priority
2413 * Description: Update turnstile priority based
2414 * on highest waiter thread and highest blocking
2419 * Returns: TRUE: if the turnstile priority changed and needs propagation.
2420 * FALSE: if the turnstile priority did not change or it does not need propagation.
2423 turnstile_recompute_priority(
2424 struct turnstile
*turnstile
)
2426 boolean_t needs_priority_update
= FALSE
;
2427 spl_t s
= splsched();
2429 waitq_lock(&turnstile
->ts_waitq
);
2431 needs_priority_update
= turnstile_recompute_priority_locked(turnstile
);
2433 waitq_unlock(&turnstile
->ts_waitq
);
2435 return needs_priority_update
;
2440 * Name: turnstile_workq_proprietor_of_max_turnstile
2442 * Description: Returns the highest priority and proprietor of a turnstile
2443 * pushing on a workqueue turnstile.
2445 * This will not return waiters that are at priority
2446 * MAXPRI_THROTTLE or lower.
2451 * Priority of the max entry, or 0
2452 * Pointer to the max entry proprietor
2455 turnstile_workq_proprietor_of_max_turnstile(
2456 struct turnstile
*turnstile
,
2457 uintptr_t *proprietor_out
)
2459 struct turnstile
*max_turnstile
;
2460 int max_priority
= 0;
2461 uintptr_t proprietor
= 0;
2463 assert(turnstile_get_type(turnstile
) == TURNSTILE_WORKQS
);
2465 spl_t s
= splsched();
2467 waitq_lock(&turnstile
->ts_waitq
);
2469 max_turnstile
= priority_queue_max(&turnstile
->ts_inheritor_queue
,
2470 struct turnstile
, ts_inheritor_links
);
2471 if (max_turnstile
) {
2472 max_priority
= priority_queue_entry_sched_pri(
2473 &turnstile
->ts_inheritor_queue
,
2474 &max_turnstile
->ts_inheritor_links
);
2475 proprietor
= max_turnstile
->ts_proprietor
;
2478 waitq_unlock(&turnstile
->ts_waitq
);
2481 if (max_priority
<= MAXPRI_THROTTLE
) {
2485 if (proprietor_out
) {
2486 *proprietor_out
= proprietor
;
2488 return max_priority
;
2492 * Name: turnstile_workloop_pusher_info
2494 * Description: Returns the priority of the turnstile push for a workloop,
2495 * and the thread or knote responsible for this push.
2497 * Args: workloop turnstile
2500 * Priority of the push or 0
2501 * Thread (with a +1 reference) with that push or THREAD_NULL.
2502 * Port (with a +1 reference) with that push, or IP_NULL.
2503 * Sync IPC knote with the highest push (or NULL)
2506 turnstile_workloop_pusher_info(
2507 struct turnstile
*turnstile
,
2508 thread_t
*thread_out
,
2509 ipc_port_t
*port_out
,
2510 struct knote
**knote_out
)
2512 struct turnstile
*max_ts
;
2513 thread_t max_thread
;
2514 int max_thread_pri
= 0;
2518 assert(turnstile_get_type(turnstile
) == TURNSTILE_WORKLOOPS
);
2520 spl_t s
= splsched();
2521 waitq_lock(&turnstile
->ts_waitq
);
2523 max_thread
= priority_queue_max(&turnstile
->ts_waitq
.waitq_prio_queue
,
2524 struct thread
, wait_prioq_links
);
2526 max_thread_pri
= priority_queue_entry_sched_pri(
2527 &turnstile
->ts_waitq
.waitq_prio_queue
,
2528 &max_thread
->wait_prioq_links
);
2531 max_ts
= priority_queue_max(&turnstile
->ts_inheritor_queue
,
2532 struct turnstile
, ts_inheritor_links
);
2534 max_ts_pri
= priority_queue_entry_sched_pri(
2535 &turnstile
->ts_inheritor_queue
,
2536 &max_ts
->ts_inheritor_links
);
2540 * Reasons to push on a workloop turnstile are:
2542 * 1. threads in dispatch sync
2544 * 2. sync IPC pushes, which in turn have 4 sub-cases:
2546 * 2.a. special reply port or receive right pushing through a knote
2549 * 2.b. special reply port stashed on a knote, pushing on the workloop
2552 * 2.c. receive right stashed on a knote, pushing on the workloop
2555 * 2.d. a receive right monitored by a knote, pushing on the workloop
2558 * See ipc_port_send_update_inheritor(), ipc_port_recv_update_inheritor().
2560 * Note: dereferencing the knote in the caller is safe provided this
2561 * function i scalled under the proper interlocks (the filt_wllock + req
2562 * lock) which serializes with the knote going away.
2564 if (max_thread_pri
> max_ts_pri
) {
2565 thread_reference(max_thread
);
2566 *thread_out
= max_thread
;
2569 } else if (max_ts_pri
) {
2570 switch (turnstile_get_type(max_ts
)) {
2571 case TURNSTILE_KNOTE
:
2573 *thread_out
= THREAD_NULL
;
2574 *port_out
= IP_NULL
;
2575 *knote_out
= (struct knote
*)max_ts
->ts_proprietor
;
2578 case TURNSTILE_SYNC_IPC
:
2580 port
= (ipc_port_t
)max_ts
->ts_proprietor
;
2582 *thread_out
= THREAD_NULL
;
2588 panic("Unexpected type for turnstile %p", max_ts
);
2591 *thread_out
= THREAD_NULL
;
2592 *port_out
= IP_NULL
;
2596 waitq_unlock(&turnstile
->ts_waitq
);
2599 return max(max_thread_pri
, max_ts_pri
);
2603 * Name: turnstile_has_waiters
2605 * Description: returns if there are waiters on the turnstile
2607 * Arg1: turnstile: turnstile
2609 * Returns: TRUE if there are waiters, FALSE otherwise.
2613 turnstile_has_waiters(struct turnstile
*turnstile
)
2617 spl_t s
= splsched();
2618 waitq_lock(&turnstile
->ts_waitq
);
2619 ret
= !priority_queue_empty(&turnstile
->ts_waitq
.waitq_prio_queue
);
2620 waitq_unlock(&turnstile
->ts_waitq
);
2627 * Name: turnstile_update_inheritor_priority_chain
2629 * Description: Update turnstile inheritor's priority and propagate
2630 * the priority if the inheritor is blocked on a turnstile.
2633 * Arg2: inheritor flags
2638 turnstile_update_inheritor_priority_chain(
2639 turnstile_inheritor_t inheritor
,
2640 turnstile_update_flags_t turnstile_flags
)
2642 struct turnstile
*turnstile
= TURNSTILE_NULL
;
2643 thread_t thread
= THREAD_NULL
;
2644 int total_hop
= 0, thread_hop
= 0;
2646 turnstile_stats_update_flags_t tsu_flags
= ((turnstile_flags
& TURNSTILE_UPDATE_BOOST
) ?
2647 TSU_BOOST_ARG
: TSU_FLAGS_NONE
) | TSU_PRI_PROPAGATION
;
2649 if (inheritor
== NULL
) {
2655 if (turnstile_flags
& TURNSTILE_INHERITOR_THREAD
) {
2657 thread_lock(thread
);
2658 thread_recompute_user_promotion_locked(thread
);
2659 thread_recompute_kernel_promotion_locked(thread
);
2660 } else if (turnstile_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
2661 turnstile
= inheritor
;
2662 waitq_lock(&turnstile
->ts_waitq
);
2663 turnstile_recompute_priority_locked(turnstile
);
2664 tsu_flags
|= turnstile_get_update_flags_for_above_UI_pri_change(turnstile
);
2667 * we should never call turnstile_update_inheritor_priority_chain()
2668 * for a workqueue, they have no "chain" after them.
2670 assert((turnstile_flags
& TURNSTILE_INHERITOR_WORKQ
) == 0);
2673 while (turnstile
!= TURNSTILE_NULL
|| thread
!= THREAD_NULL
) {
2674 if (turnstile
!= TURNSTILE_NULL
) {
2675 if (turnstile
->ts_inheritor
== NULL
) {
2676 turnstile_stats_update(total_hop
+ 1, TSU_NO_INHERITOR
|
2677 TSU_TURNSTILE_ARG
| tsu_flags
,
2679 waitq_unlock(&turnstile
->ts_waitq
);
2680 turnstile
= TURNSTILE_NULL
;
2683 if (turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
2684 turnstile_update_inheritor_thread_priority_chain(&turnstile
, &thread
,
2685 total_hop
, tsu_flags
);
2686 } else if (turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
2687 turnstile_update_inheritor_turnstile_priority_chain(&turnstile
,
2688 total_hop
, tsu_flags
);
2689 } else if (turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
2690 turnstile_update_inheritor_workq_priority_chain(turnstile
, s
);
2691 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
| tsu_flags
,
2695 panic("Inheritor flags not passed in turnstile_update_inheritor");
2697 } else if (thread
!= THREAD_NULL
) {
2698 thread_update_waiting_turnstile_priority_chain(&thread
, &turnstile
,
2699 thread_hop
, total_hop
, tsu_flags
);
2710 * Name: turnstile_update_inheritor_complete
2712 * Description: Update turnstile inheritor's priority and propagate the
2713 * priority if the inheritor is blocked on a turnstile.
2714 * Consumes thread ref of old inheritor returned by
2715 * turnstile_update_inheritor. Recursive priority update
2716 * will only happen when called with interlock dropped.
2720 * Arg2: interlock held
2725 turnstile_update_inheritor_complete(
2726 struct turnstile
*turnstile
,
2727 turnstile_update_complete_flags_t flags __unused
)
2729 thread_t thread
= current_thread();
2731 turnstile_update_flags_t inheritor_flags
= thread
->inheritor_flags
;
2733 turnstile_cleanup();
2735 /* Perform priority update for new inheritor */
2736 if (inheritor_flags
& TURNSTILE_NEEDS_PRI_UPDATE
) {
2737 turnstile_update_inheritor_priority_chain(turnstile
,
2738 TURNSTILE_INHERITOR_TURNSTILE
| TURNSTILE_UPDATE_BOOST
);
2743 * Name: turnstile_cleanup
2745 * Description: Update priority of a turnstile inheritor
2748 * Args: inheritor and flags passed on thread struct.
2753 turnstile_cleanup(void)
2755 thread_t thread
= current_thread();
2757 /* Get the old inheritor from calling thread struct */
2758 turnstile_inheritor_t old_inheritor
= thread
->inheritor
;
2759 turnstile_update_flags_t inheritor_flags
= thread
->inheritor_flags
;
2760 thread
->inheritor
= THREAD_NULL
;
2761 thread
->inheritor_flags
= TURNSTILE_UPDATE_FLAGS_NONE
;
2763 if (old_inheritor
== TURNSTILE_INHERITOR_NULL
) {
2764 /* no cleanup to do */
2768 /* Perform priority demotion for old inheritor */
2769 if (inheritor_flags
& TURNSTILE_INHERITOR_NEEDS_PRI_UPDATE
) {
2770 turnstile_update_inheritor_priority_chain(old_inheritor
,
2774 /* Drop thread reference for old inheritor */
2775 if (inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
2776 thread_deallocate_safe(old_inheritor
);
2777 } else if (inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
2778 turnstile_deallocate_safe((struct turnstile
*)old_inheritor
);
2779 } else if (inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
2780 workq_deallocate_safe((struct workqueue
*)old_inheritor
);
2782 panic("Inheritor flags lost along the way");
2787 * Name: turnstile_update_thread_priority_chain
2789 * Description: Priority of a thread blocked on a turnstile
2790 * has changed, update the turnstile priority.
2792 * Arg1: thread: thread whose priority has changed.
2797 turnstile_update_thread_priority_chain(thread_t thread
)
2799 turnstile_update_inheritor_priority_chain(thread
,
2800 TURNSTILE_INHERITOR_THREAD
| TURNSTILE_UPDATE_BOOST
);
2804 * Name: turnstile_update_inheritor_workq_priority_chain
2806 * Description: Helper function to update turnstile's inheritor(workq)
2807 * priority and possibly redrive thread creation
2809 * Arg1: turnstile: turnstile
2810 * Arg2: s: whether iterrupts are disabled.
2812 * Condition: turnstile is locked on entry, it is unlocked on exit,
2813 * and interrupts re-enabled.
2816 turnstile_update_inheritor_workq_priority_chain(struct turnstile
*turnstile
, spl_t s
)
2818 struct workqueue
*wq
= turnstile
->ts_inheritor
;
2819 bool workq_lock_held
= workq_is_current_thread_updating_turnstile(wq
);
2821 if (__improbable(turnstile
->ts_priority
<= MAXPRI_THROTTLE
)) {
2822 waitq_unlock(&turnstile
->ts_waitq
);
2827 if (!workq_lock_held
) {
2828 workq_reference(wq
);
2829 disable_preemption();
2831 waitq_unlock(&turnstile
->ts_waitq
);
2834 workq_schedule_creator_turnstile_redrive(wq
, workq_lock_held
);
2836 if (!workq_lock_held
) {
2837 enable_preemption();
2838 workq_deallocate_safe(wq
);
2843 * Name: turnstile_update_inheritor_thread_priority_chain
2845 * Description: Helper function to update turnstile's inheritor(thread)
2848 * Arg1: in_turnstile: address to turnstile
2849 * Arg2: out_thread: address to return the thread inheritor
2850 * Arg3: thread_hop: number to thread hop in propagation chain
2851 * Arg4: tsu_flags: turnstile update flags
2853 * Returns: Implicit returns locked thread in out_thread if it needs
2854 * further propagation.
2856 * Condition: *in_turnstile is locked on entry, it is unlocked on exit and
2857 * *in_turnstile is set to NULL.
2860 turnstile_update_inheritor_thread_priority_chain(
2861 struct turnstile
**in_turnstile
,
2862 thread_t
*out_thread
,
2864 turnstile_stats_update_flags_t tsu_flags
)
2866 boolean_t needs_update
= FALSE
;
2867 struct turnstile
*turnstile
= *in_turnstile
;
2868 thread_t thread_inheritor
= turnstile
->ts_inheritor
;
2869 boolean_t first_update
= !total_hop
;
2871 assert(turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
);
2872 *in_turnstile
= TURNSTILE_NULL
;
2874 /* Check if update is needed before grabbing the thread lock */
2875 needs_update
= thread_needs_turnstile_promotion_update(thread_inheritor
, turnstile
);
2876 if (!needs_update
&& !first_update
) {
2877 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
|
2878 TSU_TURNSTILE_ARG
| tsu_flags
, turnstile
);
2879 waitq_unlock(&turnstile
->ts_waitq
);
2883 thread_lock(thread_inheritor
);
2885 /* adjust turnstile position in the thread's inheritor list */
2886 needs_update
= thread_update_turnstile_promotion_locked(
2887 thread_inheritor
, turnstile
);
2890 * Check if thread needs further priority propagation,
2891 * since the first hop priority update was done in
2892 * turnstile_update_inheritor, do not bailout if it is
2893 * the first update as needs_update flag would evaluate to
2894 * false for that case.
2896 if (!needs_update
&& !first_update
) {
2897 /* Update turnstile stats before returning */
2898 turnstile_stats_update(total_hop
+ 1,
2899 (thread_get_update_flags_for_turnstile_propagation_stoppage(thread_inheritor
)) |
2900 TSU_TURNSTILE_ARG
| tsu_flags
,
2902 thread_unlock(thread_inheritor
);
2903 waitq_unlock(&turnstile
->ts_waitq
);
2907 /* Unlock the turnstile and update the thread */
2908 waitq_unlock(&turnstile
->ts_waitq
);
2909 *out_thread
= thread_inheritor
;
2914 * Name: turnstile_update_inheritor_turnstile_priority_chain
2916 * Description: Helper function to update turnstile's inheritor(turnstile)
2919 * Arg1: in_out_turnstile: address to turnstile
2920 * Arg2: thread_hop: number of thread hop in propagation chain
2921 * Arg3: tsu_flags: turnstile update flags
2923 * Returns: Implicit returns locked turnstile in in_out_turnstile if it needs
2924 * further propagation.
2926 * Condition: *in_out_turnstile is locked on entry, *in_out_turnstile on exit,
2927 * but the value of *in_out_turnstile might change and turnstile lock
2928 * will be dropped for old value and will be acquired for the new value.
2931 turnstile_update_inheritor_turnstile_priority_chain(
2932 struct turnstile
**in_out_turnstile
,
2934 turnstile_stats_update_flags_t tsu_flags
)
2936 boolean_t needs_update
= FALSE
;
2937 struct turnstile
*turnstile
= *in_out_turnstile
;
2938 struct turnstile
*inheritor_turnstile
= turnstile
->ts_inheritor
;
2939 boolean_t first_update
= !total_hop
;
2941 assert(turnstile
->ts_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
);
2942 *in_out_turnstile
= TURNSTILE_NULL
;
2944 /* Check if the inheritor turnstile needs to be updated before grabbing the lock */
2945 needs_update
= turnstile_need_turnstile_promotion_update(inheritor_turnstile
, turnstile
);
2946 if (!needs_update
&& !first_update
) {
2947 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
|
2948 TSU_TURNSTILE_ARG
| tsu_flags
,
2950 waitq_unlock(&turnstile
->ts_waitq
);
2954 waitq_lock(&inheritor_turnstile
->ts_waitq
);
2956 needs_update
= turnstile_update_turnstile_promotion_locked(
2957 inheritor_turnstile
, turnstile
);
2960 * Check if turnstile needs further priority propagation,
2961 * since the first hop priority update was done in
2962 * turnstile_update_inheritor, do not bailout if it is
2963 * the first update as needs_update flag would evaluate to
2964 * false for that case.
2966 if (!needs_update
&& !first_update
) {
2967 /* Update turnstile stats before returning */
2968 turnstile_stats_update(total_hop
+ 1,
2969 (inheritor_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
2970 TSU_TURNSTILE_ARG
| tsu_flags
,
2972 waitq_unlock(&inheritor_turnstile
->ts_waitq
);
2973 waitq_unlock(&turnstile
->ts_waitq
);
2977 /* Unlock the outer turnstile and update the inner turnstile */
2978 waitq_unlock(&turnstile
->ts_waitq
);
2979 *in_out_turnstile
= inheritor_turnstile
;
2984 * Name: thread_update_waiting_turnstile_priority_chain
2986 * Description: Helper function to update thread's waiting
2987 * turnstile priority.
2989 * Arg1: in_thread: pointer to thread
2990 * Arg2: out_turnstile: pointer to turnstile to return to caller
2991 * Arg3: thread_hop: Number of thread hops visited
2992 * Arg4: total_hop: total hops visited
2993 * Arg5: tsu_flags: turnstile update flags
2995 * Returns: *out_turnstile returns the inheritor if it needs further propagation.
2997 * Condition: *in_thread locked on entry, unlocked on exit and set to NULL.
3000 thread_update_waiting_turnstile_priority_chain(
3001 thread_t
*in_thread
,
3002 struct turnstile
**out_turnstile
,
3005 turnstile_stats_update_flags_t tsu_flags
)
3007 boolean_t needs_update
= FALSE
;
3008 thread_t thread
= *in_thread
;
3009 struct turnstile
*waiting_turnstile
= TURNSTILE_NULL
;
3010 uint32_t turnstile_gencount
;
3011 boolean_t first_update
= !total_hop
;
3013 *in_thread
= THREAD_NULL
;
3015 /* Check if thread waiting on a turnstile */
3016 waiting_turnstile
= thread_get_waiting_turnstile(thread
);
3018 if (waiting_turnstile
== TURNSTILE_NULL
|| thread_hop
> turnstile_max_hop
) {
3019 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
3020 (TURNSTILE_CODE(TURNSTILE_HEAP_OPERATIONS
,
3021 (waiting_turnstile
? TURNSTILE_UPDATE_STOPPED_BY_LIMIT
: THREAD_NOT_WAITING_ON_TURNSTILE
)
3026 VM_KERNEL_UNSLIDE_OR_PERM(waiting_turnstile
), 0);
3027 turnstile_stats_update(total_hop
+ 1, TSU_NO_TURNSTILE
|
3028 TSU_THREAD_ARG
| tsu_flags
, thread
);
3029 thread_unlock(thread
);
3033 /* Check if the thread needs to update the waiting turnstile */
3034 needs_update
= turnstile_need_thread_promotion_update(waiting_turnstile
, thread
);
3035 if (!needs_update
&& !first_update
) {
3036 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
|
3037 TSU_THREAD_ARG
| tsu_flags
, thread
);
3038 thread_unlock(thread
);
3042 /* take a reference on thread, turnstile and snapshot of gencount */
3043 turnstile_gencount
= turnstile_get_gencount(waiting_turnstile
);
3044 turnstile_reference(waiting_turnstile
);
3045 thread_reference(thread
);
3047 /* drop the thread lock and acquire the turnstile lock */
3048 thread_unlock(thread
);
3049 waitq_lock(&waiting_turnstile
->ts_waitq
);
3050 thread_lock(thread
);
3052 /* Check if the gencount matches and thread is still waiting on same turnstile */
3053 if (turnstile_gencount
!= turnstile_get_gencount(waiting_turnstile
) ||
3054 waiting_turnstile
!= thread_get_waiting_turnstile(thread
)) {
3055 turnstile_stats_update(total_hop
+ 1, TSU_NO_PRI_CHANGE_NEEDED
|
3056 TSU_THREAD_ARG
| tsu_flags
, thread
);
3057 /* No updates required, bail out */
3058 thread_unlock(thread
);
3059 waitq_unlock(&waiting_turnstile
->ts_waitq
);
3060 thread_deallocate_safe(thread
);
3061 turnstile_deallocate_safe(waiting_turnstile
);
3066 * The thread is waiting on the waiting_turnstile and we have thread lock,
3067 * we can drop the thread and turnstile reference since its on waitq and
3068 * it could not be removed from the waitq without the thread lock.
3070 thread_deallocate_safe(thread
);
3071 turnstile_deallocate_safe(waiting_turnstile
);
3073 /* adjust thread's position on turnstile waitq */
3074 needs_update
= turnstile_update_thread_promotion_locked(waiting_turnstile
, thread
);
3077 * Check if thread needs further priority propagation,
3078 * since the first hop priority update was done in
3079 * turnstile_update_inheritor, do not bailout if it is
3080 * the first update as needs_update flag would evaluate to
3081 * false for that case.
3083 if (!needs_update
&& !first_update
) {
3084 turnstile_stats_update(total_hop
+ 1,
3085 (waiting_turnstile
->ts_inheritor
? TSU_NO_PRI_CHANGE_NEEDED
: TSU_NO_INHERITOR
) |
3086 TSU_THREAD_ARG
| tsu_flags
, thread
);
3087 thread_unlock(thread
);
3088 waitq_unlock(&waiting_turnstile
->ts_waitq
);
3092 /* drop the thread lock and update the turnstile */
3093 thread_unlock(thread
);
3094 *out_turnstile
= waiting_turnstile
;
3098 * Name: turnstile_stats_update
3100 * Description: Function to update turnstile stats for dev kernel.
3102 * Arg1: hops : number of thread hops in priority propagation
3103 * Arg2: flags : turnstile stats update flags
3104 * Arg3: inheritor: inheritor
3109 turnstile_stats_update(
3110 int hop __assert_only
,
3111 turnstile_stats_update_flags_t flags __assert_only
,
3112 turnstile_inheritor_t inheritor __assert_only
)
3114 #if DEVELOPMENT || DEBUG
3115 if (flags
& TSU_TURNSTILE_BLOCK_COUNT
) {
3116 os_atomic_inc(&thread_block_on_turnstile_count
, relaxed
);
3119 if (flags
& TSU_REGULAR_WAITQ_BLOCK_COUNT
) {
3120 os_atomic_inc(&thread_block_on_regular_waitq_count
, relaxed
);
3123 if (hop
> TURNSTILE_MAX_HOP_DEFAULT
|| hop
== 0) {
3130 * Check if turnstile stats needs to be updated.
3131 * Bail out if the turnstile or thread does not
3132 * have any user promotion.
3133 * Bail out if it is the first hop of WQ turnstile
3134 * since WQ's use of a turnstile for the admission check
3135 * introduces a lot of noise due to state changes.
3137 if (flags
& TSU_TURNSTILE_ARG
) {
3138 struct turnstile
*ts
= (struct turnstile
*)inheritor
;
3139 if (ts
->ts_priority
== 0) {
3143 if (hop
== 1 && turnstile_get_type(ts
) == TURNSTILE_WORKQS
) {
3146 } else if (flags
& TSU_THREAD_ARG
) {
3147 thread_t thread
= (thread_t
)inheritor
;
3148 if (thread
->user_promotion_basepri
== 0) {
3152 assert(inheritor
== NULL
);
3155 struct turnstile_stats
*turnstile_stats
;
3156 if (flags
& TSU_BOOST_ARG
) {
3157 turnstile_stats
= turnstile_boost_stats
;
3159 turnstile_stats
= turnstile_unboost_stats
;
3162 if (flags
& TSU_PRI_PROPAGATION
) {
3163 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_priority_propagation
, relaxed
);
3166 if (flags
& TSU_NO_INHERITOR
) {
3167 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_inheritor
, relaxed
);
3170 if (flags
& TSU_NO_TURNSTILE
) {
3171 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_turnstile
, relaxed
);
3174 if (flags
& TSU_NO_PRI_CHANGE_NEEDED
) {
3175 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_no_priority_change_required
, relaxed
);
3178 if (flags
& TSU_THREAD_RUNNABLE
) {
3179 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_thread_runnable
, relaxed
);
3182 if (flags
& TSU_ABOVE_UI_PRI_CHANGE
) {
3183 os_atomic_inc(&turnstile_stats
[hop
- 1].ts_above_ui_pri_change
, relaxed
);
3189 kdp_turnstile_traverse_inheritor_chain(struct turnstile
*ts
, uint64_t *flags
, uint8_t *hops
)
3191 uint8_t unknown_hops
;
3193 if (waitq_held(&ts
->ts_waitq
)) {
3194 *flags
|= STACKSHOT_TURNSTILE_STATUS_LOCKED_WAITQ
;
3199 unknown_hops
= *hops
;
3202 * If a turnstile is inheriting our priority, recurse. If we get back *exactly* UNKNOWN,
3203 * continue on, since we may be able to give a more specific answer. To
3204 * give an accurate hops count, we reset *hops, saving the recursive value in
3205 * unknown_hops to use if we can't give a better answer.
3207 if (ts
->ts_inheritor_flags
& TURNSTILE_INHERITOR_TURNSTILE
) {
3208 uint8_t pre_hops
= *hops
;
3209 uint64_t ret
= kdp_turnstile_traverse_inheritor_chain(ts
->ts_inheritor
, flags
, hops
);
3211 * Note that while flags is usually |=ed, we're checking with != here to
3212 * make sure we only replace *exactly* UNKNOWN
3214 if (ret
!= 0 || *flags
!= STACKSHOT_TURNSTILE_STATUS_UNKNOWN
) {
3217 /* restore original hops value, saving the new one if we fall through to unknown */
3218 unknown_hops
= *hops
;
3223 if (ts
->ts_inheritor_flags
& TURNSTILE_INHERITOR_THREAD
) {
3224 *flags
|= STACKSHOT_TURNSTILE_STATUS_THREAD
;
3225 return (uint64_t) thread_tid(ts
->ts_inheritor
);
3228 if (ts
->ts_inheritor_flags
& TURNSTILE_INHERITOR_WORKQ
) {
3229 *flags
|= STACKSHOT_TURNSTILE_STATUS_WORKQUEUE
;
3230 return VM_KERNEL_UNSLIDE_OR_PERM(ts
->ts_inheritor
);
3234 * If we found a send turnstile, try to get the task that the turnstile's
3235 * port is in the ipc space of
3237 if (turnstile_is_send_turnstile(ts
)) {
3238 ipc_port_t port
= (ipc_port_t
)ts
->ts_proprietor
;
3240 if (port
&& ip_active(port
)) {
3241 if (ip_lock_held_kdp(port
)) {
3242 *flags
|= STACKSHOT_TURNSTILE_STATUS_HELD_IPLOCK
;
3245 if (port
->ip_receiver_name
!= 0 && port
->ip_receiver
) {
3246 ipc_space_t space
= (ipc_space_t
) port
->ip_receiver
;
3247 task_t dest_task
= space
->is_task
;
3249 if (dest_task
!= TASK_NULL
) {
3250 *flags
|= STACKSHOT_TURNSTILE_STATUS_BLOCKED_ON_TASK
;
3251 return pid_from_task(dest_task
);
3257 if (turnstile_is_receive_turnstile(ts
)) {
3258 ipc_port_t port
= (ipc_port_t
)ts
->ts_proprietor
;
3259 if (port
&& ip_active(port
)) {
3260 if (ip_lock_held_kdp(port
)) {
3261 *flags
|= STACKSHOT_TURNSTILE_STATUS_HELD_IPLOCK
;
3264 if (port
->ip_specialreply
) {
3265 /* try getting the pid stored in the port */
3266 uint64_t pid_candidate
= ipc_special_reply_get_pid_locked(port
);
3268 if (pid_candidate
) {
3269 *flags
|= STACKSHOT_TURNSTILE_STATUS_BLOCKED_ON_TASK
;
3270 return pid_candidate
;
3276 *hops
= unknown_hops
;
3277 *flags
|= STACKSHOT_TURNSTILE_STATUS_UNKNOWN
;
3282 kdp_turnstile_fill_tsinfo(struct turnstile
*ts
, thread_turnstileinfo_t
*tsinfo
)
3284 uint64_t final_inheritor
;
3288 tsinfo
->turnstile_context
= 0;
3289 tsinfo
->number_of_hops
= 0;
3290 tsinfo
->turnstile_priority
= 0;
3292 assert(ts
!= TURNSTILE_NULL
);
3294 if (waitq_held(&ts
->ts_waitq
)) {
3295 tsinfo
->turnstile_flags
|= STACKSHOT_TURNSTILE_STATUS_LOCKED_WAITQ
;
3299 final_inheritor
= kdp_turnstile_traverse_inheritor_chain(ts
, &flags
, &hops
);
3301 /* store some metadata about the turnstile itself */
3302 tsinfo
->turnstile_flags
= flags
;
3303 tsinfo
->number_of_hops
= hops
;
3304 tsinfo
->turnstile_priority
= ts
->ts_priority
;
3305 tsinfo
->turnstile_context
= final_inheritor
;
3308 #if DEVELOPMENT || DEBUG
3310 int sysctl_io_opaque(void *req
, void *pValue
, size_t valueSize
, int *changed
);
3313 * Name: turnstile_get_boost_stats_sysctl
3315 * Description: Function to get turnstile stats.
3317 * Args: req : opaque struct to pass to sysctl_io_opaque
3322 turnstile_get_boost_stats_sysctl(
3325 return sysctl_io_opaque(req
, turnstile_boost_stats
, sizeof(struct turnstile_stats
) * TURNSTILE_MAX_HOP_DEFAULT
, NULL
);
3329 * Name: get_turnstile_stats_sysctl
3331 * Description: Function to get turnstile stats.
3333 * Args: req : opaque struct to pass to sysctl_io_opaque
3338 turnstile_get_unboost_stats_sysctl(
3341 return sysctl_io_opaque(req
, turnstile_unboost_stats
, sizeof(struct turnstile_stats
) * TURNSTILE_MAX_HOP_DEFAULT
, NULL
);
3344 /* Testing interface for Development kernels */
3345 #define tstile_test_prim_lock_interlock(test_prim) \
3346 lck_spin_lock(&test_prim->ttprim_interlock)
3347 #define tstile_test_prim_unlock_interlock(test_prim) \
3348 lck_spin_unlock(&test_prim->ttprim_interlock)
3351 tstile_test_prim_init(struct tstile_test_prim
**test_prim_ptr
)
3353 struct tstile_test_prim
*test_prim
= (struct tstile_test_prim
*) kalloc(sizeof(struct tstile_test_prim
));
3355 test_prim
->ttprim_turnstile
= TURNSTILE_NULL
;
3356 test_prim
->ttprim_owner
= NULL
;
3357 lck_spin_init(&test_prim
->ttprim_interlock
, &turnstiles_dev_lock_grp
, LCK_ATTR_NULL
);
3358 test_prim
->tt_prim_waiters
= 0;
3360 *test_prim_ptr
= test_prim
;
3365 tstile_test_prim_lock(int val
)
3367 struct tstile_test_prim
*test_prim
;
3368 boolean_t use_hashtable
;
3369 turnstile_type_t type
;
3370 wait_interrupt_t wait_type
;
3373 case SYSCTL_TURNSTILE_TEST_USER_DEFAULT
:
3374 test_prim
= test_prim_ts_inline
;
3375 use_hashtable
= FALSE
;
3376 wait_type
= THREAD_ABORTSAFE
;
3377 type
= TURNSTILE_ULOCK
;
3379 case SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
:
3380 test_prim
= test_prim_global_htable
;
3381 use_hashtable
= TRUE
;
3382 wait_type
= THREAD_ABORTSAFE
;
3383 type
= TURNSTILE_ULOCK
;
3385 case SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
:
3386 test_prim
= test_prim_global_ts_kernel
;
3387 use_hashtable
= FALSE
;
3388 wait_type
= THREAD_UNINT
| THREAD_WAIT_NOREPORT_USER
;
3389 type
= TURNSTILE_KERNEL_MUTEX
;
3391 case SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE
:
3392 test_prim
= test_prim_global_ts_kernel_hash
;
3393 use_hashtable
= TRUE
;
3394 wait_type
= THREAD_UNINT
| THREAD_WAIT_NOREPORT_USER
;
3395 type
= TURNSTILE_KERNEL_MUTEX
;
3404 /* take the interlock of the primitive */
3405 tstile_test_prim_lock_interlock(test_prim
);
3407 /* Check if the lock is available */
3408 if (test_prim
->ttprim_owner
== NULL
&& test_prim
->tt_prim_waiters
== 0) {
3409 thread_reference(current_thread());
3410 test_prim
->ttprim_owner
= current_thread();
3411 tstile_test_prim_unlock_interlock(test_prim
);
3415 struct turnstile
*prim_turnstile
= TURNSTILE_NULL
;
3417 /* primitive locked, get a turnstile */
3418 prim_turnstile
= turnstile_prepare((uintptr_t)test_prim
,
3419 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
,
3420 TURNSTILE_NULL
, type
);
3422 assert(prim_turnstile
!= TURNSTILE_NULL
);
3424 /* This is contented acquire case */
3425 if (test_prim
->ttprim_owner
== NULL
) {
3426 thread_reference(current_thread());
3427 test_prim
->ttprim_owner
= current_thread();
3429 /* Update the turnstile owner */
3430 turnstile_update_inheritor(prim_turnstile
,
3432 (TURNSTILE_IMMEDIATE_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3434 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_HELD
);
3436 turnstile_complete((uintptr_t)test_prim
,
3437 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
, NULL
, type
);
3439 tstile_test_prim_unlock_interlock(test_prim
);
3441 turnstile_cleanup();
3445 test_prim
->tt_prim_waiters
++;
3446 turnstile_update_inheritor(prim_turnstile
,
3447 test_prim
->ttprim_owner
,
3448 (TURNSTILE_DELAYED_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3450 waitq_assert_wait64(&prim_turnstile
->ts_waitq
,
3451 CAST_EVENT64_T(test_prim
), wait_type
,
3452 TIMEOUT_WAIT_FOREVER
);
3454 /* drop the interlock */
3455 tstile_test_prim_unlock_interlock(test_prim
);
3457 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_NOT_HELD
);
3459 wait_result_t result
;
3460 result
= thread_block(THREAD_CONTINUE_NULL
);
3462 /* re-acquire the interlock to get turnstile back */
3463 tstile_test_prim_lock_interlock(test_prim
);
3464 test_prim
->tt_prim_waiters
--;
3465 turnstile_complete((uintptr_t)test_prim
,
3466 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
, NULL
, type
);
3468 tstile_test_prim_unlock_interlock(test_prim
);
3470 turnstile_cleanup();
3472 /* Return if thread interrupted */
3473 if (result
== THREAD_INTERRUPTED
) {
3481 tstile_test_prim_unlock(int val
)
3483 struct tstile_test_prim
*test_prim
;
3484 boolean_t use_hashtable
;
3485 turnstile_type_t type
;
3488 case SYSCTL_TURNSTILE_TEST_USER_DEFAULT
:
3489 test_prim
= test_prim_ts_inline
;
3490 use_hashtable
= FALSE
;
3491 type
= TURNSTILE_ULOCK
;
3493 case SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
:
3494 test_prim
= test_prim_global_htable
;
3495 use_hashtable
= TRUE
;
3496 type
= TURNSTILE_ULOCK
;
3498 case SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
:
3499 test_prim
= test_prim_global_ts_kernel
;
3500 use_hashtable
= FALSE
;
3501 type
= TURNSTILE_KERNEL_MUTEX
;
3503 case SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE
:
3504 test_prim
= test_prim_global_ts_kernel_hash
;
3505 use_hashtable
= TRUE
;
3506 type
= TURNSTILE_KERNEL_MUTEX
;
3512 /* take the interlock of the primitive */
3513 tstile_test_prim_lock_interlock(test_prim
);
3515 if (test_prim
->ttprim_owner
== NULL
) {
3516 tstile_test_prim_unlock_interlock(test_prim
);
3520 /* Check if the lock is contended */
3521 if (test_prim
->ttprim_owner
!= NULL
&& test_prim
->tt_prim_waiters
== 0) {
3522 /* lock is not contended */
3523 thread_t old_owner
= test_prim
->ttprim_owner
;
3524 test_prim
->ttprim_owner
= NULL
;
3525 tstile_test_prim_unlock_interlock(test_prim
);
3527 thread_deallocate(old_owner
);
3531 struct turnstile
*prim_turnstile
= TURNSTILE_NULL
;
3533 thread_t old_owner
= test_prim
->ttprim_owner
;
3534 test_prim
->ttprim_owner
= NULL
;
3536 /* primitive locked, get a turnstile */
3537 prim_turnstile
= turnstile_prepare((uintptr_t)test_prim
,
3538 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
,
3539 TURNSTILE_NULL
, type
);
3541 assert(prim_turnstile
!= TURNSTILE_NULL
);
3543 /* Update the turnstile owner */
3544 turnstile_update_inheritor(prim_turnstile
,
3546 (TURNSTILE_IMMEDIATE_UPDATE
| TURNSTILE_INHERITOR_THREAD
));
3548 waitq_wakeup64_one(&prim_turnstile
->ts_waitq
,
3549 CAST_EVENT64_T(test_prim
),
3550 THREAD_AWAKENED
, WAITQ_ALL_PRIORITIES
);
3552 turnstile_update_inheritor_complete(prim_turnstile
, TURNSTILE_INTERLOCK_HELD
);
3554 turnstile_complete((uintptr_t)test_prim
,
3555 use_hashtable
? NULL
: &test_prim
->ttprim_turnstile
, NULL
, type
);
3557 tstile_test_prim_unlock_interlock(test_prim
);
3559 turnstile_cleanup();
3562 /* Changing this to thread_deallocate_safe to exercise thread_deallocate_safe path */
3563 thread_deallocate_safe(old_owner
);