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