]>
Commit | Line | Data |
---|---|---|
5ba3f43e A |
1 | /* |
2 | * kevent_qos: Tests Synchronous IPC QOS override. | |
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 <dispatch/dispatch.h> | |
13 | #include <pthread.h> | |
14 | #include <launch.h> | |
15 | #include <mach/mach.h> | |
16 | #include <mach/message.h> | |
17 | #include <mach/mach_voucher.h> | |
18 | #include <pthread/workqueue_private.h> | |
19 | #include <voucher/ipc_pthread_priority_types.h> | |
20 | #include <servers/bootstrap.h> | |
21 | #include <stdlib.h> | |
22 | #include <sys/event.h> | |
23 | #include <unistd.h> | |
24 | #include <crt_externs.h> | |
25 | ||
26 | T_GLOBAL_META(T_META_NAMESPACE("xnu.kevent_qos")); | |
27 | ||
28 | #define ARRAYLEN(arr) (sizeof(arr) / sizeof(arr[0])) | |
29 | ||
30 | #define RECV_TIMEOUT_SECS (4) | |
31 | #define SEND_TIMEOUT_SECS (6) | |
32 | #define HELPER_TIMEOUT_SECS (15) | |
33 | ||
34 | #define ENV_VAR_QOS (3) | |
35 | static const char *qos_env[ENV_VAR_QOS] = {"XNU_TEST_QOS_BO", "XNU_TEST_QOS_QO", "XNU_TEST_QOS_AO"}; | |
36 | static const char *qos_name_env[ENV_VAR_QOS] = {"XNU_TEST_QOS_NAME_BO", "XNU_TEST_QOS_NAME_QO", "XNU_TEST_QOS_NAME_AO"}; | |
37 | ||
38 | #define ENV_VAR_FUNCTION (1) | |
39 | static const char *wl_function_name = "XNU_TEST_WL_FUNCTION"; | |
40 | ||
41 | static qos_class_t g_expected_qos[ENV_VAR_QOS]; | |
42 | static const char *g_expected_qos_name[ENV_VAR_QOS]; | |
43 | ||
44 | #define ENV_QOS_BEFORE_OVERRIDE (0) | |
45 | #define ENV_QOS_QUEUE_OVERRIDE (1) | |
46 | #define ENV_QOS_AFTER_OVERRIDE (2) | |
47 | ||
48 | #pragma mark pthread callbacks | |
49 | ||
50 | static void | |
51 | worker_cb(pthread_priority_t __unused priority) | |
52 | { | |
53 | T_FAIL("a worker thread was created"); | |
54 | } | |
55 | ||
56 | static void | |
57 | event_cb(void ** __unused events, int * __unused nevents) | |
58 | { | |
59 | T_FAIL("a kevent routine was called instead of workloop"); | |
60 | } | |
61 | ||
62 | /* | |
63 | * Basic WL handler callback, it sleeps for n seconds and then checks the | |
64 | * effective Qos of the servicer thread. | |
65 | */ | |
66 | static void | |
67 | workloop_cb_test_intransit(uint64_t *workloop_id __unused, void **eventslist __unused, int *events) | |
68 | { | |
69 | T_LOG("Workloop handler workloop_cb_test_intransit called. " | |
70 | "Will wait for %d seconds to make sure client enqueues the sync msg \n", | |
71 | 2 * RECV_TIMEOUT_SECS); | |
72 | ||
73 | /* Wait for the client to send the high priority message to override the qos */ | |
74 | sleep(2 * RECV_TIMEOUT_SECS); | |
75 | ||
76 | /* Skip the test if we can't check Qos */ | |
77 | if (geteuid() != 0) { | |
78 | T_SKIP("kevent_qos test requires root privileges to run."); | |
79 | } | |
80 | ||
81 | /* The effective Qos should be the one expected after override */ | |
82 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], | |
83 | "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]); | |
84 | ||
85 | T_END; | |
86 | *events = 0; | |
87 | } | |
88 | ||
89 | /* | |
90 | * WL handler which checks if the servicer thread has correct Qos. | |
91 | */ | |
92 | static void | |
93 | workloop_cb_test_sync_send(uint64_t *workloop_id __unused, void **eventslist __unused, int *events) | |
94 | { | |
95 | T_LOG("Workloop handler workloop_cb_test_sync_send called"); | |
96 | ||
97 | if (geteuid() != 0) { | |
98 | T_SKIP("kevent_qos test requires root privileges to run."); | |
99 | } | |
100 | ||
101 | /* The effective Qos should be the one expected after override */ | |
102 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], | |
103 | "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]); | |
104 | ||
105 | T_END; | |
106 | *events = 0; | |
107 | } | |
108 | ||
109 | /* | |
110 | * WL handler which checks the overridden Qos and then enables the knote and checks | |
111 | * for the Qos again if that dropped the sync ipc override. | |
112 | */ | |
113 | static void | |
114 | workloop_cb_test_sync_send_and_enable(uint64_t *workloop_id, struct kevent_qos_s **eventslist, int *events) | |
115 | { | |
116 | int r; | |
117 | T_LOG("Workloop handler workloop_cb_test_sync_send_and_enable called"); | |
118 | ||
119 | if (geteuid() != 0) { | |
120 | T_SKIP("kevent_qos test requires root privileges to run."); | |
121 | } | |
122 | ||
123 | /* The effective Qos should be the one expected after override */ | |
124 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], | |
125 | "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]); | |
126 | ||
127 | /* Enable the knote */ | |
128 | struct kevent_qos_s *kev = *eventslist; | |
129 | kev->flags = EV_ADD | EV_ENABLE | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED; | |
130 | struct kevent_qos_s kev_err[] = {{ 0 }}; | |
131 | ||
132 | r = kevent_id(*workloop_id, kev, 1, kev_err, 1, NULL, | |
133 | NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS | KEVENT_FLAG_DYNAMIC_KQ_MUST_EXIST); | |
134 | T_QUIET; T_ASSERT_POSIX_SUCCESS(r, "kevent_id"); | |
135 | ||
136 | /* Sync override should have been removed */ | |
137 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], | |
138 | "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_BEFORE_OVERRIDE]); | |
139 | ||
140 | T_END; | |
141 | *events = 0; | |
142 | } | |
143 | ||
144 | /* | |
145 | * WL handler receives the first message and checks sync ipc override, then enables the knote | |
146 | * and receives 2nd message and checks it sync ipc override. | |
147 | */ | |
148 | static int send_two_sync_handler_called = 0; | |
149 | static void | |
150 | workloop_cb_test_send_two_sync(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, int *events) | |
151 | { | |
152 | T_LOG("Workloop handler workloop_cb_test_send_two_sync called for %d time", send_two_sync_handler_called + 1); | |
153 | ||
154 | if (geteuid() != 0) { | |
155 | T_SKIP("kevent_qos test requires root privileges to run."); | |
156 | } | |
157 | ||
158 | T_LOG("Number of events received is %d\n", *events); | |
159 | ||
160 | if (send_two_sync_handler_called == 0) { | |
161 | /* The effective Qos should be the one expected after override */ | |
162 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], | |
163 | "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]); | |
164 | ||
165 | /* Enable the knote to get 2nd message */ | |
166 | struct kevent_qos_s *kev = *eventslist; | |
167 | kev->flags = EV_ADD | EV_ENABLE | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED; | |
168 | kev->fflags = (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY | | |
169 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) | | |
170 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | | |
171 | MACH_RCV_VOUCHER); | |
172 | *events = 1; | |
173 | } else { | |
174 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], | |
175 | "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_BEFORE_OVERRIDE]); | |
176 | T_END; | |
177 | *events = 0; | |
178 | } | |
179 | send_two_sync_handler_called++; | |
180 | } | |
181 | ||
182 | /* | |
183 | * Checks the sync ipc override and then waits for client to destroy the | |
184 | * special reply port and checks if that removes the sync ipc override. | |
185 | */ | |
186 | static boolean_t two_send_and_destroy_test_passed = FALSE; | |
187 | static int two_send_and_destroy_handler = 0; | |
188 | static void | |
189 | workloop_cb_test_two_send_and_destroy(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist __unused, int *events) | |
190 | { | |
191 | T_LOG("Workloop handler workloop_cb_test_two_send_and_destroy called %d times", two_send_and_destroy_handler + 1); | |
192 | ||
193 | if (geteuid() != 0) { | |
194 | T_SKIP("kevent_qos test requires root privileges to run."); | |
195 | } | |
196 | ||
197 | if (two_send_and_destroy_handler == 0) { | |
198 | /* The effective Qos should be the one expected after override */ | |
199 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], | |
200 | "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]); | |
201 | ||
202 | sleep(2 * RECV_TIMEOUT_SECS); | |
203 | ||
204 | /* Special reply port should have been destroyed, check Qos again */ | |
205 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], | |
206 | "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_BEFORE_OVERRIDE]); | |
207 | ||
208 | two_send_and_destroy_test_passed = TRUE; | |
209 | } else { | |
210 | if (two_send_and_destroy_test_passed) { | |
211 | T_END; | |
212 | } | |
213 | } | |
214 | ||
215 | /* Enable the knote to get next message */ | |
216 | struct kevent_qos_s *kev = *eventslist; | |
217 | kev->flags = EV_ADD | EV_ENABLE | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED; | |
218 | kev->fflags = (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY | | |
219 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) | | |
220 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | | |
221 | MACH_RCV_VOUCHER); | |
222 | *events = 1; | |
223 | two_send_and_destroy_handler++; | |
224 | T_LOG("Handler returning \n"); | |
225 | } | |
226 | ||
227 | #pragma mark Mach receive | |
228 | ||
229 | #define KEVENT_QOS_SERVICE_NAME "com.apple.xnu.test.kevent_qos" | |
230 | ||
231 | static mach_port_t | |
232 | get_server_port(void) | |
233 | { | |
234 | mach_port_t port; | |
235 | kern_return_t kr = bootstrap_check_in(bootstrap_port, | |
236 | KEVENT_QOS_SERVICE_NAME, &port); | |
237 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "server bootstrap_check_in"); | |
238 | return port; | |
239 | } | |
240 | ||
241 | static void | |
242 | env_set_qos(char **env, qos_class_t qos[], const char *qos_name[], const char *wl_function) | |
243 | { | |
244 | int i; | |
245 | char *qos_str, *qos_name_str; | |
246 | for (i = 0; i < ENV_VAR_QOS; i++) { | |
247 | T_QUIET; T_ASSERT_POSIX_SUCCESS(asprintf(&qos_str, "%s=%d", qos_env[i] , qos[i]), | |
248 | NULL); | |
249 | T_QUIET; T_ASSERT_POSIX_SUCCESS( | |
250 | asprintf(&qos_name_str, "%s=%s", qos_name_env[i], qos_name[i]), NULL); | |
251 | env[2 * i] = qos_str; | |
252 | env[2 * i + 1] = qos_name_str; | |
253 | } | |
254 | T_QUIET; T_ASSERT_POSIX_SUCCESS(asprintf(&env[2 * i], "%s=%s", wl_function_name, wl_function), | |
255 | NULL); | |
256 | env[2 * i + 1] = NULL; | |
257 | } | |
258 | ||
259 | static void | |
260 | environ_get_qos(qos_class_t qos[], const char *qos_name[], const char **wl_function) | |
261 | { | |
262 | char *qos_str; | |
263 | char *qos_end; | |
264 | int i; | |
265 | ||
266 | for (i = 0; i < ENV_VAR_QOS; i++) { | |
267 | qos_str = getenv(qos_env[i]); | |
268 | T_QUIET; T_ASSERT_NOTNULL(qos_str, "getenv(%s)", qos_env[i]); | |
269 | ||
270 | unsigned long qos_l = strtoul(qos_str, &qos_end, 10); | |
271 | T_QUIET; T_ASSERT_EQ(*qos_end, '\0', "getenv(%s) = '%s' should be an " | |
272 | "integer", qos_env[i], qos_str); | |
273 | ||
274 | T_QUIET; T_ASSERT_LT(qos_l, (unsigned long)100, "getenv(%s) = '%s' should " | |
275 | "be less than 100", qos_env[i], qos_str); | |
276 | ||
277 | qos[i] = (qos_class_t)qos_l; | |
278 | qos_name[i] = getenv(qos_name_env[i]); | |
279 | T_QUIET; T_ASSERT_NOTNULL(qos_name[i], "getenv(%s)", qos_name_env[i]); | |
280 | } | |
281 | *wl_function = getenv(wl_function_name); | |
282 | T_QUIET; T_ASSERT_NOTNULL(*wl_function, "getenv(%s)", wl_function_name); | |
283 | } | |
284 | ||
285 | static mach_voucher_t | |
286 | create_pthpriority_voucher(mach_msg_priority_t qos) | |
287 | { | |
288 | char voucher_buf[sizeof(mach_voucher_attr_recipe_data_t) + sizeof(ipc_pthread_priority_value_t)]; | |
289 | ||
290 | mach_voucher_t voucher = MACH_PORT_NULL; | |
291 | kern_return_t ret; | |
292 | ipc_pthread_priority_value_t ipc_pthread_priority_value = | |
293 | (ipc_pthread_priority_value_t)qos; | |
294 | ||
295 | mach_voucher_attr_raw_recipe_array_t recipes; | |
296 | mach_voucher_attr_raw_recipe_size_t recipe_size = 0; | |
297 | mach_voucher_attr_recipe_t recipe = | |
298 | (mach_voucher_attr_recipe_t)&voucher_buf[recipe_size]; | |
299 | ||
300 | recipe->key = MACH_VOUCHER_ATTR_KEY_PTHPRIORITY; | |
301 | recipe->command = MACH_VOUCHER_ATTR_PTHPRIORITY_CREATE; | |
302 | recipe->previous_voucher = MACH_VOUCHER_NULL; | |
303 | memcpy((char *)&recipe->content[0], &ipc_pthread_priority_value, sizeof(ipc_pthread_priority_value)); | |
304 | recipe->content_size = sizeof(ipc_pthread_priority_value_t); | |
305 | recipe_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size; | |
306 | ||
307 | recipes = (mach_voucher_attr_raw_recipe_array_t)&voucher_buf[0]; | |
308 | ||
309 | ret = host_create_mach_voucher(mach_host_self(), | |
310 | recipes, | |
311 | recipe_size, | |
312 | &voucher); | |
313 | ||
314 | T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "client host_create_mach_voucher"); | |
315 | return voucher; | |
316 | } | |
317 | ||
318 | static void | |
319 | send( | |
320 | mach_port_t send_port, | |
321 | mach_port_t reply_port, | |
322 | mach_port_t msg_port, | |
323 | mach_msg_priority_t qos) | |
324 | { | |
325 | kern_return_t ret = 0; | |
326 | ||
327 | struct { | |
328 | mach_msg_header_t header; | |
329 | mach_msg_body_t body; | |
330 | mach_msg_port_descriptor_t port_descriptor; | |
331 | } send_msg = { | |
332 | .header = | |
333 | { | |
334 | .msgh_remote_port = send_port, | |
335 | .msgh_local_port = reply_port, | |
336 | .msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, | |
337 | reply_port ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0, | |
338 | MACH_MSG_TYPE_MOVE_SEND, | |
339 | MACH_MSGH_BITS_COMPLEX), | |
340 | .msgh_id = 0x100, | |
341 | .msgh_size = sizeof(send_msg), | |
342 | .msgh_voucher_port = create_pthpriority_voucher(qos), | |
343 | }, | |
344 | .body = | |
345 | { | |
346 | .msgh_descriptor_count = 1, | |
347 | }, | |
348 | .port_descriptor = | |
349 | { | |
350 | .name = msg_port, .disposition = MACH_MSG_TYPE_MOVE_RECEIVE, .type = MACH_MSG_PORT_DESCRIPTOR, | |
351 | }, | |
352 | }; | |
353 | ||
354 | if (msg_port == MACH_PORT_NULL) { | |
355 | send_msg.body.msgh_descriptor_count = 0; | |
356 | } | |
357 | ||
358 | ret = mach_msg(&(send_msg.header), | |
359 | MACH_SEND_MSG | | |
360 | MACH_SEND_TIMEOUT | | |
361 | MACH_SEND_OVERRIDE| | |
362 | (reply_port ? MACH_SEND_SYNC_OVERRIDE : 0) , | |
363 | send_msg.header.msgh_size, | |
364 | 0, | |
365 | MACH_PORT_NULL, | |
366 | 0, | |
367 | 0); | |
368 | ||
369 | T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "client mach_msg"); | |
370 | } | |
371 | ||
372 | static void | |
373 | receive( | |
374 | mach_port_t rcv_port, | |
375 | mach_port_t notify_port) | |
376 | { | |
377 | kern_return_t ret = 0; | |
378 | ||
379 | struct { | |
380 | mach_msg_header_t header; | |
381 | mach_msg_body_t body; | |
382 | mach_msg_port_descriptor_t port_descriptor; | |
383 | } rcv_msg = { | |
384 | .header = | |
385 | { | |
386 | .msgh_remote_port = MACH_PORT_NULL, | |
387 | .msgh_local_port = rcv_port, | |
388 | .msgh_size = sizeof(rcv_msg), | |
389 | }, | |
390 | }; | |
391 | ||
392 | T_LOG("Client: Starting sync receive\n"); | |
393 | ||
394 | ret = mach_msg(&(rcv_msg.header), | |
395 | MACH_RCV_MSG | | |
396 | MACH_RCV_TIMEOUT | | |
397 | MACH_RCV_SYNC_WAIT, | |
398 | 0, | |
399 | rcv_msg.header.msgh_size, | |
400 | rcv_port, | |
401 | SEND_TIMEOUT_SECS * 1000, | |
402 | notify_port); | |
403 | ||
404 | if (!(ret == MACH_RCV_TIMED_OUT || ret == MACH_MSG_SUCCESS)) { | |
405 | T_ASSERT_FAIL("Sync rcv failed \n"); | |
406 | } | |
407 | } | |
408 | ||
409 | T_HELPER_DECL(qos_get_special_reply_port, | |
410 | "Test get_special_reply_port and it's corner cases.") | |
411 | { | |
412 | mach_port_t special_reply_port; | |
413 | mach_port_t new_special_reply_port; | |
414 | ||
415 | special_reply_port = thread_get_special_reply_port(); | |
416 | T_QUIET; T_ASSERT_NOTNULL(special_reply_port , "get_thread_special_reply_port"); | |
417 | ||
418 | new_special_reply_port = thread_get_special_reply_port(); | |
419 | T_QUIET; T_ASSERT_NOTNULL(new_special_reply_port , "get_thread_special_reply_port"); | |
420 | ||
421 | mach_port_destroy(mach_task_self(), special_reply_port); | |
422 | mach_port_destroy(mach_task_self(), new_special_reply_port); | |
423 | ||
424 | new_special_reply_port = thread_get_special_reply_port(); | |
425 | T_QUIET; T_ASSERT_NOTNULL(new_special_reply_port , "get_thread_special_reply_port"); | |
426 | ||
427 | T_END; | |
428 | } | |
429 | ||
430 | T_HELPER_DECL(qos_client_send_to_intransit, | |
431 | "Send synchronous messages to an intransit port") | |
432 | { | |
433 | mach_port_t qos_send_port; | |
434 | mach_port_t msg_port; | |
435 | mach_port_t special_reply_port; | |
436 | ||
437 | kern_return_t kr = bootstrap_look_up(bootstrap_port, | |
438 | KEVENT_QOS_SERVICE_NAME, &qos_send_port); | |
439 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up"); | |
440 | ||
441 | special_reply_port = thread_get_special_reply_port(); | |
442 | T_QUIET; T_ASSERT_NOTNULL(special_reply_port , "get_thread_special_reply_port"); | |
443 | ||
444 | /* Create a rcv right to send in a msg */ | |
445 | kr = mach_port_allocate(mach_task_self(), | |
446 | MACH_PORT_RIGHT_RECEIVE, | |
447 | &msg_port); | |
448 | ||
449 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_port_allocate"); | |
450 | ||
451 | kr = mach_port_insert_right(mach_task_self(), | |
452 | msg_port, | |
453 | msg_port, | |
454 | MACH_MSG_TYPE_MAKE_SEND); | |
455 | ||
456 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_port_insert_right"); | |
457 | ||
458 | /* Send an empty msg on the port to fire the WL thread */ | |
459 | send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL, | |
460 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0)); | |
461 | ||
462 | sleep(SEND_TIMEOUT_SECS); | |
463 | ||
464 | /* Send the message with msg port as in-transit port, this msg will not be dequeued */ | |
465 | send(qos_send_port, MACH_PORT_NULL, msg_port, | |
466 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0)); | |
467 | ||
468 | /* Send the message to the in-transit port, it should override the rcv's workloop */ | |
469 | send(msg_port, special_reply_port, MACH_PORT_NULL, | |
470 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0)); | |
471 | T_LOG("Client done sending messages, now waiting for server to end the test"); | |
472 | sleep(2 * SEND_TIMEOUT_SECS); | |
473 | ||
474 | T_ASSERT_FAIL("client timed out"); | |
475 | } | |
476 | ||
477 | T_HELPER_DECL(qos_client_send_sync_and_enqueue_rcv, | |
478 | "Send synchronous messages and enqueue the rcv right") | |
479 | { | |
480 | mach_port_t qos_send_port; | |
481 | mach_port_t msg_port; | |
482 | mach_port_t special_reply_port; | |
483 | ||
484 | kern_return_t kr = bootstrap_look_up(bootstrap_port, | |
485 | KEVENT_QOS_SERVICE_NAME, &qos_send_port); | |
486 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up"); | |
487 | ||
488 | special_reply_port = thread_get_special_reply_port(); | |
489 | T_QUIET; T_ASSERT_NOTNULL(special_reply_port , "get_thread_special_reply_port"); | |
490 | ||
491 | /* Create a rcv right to send in a msg */ | |
492 | kr = mach_port_allocate(mach_task_self(), | |
493 | MACH_PORT_RIGHT_RECEIVE, | |
494 | &msg_port); | |
495 | ||
496 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_port_allocate"); | |
497 | ||
498 | kr = mach_port_insert_right(mach_task_self(), | |
499 | msg_port, | |
500 | msg_port, | |
501 | MACH_MSG_TYPE_MAKE_SEND); | |
502 | ||
503 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_port_insert_right"); | |
504 | ||
505 | /* Send the message to msg port */ | |
506 | send(msg_port, special_reply_port, MACH_PORT_NULL, | |
507 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0)); | |
508 | ||
509 | /* Send the message with msg port as in-transit port, copyin of in-transit will cause sync override */ | |
510 | send(qos_send_port, MACH_PORT_NULL, msg_port, | |
511 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0)); | |
512 | ||
513 | T_LOG("Client done sending messages, now waiting for server to end the test"); | |
514 | sleep(3 * SEND_TIMEOUT_SECS); | |
515 | ||
516 | T_ASSERT_FAIL("client timed out"); | |
517 | } | |
518 | ||
519 | static void | |
520 | thread_create_at_qos(qos_class_t qos, void * (*function)(void *)) | |
521 | { | |
522 | qos_class_t qos_thread; | |
523 | pthread_t thread; | |
524 | pthread_attr_t attr; | |
525 | int ret; | |
526 | ||
527 | ret = setpriority(PRIO_DARWIN_ROLE, 0, PRIO_DARWIN_ROLE_UI_FOCAL); | |
528 | if (ret != 0) { | |
529 | T_LOG("set priority failed\n"); | |
530 | } | |
531 | ||
532 | pthread_attr_init(&attr); | |
533 | pthread_attr_set_qos_class_np(&attr, qos, 0); | |
534 | pthread_create(&thread, &attr, function, NULL); | |
535 | ||
536 | T_LOG("pthread created\n"); | |
537 | pthread_get_qos_class_np(thread, &qos_thread, NULL); | |
538 | T_EXPECT_EQ(qos_thread, (qos_class_t)qos, NULL); | |
539 | } | |
540 | ||
541 | static void * | |
542 | qos_send_and_sync_rcv(void *arg __unused) | |
543 | { | |
544 | mach_port_t qos_send_port; | |
545 | mach_port_t special_reply_port; | |
546 | ||
547 | T_LOG("Client: from created thread\n"); | |
548 | ||
549 | T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], | |
550 | "pthread QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]); | |
551 | ||
552 | kern_return_t kr = bootstrap_look_up(bootstrap_port, | |
553 | KEVENT_QOS_SERVICE_NAME, &qos_send_port); | |
554 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up"); | |
555 | ||
556 | special_reply_port = thread_get_special_reply_port(); | |
557 | T_QUIET; T_ASSERT_NOTNULL(special_reply_port , "get_thread_special_reply_port"); | |
558 | ||
559 | /* enqueue two messages to make sure that mqueue is not empty */ | |
560 | send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL, | |
561 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_QUEUE_OVERRIDE], 0, 0)); | |
562 | ||
563 | send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL, | |
564 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_QUEUE_OVERRIDE], 0, 0)); | |
565 | ||
566 | sleep(SEND_TIMEOUT_SECS); | |
567 | ||
568 | /* sync wait on msg port */ | |
569 | receive(special_reply_port, qos_send_port); | |
570 | ||
571 | T_LOG("Client done doing sync rcv, now waiting for server to end the test"); | |
572 | sleep(SEND_TIMEOUT_SECS); | |
573 | ||
574 | T_ASSERT_FAIL("client timed out"); | |
575 | return 0; | |
576 | } | |
577 | ||
578 | T_HELPER_DECL(qos_client_send_sync_and_sync_rcv, | |
579 | "Send messages and syncronously wait for rcv") | |
580 | { | |
581 | thread_create_at_qos(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], qos_send_and_sync_rcv); | |
582 | sleep(HELPER_TIMEOUT_SECS); | |
583 | } | |
584 | ||
585 | T_HELPER_DECL(qos_client_send_sync_msg, | |
586 | "Send synchronous messages") | |
587 | { | |
588 | mach_port_t qos_send_port; | |
589 | mach_port_t special_reply_port; | |
590 | ||
591 | kern_return_t kr = bootstrap_look_up(bootstrap_port, | |
592 | KEVENT_QOS_SERVICE_NAME, &qos_send_port); | |
593 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up"); | |
594 | ||
595 | special_reply_port = thread_get_special_reply_port(); | |
596 | T_QUIET; T_ASSERT_NOTNULL(special_reply_port , "get_thread_special_reply_port"); | |
597 | ||
598 | /* Send the message to msg port */ | |
599 | send(qos_send_port, special_reply_port, MACH_PORT_NULL, | |
600 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0)); | |
601 | ||
602 | T_LOG("Client done sending messages, now waiting for server to end the test"); | |
603 | sleep(2 * SEND_TIMEOUT_SECS); | |
604 | ||
605 | T_ASSERT_FAIL("client timed out"); | |
606 | } | |
607 | ||
608 | T_HELPER_DECL(qos_client_send_two_sync_msg, | |
609 | "Send two synchronous messages at different qos") | |
610 | { | |
611 | mach_port_t qos_send_port; | |
612 | mach_port_t special_reply_port; | |
613 | ||
614 | kern_return_t kr = bootstrap_look_up(bootstrap_port, | |
615 | KEVENT_QOS_SERVICE_NAME, &qos_send_port); | |
616 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up"); | |
617 | ||
618 | special_reply_port = thread_get_special_reply_port(); | |
619 | T_QUIET; T_ASSERT_NOTNULL(special_reply_port , "get_thread_special_reply_port"); | |
620 | ||
621 | /* Send the message to msg port */ | |
622 | send(qos_send_port, special_reply_port, MACH_PORT_NULL, | |
623 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0)); | |
624 | ||
625 | /* Send the message to msg port */ | |
626 | send(qos_send_port, special_reply_port, MACH_PORT_NULL, | |
627 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0)); | |
628 | ||
629 | T_LOG("Client done sending messages, now waiting for server to end the test"); | |
630 | sleep(SEND_TIMEOUT_SECS); | |
631 | ||
632 | T_ASSERT_FAIL("client timed out"); | |
633 | } | |
634 | ||
635 | T_HELPER_DECL(qos_client_send_two_msg_and_destroy, | |
636 | "Send two messages with 2nd one as sync and then destory the special reply port") | |
637 | { | |
638 | mach_port_t qos_send_port; | |
639 | mach_port_t special_reply_port; | |
640 | ||
641 | kern_return_t kr = bootstrap_look_up(bootstrap_port, | |
642 | KEVENT_QOS_SERVICE_NAME, &qos_send_port); | |
643 | T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up"); | |
644 | ||
645 | special_reply_port = thread_get_special_reply_port(); | |
646 | T_QUIET; T_ASSERT_NOTNULL(special_reply_port , "get_thread_special_reply_port"); | |
647 | ||
648 | /* Send an async message to msg port */ | |
649 | send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL, | |
650 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0)); | |
651 | ||
652 | /* Send the message to msg port */ | |
653 | send(qos_send_port, special_reply_port, MACH_PORT_NULL, | |
654 | (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0)); | |
655 | ||
656 | T_LOG("Client done sending messages, waiting for destroy the special reply_port"); | |
657 | sleep(SEND_TIMEOUT_SECS); | |
658 | ||
659 | mach_port_destroy(mach_task_self(), special_reply_port); | |
660 | sleep(SEND_TIMEOUT_SECS); | |
661 | ||
662 | T_ASSERT_FAIL("client timed out"); | |
663 | } | |
664 | ||
665 | static void | |
666 | run_client_server(const char *server_name, const char *client_name, qos_class_t qos[], | |
667 | const char *qos_name[], const char *wl_function) | |
668 | { | |
669 | char *env[2 * ENV_VAR_QOS + ENV_VAR_FUNCTION + 1]; | |
670 | env_set_qos(env, qos, qos_name, wl_function); | |
671 | ||
672 | for (int i = 0; i < ENV_VAR_QOS; i++) { | |
673 | g_expected_qos[i] = qos[i]; | |
674 | g_expected_qos_name[i] = qos_name[i]; | |
675 | } | |
676 | ||
677 | dt_helper_t helpers[] = { | |
678 | dt_launchd_helper_env("com.apple.xnu.test.kevent_qos.plist", | |
679 | server_name, env), | |
680 | dt_fork_helper(client_name) | |
681 | }; | |
682 | dt_run_helpers(helpers, 2, HELPER_TIMEOUT_SECS); | |
683 | } | |
684 | ||
685 | #pragma mark Mach receive - kevent_qos | |
686 | ||
687 | ||
688 | static void | |
689 | expect_kevent_id_recv(mach_port_t port, qos_class_t qos[], const char *qos_name[], const char *wl_function) | |
690 | { | |
691 | int r; | |
692 | ||
693 | /* Qos expected by workloop thread */ | |
694 | for (int i = 0; i < ENV_VAR_QOS; i++) { | |
695 | g_expected_qos[i] = qos[i]; | |
696 | g_expected_qos_name[i] = qos_name[i]; | |
697 | } | |
698 | ||
699 | if (strcmp(wl_function, "workloop_cb_test_intransit") == 0) { | |
700 | T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop( | |
701 | worker_cb, event_cb, | |
702 | (pthread_workqueue_function_workloop_t)workloop_cb_test_intransit, 0, 0), NULL); | |
703 | } else if (strcmp(wl_function, "workloop_cb_test_sync_send") == 0) { | |
704 | T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop( | |
705 | worker_cb, event_cb, | |
706 | (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send, 0, 0), NULL); | |
707 | } else if (strcmp(wl_function, "workloop_cb_test_sync_send_and_enable") == 0) { | |
708 | T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop( | |
709 | worker_cb, event_cb, | |
710 | (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_and_enable, 0, 0), NULL); | |
711 | } else if (strcmp(wl_function, "workloop_cb_test_send_two_sync") == 0) { | |
712 | T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop( | |
713 | worker_cb, event_cb, | |
714 | (pthread_workqueue_function_workloop_t)workloop_cb_test_send_two_sync, 0, 0), NULL); | |
715 | } else if (strcmp(wl_function, "workloop_cb_test_two_send_and_destroy") == 0) { | |
716 | T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop( | |
717 | worker_cb, event_cb, | |
718 | (pthread_workqueue_function_workloop_t)workloop_cb_test_two_send_and_destroy, 0, 0), NULL); | |
719 | } else { | |
720 | T_ASSERT_FAIL("no workloop function specified \n"); | |
721 | } | |
722 | ||
723 | struct kevent_qos_s kev[] = {{ | |
724 | .ident = port, | |
725 | .filter = EVFILT_MACHPORT, | |
726 | .flags = EV_ADD | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED, | |
727 | .fflags = (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY | | |
728 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) | | |
729 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | | |
730 | MACH_RCV_VOUCHER), | |
731 | .data = 1, | |
732 | .qos = (int32_t)_pthread_qos_class_encode(qos[ENV_QOS_QUEUE_OVERRIDE], 0, 0) | |
733 | }}; | |
734 | ||
735 | struct kevent_qos_s kev_err[] = {{ 0 }}; | |
736 | ||
737 | /* Setup workloop for mach msg rcv */ | |
738 | r = kevent_id(25, kev, 1, kev_err, 1, NULL, | |
739 | NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS); | |
740 | ||
741 | T_QUIET; T_ASSERT_POSIX_SUCCESS(r, "kevent_id"); | |
742 | T_QUIET; T_ASSERT_EQ(r, 0, "no errors returned from kevent_id"); | |
743 | sleep(HELPER_TIMEOUT_SECS); | |
744 | } | |
745 | ||
746 | T_HELPER_DECL(server_kevent_id, | |
747 | "Reply with the QoS that a dispatch source event handler ran with") | |
748 | { | |
749 | qos_class_t qos[ENV_VAR_QOS]; | |
750 | const char *qos_name[ENV_VAR_QOS]; | |
751 | const char *wl_function; | |
752 | environ_get_qos(qos, qos_name, &wl_function); | |
753 | ||
754 | expect_kevent_id_recv(get_server_port(), qos, qos_name, wl_function); | |
755 | sleep(HELPER_TIMEOUT_SECS); | |
756 | T_ASSERT_FAIL("should receive a message within %d seconds", | |
757 | RECV_TIMEOUT_SECS); | |
758 | } | |
759 | ||
760 | #define TEST_QOS(server_name, client_name, name, wl_function_name, qos_bo, qos_bo_name, qos_qo, qos_qo_name, qos_ao, qos_ao_name) \ | |
761 | T_DECL(server_kevent_id_##name, \ | |
762 | "Event delivery at " qos_ao_name " QoS using a kevent_id", \ | |
763 | T_META_ASROOT(YES)) \ | |
764 | { \ | |
765 | qos_class_t qos_array[ENV_VAR_QOS] = {qos_bo, qos_qo, qos_ao}; \ | |
766 | const char *qos_name_array[ENV_VAR_QOS] = {qos_bo_name, qos_qo_name, qos_ao_name}; \ | |
767 | run_client_server(server_name, client_name, qos_array, qos_name_array, wl_function_name); \ | |
768 | } | |
769 | ||
770 | /* | |
771 | * Test 1: Test special reply port SPI | |
772 | * | |
773 | * Create thread special reply port and check any subsequent calls to | |
774 | * the same should return MACH_PORT_NULL, unless the reply port is destroyed. | |
775 | */ | |
776 | TEST_QOS("server_kevent_id", "qos_get_special_reply_port", special_reply_port, "workloop_cb_test_intransit", | |
777 | QOS_CLASS_DEFAULT, "default", | |
778 | QOS_CLASS_DEFAULT, "default", | |
779 | QOS_CLASS_DEFAULT, "default") | |
780 | ||
781 | /* | |
782 | * Test 2: Test sync ipc send to an in-transit port | |
783 | * | |
784 | * Send a sync ipc message (at IN qos) to an in-transit port enqueued in a port | |
785 | * attached to a workloop. Test that the servicer of the workloop gets | |
786 | * sync ipc override. | |
787 | */ | |
788 | TEST_QOS("server_kevent_id", "qos_client_send_to_intransit", transit_IN, "workloop_cb_test_intransit", | |
789 | QOS_CLASS_DEFAULT, "default", | |
790 | QOS_CLASS_MAINTENANCE, "maintenance", | |
791 | QOS_CLASS_USER_INITIATED, "user initiated") | |
792 | ||
793 | /* | |
794 | * Test 3: Test sync ipc send to an in-transit port | |
795 | * | |
796 | * Send a sync ipc message (at UI qos) to an in-transit port enqueued in a port | |
797 | * attached to a workloop. Test that the servicer of the workloop gets | |
798 | * sync ipc override. | |
799 | */ | |
800 | TEST_QOS("server_kevent_id", "qos_client_send_to_intransit", transit_UI, "workloop_cb_test_intransit", | |
801 | QOS_CLASS_USER_INITIATED, "user initiated", | |
802 | QOS_CLASS_MAINTENANCE, "maintenance", | |
803 | QOS_CLASS_USER_INTERACTIVE, "user interactive") | |
804 | ||
805 | /* | |
806 | * Test 4: Test enqueue of a receive right having sync ipc override | |
807 | * | |
808 | * Enqueue a receive right which has a sync ipc override (at IN qos) | |
809 | * and test that servicer of the workloop on other side gets sync ipc | |
810 | * override. | |
811 | */ | |
812 | TEST_QOS("server_kevent_id", "qos_client_send_sync_and_enqueue_rcv", enqueue_IN, "workloop_cb_test_intransit", | |
813 | QOS_CLASS_DEFAULT, "default", | |
814 | QOS_CLASS_MAINTENANCE, "maintenance", | |
815 | QOS_CLASS_USER_INITIATED, "user initiated") | |
816 | ||
817 | /* | |
818 | * Test 5: Test enqueue of a receive right having sync ipc override | |
819 | * | |
820 | * Enqueue a receive right which has a sync ipc override (at UI qos) | |
821 | * and test that servicer of the workloop on other side gets sync ipc | |
822 | * override. | |
823 | */ | |
824 | TEST_QOS("server_kevent_id", "qos_client_send_sync_and_enqueue_rcv", enqueue_UI, "workloop_cb_test_intransit", | |
825 | QOS_CLASS_DEFAULT, "default", | |
826 | QOS_CLASS_MAINTENANCE, "maintenance", | |
827 | QOS_CLASS_USER_INTERACTIVE, "user interactive") | |
828 | ||
829 | /* | |
830 | * Test 6: Test starting a sync rcv overrides the servicer | |
831 | * | |
832 | * Send an async message to a port and then start waiting on | |
833 | * the port in mach msg rcv (at IN qos) with sync wait and test if the | |
834 | * servicer of the workloop gets sync ipc override. | |
835 | */ | |
836 | TEST_QOS("server_kevent_id", "qos_client_send_sync_and_sync_rcv", rcv_IN, "workloop_cb_test_intransit", | |
837 | QOS_CLASS_DEFAULT, "default", | |
838 | QOS_CLASS_MAINTENANCE, "maintenance", | |
839 | QOS_CLASS_USER_INITIATED, "user initiated") | |
840 | ||
841 | /* | |
842 | * Test 7: Test starting a sync rcv overrides the servicer | |
843 | * | |
844 | * Send an async message to a port and then start waiting on | |
845 | * the port in mach msg rcv (at UI qos) with sync wait and test if the | |
846 | * servicer of the workloop gets sync ipc override. | |
847 | */ | |
848 | TEST_QOS("server_kevent_id", "qos_client_send_sync_and_sync_rcv", rcv_UI, "workloop_cb_test_intransit", | |
849 | QOS_CLASS_DEFAULT, "default", | |
850 | QOS_CLASS_MAINTENANCE, "maintenance", | |
851 | QOS_CLASS_USER_INTERACTIVE, "user interactive") | |
852 | ||
853 | /* | |
854 | * Test 8: test sending sync ipc message (at IN qos) to port will override the servicer | |
855 | * | |
856 | * Send a message with sync ipc override to a port and check if the servicer | |
857 | * of the workloop on other side gets sync ipc override. | |
858 | */ | |
859 | TEST_QOS("server_kevent_id", "qos_client_send_sync_msg", send_sync_IN, "workloop_cb_test_sync_send", | |
860 | QOS_CLASS_DEFAULT, "default", | |
861 | QOS_CLASS_MAINTENANCE, "maintenance", | |
862 | QOS_CLASS_USER_INITIATED, "user initiated") | |
863 | ||
864 | /* | |
865 | * Test 9: test sending sync ipc message (at UI qos) to port will override the servicer | |
866 | * | |
867 | * Send a message with sync ipc override to a port and check if the servicer | |
868 | * of the workloop on other side gets sync ipc override. | |
869 | */ | |
870 | TEST_QOS("server_kevent_id", "qos_client_send_sync_msg", send_sync_UI, "workloop_cb_test_sync_send", | |
871 | QOS_CLASS_USER_INITIATED, "user initiated", | |
872 | QOS_CLASS_MAINTENANCE, "maintenance", | |
873 | QOS_CLASS_USER_INTERACTIVE, "user interactive") | |
874 | ||
875 | /* | |
876 | * Test 10: test enabling a knote in workloop handler will drop the sync ipc override of delivered message | |
877 | * | |
878 | * Send a sync ipc message to port and check the servicer of the workloop | |
879 | * on other side gets sync ipc override and once the handler enables the knote, | |
880 | * that sync ipc override is dropped. | |
881 | */ | |
882 | TEST_QOS("server_kevent_id", "qos_client_send_sync_msg", send_sync_UI_and_enable, "workloop_cb_test_sync_send_and_enable", | |
883 | QOS_CLASS_USER_INITIATED, "user initiated", | |
884 | QOS_CLASS_MAINTENANCE, "maintenance", | |
885 | QOS_CLASS_USER_INTERACTIVE, "user interactive") | |
886 | ||
887 | /* | |
888 | * Test 11: test returning to begin processing drops sync ipc override of delivered message | |
889 | * | |
890 | * Send a sync ipc message and check if enabling the knote clears the override of | |
891 | * the delivered message, but should still have the override of an enqueued message. | |
892 | */ | |
893 | TEST_QOS("server_kevent_id", "qos_client_send_two_sync_msg", send_two_sync_UI, "workloop_cb_test_send_two_sync", | |
894 | QOS_CLASS_USER_INITIATED, "user initiated", | |
895 | QOS_CLASS_MAINTENANCE, "maintenance", | |
896 | QOS_CLASS_USER_INTERACTIVE, "user interactive") | |
897 | ||
898 | /* | |
899 | * Test 12: test destorying the special reply port drops the override | |
900 | * | |
901 | * Send two async messages and a sync ipc message, the workloop handler | |
902 | * should get a sync ipc override, now test if destroying the special | |
903 | * reply port drops the sync ipc override on the servicer. | |
904 | */ | |
905 | TEST_QOS("server_kevent_id", "qos_client_send_two_msg_and_destroy", send_two_UI_and_destroy, "workloop_cb_test_two_send_and_destroy", | |
906 | QOS_CLASS_USER_INITIATED, "user initiated", | |
907 | QOS_CLASS_MAINTENANCE, "maintenance", | |
908 | QOS_CLASS_USER_INTERACTIVE, "user interactive") |