]> git.saurik.com Git - apple/xnu.git/blame_incremental - tests/kevent_qos.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / tests / kevent_qos.c
... / ...
CommitLineData
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#include <mach/mach_port.h>
26#include <mach/mach_sync_ipc.h>
27
28T_GLOBAL_META(T_META_NAMESPACE("xnu.kevent_qos"));
29
30#define ARRAYLEN(arr) (sizeof(arr) / sizeof(arr[0]))
31
32#define INTERMITTENT_TIMEOUT_SEC (3)
33#define RECV_TIMEOUT_SECS (4)
34#define SEND_TIMEOUT_SECS (6)
35#define HELPER_TIMEOUT_SECS (15)
36
37#define ENV_VAR_QOS (3)
38static const char *qos_env[ENV_VAR_QOS] = {"XNU_TEST_QOS_BO", "XNU_TEST_QOS_QO", "XNU_TEST_QOS_AO"};
39static const char *qos_name_env[ENV_VAR_QOS] = {"XNU_TEST_QOS_NAME_BO", "XNU_TEST_QOS_NAME_QO", "XNU_TEST_QOS_NAME_AO"};
40
41#define ENV_VAR_FUNCTION (1)
42static const char *wl_function_name = "XNU_TEST_WL_FUNCTION";
43
44static qos_class_t g_expected_qos[ENV_VAR_QOS];
45static const char *g_expected_qos_name[ENV_VAR_QOS];
46
47#define ENV_QOS_BEFORE_OVERRIDE (0)
48#define ENV_QOS_QUEUE_OVERRIDE (1)
49#define ENV_QOS_AFTER_OVERRIDE (2)
50
51struct test_msg {
52 mach_msg_header_t header;
53 mach_msg_body_t body;
54 mach_msg_port_descriptor_t port_descriptor;
55 mach_msg_option_t opts;
56 mach_msg_priority_t qos;
57};
58
59#pragma mark pthread callbacks
60
61static pthread_t
62thread_create_at_qos(qos_class_t qos, void * (*function)(void *));
63static void
64send(mach_port_t send_port, mach_port_t reply_port, mach_port_t msg_port, mach_msg_priority_t qos, mach_msg_option_t options);
65static void
66enable_kevent(uint64_t *workloop_id, unsigned long long port);
67static void
68populate_kevent(struct kevent_qos_s *kev, unsigned long long port);
69
70static void
71worker_cb(pthread_priority_t __unused priority)
72{
73 T_FAIL("a worker thread was created");
74}
75
76static void
77event_cb(void ** __unused events, int * __unused nevents)
78{
79 T_FAIL("a kevent routine was called instead of workloop");
80}
81
82static uint32_t
83get_user_promotion_basepri(void)
84{
85 mach_msg_type_number_t count = THREAD_POLICY_STATE_COUNT;
86 struct thread_policy_state thread_policy;
87 boolean_t get_default = FALSE;
88 mach_port_t thread_port = pthread_mach_thread_np(pthread_self());
89
90 kern_return_t kr = thread_policy_get(thread_port, THREAD_POLICY_STATE,
91 (thread_policy_t)&thread_policy, &count, &get_default);
92 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_policy_get");
93 return thread_policy.thps_user_promotion_basepri;
94}
95
96#define EXPECT_QOS_EQ(qos, ...) do { \
97 if ((qos) == QOS_CLASS_USER_INTERACTIVE) { \
98 T_EXPECT_EFFECTIVE_QOS_EQ(QOS_CLASS_USER_INITIATED, __VA_ARGS__); \
99 T_EXPECT_EQ(47u, get_user_promotion_basepri(), __VA_ARGS__); \
100 } else { \
101 T_EXPECT_EFFECTIVE_QOS_EQ(qos, __VA_ARGS__); \
102 } \
103 } while (0)
104
105#define EXPECT_TEST_MSG(_ke) do { \
106 struct kevent_qos_s *ke = _ke; \
107 mach_msg_header_t *hdr = (mach_msg_header_t *)ke->ext[0]; \
108 T_ASSERT_NOTNULL(hdr, "has a message"); \
109 T_ASSERT_EQ(hdr->msgh_size, (uint32_t)sizeof(struct test_msg), "of the right size"); \
110 struct test_msg *tmsg = (struct test_msg *)hdr; \
111 if (tmsg->opts & MACH_SEND_PROPAGATE_QOS) { \
112 T_EXPECT_EQ(tmsg->qos, ((uint32_t)(ke->ext[2] >> 32)), \
113 "propagation works"); \
114 } \
115 } while (0)
116
117/*
118 * Basic WL handler callback, it sleeps for n seconds and then checks the
119 * effective Qos of the servicer thread.
120 */
121static void
122workloop_cb_test_intransit(uint64_t *workloop_id __unused, void **eventslist, int *events)
123{
124 T_LOG("Workloop handler workloop_cb_test_intransit called. "
125 "Will wait for %d seconds to make sure client enqueues the sync msg \n",
126 2 * RECV_TIMEOUT_SECS);
127
128 EXPECT_TEST_MSG(*eventslist);
129
130 /* Wait for the client to send the high priority message to override the qos */
131 sleep(2 * RECV_TIMEOUT_SECS);
132
133 /* Skip the test if we can't check Qos */
134 if (geteuid() != 0) {
135 T_SKIP("kevent_qos test requires root privileges to run.");
136 }
137
138 /* The effective Qos should be the one expected after override */
139 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
140 "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]);
141
142 *events = 0;
143 T_END;
144}
145
146/*
147 * WL handler which checks if the servicer thread has correct Qos.
148 */
149static void
150workloop_cb_test_sync_send(uint64_t *workloop_id __unused, void **eventslist, int *events)
151{
152 T_LOG("Workloop handler workloop_cb_test_sync_send called");
153
154 EXPECT_TEST_MSG(*eventslist);
155
156 if (geteuid() != 0) {
157 T_SKIP("kevent_qos test requires root privileges to run.");
158 }
159
160 /* The effective Qos should be the one expected after override */
161 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
162 "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]);
163
164 *events = 0;
165 T_END;
166}
167
168/*
169 * WL handler which checks the overridden Qos and then enables the knote and checks
170 * for the Qos again if that dropped the sync ipc override.
171 */
172static void
173workloop_cb_test_sync_send_and_enable(uint64_t *workloop_id, struct kevent_qos_s **eventslist, int *events)
174{
175 unsigned override_priority;
176 unsigned reenable_priority;
177
178 T_LOG("Workloop handler workloop_cb_test_sync_send_and_enable called");
179
180 EXPECT_TEST_MSG(*eventslist);
181
182 if (geteuid() != 0) {
183 T_SKIP("kevent_qos test requires root privileges to run.");
184 }
185
186 /* The effective Qos should be the one expected after override */
187 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
188 "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]);
189
190 /* Snapshot the current override priority */
191 override_priority = get_user_promotion_basepri();
192
193 /* Enable the knote */
194 struct kevent_qos_s *kev = *eventslist;
195 enable_kevent(workloop_id, kev->ident);
196
197 /*
198 * Check if the override has been dropped, check for priority instead of qos since
199 * there will be async qos push.
200 */
201 reenable_priority = get_user_promotion_basepri();
202 T_EXPECT_LT(reenable_priority, override_priority,
203 "thread's current override priority %d should be less than override priority prior to enabling knote %d",
204 reenable_priority, override_priority);
205
206 *events = 0;
207 T_END;
208}
209
210/*
211 * WL handler which checks the overridden Qos and then handoffs the IPC,
212 * enables the knote and checks for the Qos again that it hasn't dropped the sync ipc override.
213 */
214static void
215workloop_cb_test_sync_send_and_enable_handoff(uint64_t *workloop_id, struct kevent_qos_s **eventslist, int *events)
216{
217 unsigned override_priority;
218 int error;
219
220 T_LOG("Workloop handler workloop_cb_test_sync_send_and_enable_handoff called");
221
222 EXPECT_TEST_MSG(*eventslist);
223
224 if (geteuid() != 0) {
225 T_SKIP("kevent_qos test requires root privileges to run.");
226 }
227
228 /* The effective Qos should be the one expected after override */
229 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
230 "dispatch_source event handler QoS should be %s",
231 g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]);
232
233 /* Snapshot the current override priority */
234 override_priority = get_user_promotion_basepri();
235
236 struct kevent_qos_s *kev = *eventslist;
237 mach_msg_header_t *hdr = (mach_msg_header_t *)kev->ext[0];
238
239 /* handoff the IPC */
240 struct kevent_qos_s handoff_kev = {
241 .filter = EVFILT_WORKLOOP,
242 .ident = hdr->msgh_remote_port,
243 .flags = EV_ADD | EV_DISABLE,
244 .fflags = 0x80000000,
245 };
246
247 error = kevent_id(*workloop_id, &handoff_kev, 1, &handoff_kev, 1, NULL,
248 NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS | KEVENT_FLAG_DYNAMIC_KQ_MUST_EXIST);
249 T_QUIET; T_ASSERT_POSIX_SUCCESS(error, "kevent_id");
250 T_ASSERT_EQ(0, error, "Handed off the sync IPC");
251
252 /* Enable the knote */
253 enable_kevent(workloop_id, kev->ident);
254
255 /*
256 * Check if the override has not been dropped.
257 */
258 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
259 "dispatch_source event handler QoS should still be %s",
260 g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]);
261
262 *events = 0;
263 T_END;
264}
265
266/*
267 * WL handler receives the first message and checks sync ipc override, then enables the knote
268 * and receives 2nd message and checks it sync ipc override.
269 */
270static int send_two_sync_handler_called = 0;
271static void
272workloop_cb_test_send_two_sync(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, int *events)
273{
274 T_LOG("Workloop handler workloop_cb_test_send_two_sync called for %d time", send_two_sync_handler_called + 1);
275
276 EXPECT_TEST_MSG(*eventslist);
277
278 if (geteuid() != 0) {
279 T_SKIP("kevent_qos test requires root privileges to run.");
280 }
281
282 T_LOG("Number of events received is %d\n", *events);
283
284 if (send_two_sync_handler_called == 0) {
285 /* The effective Qos should be the one expected after override */
286 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
287 "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]);
288
289 /* Enable the knote to get 2nd message */
290 struct kevent_qos_s *kev = *eventslist;
291 uint64_t port = kev->ident;
292 populate_kevent(kev, port);
293 *events = 1;
294 } else {
295 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE],
296 "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_BEFORE_OVERRIDE]);
297 *events = 0;
298 T_END;
299 }
300 send_two_sync_handler_called++;
301}
302
303/*
304 * Checks the sync ipc override and then waits for client to destroy the
305 * special reply port and checks if that removes the sync ipc override.
306 */
307static boolean_t two_send_and_destroy_test_passed = FALSE;
308static int two_send_and_destroy_handler = 0;
309static void
310workloop_cb_test_two_send_and_destroy(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist __unused, int *events)
311{
312 T_LOG("Workloop handler workloop_cb_test_two_send_and_destroy called %d times", two_send_and_destroy_handler + 1);
313
314 EXPECT_TEST_MSG(*eventslist);
315
316 if (geteuid() != 0) {
317 T_SKIP("kevent_qos test requires root privileges to run.");
318 }
319
320 if (two_send_and_destroy_handler == 0) {
321 /* Sleep to make sure the mqueue gets full */
322 sleep(RECV_TIMEOUT_SECS);
323
324 /* The effective Qos should be the one expected after override */
325 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
326 "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]);
327
328 sleep(SEND_TIMEOUT_SECS);
329
330 /* Special reply port should have been destroyed, check Qos again */
331 EXPECT_QOS_EQ(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE],
332 "dispatch_source event handler QoS should be %s", g_expected_qos_name[ENV_QOS_BEFORE_OVERRIDE]);
333
334 two_send_and_destroy_test_passed = TRUE;
335 } else {
336 if (two_send_and_destroy_test_passed) {
337 T_END;
338 }
339 }
340
341 /* Enable the knote to get next message */
342 struct kevent_qos_s *kev = *eventslist;
343 uint64_t port = kev->ident;
344 populate_kevent(kev, port);
345 *events = 1;
346 two_send_and_destroy_handler++;
347 T_LOG("Handler returning \n");
348}
349
350static mach_port_type_t
351get_reply_port(struct kevent_qos_s *kev)
352{
353 mach_msg_header_t *hdr;
354 mach_port_t reply_port;
355 mach_port_type_t type;
356 kern_return_t kr;
357
358 hdr = (void*)kev->ext[0];
359 T_QUIET; T_ASSERT_NOTNULL(hdr, "msg hdr");
360
361 reply_port = hdr->msgh_remote_port;
362 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(reply_port), "reply port valid");
363 kr = mach_port_type(mach_task_self(), reply_port, &type);
364 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_type");
365 T_QUIET; T_ASSERT_TRUE(type & MACH_PORT_TYPE_SEND_ONCE, "send once received");
366
367 return reply_port;
368}
369
370static void
371send_reply(mach_port_t reply_port)
372{
373 kern_return_t kr;
374
375 struct {
376 mach_msg_header_t header;
377 } send_msg = {
378 .header = {
379 .msgh_remote_port = reply_port,
380 .msgh_local_port = MACH_PORT_NULL,
381 .msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0),
382 .msgh_id = 0x100,
383 .msgh_size = sizeof(send_msg),
384 },
385 };
386
387 kr = mach_msg(&(send_msg.header),
388 MACH_SEND_MSG,
389 send_msg.header.msgh_size,
390 0,
391 MACH_PORT_NULL,
392 0,
393 0);
394
395 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "server mach_msg");
396}
397
398static void
399populate_kevent(struct kevent_qos_s *kev, unsigned long long port)
400{
401 memset(kev, 0, sizeof(struct kevent_qos_s));
402 kev->ident = port;
403 kev->filter = EVFILT_MACHPORT;
404 kev->flags = EV_ADD | EV_ENABLE | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED;
405 kev->fflags = (MACH_RCV_MSG | MACH_RCV_VOUCHER | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY |
406 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) |
407 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0));
408 kev->data = 1;
409}
410
411static void
412enable_kevent(uint64_t *workloop_id, unsigned long long port)
413{
414 struct kevent_qos_s kev;
415 int error;
416
417 populate_kevent(&kev, port);
418 struct kevent_qos_s kev_err[] = {{ 0 }};
419
420 error = kevent_id(*workloop_id, &kev, 1, kev_err, 1, NULL,
421 NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS | KEVENT_FLAG_DYNAMIC_KQ_MUST_EXIST);
422 T_QUIET; T_ASSERT_POSIX_SUCCESS(error, "kevent_id");
423}
424
425/*
426 * WL handler which sends a msg to the client from handler.
427 */
428static void
429workloop_cb_test_sync_send_reply(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, int *events)
430{
431 T_LOG("Workloop handler workloop_cb_test_sync_send_reply called");
432
433 if (geteuid() != 0) {
434 T_SKIP("kevent_qos test requires root privileges to run.");
435 }
436
437 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
438 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
439
440 /* send reply */
441 send_reply(get_reply_port(*eventslist));
442
443 *events = 0;
444}
445
446/*
447 * WL handler which deallocates reply port.
448 */
449static void
450workloop_cb_test_sync_send_deallocate(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, int *events)
451{
452 mach_port_t reply_port;
453 kern_return_t kr;
454
455 T_LOG("Workloop handler workloop_cb_test_sync_send_deallocate called");
456
457 if (geteuid() != 0) {
458 T_SKIP("kevent_qos test requires root privileges to run.");
459 }
460
461 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
462 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
463
464 reply_port = get_reply_port(*eventslist);
465
466 /* deallocate port */
467 kr = mach_port_deallocate(mach_task_self(), reply_port);
468
469 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_deallocate");
470
471 *events = 0;
472
473 T_LOG("Handler returning \n");
474}
475
476
477/*
478 * WL handler which sends a msg to the client before enabling the event from handler.
479 */
480static void
481workloop_cb_test_sync_send_reply_kevent(uint64_t *workloop_id, struct kevent_qos_s **eventslist, int *events)
482{
483 T_LOG("Workloop handler workloop_cb_test_sync_send_reply_kevent called");
484
485 if (geteuid() != 0) {
486 T_SKIP("kevent_qos test requires root privileges to run.");
487 }
488
489 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
490 T_QUIET; T_ASSERT_EQ_INT(((*eventslist)->filter), EVFILT_MACHPORT, "received EVFILT_MACHPORT");
491
492 struct kevent_qos_s *kev = *eventslist;
493
494 /* send reply */
495 send_reply(get_reply_port(kev));
496
497 /* Enable the knote */
498 enable_kevent(workloop_id, kev->ident);
499
500 *events = 0;
501
502 T_LOG("Handler returning \n");
503}
504
505/*
506 * WL handler which sends a msg to the client before enabling the event from pthread.
507 */
508static void
509workloop_cb_test_sync_send_reply_kevent_pthread(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, int *events)
510{
511 T_LOG("Workloop handler workloop_cb_test_sync_send_reply_kevent_pthread called");
512
513 if (geteuid() != 0) {
514 T_SKIP("kevent_qos test requires root privileges to run.");
515 }
516
517 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
518 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
519
520 struct kevent_qos_s *kev = *eventslist;
521
522 /* send reply */
523 send_reply(get_reply_port(kev));
524
525 populate_kevent(kev, kev->ident);
526
527 *events = 1;
528
529 T_LOG("Handler returning \n");
530}
531
532/*
533 * WL handler which sends a msg to the client after reenabling the event.
534 */
535static void
536workloop_cb_test_sync_send_kevent_reply(uint64_t *workloop_id, struct kevent_qos_s **eventslist, int *events)
537{
538 T_LOG("workloop handler workloop_cb_test_sync_send_kevent_reply called");
539
540 if (geteuid() != 0) {
541 T_SKIP("kevent_qos test requires root privileges to run.");
542 }
543
544 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
545 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
546
547 struct kevent_qos_s *kev = *eventslist;
548 mach_port_t reply_port = get_reply_port(*eventslist);
549
550 /* Enable the knote */
551 enable_kevent(workloop_id, kev->ident);
552
553 /* send reply */
554 send_reply(reply_port);
555
556 *events = 0;
557
558 T_LOG("Handler returning \n");
559}
560
561/*
562 * WL handler that does nothing.
563 */
564static void
565workloop_cb_test_sync_send_do_nothing(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, int *events)
566{
567 T_LOG("Workloop handler workloop_cb_test_sync_send_do_nothing called");
568
569 if (geteuid() != 0) {
570 T_SKIP("kevent_qos test requires root privileges to run.");
571 }
572
573 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
574 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
575
576 /* do nothing */
577
578 *events = 0;
579
580 T_LOG("Handler returning \n");
581}
582
583/*
584 * WL handler that returns the event to reenable.
585 */
586static void
587workloop_cb_test_sync_send_do_nothing_kevent_pthread(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, int *events)
588{
589 T_LOG("Workloop handler workloop_cb_test_sync_send_do_nothing_kevent_pthread called");
590
591 if (geteuid() != 0) {
592 T_SKIP("kevent_qos test requires root privileges to run.");
593 }
594
595 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
596 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
597
598 struct kevent_qos_s *kev = *eventslist;
599 populate_kevent(kev, kev->ident);
600
601 *events = 1;
602
603 T_LOG("handler returning \n");
604}
605
606/*
607 * WL handler that exits.
608 */
609static void
610workloop_cb_test_sync_send_do_nothing_exit(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, __unused int *events)
611{
612 T_LOG("workloop handler workloop_cb_test_sync_send_do_nothing_exit called");
613
614 if (geteuid() != 0) {
615 T_SKIP("kevent_qos test requires root privileges to run.");
616 }
617
618 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
619 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
620
621 /* call exit */
622 exit(0);
623}
624
625/*
626 * WL handler which:
627 * first sync sends a msg to the client and reenables kevent after
628 * second sync sends a msg and reenables kevent after.
629 */
630static void
631workloop_cb_test_sync_send_reply_kevent_reply_kevent(uint64_t *workloop_id __unused, struct kevent_qos_s **eventslist, int *events)
632{
633 T_LOG("Workloop handler workloop_cb_test_sync_send_reply_kevent_reply_kevent called");
634
635 if (geteuid() != 0) {
636 T_SKIP("kevent_qos test requires root privileges to run.");
637 }
638
639 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
640 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
641
642 struct kevent_qos_s *kev = *eventslist;
643
644 /* send reply */
645 send_reply(get_reply_port(kev));
646
647 populate_kevent(kev, kev->ident);
648
649 *events = 1;
650
651 T_LOG("Handler returning \n");
652}
653
654/*
655 * WL handler which:
656 * first sync reenables kevent and after sends a msg
657 * second sync sends a msg and reenables kevent after.
658 */
659static int workloop_cb_test_sync_send_kevent_reply_reply_kevent_handler_called = 0;
660static void
661workloop_cb_test_sync_send_kevent_reply_reply_kevent(uint64_t *workloop_id, struct kevent_qos_s **eventslist, int *events)
662{
663 T_LOG("workloop handler workloop_cb_test_sync_send_kevent_reply_reply_kevent called");
664
665 if (geteuid() != 0) {
666 T_SKIP("kevent_qos test requires root privileges to run.");
667 }
668
669 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
670 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
671
672 struct kevent_qos_s *kev = *eventslist;
673 mach_port_t reply_port = get_reply_port(kev);
674
675 if (workloop_cb_test_sync_send_kevent_reply_reply_kevent_handler_called == 0) {
676 workloop_cb_test_sync_send_kevent_reply_reply_kevent_handler_called = 1;
677
678 /* Enable the knote */
679 enable_kevent(workloop_id, kev->ident);
680
681 /* send reply */
682 send_reply(reply_port);
683
684 *events = 0;
685 } else {
686 /* send reply */
687 send_reply(reply_port);
688
689 /* Enable the knote */
690 enable_kevent(workloop_id, kev->ident);
691
692 *events = 0;
693 }
694
695 T_LOG("Handler returning \n");
696}
697
698/*
699 * WL handler which:
700 * first sync reenables kevent and after sends a msg
701 * second sync reenables kevent and after sends a msg
702 */
703static void
704workloop_cb_test_sync_send_kevent_reply_kevent_reply(uint64_t *workloop_id, struct kevent_qos_s **eventslist, int *events)
705{
706 T_LOG("workloop handler workloop_cb_test_sync_send_kevent_reply_kevent_reply called");
707
708 if (geteuid() != 0) {
709 T_SKIP("kevent_qos test requires root privileges to run.");
710 }
711
712 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
713 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
714
715 struct kevent_qos_s *kev = *eventslist;
716 mach_port_t reply_port = get_reply_port(kev);
717
718 /* Enable the knote */
719 enable_kevent(workloop_id, kev->ident);
720
721 /* send reply */
722 send_reply(reply_port);
723
724 *events = 0;
725 T_LOG("Handler returning \n");
726}
727
728/*
729 * WL handler which:
730 * first sync ends a msg and reenables kevent after
731 * second sync reenables kevent and sends a msg after
732 */
733static int workloop_cb_test_sync_send_reply_kevent_kevent_reply_handler_called = 0;
734static void
735workloop_cb_test_sync_send_reply_kevent_kevent_reply(uint64_t *workloop_id, struct kevent_qos_s **eventslist, int *events)
736{
737 T_LOG("workloop handler workloop_cb_test_sync_send_reply_kevent_kevent_reply called");
738
739 if (geteuid() != 0) {
740 T_SKIP("kevent_qos test requires root privileges to run.");
741 }
742
743 T_QUIET; T_ASSERT_EQ_INT(*events, 1, "events received");
744 T_QUIET; T_ASSERT_EQ_INT((*eventslist)->filter, EVFILT_MACHPORT, "received EVFILT_MACHPORT");
745
746 struct kevent_qos_s *kev = *eventslist;
747 mach_port_t reply_port = get_reply_port(kev);
748
749 if (workloop_cb_test_sync_send_reply_kevent_kevent_reply_handler_called == 0) {
750 workloop_cb_test_sync_send_reply_kevent_kevent_reply_handler_called = 1;
751
752 /* send reply */
753 send_reply(reply_port);
754
755 populate_kevent(kev, kev->ident);
756
757 *events = 1;
758 } else {
759 /* Enable the knote */
760 enable_kevent(workloop_id, kev->ident);
761 /* send reply */
762 send_reply(reply_port);
763
764 *events = 0;
765 }
766
767 T_LOG("Handler returning \n");
768}
769#pragma mark Mach receive
770
771#define KEVENT_QOS_SERVICE_NAME "com.apple.xnu.test.kevent_qos"
772
773static mach_port_t
774get_server_port(void)
775{
776 mach_port_t port;
777 kern_return_t kr = bootstrap_check_in(bootstrap_port,
778 KEVENT_QOS_SERVICE_NAME, &port);
779 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "server bootstrap_check_in");
780 return port;
781}
782
783static void
784env_set_qos(char **env, qos_class_t qos[], const char *qos_name[], const char *wl_function)
785{
786 int i;
787 char *qos_str, *qos_name_str;
788 for (i = 0; i < ENV_VAR_QOS; i++) {
789 T_QUIET; T_ASSERT_POSIX_SUCCESS(asprintf(&qos_str, "%s=%d", qos_env[i], qos[i]),
790 NULL);
791 T_QUIET; T_ASSERT_POSIX_SUCCESS(
792 asprintf(&qos_name_str, "%s=%s", qos_name_env[i], qos_name[i]), NULL);
793 env[2 * i] = qos_str;
794 env[2 * i + 1] = qos_name_str;
795 }
796 T_QUIET; T_ASSERT_POSIX_SUCCESS(asprintf(&env[2 * i], "%s=%s", wl_function_name, wl_function),
797 NULL);
798 env[2 * i + 1] = NULL;
799}
800
801static void
802environ_get_qos(qos_class_t qos[], const char *qos_name[], const char **wl_function)
803{
804 char *qos_str;
805 char *qos_end;
806 int i;
807
808 for (i = 0; i < ENV_VAR_QOS; i++) {
809 qos_str = getenv(qos_env[i]);
810 T_QUIET; T_ASSERT_NOTNULL(qos_str, "getenv(%s)", qos_env[i]);
811
812 unsigned long qos_l = strtoul(qos_str, &qos_end, 10);
813 T_QUIET; T_ASSERT_EQ(*qos_end, '\0', "getenv(%s) = '%s' should be an "
814 "integer", qos_env[i], qos_str);
815
816 T_QUIET; T_ASSERT_LT(qos_l, (unsigned long)100, "getenv(%s) = '%s' should "
817 "be less than 100", qos_env[i], qos_str);
818
819 qos[i] = (qos_class_t)qos_l;
820 qos_name[i] = getenv(qos_name_env[i]);
821 T_QUIET; T_ASSERT_NOTNULL(qos_name[i], "getenv(%s)", qos_name_env[i]);
822 }
823 *wl_function = getenv(wl_function_name);
824 T_QUIET; T_ASSERT_NOTNULL(*wl_function, "getenv(%s)", wl_function_name);
825}
826
827static mach_voucher_t
828create_pthpriority_voucher(mach_msg_priority_t qos)
829{
830 char voucher_buf[sizeof(mach_voucher_attr_recipe_data_t) + sizeof(ipc_pthread_priority_value_t)];
831
832 mach_voucher_t voucher = MACH_PORT_NULL;
833 kern_return_t ret;
834 ipc_pthread_priority_value_t ipc_pthread_priority_value =
835 (ipc_pthread_priority_value_t)qos;
836
837 mach_voucher_attr_raw_recipe_array_t recipes;
838 mach_voucher_attr_raw_recipe_size_t recipe_size = 0;
839 mach_voucher_attr_recipe_t recipe =
840 (mach_voucher_attr_recipe_t)&voucher_buf[recipe_size];
841
842 recipe->key = MACH_VOUCHER_ATTR_KEY_PTHPRIORITY;
843 recipe->command = MACH_VOUCHER_ATTR_PTHPRIORITY_CREATE;
844 recipe->previous_voucher = MACH_VOUCHER_NULL;
845 memcpy((char *)&recipe->content[0], &ipc_pthread_priority_value, sizeof(ipc_pthread_priority_value));
846 recipe->content_size = sizeof(ipc_pthread_priority_value_t);
847 recipe_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size;
848
849 recipes = (mach_voucher_attr_raw_recipe_array_t)&voucher_buf[0];
850
851 ret = host_create_mach_voucher(mach_host_self(),
852 recipes,
853 recipe_size,
854 &voucher);
855
856 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "client host_create_mach_voucher");
857 return voucher;
858}
859
860static void
861send(
862 mach_port_t send_port,
863 mach_port_t reply_port,
864 mach_port_t msg_port,
865 mach_msg_priority_t qos,
866 mach_msg_option_t options)
867{
868 kern_return_t ret = 0;
869
870 struct test_msg send_msg = {
871 .header = {
872 .msgh_remote_port = send_port,
873 .msgh_local_port = reply_port,
874 .msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
875 reply_port ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0,
876 MACH_MSG_TYPE_MOVE_SEND,
877 MACH_MSGH_BITS_COMPLEX),
878 .msgh_id = 0x100,
879 .msgh_size = sizeof(send_msg),
880 },
881 .body = {
882 .msgh_descriptor_count = 1,
883 },
884 .port_descriptor = {
885 .name = msg_port,
886 .disposition = MACH_MSG_TYPE_MOVE_RECEIVE,
887 .type = MACH_MSG_PORT_DESCRIPTOR,
888 },
889 .opts = options,
890 };
891
892 if (msg_port == MACH_PORT_NULL) {
893 send_msg.body.msgh_descriptor_count = 0;
894 }
895
896 if ((options & MACH_SEND_PROPAGATE_QOS) == 0) {
897 send_msg.header.msgh_voucher_port = create_pthpriority_voucher(qos);
898 send_msg.qos = qos;
899 } else {
900 qos_class_t qc;
901 int relpri;
902 pthread_get_qos_class_np(pthread_self(), &qc, &relpri);
903 send_msg.qos = (uint32_t)_pthread_qos_class_encode(qc, relpri, 0);
904 }
905
906 mach_msg_option_t send_opts = options;
907 if (reply_port) {
908 send_opts |= MACH_SEND_SYNC_OVERRIDE;
909 }
910 send_opts |= MACH_SEND_MSG | MACH_SEND_TIMEOUT | MACH_SEND_OVERRIDE;
911
912 ret = mach_msg(&send_msg.header, send_opts, send_msg.header.msgh_size,
913 0, MACH_PORT_NULL, 10000, qos);
914
915 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "client mach_msg");
916}
917
918static kern_return_t
919receive(
920 mach_port_t rcv_port,
921 mach_port_t notify_port)
922{
923 kern_return_t ret = 0;
924
925 struct test_msg rcv_msg = {
926 .header = {
927 .msgh_remote_port = MACH_PORT_NULL,
928 .msgh_local_port = rcv_port,
929 .msgh_size = sizeof(rcv_msg),
930 },
931 };
932
933 T_LOG("Client: Starting sync receive\n");
934
935 ret = mach_msg(&(rcv_msg.header),
936 MACH_RCV_MSG |
937 MACH_RCV_TIMEOUT |
938 MACH_RCV_SYNC_WAIT,
939 0,
940 rcv_msg.header.msgh_size,
941 rcv_port,
942 SEND_TIMEOUT_SECS * 1000,
943 notify_port);
944
945 return ret;
946}
947
948T_HELPER_DECL(qos_get_special_reply_port,
949 "Test get_special_reply_port and it's corner cases.")
950{
951 mach_port_t special_reply_port;
952 mach_port_t new_special_reply_port;
953
954 special_reply_port = thread_get_special_reply_port();
955 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
956
957 new_special_reply_port = thread_get_special_reply_port();
958 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(new_special_reply_port), "get_thread_special_reply_port");
959
960 mach_port_destroy(mach_task_self(), special_reply_port);
961 mach_port_destroy(mach_task_self(), new_special_reply_port);
962
963 new_special_reply_port = thread_get_special_reply_port();
964 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(new_special_reply_port), "get_thread_special_reply_port");
965
966 T_END;
967}
968
969static void *
970qos_client_send_to_intransit(void *arg __unused)
971{
972 mach_port_t qos_send_port;
973 mach_port_t msg_port;
974 mach_port_t special_reply_port;
975
976 kern_return_t kr = bootstrap_look_up(bootstrap_port,
977 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
978 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
979
980 special_reply_port = thread_get_special_reply_port();
981 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
982
983 /* Create a rcv right to send in a msg */
984 kr = mach_port_allocate(mach_task_self(),
985 MACH_PORT_RIGHT_RECEIVE,
986 &msg_port);
987
988 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_port_allocate");
989
990 kr = mach_port_insert_right(mach_task_self(),
991 msg_port,
992 msg_port,
993 MACH_MSG_TYPE_MAKE_SEND);
994
995 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_port_insert_right");
996
997 /* Send an empty msg on the port to fire the WL thread */
998 send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL,
999 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0), 0);
1000
1001 /* Sleep 3 seconds for the server to start */
1002 sleep(3);
1003
1004 /* Send the message with msg port as in-transit port, this msg will not be dequeued */
1005 send(qos_send_port, MACH_PORT_NULL, msg_port,
1006 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0), 0);
1007
1008 /* Send 5 messages to msg port to make sure the port is full */
1009 for (int i = 0; i < 5; i++) {
1010 send(msg_port, MACH_PORT_NULL, MACH_PORT_NULL,
1011 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0), 0);
1012 }
1013
1014 T_LOG("Sent 5 msgs, now trying to send sync ipc message, which will block with a timeout\n");
1015 /* Send the message to the in-transit port, it should block and override the rcv's workloop */
1016 send(msg_port, special_reply_port, MACH_PORT_NULL,
1017 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0), 0);
1018 T_LOG("Client done sending messages, now waiting for server to end the test");
1019
1020 T_ASSERT_FAIL("client timed out");
1021 return NULL;
1022}
1023
1024T_HELPER_DECL(qos_client_send_to_intransit_with_thr_pri,
1025 "Send synchronous messages from a pri thread to an intransit port")
1026{
1027 thread_create_at_qos(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], qos_client_send_to_intransit);
1028 sleep(HELPER_TIMEOUT_SECS);
1029}
1030
1031static pthread_t
1032thread_create_at_qos(qos_class_t qos, void * (*function)(void *))
1033{
1034 qos_class_t qos_thread;
1035 pthread_t thread;
1036 pthread_attr_t attr;
1037 int ret;
1038
1039 ret = setpriority(PRIO_DARWIN_ROLE, 0, PRIO_DARWIN_ROLE_UI_FOCAL);
1040 if (ret != 0) {
1041 T_LOG("set priority failed\n");
1042 }
1043
1044 pthread_attr_init(&attr);
1045 pthread_attr_set_qos_class_np(&attr, qos, 0);
1046 pthread_create(&thread, &attr, function, NULL);
1047
1048 T_LOG("pthread created\n");
1049 pthread_get_qos_class_np(thread, &qos_thread, NULL);
1050 T_EXPECT_EQ(qos_thread, (qos_class_t)qos, NULL);
1051 return thread;
1052}
1053
1054static void *
1055qos_send_and_sync_rcv(void *arg __unused)
1056{
1057 mach_port_t qos_send_port;
1058 mach_port_t special_reply_port;
1059
1060 T_LOG("Client: from created thread\n");
1061 T_EXPECT_EFFECTIVE_QOS_EQ(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
1062 "pthread QoS should be %s", g_expected_qos_name[ENV_QOS_AFTER_OVERRIDE]);
1063
1064 kern_return_t kr = bootstrap_look_up(bootstrap_port,
1065 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
1066 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1067
1068 special_reply_port = thread_get_special_reply_port();
1069 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
1070
1071 /* enqueue two messages to make sure that mqueue is not empty */
1072 send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL,
1073 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_QUEUE_OVERRIDE], 0, 0), 0);
1074
1075 send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL,
1076 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_QUEUE_OVERRIDE], 0, 0), 0);
1077
1078 sleep(SEND_TIMEOUT_SECS);
1079
1080 /* sync wait on msg port */
1081 receive(special_reply_port, qos_send_port);
1082
1083 T_LOG("Client done doing sync rcv, now waiting for server to end the test");
1084 sleep(SEND_TIMEOUT_SECS);
1085
1086 T_ASSERT_FAIL("client timed out");
1087 return NULL;
1088}
1089
1090static void *
1091qos_sync_rcv(void *arg __unused)
1092{
1093 mach_port_t qos_send_port;
1094 mach_port_t special_reply_port;
1095
1096 T_LOG("Client: from created thread\n");
1097
1098 kern_return_t kr = bootstrap_look_up(bootstrap_port,
1099 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
1100 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1101
1102 special_reply_port = thread_get_special_reply_port();
1103 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
1104
1105 /* enqueue two messages to make sure that mqueue is not empty */
1106 send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL,
1107 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_QUEUE_OVERRIDE], 0, 0), 0);
1108
1109 sleep(RECV_TIMEOUT_SECS);
1110
1111 /* sync wait on msg port */
1112 receive(special_reply_port, qos_send_port);
1113
1114 T_LOG("Client done doing sync rcv, now waiting for server to end the test");
1115 sleep(SEND_TIMEOUT_SECS);
1116
1117 T_ASSERT_FAIL("client timed out");
1118 return NULL;
1119}
1120
1121static void
1122thread_wait_to_block(mach_port_t thread_port)
1123{
1124 thread_extended_info_data_t extended_info;
1125 kern_return_t kr;
1126
1127 while (1) {
1128 mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT;
1129 kr = thread_info(thread_port, THREAD_EXTENDED_INFO,
1130 (thread_info_t)&extended_info, &count);
1131
1132 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_info");
1133
1134 if (extended_info.pth_run_state == TH_STATE_WAITING) {
1135 T_LOG("Target thread blocked\n");
1136 break;
1137 }
1138 thread_switch(thread_port, SWITCH_OPTION_DEPRESS, 0);
1139 }
1140}
1141
1142T_HELPER_DECL(qos_client_send_sync_and_sync_rcv,
1143 "Send messages and syncronously wait for rcv")
1144{
1145 thread_create_at_qos(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], qos_send_and_sync_rcv);
1146 sleep(HELPER_TIMEOUT_SECS);
1147}
1148
1149T_HELPER_DECL(qos_client_sync_rcv_qos_change,
1150 "Send messages and syncronously wait for rcv and change qos of waiting thread")
1151{
1152 pthread_t rcv_thread;
1153
1154 rcv_thread = thread_create_at_qos(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], qos_sync_rcv);
1155
1156 T_LOG("Waiting for %d seconds before changing qos of rcv thread", SEND_TIMEOUT_SECS);
1157 sleep(SEND_TIMEOUT_SECS);
1158
1159 /* Wait for the thread to block */
1160 thread_wait_to_block(pthread_mach_thread_np(rcv_thread));
1161
1162 /* Update the rcv thread's qos */
1163 pthread_override_qos_class_start_np(rcv_thread, g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0);
1164
1165 sleep(HELPER_TIMEOUT_SECS);
1166}
1167
1168static void *
1169qos_client_send_sync_msg_and_test_link(void *arg)
1170{
1171 mach_port_t qos_send_port;
1172 mach_port_t special_reply_port;
1173 boolean_t in_effect = FALSE;
1174 kern_return_t kr;
1175 unsigned long expected_result = (unsigned long) arg;
1176
1177 kr = bootstrap_look_up(bootstrap_port,
1178 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
1179 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1180
1181 /* start monitoring sync ipc link */
1182 kr = mach_sync_ipc_link_monitoring_start(&special_reply_port);
1183 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_sync_ipc_link_monitoring_start");
1184
1185 /* Send the message to msg port */
1186 send(qos_send_port, special_reply_port, MACH_PORT_NULL,
1187 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0), 0);
1188
1189 /*
1190 * wait for the reply
1191 * some tests do not send a msg back so the receive
1192 * might fail
1193 */
1194 receive(special_reply_port, qos_send_port);
1195
1196 /* stop monitoring link */
1197 kr = mach_sync_ipc_link_monitoring_stop(special_reply_port, &in_effect);
1198 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_sync_ipc_link_monitoring_stop");
1199
1200 if (!in_effect) {
1201 T_LOG("Link was broken");
1202 } else {
1203 T_LOG("Link correct");
1204 }
1205
1206 if (expected_result == 1) {
1207 T_ASSERT_TRUE(in_effect, "special reply port link after rcv");
1208 } else {
1209 T_ASSERT_FALSE(in_effect, "special reply port link after rcv");
1210 }
1211 T_END;
1212}
1213
1214static void *
1215qos_client_send_2sync_msg_and_test_link(void *arg)
1216{
1217 mach_port_t qos_send_port;
1218 mach_port_t special_reply_port;
1219 boolean_t in_effect = FALSE;
1220 kern_return_t kr;
1221 unsigned long expected_result = (unsigned long) arg;
1222
1223 kr = bootstrap_look_up(bootstrap_port,
1224 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
1225 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1226
1227 /* start monitoring sync ipc link */
1228 kr = mach_sync_ipc_link_monitoring_start(&special_reply_port);
1229 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_sync_ipc_link_monitoring_start");
1230
1231 /* Send the first message to msg port */
1232 send(qos_send_port, special_reply_port, MACH_PORT_NULL,
1233 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0), 0);
1234
1235 /* wait for the reply */
1236 kr = receive(special_reply_port, qos_send_port);
1237 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "receive");
1238
1239 /* Send the second message to msg port */
1240 send(qos_send_port, special_reply_port, MACH_PORT_NULL,
1241 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0), 0);
1242
1243 /* wait for the reply */
1244 kr = receive(special_reply_port, qos_send_port);
1245 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "receive");
1246
1247 /* stop monitoring link */
1248 kr = mach_sync_ipc_link_monitoring_stop(special_reply_port, &in_effect);
1249 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_sync_ipc_link_monitoring_stop");
1250
1251 if (!in_effect) {
1252 T_LOG("Link was broken");
1253 } else {
1254 T_LOG("Link correct");
1255 }
1256
1257 if (expected_result == 1) {
1258 T_ASSERT_TRUE(in_effect, "special reply port link after rcv");
1259 } else {
1260 T_ASSERT_FALSE(in_effect, "special reply port link after rcv");
1261 }
1262 T_END;
1263}
1264T_HELPER_DECL(qos_client_send_sync_msg_with_link_check_correct_server,
1265 "Send sync message, wait for reply and check sync ipc link")
1266{
1267 pthread_t thread;
1268 pthread_attr_t attr;
1269 unsigned long expected_result = 1;
1270
1271 pthread_attr_init(&attr);
1272 pthread_create(&thread, &attr, qos_client_send_sync_msg_and_test_link, (void *)expected_result);
1273
1274 sleep(HELPER_TIMEOUT_SECS);
1275}
1276
1277T_HELPER_DECL(qos_client_send_sync_msg_with_link_check_incorrect_server,
1278 "Send sync message, wait for reply and check sync ipc link")
1279{
1280 pthread_t thread;
1281 pthread_attr_t attr;
1282 unsigned long expected_result = 0;
1283
1284 pthread_attr_init(&attr);
1285 pthread_create(&thread, &attr, qos_client_send_sync_msg_and_test_link, (void *)expected_result);
1286
1287 sleep(HELPER_TIMEOUT_SECS);
1288}
1289
1290T_HELPER_DECL(qos_client_send_2sync_msg_with_link_check_correct_server,
1291 "Send sync message, wait for reply and check sync ipc link")
1292{
1293 pthread_t thread;
1294 pthread_attr_t attr;
1295 unsigned long expected_result = 1;
1296
1297 pthread_attr_init(&attr);
1298 pthread_create(&thread, &attr, qos_client_send_2sync_msg_and_test_link, (void *)expected_result);
1299
1300 sleep(HELPER_TIMEOUT_SECS);
1301}
1302
1303T_HELPER_DECL(qos_client_send_2sync_msg_with_link_check_incorrect_server,
1304 "Send sync message, wait for reply and check sync ipc link")
1305{
1306 pthread_t thread;
1307 pthread_attr_t attr;
1308 unsigned long expected_result = 0;
1309
1310 pthread_attr_init(&attr);
1311 pthread_create(&thread, &attr, qos_client_send_2sync_msg_and_test_link, (void *)expected_result);
1312
1313 sleep(HELPER_TIMEOUT_SECS);
1314}
1315
1316static void *
1317qos_client_send_sync_msg(void *arg __unused)
1318{
1319 mach_port_t qos_send_port;
1320 mach_port_t special_reply_port;
1321
1322 kern_return_t kr = bootstrap_look_up(bootstrap_port,
1323 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
1324 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1325
1326 special_reply_port = thread_get_special_reply_port();
1327 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
1328
1329 /* Send the message to msg port */
1330 send(qos_send_port, special_reply_port, MACH_PORT_NULL,
1331 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0), 0);
1332
1333 /* wait for the reply */
1334 receive(special_reply_port, qos_send_port);
1335
1336 T_LOG("Client done sending messages, now waiting for server to end the test");
1337 sleep(2 * SEND_TIMEOUT_SECS);
1338
1339 T_ASSERT_FAIL("client timed out");
1340 return NULL;
1341}
1342
1343T_HELPER_DECL(qos_client_send_sync_msg_with_pri,
1344 "Send sync message and wait for reply")
1345{
1346 thread_create_at_qos(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], qos_client_send_sync_msg);
1347 sleep(HELPER_TIMEOUT_SECS);
1348}
1349
1350static void *
1351qos_client_send_two_sync_msg_high_qos(void *arg __unused)
1352{
1353 mach_port_t qos_send_port;
1354 mach_port_t special_reply_port;
1355
1356 kern_return_t kr = bootstrap_look_up(bootstrap_port,
1357 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
1358 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1359
1360 special_reply_port = thread_get_special_reply_port();
1361 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
1362
1363 /* Send the message to msg port */
1364 send(qos_send_port, special_reply_port, MACH_PORT_NULL,
1365 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], 0, 0), 0);
1366
1367 /* wait for the reply */
1368 receive(special_reply_port, qos_send_port);
1369
1370 T_LOG("Client done sending messages, now waiting for server to end the test");
1371 sleep(SEND_TIMEOUT_SECS);
1372
1373 T_ASSERT_FAIL("client timed out");
1374 return NULL;
1375}
1376
1377static void *
1378qos_client_send_two_sync_msg_low_qos(void *arg __unused)
1379{
1380 mach_port_t qos_send_port;
1381 mach_port_t special_reply_port;
1382
1383 kern_return_t kr = bootstrap_look_up(bootstrap_port,
1384 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
1385 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1386
1387 special_reply_port = thread_get_special_reply_port();
1388 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
1389
1390 /* Send the message to msg port */
1391 send(qos_send_port, special_reply_port, MACH_PORT_NULL,
1392 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0), 0);
1393
1394 /* wait for the reply */
1395 receive(special_reply_port, qos_send_port);
1396
1397 T_LOG("Client done sending messages, now waiting for server to end the test");
1398 sleep(SEND_TIMEOUT_SECS);
1399
1400 T_ASSERT_FAIL("client timed out");
1401 return NULL;
1402}
1403
1404T_HELPER_DECL(qos_client_send_two_sync_msg_with_thr_pri,
1405 "Send messages sync msgs from 2 threads at given thread pri")
1406{
1407 thread_create_at_qos(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], qos_client_send_two_sync_msg_high_qos);
1408 sleep(INTERMITTENT_TIMEOUT_SEC);
1409 thread_create_at_qos(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], qos_client_send_two_sync_msg_low_qos);
1410 sleep(HELPER_TIMEOUT_SECS);
1411}
1412
1413static mach_port_t other_thread_reply_port = MACH_PORT_NULL;
1414static void *
1415qos_client_destroy_other_threads_port(void *arg __unused)
1416{
1417 T_LOG("Waiting 6 seconds before destroying other thread's reply port");
1418 sleep(SEND_TIMEOUT_SECS);
1419
1420 T_LOG("Destroying other thread's special reply port ");
1421 mach_port_destroy(mach_task_self(), other_thread_reply_port);
1422
1423 T_LOG("Other thread done destroying ");
1424 sleep(3 * SEND_TIMEOUT_SECS);
1425
1426 T_ASSERT_FAIL("client timed out");
1427 return NULL;
1428}
1429
1430static void *
1431qos_client_create_sepcial_reply_and_spawn_thread(void *arg __unused)
1432{
1433 mach_port_t qos_send_port;
1434 mach_port_t special_reply_port;
1435
1436 kern_return_t kr = bootstrap_look_up(bootstrap_port,
1437 KEVENT_QOS_SERVICE_NAME, &qos_send_port);
1438 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1439
1440 special_reply_port = thread_get_special_reply_port();
1441 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
1442
1443 other_thread_reply_port = special_reply_port;
1444
1445 /* Send an async message */
1446 send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL,
1447 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0), 0);
1448
1449 /* Send the sync ipc message */
1450 send(qos_send_port, special_reply_port, MACH_PORT_NULL,
1451 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0), 0);
1452
1453 /* Create a new thread to send the sync message on our special reply port */
1454 thread_create_at_qos(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], qos_client_destroy_other_threads_port);
1455
1456 /* Client starting to receive message */
1457 receive(special_reply_port, qos_send_port);
1458
1459 sleep(3 * SEND_TIMEOUT_SECS);
1460
1461 T_ASSERT_FAIL("client timed out");
1462 return NULL;
1463}
1464
1465T_HELPER_DECL(qos_client_send_two_msg_and_destroy,
1466 "Send a message with another threads special reply port while that thread destroys the port")
1467{
1468 thread_create_at_qos(g_expected_qos[ENV_QOS_AFTER_OVERRIDE], qos_client_create_sepcial_reply_and_spawn_thread);
1469 sleep(HELPER_TIMEOUT_SECS);
1470}
1471
1472static mach_port_t send_complex_connection_port = MACH_PORT_NULL;
1473
1474static void *
1475qos_client_send_complex_msg_to_service_port(void *arg __unused)
1476{
1477 mach_port_t svc_port, tsr_port, conn_port;
1478 kern_return_t kr;
1479
1480 kr = bootstrap_look_up(bootstrap_port, KEVENT_QOS_SERVICE_NAME, &svc_port);
1481 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
1482
1483 tsr_port = thread_get_special_reply_port();
1484 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(tsr_port), "get_thread_special_reply_port");
1485
1486 conn_port = send_complex_connection_port;
1487
1488 T_LOG("Sending to the service port with a sync IPC");
1489 send(svc_port, tsr_port, conn_port,
1490 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0),
1491 MACH_SEND_PROPAGATE_QOS);
1492
1493 receive(tsr_port, svc_port);
1494
1495 sleep(3 * SEND_TIMEOUT_SECS);
1496
1497 T_ASSERT_FAIL("client timed out");
1498 return NULL;
1499}
1500
1501static void *
1502qos_client_send_to_connection_then_service_port(void *arg __unused)
1503{
1504 mach_port_t tsr_port, conn_port;
1505 mach_port_options_t opts = {
1506 .flags = MPO_INSERT_SEND_RIGHT,
1507 };
1508 kern_return_t kr;
1509
1510 kr = mach_port_construct(mach_task_self(), &opts, 0ull, &conn_port);
1511 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
1512 send_complex_connection_port = conn_port;
1513
1514 tsr_port = thread_get_special_reply_port();
1515 T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(tsr_port), "get_thread_special_reply_port");
1516
1517 T_LOG("Sending to the connection port with a sync IPC");
1518 send(conn_port, tsr_port, MACH_PORT_NULL,
1519 (uint32_t)_pthread_qos_class_encode(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE], 0, 0),
1520 MACH_SEND_PROPAGATE_QOS);
1521
1522 thread_create_at_qos(g_expected_qos[ENV_QOS_AFTER_OVERRIDE],
1523 qos_client_send_complex_msg_to_service_port);
1524
1525 receive(tsr_port, conn_port);
1526
1527 sleep(3 * SEND_TIMEOUT_SECS);
1528
1529 T_ASSERT_FAIL("client timed out");
1530 return NULL;
1531}
1532
1533T_HELPER_DECL(qos_client_send_complex_msg_with_pri,
1534 "Send a message with several ports causing links")
1535{
1536 thread_create_at_qos(g_expected_qos[ENV_QOS_BEFORE_OVERRIDE],
1537 qos_client_send_to_connection_then_service_port);
1538 sleep(HELPER_TIMEOUT_SECS);
1539}
1540
1541static void
1542run_client_server(const char *server_name, const char *client_name, qos_class_t qos[],
1543 const char *qos_name[], const char *wl_function)
1544{
1545 char *env[2 * ENV_VAR_QOS + ENV_VAR_FUNCTION + 1];
1546 env_set_qos(env, qos, qos_name, wl_function);
1547
1548 for (int i = 0; i < ENV_VAR_QOS; i++) {
1549 g_expected_qos[i] = qos[i];
1550 g_expected_qos_name[i] = qos_name[i];
1551 }
1552
1553 dt_helper_t helpers[] = {
1554 dt_launchd_helper_env("com.apple.xnu.test.kevent_qos.plist",
1555 server_name, env),
1556 dt_fork_helper(client_name)
1557 };
1558 dt_run_helpers(helpers, 2, HELPER_TIMEOUT_SECS);
1559}
1560
1561#pragma mark Mach receive - kevent_qos
1562
1563static void
1564expect_kevent_id_recv(mach_port_t port, qos_class_t qos[], const char *qos_name[], const char *wl_function)
1565{
1566 int r;
1567
1568 /* Qos expected by workloop thread */
1569 for (int i = 0; i < ENV_VAR_QOS; i++) {
1570 g_expected_qos[i] = qos[i];
1571 g_expected_qos_name[i] = qos_name[i];
1572 }
1573
1574 if (strcmp(wl_function, "workloop_cb_test_intransit") == 0) {
1575 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1576 worker_cb, event_cb,
1577 (pthread_workqueue_function_workloop_t)workloop_cb_test_intransit, 0, 0), NULL);
1578 } else if (strcmp(wl_function, "workloop_cb_test_sync_send") == 0) {
1579 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1580 worker_cb, event_cb,
1581 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send, 0, 0), NULL);
1582 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_and_enable") == 0) {
1583 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1584 worker_cb, event_cb,
1585 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_and_enable, 0, 0), NULL);
1586 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_and_enable_handoff") == 0) {
1587 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1588 worker_cb, event_cb,
1589 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_and_enable_handoff, 0, 0), NULL);
1590 } else if (strcmp(wl_function, "workloop_cb_test_send_two_sync") == 0) {
1591 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1592 worker_cb, event_cb,
1593 (pthread_workqueue_function_workloop_t)workloop_cb_test_send_two_sync, 0, 0), NULL);
1594 } else if (strcmp(wl_function, "workloop_cb_test_two_send_and_destroy") == 0) {
1595 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1596 worker_cb, event_cb,
1597 (pthread_workqueue_function_workloop_t)workloop_cb_test_two_send_and_destroy, 0, 0), NULL);
1598 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_reply") == 0) {
1599 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1600 worker_cb, event_cb,
1601 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_reply, 0, 0), NULL);
1602 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_deallocate") == 0) {
1603 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1604 worker_cb, event_cb,
1605 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_deallocate, 0, 0), NULL);
1606 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_reply_kevent") == 0) {
1607 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1608 worker_cb, event_cb,
1609 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_reply_kevent, 0, 0), NULL);
1610 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_reply_kevent_pthread") == 0) {
1611 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1612 worker_cb, event_cb,
1613 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_reply_kevent_pthread, 0, 0), NULL);
1614 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_kevent_reply") == 0) {
1615 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1616 worker_cb, event_cb,
1617 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_kevent_reply, 0, 0), NULL);
1618 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_do_nothing") == 0) {
1619 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1620 worker_cb, event_cb,
1621 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_do_nothing, 0, 0), NULL);
1622 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_do_nothing_kevent_pthread") == 0) {
1623 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1624 worker_cb, event_cb,
1625 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_do_nothing_kevent_pthread, 0, 0), NULL);
1626 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_do_nothing_exit") == 0) {
1627 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1628 worker_cb, event_cb,
1629 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_do_nothing_exit, 0, 0), NULL);
1630 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_reply_kevent_reply_kevent") == 0) {
1631 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1632 worker_cb, event_cb,
1633 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_reply_kevent_reply_kevent, 0, 0), NULL);
1634 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_kevent_reply_reply_kevent") == 0) {
1635 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1636 worker_cb, event_cb,
1637 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_kevent_reply_reply_kevent, 0, 0), NULL);
1638 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_kevent_reply_kevent_reply") == 0) {
1639 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1640 worker_cb, event_cb,
1641 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_kevent_reply_kevent_reply, 0, 0), NULL);
1642 } else if (strcmp(wl_function, "workloop_cb_test_sync_send_reply_kevent_kevent_reply") == 0) {
1643 T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
1644 worker_cb, event_cb,
1645 (pthread_workqueue_function_workloop_t)workloop_cb_test_sync_send_reply_kevent_kevent_reply, 0, 0), NULL);
1646 } else {
1647 T_ASSERT_FAIL("no workloop function specified \n");
1648 }
1649
1650 struct kevent_qos_s kev[] = {{
1651 .ident = port,
1652 .filter = EVFILT_MACHPORT,
1653 .flags = EV_ADD | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED,
1654 .fflags = (MACH_RCV_MSG | MACH_RCV_VOUCHER | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY |
1655 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) |
1656 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)),
1657 .data = 1,
1658 .qos = (int32_t)_pthread_qos_class_encode(qos[ENV_QOS_QUEUE_OVERRIDE], 0, 0)
1659 }};
1660
1661 struct kevent_qos_s kev_err[] = {{ 0 }};
1662
1663 /* Setup workloop for mach msg rcv */
1664 r = kevent_id(25, kev, 1, kev_err, 1, NULL,
1665 NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS);
1666
1667 T_QUIET; T_ASSERT_POSIX_SUCCESS(r, "kevent_id");
1668 T_QUIET; T_ASSERT_EQ(r, 0, "no errors returned from kevent_id");
1669 sleep(HELPER_TIMEOUT_SECS);
1670}
1671
1672T_HELPER_DECL(server_kevent_id,
1673 "Reply with the QoS that a dispatch source event handler ran with")
1674{
1675 qos_class_t qos[ENV_VAR_QOS];
1676 const char *qos_name[ENV_VAR_QOS];
1677 const char *wl_function;
1678 environ_get_qos(qos, qos_name, &wl_function);
1679
1680 expect_kevent_id_recv(get_server_port(), qos, qos_name, wl_function);
1681 sleep(HELPER_TIMEOUT_SECS);
1682 T_ASSERT_FAIL("should receive a message within %d seconds",
1683 RECV_TIMEOUT_SECS);
1684}
1685
1686static void *
1687special_reply_port_thread(void *ctxt)
1688{
1689 kern_return_t ret;
1690 mach_port_t rcv_port = *(mach_port_t *)ctxt;
1691 struct test_msg rcv_msg = {
1692 .header = {
1693 .msgh_remote_port = MACH_PORT_NULL,
1694 .msgh_local_port = rcv_port,
1695 .msgh_size = sizeof(rcv_msg),
1696 },
1697 };
1698
1699 ret = mach_msg(&rcv_msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
1700 rcv_msg.header.msgh_size, rcv_port, 1000, MACH_PORT_NULL);
1701
1702 T_EXPECT_EQ(ret, MACH_RCV_TIMED_OUT, "receive should not panic");
1703
1704 *(mach_port_t *)ctxt = MACH_PORT_NULL;
1705
1706 sleep(1); // give some time to pthread_exit
1707
1708 ret = mach_msg(&rcv_msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
1709 rcv_msg.header.msgh_size, rcv_port, 1000, MACH_PORT_NULL);
1710
1711 T_EXPECT_EQ(ret, MACH_RCV_TIMED_OUT, "receive should not panic");
1712
1713 T_END;
1714}
1715
1716T_DECL(special_reply_port, "basic special reply port robustness checks",
1717 T_META_RUN_CONCURRENTLY(true))
1718{
1719 pthread_t thread;
1720 mach_port_t srp = thread_get_special_reply_port();
1721
1722 pthread_create(&thread, NULL, special_reply_port_thread, &srp);
1723
1724 while (srp) {
1725 usleep(1000);
1726 }
1727
1728 pthread_exit(NULL);
1729}
1730
1731#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) \
1732 T_DECL(server_kevent_id_##name, \
1733 "Event delivery at " qos_ao_name " QoS using a kevent_id", \
1734 T_META_ASROOT(YES)) \
1735 { \
1736 qos_class_t qos_array[ENV_VAR_QOS] = {qos_bo, qos_qo, qos_ao}; \
1737 const char *qos_name_array[ENV_VAR_QOS] = {qos_bo_name, qos_qo_name, qos_ao_name}; \
1738 run_client_server(server_name, client_name, qos_array, qos_name_array, wl_function_name); \
1739 }
1740/*
1741 * Test 1: Test special reply port SPI
1742 *
1743 * Create thread special reply port and check any subsequent calls to
1744 * the same should return MACH_PORT_NULL, unless the reply port is destroyed.
1745 */
1746TEST_QOS("server_kevent_id", "qos_get_special_reply_port", special_reply_port, "workloop_cb_test_intransit",
1747 QOS_CLASS_DEFAULT, "default",
1748 QOS_CLASS_DEFAULT, "default",
1749 QOS_CLASS_DEFAULT, "default")
1750
1751/*
1752 * Test 2: Test sync ipc send to an in-transit port
1753 *
1754 * Send a sync ipc message (at IN qos) to an in-transit port enqueued in a port
1755 * attached to a workloop. Test that the servicer of the workloop gets
1756 * sync ipc override.
1757 */
1758TEST_QOS("server_kevent_id", "qos_client_send_to_intransit_with_thr_pri", transit_IN, "workloop_cb_test_intransit",
1759 QOS_CLASS_DEFAULT, "default",
1760 QOS_CLASS_MAINTENANCE, "maintenance",
1761 QOS_CLASS_USER_INITIATED, "user initiated")
1762
1763/*
1764 * Test 3: Test sync ipc send to an in-transit port
1765 *
1766 * Send a sync ipc message (at UI qos) to an in-transit port enqueued in a port
1767 * attached to a workloop. Test that the servicer of the workloop gets
1768 * sync ipc override.
1769 */
1770TEST_QOS("server_kevent_id", "qos_client_send_to_intransit_with_thr_pri", transit_UI, "workloop_cb_test_intransit",
1771 QOS_CLASS_USER_INITIATED, "user initiated",
1772 QOS_CLASS_MAINTENANCE, "maintenance",
1773 QOS_CLASS_USER_INTERACTIVE, "user initiated with 47 basepri promotion")
1774
1775/*
1776 * Test 4: Test starting a sync rcv overrides the servicer
1777 *
1778 * Send an async message to a port and then start waiting on
1779 * the port in mach msg rcv (at IN qos) with sync wait and test if the
1780 * servicer of the workloop gets sync ipc override.
1781 */
1782TEST_QOS("server_kevent_id", "qos_client_send_sync_and_sync_rcv", rcv_IN, "workloop_cb_test_intransit",
1783 QOS_CLASS_DEFAULT, "default",
1784 QOS_CLASS_MAINTENANCE, "maintenance",
1785 QOS_CLASS_USER_INITIATED, "user initiated")
1786
1787/*
1788 * Test 5: Test starting a sync rcv overrides the servicer
1789 *
1790 * Send an async message to a port and then start waiting on
1791 * the port in mach msg rcv (at UI qos) with sync wait and test if the
1792 * servicer of the workloop gets sync ipc override.
1793 */
1794TEST_QOS("server_kevent_id", "qos_client_send_sync_and_sync_rcv", rcv_UI, "workloop_cb_test_intransit",
1795 QOS_CLASS_DEFAULT, "default",
1796 QOS_CLASS_MAINTENANCE, "maintenance",
1797 QOS_CLASS_USER_INTERACTIVE, "user interactive with 47 basepri promotion")
1798
1799/*
1800 * Test 6: test sending sync ipc message (at IN qos) to port will override the servicer
1801 *
1802 * Send a message with sync ipc override to a port and check if the servicer
1803 * of the workloop on other side gets sync ipc override.
1804 */
1805TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_pri", send_sync_IN, "workloop_cb_test_sync_send",
1806 QOS_CLASS_DEFAULT, "default",
1807 QOS_CLASS_MAINTENANCE, "maintenance",
1808 QOS_CLASS_USER_INITIATED, "user initiated")
1809
1810/*
1811 * Test 7: test sending sync ipc message (at UI qos) to port will override the servicer
1812 *
1813 * Send a message with sync ipc override to a port and check if the servicer
1814 * of the workloop on other side gets sync ipc override.
1815 */
1816TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_pri", send_sync_UI, "workloop_cb_test_sync_send",
1817 QOS_CLASS_MAINTENANCE, "maintenance",
1818 QOS_CLASS_MAINTENANCE, "maintenance",
1819 QOS_CLASS_USER_INTERACTIVE, "user initiated with 47 basepri promotion")
1820
1821/*
1822 * Test 8: test enabling a knote in workloop handler will drop the sync ipc override of delivered message
1823 *
1824 * Send a sync ipc message to port and check the servicer of the workloop
1825 * on other side gets sync ipc override and once the handler enables the knote,
1826 * that sync ipc override is dropped.
1827 */
1828TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_pri", send_sync_UI_and_enable, "workloop_cb_test_sync_send_and_enable",
1829 QOS_CLASS_DEFAULT, "default",
1830 QOS_CLASS_DEFAULT, "default",
1831 QOS_CLASS_USER_INTERACTIVE, "user initiated with 47 basepri promotion")
1832
1833/*
1834 * Test 9: test returning to begin processing drops sync ipc override of delivered message
1835 *
1836 * Send a sync ipc message and check if enabling the knote clears the override of
1837 * the delivered message, but should still have the override of an enqueued message.
1838 */
1839TEST_QOS("server_kevent_id", "qos_client_send_two_sync_msg_with_thr_pri", send_two_sync_UI, "workloop_cb_test_send_two_sync",
1840 QOS_CLASS_BACKGROUND, "background",
1841 QOS_CLASS_MAINTENANCE, "maintenance",
1842 QOS_CLASS_USER_INTERACTIVE, "user initiated with 47 basepri promotion")
1843
1844/*
1845 * Test 10: test destroying the special reply port drops the override
1846 *
1847 * Send an async messages and a sync ipc message, the workloop handler
1848 * should get a sync ipc override, now test if destroying the special
1849 * reply port drops the sync ipc override on the servicer.
1850 */
1851TEST_QOS("server_kevent_id", "qos_client_send_two_msg_and_destroy", send_two_UI_and_destroy, "workloop_cb_test_two_send_and_destroy",
1852 QOS_CLASS_BACKGROUND, "background",
1853 QOS_CLASS_MAINTENANCE, "maintenance",
1854 QOS_CLASS_USER_INTERACTIVE, "user initiated with 47 basepri promotion")
1855
1856/*
1857 * Test 11: test sending two ports with chaining
1858 *
1859 * Send a sync IPC to a connection port, which itself is embedded in a message
1860 * sent as a sync IPC to a service port.
1861 */
1862TEST_QOS("server_kevent_id", "qos_client_send_complex_msg_with_pri", send_complex_sync_UI_and_enable, "workloop_cb_test_sync_send_and_enable",
1863 QOS_CLASS_USER_INITIATED, "user initiated",
1864 QOS_CLASS_USER_INITIATED, "user initiated",
1865 QOS_CLASS_USER_INTERACTIVE, "user initiated with 47 basepri promotion")
1866
1867/*
1868 * Test 12: test sending two ports with chaining
1869 *
1870 * Send a sync IPC to a connection port, which itself is embedded in a message
1871 * sent as a sync IPC to a service port.
1872 */
1873TEST_QOS("server_kevent_id", "qos_client_send_complex_msg_with_pri", send_complex_sync_UI_and_enable_and_handoff, "workloop_cb_test_sync_send_and_enable_handoff",
1874 QOS_CLASS_USER_INITIATED, "user initiated",
1875 QOS_CLASS_USER_INITIATED, "user initiated",
1876 QOS_CLASS_USER_INTERACTIVE, "user initiated with 47 basepri promotion")
1877
1878/*
1879 * Test 13: test changing qos of a thread to trigger turnstile push
1880 *
1881 * Send a sync IPC to a service port and change the qos of the blocked thread
1882 * to verify that changing qos triggers a turnstile push.
1883 */
1884TEST_QOS("server_kevent_id", "qos_client_sync_rcv_qos_change", qos_change_to_IN, "workloop_cb_test_intransit",
1885 QOS_CLASS_DEFAULT, "default",
1886 QOS_CLASS_MAINTENANCE, "maintenance",
1887 QOS_CLASS_USER_INITIATED, "user initiated")
1888
1889/*
1890 * Test 14 - 21
1891 *
1892 * Test single sync ipc link with server that breaks/preserves the link in different ways.
1893 */
1894TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_link_check_correct_server", send_sync_link_correct_server_s, "workloop_cb_test_sync_send_reply",
1895 QOS_CLASS_DEFAULT, "default",
1896 QOS_CLASS_DEFAULT, "default",
1897 QOS_CLASS_DEFAULT, "default")
1898
1899TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_link_check_correct_server", send_sync_link_correct_server_d, "workloop_cb_test_sync_send_deallocate",
1900 QOS_CLASS_DEFAULT, "default",
1901 QOS_CLASS_DEFAULT, "default",
1902 QOS_CLASS_DEFAULT, "default")
1903
1904TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_link_check_correct_server", send_sync_link_correct_server_sk, "workloop_cb_test_sync_send_reply_kevent",
1905 QOS_CLASS_DEFAULT, "default",
1906 QOS_CLASS_DEFAULT, "default",
1907 QOS_CLASS_DEFAULT, "default")
1908
1909TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_link_check_correct_server", send_sync_link_correct_server_skp, "workloop_cb_test_sync_send_reply_kevent_pthread",
1910 QOS_CLASS_DEFAULT, "default",
1911 QOS_CLASS_DEFAULT, "default",
1912 QOS_CLASS_DEFAULT, "default")
1913
1914TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_link_check_incorrect_server", send_sync_link_incorrect_server_ks, "workloop_cb_test_sync_send_kevent_reply",
1915 QOS_CLASS_DEFAULT, "default",
1916 QOS_CLASS_DEFAULT, "default",
1917 QOS_CLASS_DEFAULT, "default")
1918
1919TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_link_check_correct_server", send_sync_link_correct_server_n, "workloop_cb_test_sync_send_do_nothing",
1920 QOS_CLASS_DEFAULT, "default",
1921 QOS_CLASS_DEFAULT, "default",
1922 QOS_CLASS_DEFAULT, "default")
1923
1924TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_link_check_incorrect_server", send_sync_link_incorrect_server_kp, "workloop_cb_test_sync_send_do_nothing_kevent_pthread",
1925 QOS_CLASS_DEFAULT, "default",
1926 QOS_CLASS_DEFAULT, "default",
1927 QOS_CLASS_DEFAULT, "default")
1928
1929TEST_QOS("server_kevent_id", "qos_client_send_sync_msg_with_link_check_correct_server", send_sync_link_correct_server_e, "workloop_cb_test_sync_send_do_nothing_exit",
1930 QOS_CLASS_DEFAULT, "default",
1931 QOS_CLASS_DEFAULT, "default",
1932 QOS_CLASS_DEFAULT, "default")
1933
1934/*
1935 * Test 22 - 25
1936 *
1937 * Test sequential sync ipc link with server that breaks/preserves the link.
1938 */
1939TEST_QOS("server_kevent_id", "qos_client_send_2sync_msg_with_link_check_correct_server", send_2sync_link_correct_server_sksk, "workloop_cb_test_sync_send_reply_kevent_reply_kevent",
1940 QOS_CLASS_DEFAULT, "default",
1941 QOS_CLASS_DEFAULT, "default",
1942 QOS_CLASS_DEFAULT, "default")
1943
1944TEST_QOS("server_kevent_id", "qos_client_send_2sync_msg_with_link_check_incorrect_server", send_2sync_link_incorrect_server_kssk, "workloop_cb_test_sync_send_kevent_reply_reply_kevent",
1945 QOS_CLASS_DEFAULT, "default",
1946 QOS_CLASS_DEFAULT, "default",
1947 QOS_CLASS_DEFAULT, "default")
1948
1949TEST_QOS("server_kevent_id", "qos_client_send_2sync_msg_with_link_check_incorrect_server", send_2sync_link_incorrect_server_ksks, "workloop_cb_test_sync_send_kevent_reply_kevent_reply",
1950 QOS_CLASS_DEFAULT, "default",
1951 QOS_CLASS_DEFAULT, "default",
1952 QOS_CLASS_DEFAULT, "default")
1953
1954TEST_QOS("server_kevent_id", "qos_client_send_2sync_msg_with_link_check_incorrect_server", send_2sync_link_incorrect_server_skks, "workloop_cb_test_sync_send_reply_kevent_kevent_reply",
1955 QOS_CLASS_DEFAULT, "default",
1956 QOS_CLASS_DEFAULT, "default",
1957 QOS_CLASS_DEFAULT, "default")