]>
Commit | Line | Data |
---|---|---|
d9a64523 A |
1 | /* |
2 | * turnstiles_test: Tests turnstile kernel primitive. | |
3 | */ | |
4 | ||
5 | #ifdef T_NAMESPACE | |
6 | #undef T_NAMESPACE | |
7 | #endif | |
8 | ||
9 | #include <darwintest.h> | |
10 | #include <darwintest_multiprocess.h> | |
11 | ||
12 | #include <pthread.h> | |
13 | #include <launch.h> | |
14 | #include <servers/bootstrap.h> | |
15 | #include <stdlib.h> | |
16 | #include <sys/event.h> | |
17 | #include <unistd.h> | |
18 | #include <crt_externs.h> | |
19 | #include <sys/sysctl.h> | |
20 | #include <sys/types.h> | |
21 | ||
22 | #define SYSCTL_TURNSTILE_TEST_DEFAULT 1 | |
23 | #define SYSCTL_TURNSTILE_TEST_GLOBAL_HASHTABLE 2 | |
24 | ||
25 | ||
26 | T_GLOBAL_META(T_META_NAMESPACE("xnu.turnstiles_test")); | |
27 | ||
28 | static void | |
29 | thread_create_at_qos(qos_class_t qos, void * (*function)(void *), int type) | |
30 | { | |
31 | qos_class_t qos_thread; | |
32 | pthread_t thread; | |
33 | pthread_attr_t attr; | |
34 | int ret; | |
35 | ||
36 | ret = setpriority(PRIO_DARWIN_ROLE, 0, PRIO_DARWIN_ROLE_UI_FOCAL); | |
37 | if (ret != 0) { | |
38 | T_LOG("set priority failed\n"); | |
39 | } | |
40 | ||
41 | pthread_attr_init(&attr); | |
42 | pthread_attr_set_qos_class_np(&attr, qos, 0); | |
43 | pthread_create(&thread, &attr, function, (void *)type); | |
44 | ||
45 | T_LOG("pthread created\n"); | |
46 | pthread_get_qos_class_np(thread, &qos_thread, NULL); | |
47 | T_EXPECT_EQ(qos_thread, (qos_class_t)qos, NULL); | |
48 | } | |
49 | ||
50 | static int | |
51 | get_pri(thread_t thread_port) { | |
52 | kern_return_t kr; | |
53 | ||
54 | thread_extended_info_data_t extended_info; | |
55 | mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT; | |
56 | kr = thread_info(thread_port, THREAD_EXTENDED_INFO, | |
57 | (thread_info_t)&extended_info, &count); | |
58 | ||
59 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_info"); | |
60 | return extended_info.pth_curpri; | |
61 | } | |
62 | ||
63 | static void | |
64 | turnstile_prim_lock(int type) | |
65 | { | |
66 | int ret; | |
67 | uint64_t tid; | |
68 | int in_val = type; | |
69 | pthread_threadid_np(NULL, &tid); | |
70 | T_LOG("sysctlbyname lock called from thread %llu \n", tid); | |
71 | ret = sysctlbyname("kern.turnstiles_test_lock", NULL, 0, &in_val, sizeof(in_val)); | |
72 | T_LOG("sysctlbyname lock returned from thread %llu with value %d \n", tid, ret); | |
73 | } | |
74 | ||
75 | static void | |
76 | turnstile_prim_unlock(int type) | |
77 | { | |
78 | int ret; | |
79 | uint64_t tid; | |
80 | int in_val = type; | |
81 | pthread_threadid_np(NULL, &tid); | |
82 | T_LOG("sysctlbyname unlock called from thread %llu \n", tid); | |
83 | ret = sysctlbyname("kern.turnstiles_test_unlock", NULL, 0, &in_val, sizeof(in_val)); | |
84 | T_LOG("sysctlbyname unlock returned from thread %llu with value %d \n", tid, ret); | |
85 | } | |
86 | ||
87 | static void * | |
88 | take_lock_check_priority(void * arg) | |
89 | { | |
90 | int old_pri = get_pri(mach_thread_self()); | |
91 | int unboosted_pri; | |
92 | int boosted_pri; | |
93 | int after_unlock_pri; | |
94 | uint64_t tid; | |
95 | int type = (int)arg; | |
96 | ||
97 | pthread_threadid_np(NULL, &tid); | |
98 | ||
99 | T_ASSERT_EQ(old_pri, 37, "thread(%llu) priority before acquiring the lock is %d\n", tid, old_pri); | |
100 | ||
101 | /* Take the test lock */ | |
102 | turnstile_prim_lock(type); | |
103 | ||
104 | unboosted_pri = get_pri(mach_thread_self()); | |
105 | T_ASSERT_EQ(unboosted_pri, 37, "thread(%llu) priority after acquiring the lock (uncontended) is %d\n", tid, unboosted_pri); | |
106 | ||
107 | sleep(8); | |
108 | ||
109 | /* Check for elevated priority */ | |
110 | boosted_pri = get_pri(mach_thread_self()); | |
111 | T_ASSERT_EQ(boosted_pri, 47, "thread(%llu) priority after contention by 47 thread is %d\n", tid, boosted_pri); | |
112 | ||
113 | /* Drop the lock */ | |
114 | turnstile_prim_unlock(type); | |
115 | ||
116 | /* Check for regular priority */ | |
117 | after_unlock_pri = get_pri(mach_thread_self()); | |
118 | T_ASSERT_EQ(after_unlock_pri, 37, "thread(%llu) priority after dropping lock is %d\n", tid, after_unlock_pri); | |
119 | ||
120 | return NULL; | |
121 | } | |
122 | ||
123 | static void * | |
124 | try_to_take_lock_and_unlock(void *arg) | |
125 | { | |
126 | uint64_t tid; | |
127 | int type = (int)arg; | |
128 | ||
129 | pthread_threadid_np(NULL, &tid); | |
130 | sleep(4); | |
131 | ||
132 | int old_pri = get_pri(mach_thread_self()); | |
133 | T_ASSERT_EQ(old_pri, 47, "thread(%llu) priority before acquiring the lock is %d\n", tid, old_pri); | |
134 | ||
135 | /* Try taking the test lock */ | |
136 | turnstile_prim_lock(type); | |
137 | sleep (2); | |
138 | turnstile_prim_unlock(type); | |
139 | return NULL; | |
140 | } | |
141 | ||
142 | static void * | |
143 | take_lock_and_exit(void * arg) | |
144 | { | |
145 | int old_pri = get_pri(mach_thread_self()); | |
146 | int unboosted_pri; | |
147 | int boosted_pri; | |
148 | uint64_t tid; | |
149 | int type = (int)arg; | |
150 | ||
151 | pthread_threadid_np(NULL, &tid); | |
152 | ||
153 | T_ASSERT_EQ(old_pri, 37, "thread(%llu) priority before acquiring the lock is %d\n", tid, old_pri); | |
154 | ||
155 | /* Take the test lock */ | |
156 | turnstile_prim_lock(type); | |
157 | ||
158 | unboosted_pri = get_pri(mach_thread_self()); | |
159 | T_ASSERT_EQ(unboosted_pri, 37, "thread(%llu) priority after acquiring the lock (uncontended) is %d\n", tid, unboosted_pri); | |
160 | ||
161 | sleep(8); | |
162 | ||
163 | /* Check for elevated priority */ | |
164 | boosted_pri = get_pri(mach_thread_self()); | |
165 | T_ASSERT_EQ(boosted_pri, 47, "thread(%llu) priority after contention by 47 thread is %d\n", tid, boosted_pri); | |
166 | ||
167 | /* return without unlocking the lock */ | |
168 | return NULL; | |
169 | } | |
170 | ||
171 | static void * | |
172 | unlock_an_owner_exited_lock(void *arg) | |
173 | { | |
174 | uint64_t tid; | |
175 | int type = (int)arg; | |
176 | ||
177 | pthread_threadid_np(NULL, &tid); | |
178 | sleep(12); | |
179 | ||
180 | int old_pri = get_pri(mach_thread_self()); | |
181 | T_ASSERT_EQ(old_pri, 47, "thread(%llu) priority before acquiring the lock is %d\n", tid, old_pri); | |
182 | ||
183 | /* Unlock the test lock causing the turnstile code to call thread_deallocate_safe */ | |
184 | turnstile_prim_unlock(type); | |
185 | return NULL; | |
186 | } | |
187 | ||
188 | /* | |
189 | * Test 1: test if lock contended by a UI thread boosts the owner to UI qos. | |
190 | */ | |
191 | static void | |
192 | test1(int type) | |
193 | { | |
194 | T_LOG("Test 1: test if lock contended by a UI thread boosts the owner to UI qos"); | |
195 | ||
196 | /* Create a thread at IN and take lock */ | |
197 | thread_create_at_qos(QOS_CLASS_USER_INITIATED, &take_lock_check_priority, type); | |
198 | ||
199 | /* Create a thread at UI and try to take lock */ | |
200 | thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &try_to_take_lock_and_unlock, type); | |
201 | ||
202 | sleep(12); | |
203 | return; | |
204 | } | |
205 | ||
206 | /* | |
207 | * Test 2: test if lock contended by a 2 UI thread boosts the owner to UI qos. | |
208 | */ | |
209 | static void | |
210 | test2(int type) | |
211 | { | |
212 | T_LOG("Test 2: test if lock contended by a 2 UI thread boosts the owner to UI qos"); | |
213 | ||
214 | /* Create a thread at IN and take lock */ | |
215 | thread_create_at_qos(QOS_CLASS_USER_INITIATED, &take_lock_check_priority, type); | |
216 | ||
217 | /* Create a thread at UI and try to take lock */ | |
218 | thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &try_to_take_lock_and_unlock, type); | |
219 | ||
220 | /* Create a thread at UI and try to take lock */ | |
221 | thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &try_to_take_lock_and_unlock, type); | |
222 | ||
223 | sleep(16); | |
224 | return; | |
225 | } | |
226 | ||
227 | /* | |
228 | * Test 3: test if lock owner thread exiting without unlocking allows turnstile to work correctly. | |
229 | */ | |
230 | static void | |
231 | test3(int type) | |
232 | { | |
233 | T_LOG("Test 3: test if lock owner thread exiting without unlocking allows turnstile to work correctly"); | |
234 | ||
235 | /* Create a thread at IN and take lock */ | |
236 | thread_create_at_qos(QOS_CLASS_USER_INITIATED, &take_lock_and_exit, type); | |
237 | ||
238 | /* Create a thread at UI and try to take lock */ | |
239 | thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &try_to_take_lock_and_unlock, type); | |
240 | ||
241 | /* Create a thread at UI and try to take lock */ | |
242 | thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE, &unlock_an_owner_exited_lock, type); | |
243 | ||
244 | sleep(16); | |
245 | return; | |
246 | } | |
247 | ||
248 | T_DECL(turnstile_test, "Turnstile test", T_META_ASROOT(YES)) | |
249 | { | |
250 | test1(SYSCTL_TURNSTILE_TEST_DEFAULT); | |
251 | test2(SYSCTL_TURNSTILE_TEST_DEFAULT); | |
252 | test3(SYSCTL_TURNSTILE_TEST_DEFAULT); | |
253 | ||
254 | test1(SYSCTL_TURNSTILE_TEST_GLOBAL_HASHTABLE); | |
255 | test2(SYSCTL_TURNSTILE_TEST_GLOBAL_HASHTABLE); | |
256 | test3(SYSCTL_TURNSTILE_TEST_GLOBAL_HASHTABLE); | |
257 | ||
258 | } |