2 * turnstiles_test: Tests turnstile kernel primitive.
9 #include <darwintest.h>
10 #include <darwintest_multiprocess.h>
14 #include <servers/bootstrap.h>
16 #include <sys/event.h>
18 #include <crt_externs.h>
19 #include <sys/sysctl.h>
20 #include <sys/types.h>
22 #define SYSCTL_TURNSTILE_TEST_USER_DEFAULT 1
23 #define SYSCTL_TURNSTILE_TEST_USER_HASHTABLE 2
24 #define SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT 3
25 #define SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE 4
27 T_GLOBAL_META(T_META_NAMESPACE("xnu.turnstiles_test"));
30 thread_create_at_qos(qos_class_t qos
, void * (*function
)(void *), int type
)
32 qos_class_t qos_thread
;
37 ret
= setpriority(PRIO_DARWIN_ROLE
, 0, PRIO_DARWIN_ROLE_UI_FOCAL
);
39 T_LOG("set priority failed\n");
42 pthread_attr_init(&attr
);
43 pthread_attr_set_qos_class_np(&attr
, qos
, 0);
44 pthread_create(&thread
, &attr
, function
, (void *)type
);
46 T_LOG("pthread created\n");
47 pthread_get_qos_class_np(thread
, &qos_thread
, NULL
);
48 T_EXPECT_EQ(qos_thread
, (qos_class_t
)qos
, NULL
);
52 get_sched_pri(thread_t thread_port
)
56 thread_extended_info_data_t extended_info
;
57 mach_msg_type_number_t count
= THREAD_EXTENDED_INFO_COUNT
;
58 kr
= thread_info(thread_port
, THREAD_EXTENDED_INFO
,
59 (thread_info_t
)&extended_info
, &count
);
61 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "thread_info");
62 return extended_info
.pth_curpri
;
66 get_base_pri(thread_t thread_port
)
70 thread_extended_info_data_t extended_info
;
71 mach_msg_type_number_t count
= THREAD_EXTENDED_INFO_COUNT
;
72 kr
= thread_info(thread_port
, THREAD_EXTENDED_INFO
,
73 (thread_info_t
)&extended_info
, &count
);
75 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "thread_info");
76 return extended_info
.pth_priority
;
80 turnstile_prim_lock(int type
)
85 pthread_threadid_np(NULL
, &tid
);
86 T_LOG("sysctlbyname lock type %d called from thread %llu \n", type
, tid
);
87 ret
= sysctlbyname("kern.turnstiles_test_lock", NULL
, 0, &in_val
, sizeof(in_val
));
88 T_LOG("sysctlbyname lock returned from thread %llu with value %d \n", tid
, ret
);
92 turnstile_prim_unlock(int type
)
97 pthread_threadid_np(NULL
, &tid
);
98 T_LOG("sysctlbyname unlock type %d called from thread %llu \n", type
, tid
);
99 ret
= sysctlbyname("kern.turnstiles_test_unlock", NULL
, 0, &in_val
, sizeof(in_val
));
100 T_LOG("sysctlbyname unlock returned from thread %llu with value %d \n", tid
, ret
);
108 int sched_pri_to_check
;
109 int base_pri_to_check
;
113 chain_locking(void* args
)
115 struct thread_data
* data
= (struct thread_data
*) args
;
118 struct sched_param param
;
120 /* Change our priority to pri_to_set */
121 ret
= pthread_getschedparam(pthread_self(), &policy
, ¶m
);
122 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_getschedparam");
124 param
.sched_priority
= data
->pri_to_set
;
126 /* this sets both sched and base pri */
127 ret
= pthread_setschedparam(pthread_self(), policy
, ¶m
);
128 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_setschedparam");
130 pri
= get_sched_pri(mach_thread_self());
132 T_ASSERT_EQ(pri
, data
->pri_to_set
, "Priority before holding locks");
136 turnstile_prim_lock(data
->lock1
);
141 turnstile_prim_lock(data
->lock2
);
148 if (data
->sched_pri_to_check
) {
149 pri
= get_sched_pri(mach_thread_self());
150 T_ASSERT_EQ(pri
, data
->sched_pri_to_check
, "Sched priority while holding locks");
153 if (data
->base_pri_to_check
) {
154 pri
= get_base_pri(mach_thread_self());
155 T_ASSERT_EQ(pri
, data
->base_pri_to_check
, "Base priority while holding locks");
159 turnstile_prim_unlock(data
->lock2
);
163 turnstile_prim_unlock(data
->lock1
);
166 pri
= get_sched_pri(mach_thread_self());
167 T_ASSERT_EQ(pri
, data
->pri_to_set
, "Priority after releasing locks");
173 take_lock_check_priority(void * arg
)
175 int old_pri
= get_base_pri(mach_thread_self());
178 int after_unlock_pri
;
182 pthread_threadid_np(NULL
, &tid
);
184 T_ASSERT_EQ(old_pri
, 37, "thread(%llu) priority before acquiring the lock is %d\n", tid
, old_pri
);
186 /* Take the test lock */
187 turnstile_prim_lock(type
);
189 unboosted_pri
= get_base_pri(mach_thread_self());
190 T_ASSERT_EQ(unboosted_pri
, 37, "thread(%llu) priority after acquiring the lock (uncontended) is %d\n", tid
, unboosted_pri
);
194 /* Check for elevated priority */
195 boosted_pri
= get_base_pri(mach_thread_self());
196 T_ASSERT_EQ(boosted_pri
, 47, "thread(%llu) priority after contention by 47 thread is %d\n", tid
, boosted_pri
);
199 turnstile_prim_unlock(type
);
201 /* Check for regular priority */
202 after_unlock_pri
= get_base_pri(mach_thread_self());
203 T_ASSERT_EQ(after_unlock_pri
, 37, "thread(%llu) priority after dropping lock is %d\n", tid
, after_unlock_pri
);
209 try_to_take_lock_and_unlock(void *arg
)
214 pthread_threadid_np(NULL
, &tid
);
217 int old_pri
= get_base_pri(mach_thread_self());
218 T_ASSERT_EQ(old_pri
, 47, "thread(%llu) priority before acquiring the lock is %d\n", tid
, old_pri
);
220 /* Try taking the test lock */
221 turnstile_prim_lock(type
);
223 turnstile_prim_unlock(type
);
228 take_lock_and_exit(void * arg
)
230 int old_pri
= get_base_pri(mach_thread_self());
236 pthread_threadid_np(NULL
, &tid
);
238 T_ASSERT_EQ(old_pri
, 37, "thread(%llu) priority before acquiring the lock is %d\n", tid
, old_pri
);
240 /* Take the test lock */
241 turnstile_prim_lock(type
);
243 unboosted_pri
= get_base_pri(mach_thread_self());
244 T_ASSERT_EQ(unboosted_pri
, 37, "thread(%llu) priority after acquiring the lock (uncontended) is %d\n", tid
, unboosted_pri
);
248 /* Check for elevated priority */
249 boosted_pri
= get_base_pri(mach_thread_self());
250 T_ASSERT_EQ(boosted_pri
, 47, "thread(%llu) priority after contention by 47 thread is %d\n", tid
, boosted_pri
);
252 /* return without unlocking the lock */
257 unlock_an_owner_exited_lock(void *arg
)
262 pthread_threadid_np(NULL
, &tid
);
265 int old_pri
= get_base_pri(mach_thread_self());
266 T_ASSERT_EQ(old_pri
, 47, "thread(%llu) priority before acquiring the lock is %d\n", tid
, old_pri
);
268 /* Unlock the test lock causing the turnstile code to call thread_deallocate_safe */
269 turnstile_prim_unlock(type
);
274 * Test 1: test if lock contended by a UI thread boosts the owner to UI qos.
279 T_LOG("Test 1: test if lock contended by a UI thread boosts the owner to UI qos");
281 /* Create a thread at IN and take lock */
282 thread_create_at_qos(QOS_CLASS_USER_INITIATED
, &take_lock_check_priority
, type
);
284 /* Create a thread at UI and try to take lock */
285 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE
, &try_to_take_lock_and_unlock
, type
);
292 * Test 2: test if lock contended by a 2 UI thread boosts the owner to UI qos.
297 T_LOG("Test 2: test if lock contended by a 2 UI thread boosts the owner to UI qos");
299 /* Create a thread at IN and take lock */
300 thread_create_at_qos(QOS_CLASS_USER_INITIATED
, &take_lock_check_priority
, type
);
302 /* Create a thread at UI and try to take lock */
303 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE
, &try_to_take_lock_and_unlock
, type
);
305 /* Create a thread at UI and try to take lock */
306 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE
, &try_to_take_lock_and_unlock
, type
);
313 * Test 3: test if lock owner thread exiting without unlocking allows turnstile to work correctly.
318 T_LOG("Test 3: test if lock owner thread exiting without unlocking allows turnstile to work correctly");
320 /* Create a thread at IN and take lock */
321 thread_create_at_qos(QOS_CLASS_USER_INITIATED
, &take_lock_and_exit
, type
);
323 /* Create a thread at UI and try to take lock */
324 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE
, &try_to_take_lock_and_unlock
, type
);
326 /* Create a thread at UI and try to take lock */
327 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE
, &unlock_an_owner_exited_lock
, type
);
334 * Test 4: test if a chain of user-space turnstile primitives followed by kernel primitives works correctly.
339 pthread_t threads
[5] = {};
340 struct thread_data data
[5] = {};
342 T_LOG("Test 4: test if a chain of user-space turnstile primitives followed by kernel primitives works correctly");
345 * Chain: t4->ud->t3->uh->t2->kh->t1->kd->t0
346 * ud and uh (user space turnstiles) will push base pri and sched pri
347 * kd and kh (kernel space turnstiles) will push sched pri
348 * sched pri should be propagated up to the end
349 * kh is the breaking point of the chain for sched pri
353 /* Create a thread at priority 4 and take SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT lock */
354 data
[0].pri_to_set
= 4;
355 data
[0].lock1
= SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
; /* this should be not locked */
356 data
[0].lock2
= NULL
;
357 data
[0].sleep
= 10; /* long sleep, nothing is blocking this thread */
358 data
[0].sched_pri_to_check
= 60;
359 data
[0].base_pri_to_check
= 4;
360 pthread_create(&threads
[0], NULL
, chain_locking
, (void *)&data
[0]);
361 sleep(2); /* give the thread time to acquire the lock */
363 /* Create a thread at priority 31 and take SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE lock followed by SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT */
364 data
[1].pri_to_set
= 31;
365 data
[1].lock1
= SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE
; /* this should be not locked */
366 data
[1].lock2
= SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
; /* this should be locked */
367 data
[1].sleep
= 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
368 data
[1].sched_pri_to_check
= 60;
369 data
[1].base_pri_to_check
= 31;
370 pthread_create(&threads
[1], NULL
, chain_locking
, (void *)&data
[1]);
371 sleep(2); /* give the thread time to acquire the lock */
373 /* Create a thread at priority 40 and take SYSCTL_TURNSTILE_TEST_USER_HASHTABLE lock followed by SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE */
374 data
[2].pri_to_set
= 40;
375 data
[2].lock1
= SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
; /* this should be not locked */
376 data
[2].lock2
= SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE
; /* this should be locked */
377 data
[2].sleep
= 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
378 data
[2].sched_pri_to_check
= 60;
379 data
[2].base_pri_to_check
= 60;
380 pthread_create(&threads
[2], NULL
, chain_locking
, (void *)&data
[2]);
381 sleep(2); /* give the thread time to acquire the lock */
383 /* Create a thread at priority 47 and take SYSCTL_TURNSTILE_TEST_USER_DEFAULT lock followed by SYSCTL_TURNSTILE_TEST_USER_HASHTABLE */
384 data
[3].pri_to_set
= 47;
385 data
[3].lock1
= SYSCTL_TURNSTILE_TEST_USER_DEFAULT
; /* this should be not locked */
386 data
[3].lock2
= SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
; /* this should be locked */
387 data
[3].sleep
= 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
388 data
[3].sched_pri_to_check
= 60;
389 data
[3].base_pri_to_check
= 60;
390 pthread_create(&threads
[3], NULL
, chain_locking
, (void *)&data
[3]);
391 sleep(2); /* give the thread time to acquire the lock */
393 /* Create a thread at priority 60 and take SYSCTL_TURNSTILE_TEST_USER_DEFAULT */
394 data
[4].pri_to_set
= 60;
395 data
[4].lock1
= SYSCTL_TURNSTILE_TEST_USER_DEFAULT
; /* this should be locked */
396 data
[4].lock2
= NULL
;
397 data
[4].sleep
= 0; /* no need to sleep, nothing should be pushing by the time it acquires the lock */
398 data
[4].sched_pri_to_check
= 60; /* this is its own priority */
399 data
[4].base_pri_to_check
= 60;
400 pthread_create(&threads
[4], NULL
, chain_locking
, (void *)&data
[4]);
407 * Test 5: test if a chain of user-space turnstile primitives interleaved by kernel primitives works correctly.
412 pthread_t threads
[5] = {};
413 struct thread_data data
[5] = {};
415 T_LOG("Test 5: test if a chain of user-space turnstile primitives interleaved by kernel primitives works correctly");
418 * Chain: t4->ud->t3->kh->t2->uh->t1->kd->t0
419 * ud and uh (user space turnstiles) will push base pri and sched pri
420 * kd and kh (kernel space turnstiles) will push sched pri
421 * uh is the breaking point of the chain for sched pri
424 /* Create a thread at priority 4 and take SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT lock */
425 data
[0].pri_to_set
= 4;
426 data
[0].lock1
= SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
; /* this should be not locked */
427 data
[0].lock2
= NULL
;
428 data
[0].sleep
= 10; /* long sleep, nothing is blocking this thread */
429 data
[0].sched_pri_to_check
= 41;
430 data
[0].base_pri_to_check
= 4;
431 pthread_create(&threads
[0], NULL
, chain_locking
, (void *)&data
[0]);
432 sleep(2); /* give the thread time to acquire the lock */
434 /* Create a thread at priority 31 and take SYSCTL_TURNSTILE_TEST_USER_HASHTABLE lock followed by SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT */
435 data
[1].pri_to_set
= 31;
436 data
[1].lock1
= SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
; /* this should be not locked */
437 data
[1].lock2
= SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT
; /* this should be locked */
438 data
[1].sleep
= 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
439 data
[1].sched_pri_to_check
= 41;
440 data
[1].base_pri_to_check
= 41;
441 pthread_create(&threads
[1], NULL
, chain_locking
, (void *)&data
[1]);
442 sleep(2); /* give the thread time to acquire the lock */
444 /* Create a thread at priority 41 and take SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE lock followed by SYSCTL_TURNSTILE_TEST_USER_HASHTABLE */
445 data
[2].pri_to_set
= 41;
446 data
[2].lock1
= SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE
; /* this should be not locked */
447 data
[2].lock2
= SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
; /* this should be locked */
448 data
[2].sleep
= 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
449 data
[2].sched_pri_to_check
= 60;
450 data
[2].base_pri_to_check
= 41;
451 pthread_create(&threads
[2], NULL
, chain_locking
, (void *)&data
[2]);
452 sleep(2); /* give the thread time to acquire the lock */
454 /* Create a thread at priority 47 and take SYSCTL_TURNSTILE_TEST_USER_DEFAULT lock followed by SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE */
455 data
[3].pri_to_set
= 47;
456 data
[3].lock1
= SYSCTL_TURNSTILE_TEST_USER_DEFAULT
; /* this should be not locked */
457 data
[3].lock2
= SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE
; /* this should be locked */
458 data
[3].sleep
= 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
459 data
[3].sched_pri_to_check
= 60;
460 data
[3].base_pri_to_check
= 60;
461 pthread_create(&threads
[3], NULL
, chain_locking
, (void *)&data
[3]);
462 sleep(2); /* give the thread time to acquire the lock */
464 /* Create a thread at priority 60 and take SYSCTL_TURNSTILE_TEST_USER_DEFAULT */
465 data
[4].pri_to_set
= 60;
466 data
[4].lock1
= SYSCTL_TURNSTILE_TEST_USER_DEFAULT
; /* this should be locked */
467 data
[4].lock2
= NULL
;
468 data
[4].sleep
= 0; /* no need to sleep, nothing should be pushing by the time it acquires the lock */
469 data
[4].sched_pri_to_check
= 60; /* this is its own priority */
470 data
[4].base_pri_to_check
= 60;
471 pthread_create(&threads
[4], NULL
, chain_locking
, (void *)&data
[4]);
477 T_DECL(turnstile_test
, "Turnstile test", T_META_ASROOT(YES
))
479 test1(SYSCTL_TURNSTILE_TEST_USER_DEFAULT
);
480 test2(SYSCTL_TURNSTILE_TEST_USER_DEFAULT
);
481 test3(SYSCTL_TURNSTILE_TEST_USER_DEFAULT
);
483 test1(SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
);
484 test2(SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
);
485 test3(SYSCTL_TURNSTILE_TEST_USER_HASHTABLE
);
488 * rdar://problem/46302128
489 * These tests are using a sysctl to lock a dummy kernel resource that uses turnstile.
490 * However a thread holding a kernel push from turnstile should never return in
491 * userspace, and rdar://problem/24194397 adds an assert for it.