#define HELPER_TIMEOUT_SECS (3000)
+struct test_msg {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t port_descriptor;
+};
+
static boolean_t spin_for_ever = false;
+static boolean_t test_noimportance = false;
+
+#define EXPECTED_MESSAGE_ID 0x100
+
static void
thread_create_at_qos(qos_class_t qos, void * (*function)(void *));
static uint64_t
return thread_policy.thps_user_promotion_basepri;
}
-static int messages_received = 0;
+static uint32_t
+get_thread_base_priority(void)
+{
+ kern_return_t kr;
+ mach_port_t thread_port = pthread_mach_thread_np(pthread_self());
+
+ policy_timeshare_info_data_t timeshare_info;
+ mach_msg_type_number_t count = POLICY_TIMESHARE_INFO_COUNT;
+
+ kr = thread_info(thread_port, THREAD_SCHED_TIMESHARE_INFO,
+ (thread_info_t)×hare_info, &count);
+ T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_info");
+
+ return (uint32_t)timeshare_info.base_priority;
+}
+
+
+#define LISTENER_WLID 0x100
+#define CONN_WLID 0x200
+
+static uint32_t
+register_port_options(void)
+{
+ return MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY |
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) |
+ MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
+ MACH_RCV_VOUCHER;
+}
+
+static void
+register_port(uint64_t wlid, mach_port_t port)
+{
+ int r;
+
+ struct kevent_qos_s kev = {
+ .ident = port,
+ .filter = EVFILT_MACHPORT,
+ .flags = EV_ADD | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED,
+ .fflags = register_port_options(),
+ .data = 1,
+ .qos = (int32_t)_pthread_qos_class_encode(QOS_CLASS_MAINTENANCE, 0, 0)
+ };
+
+ struct kevent_qos_s kev_err = { 0 };
+
+ /* Setup workloop for mach msg rcv */
+ r = kevent_id(wlid, &kev, 1, &kev_err, 1, NULL,
+ NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS);
+
+ T_QUIET; T_ASSERT_POSIX_SUCCESS(r, "kevent_id");
+ T_QUIET; T_ASSERT_EQ(r, 0, "no errors returned from kevent_id");
+}
+
/*
* Basic WL handler callback, it checks the
* effective Qos of the servicer thread.
*/
static void
-workloop_cb_test_intransit(uint64_t *workloop_id __unused, void **eventslist __unused, int *events)
+workloop_cb_test_intransit(uint64_t *workloop_id, void **eventslist, int *events)
{
- messages_received++;
- T_LOG("Workloop handler workloop_cb_test_intransit called. Received message no %d",
- messages_received);
+ static bool got_peer;
+ struct kevent_qos_s *kev = eventslist[0];
+ mach_msg_header_t *hdr;
+ struct test_msg *tmsg;
+
+ T_LOG("Workloop handler %s called. Received message on 0x%llx",
+ __func__, *workloop_id);
/* Skip the test if we can't check Qos */
if (geteuid() != 0) {
T_SKIP("kevent_qos test requires root privileges to run.");
}
- if (messages_received == 1) {
- sleep(5);
- T_LOG("Do some CPU work.");
- do_work(5000);
+ T_QUIET; T_ASSERT_EQ(*events, 1, "should have one event");
+
+ T_EXPECT_REQUESTED_QOS_EQ(QOS_CLASS_MAINTENANCE, "message handler should have MT requested QoS");
+
+ hdr = (mach_msg_header_t *)kev->ext[0];
+ T_ASSERT_NOTNULL(hdr, "has a message");
+ T_ASSERT_EQ(hdr->msgh_size, (uint32_t)sizeof(struct test_msg), "of the right size");
+ tmsg = (struct test_msg *)hdr;
- /* Check if the override now is IN + 60 boost */
- T_EXPECT_EFFECTIVE_QOS_EQ(QOS_CLASS_USER_INITIATED,
- "dispatch_source event handler QoS should be QOS_CLASS_USER_INITIATED");
- T_EXPECT_EQ(get_user_promotion_basepri(), 60u,
- "dispatch_source event handler should be overridden at 60");
+ switch (*workloop_id) {
+ case LISTENER_WLID:
+ T_LOG("Registering peer connection");
+ T_QUIET; T_ASSERT_FALSE(got_peer, "Should not have seen peer yet");
+ got_peer = true;
+ break;
+
+ case CONN_WLID:
+ T_LOG("Received message on peer");
+ break;
+
+ default:
+ T_FAIL("???");
+ }
+
+ sleep(5);
+ T_LOG("Do some CPU work.");
+ do_work(5000);
+
+ /* Check if the override now is IN + 60 boost */
+ T_EXPECT_EFFECTIVE_QOS_EQ(QOS_CLASS_USER_INITIATED,
+ "dispatch_source event handler QoS should be QOS_CLASS_USER_INITIATED");
+ T_EXPECT_EQ(get_user_promotion_basepri(), 60u,
+ "dispatch_source event handler should be overridden at 60");
+
+ T_EXPECT_EQ(get_thread_base_priority(), 60u,
+ "dispatch_source event handler should have base pri at 60");
+
+ if (*workloop_id == LISTENER_WLID) {
+ register_port(CONN_WLID, tmsg->port_descriptor.name);
- /* Enable the knote to get 2nd message */
- struct kevent_qos_s *kev = *eventslist;
kev->flags = EV_ADD | EV_ENABLE | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED;
- kev->fflags = (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY |
- MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) |
- MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
- MACH_RCV_VOUCHER);
+ kev->fflags = register_port_options();
+ kev->ext[0] = kev->ext[1] = kev->ext[2] = kev->ext[3] = 0;
*events = 1;
} else {
+ /* this will unblock the waiter */
+ mach_msg_destroy(hdr);
*events = 0;
- exit(0);
+
+ /* now that the message is destroyed, the priority should be gone */
+ T_EXPECT_EFFECTIVE_QOS_EQ(QOS_CLASS_MAINTENANCE,
+ "dispatch_source event handler QoS should be QOS_CLASS_MAINTENANCE after destroying message");
+ T_EXPECT_LE(get_user_promotion_basepri(), 0u,
+ "dispatch_source event handler should not be overridden after destroying message");
+ T_EXPECT_LE(get_thread_base_priority(), 4u,
+ "dispatch_source event handler should have base pri at 4 or less after destroying message");
}
}
{
kern_return_t ret = 0;
- struct {
- mach_msg_header_t header;
- mach_msg_body_t body;
- mach_msg_port_descriptor_t port_descriptor;
- } send_msg = {
+ struct test_msg send_msg = {
.header = {
.msgh_remote_port = send_port,
.msgh_local_port = reply_port,
reply_port ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0,
MACH_MSG_TYPE_MOVE_SEND,
MACH_MSGH_BITS_COMPLEX),
- .msgh_id = 0x100,
+ .msgh_id = EXPECTED_MESSAGE_ID,
.msgh_size = sizeof(send_msg),
},
.body = {
MACH_SEND_MSG |
MACH_SEND_TIMEOUT |
MACH_SEND_OVERRIDE |
+ (test_noimportance ? MACH_SEND_NOIMPORTANCE : 0) |
((reply_port ? MACH_SEND_SYNC_OVERRIDE : 0) | options),
send_msg.header.msgh_size,
0,
T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "client mach_msg");
}
-static void
+static mach_msg_id_t
receive(
mach_port_t rcv_port,
mach_port_t notify_port)
{
- kern_return_t ret = 0;
-
struct {
mach_msg_header_t header;
mach_msg_body_t body;
T_LOG("Client: Starting sync receive\n");
- ret = mach_msg(&(rcv_msg.header),
+ kern_return_t kr;
+ kr = mach_msg(&(rcv_msg.header),
MACH_RCV_MSG |
MACH_RCV_SYNC_WAIT,
0,
rcv_port,
0,
notify_port);
+
+ T_ASSERT_MACH_SUCCESS(kr, "mach_msg rcv");
+
+ return rcv_msg.header.msgh_id;
}
static lock_t lock_DEF;
T_QUIET; T_LOG("The time for priority 60 thread to acquire lock was %llu \n",
(after_lock_time - before_lock_time));
- exit(0);
+ T_END;
}
static void *
static void *
thread_at_maintenance(void *arg __unused)
{
- mach_port_t qos_send_port;
+ mach_port_t service_port;
+ mach_port_t conn_port;
mach_port_t special_reply_port;
+ mach_port_options_t opts = {
+ .flags = MPO_INSERT_SEND_RIGHT,
+ };
main_thread_port = mach_thread_self();
set_thread_name(__FUNCTION__);
kern_return_t kr = bootstrap_look_up(bootstrap_port,
- TURNSTILE_MULTIHOP_SERVICE_NAME, &qos_send_port);
+ TURNSTILE_MULTIHOP_SERVICE_NAME, &service_port);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client bootstrap_look_up");
+ kr = mach_port_construct(mach_task_self(), &opts, 0ull, &conn_port);
+ T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
+
special_reply_port = thread_get_special_reply_port();
T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
/* Become the dispatch sync owner, dispatch_sync_owner will be set in dispatch_sync_wait function */
- /* Send an async message */
- send(qos_send_port, MACH_PORT_NULL, MACH_PORT_NULL,
+ /* Send a sync message */
+ send(conn_port, special_reply_port, MACH_PORT_NULL,
(uint32_t)_pthread_qos_class_encode(QOS_CLASS_MAINTENANCE, 0, 0), 0);
- /* Send a sync message */
- send(qos_send_port, special_reply_port, MACH_PORT_NULL,
+ /* Send an async checkin message */
+ send(service_port, MACH_PORT_NULL, conn_port,
(uint32_t)_pthread_qos_class_encode(QOS_CLASS_MAINTENANCE, 0, 0), 0);
/* Create a new thread at QOS_CLASS_DEFAULT qos */
thread_create_at_qos(QOS_CLASS_DEFAULT, thread_at_default);
/* Block on Sync IPC */
- receive(special_reply_port, qos_send_port);
+ mach_msg_id_t message_id = receive(special_reply_port, service_port);
+
+ T_ASSERT_EQ(message_id, MACH_NOTIFY_SEND_ONCE, "got the expected send-once notification");
+
+ T_LOG("received reply");
dispatch_sync_cancel(def_thread_port, QOS_CLASS_DEFAULT);
return NULL;
T_HELPER_DECL(three_ulock_sync_ipc_hop,
"Create chain of 4 threads with 3 ulocks and 1 sync IPC at different qos")
{
- dt_stat_time_t roundtrip_stat = dt_stat_time_create("multihop_lock_acquire");
-
- T_STAT_MEASURE_LOOP(roundtrip_stat) {
- if (fork() == 0) {
- thread_create_at_qos(QOS_CLASS_MAINTENANCE, thread_at_maintenance);
- sigsuspend(0);
- exit(0);
- }
- wait(NULL);
- }
+ thread_create_at_qos(QOS_CLASS_MAINTENANCE, thread_at_maintenance);
+ sigsuspend(0);
+}
- dt_stat_finalize(roundtrip_stat);
- T_END;
+T_HELPER_DECL(three_ulock_sync_ipc_hop_noimportance,
+ "Create chain of 4 threads with 3 ulocks and 1 no-importance sync IPC at different qos")
+{
+ test_noimportance = true;
+ thread_create_at_qos(QOS_CLASS_MAINTENANCE, thread_at_maintenance);
+ sigsuspend(0);
}
+
static void
thread_create_at_qos(qos_class_t qos, void * (*function)(void *))
{
#pragma mark Mach receive - kevent_qos
-static void
-expect_kevent_id_recv(mach_port_t port)
+T_HELPER_DECL(server_kevent_id,
+ "Reply with the QoS that a dispatch source event handler ran with")
{
- int r;
-
T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
worker_cb, event_cb,
(pthread_workqueue_function_workloop_t)workloop_cb_test_intransit, 0, 0), NULL);
- struct kevent_qos_s kev[] = {{
- .ident = port,
- .filter = EVFILT_MACHPORT,
- .flags = EV_ADD | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED,
- .fflags = (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY |
- MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) |
- MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
- MACH_RCV_VOUCHER),
- .data = 1,
- .qos = (int32_t)_pthread_qos_class_encode(QOS_CLASS_MAINTENANCE, 0, 0)
- }};
-
- struct kevent_qos_s kev_err[] = {{ 0 }};
-
- /* Setup workloop for mach msg rcv */
- r = kevent_id(25, kev, 1, kev_err, 1, NULL,
- NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS);
-
- T_QUIET; T_ASSERT_POSIX_SUCCESS(r, "kevent_id");
- T_QUIET; T_ASSERT_EQ(r, 0, "no errors returned from kevent_id");
-}
-
-T_HELPER_DECL(server_kevent_id,
- "Reply with the QoS that a dispatch source event handler ran with")
-{
- expect_kevent_id_recv(get_server_port());
+ register_port(LISTENER_WLID, get_server_port());
sigsuspend(0);
T_ASSERT_FAIL("should receive a message");
}
*/
TEST_MULTIHOP("server_kevent_id", "three_ulock_sync_ipc_hop", three_ulock_sync_ipc_hop)
+TEST_MULTIHOP("server_kevent_id", "three_ulock_sync_ipc_hop_noimportance", three_ulock_sync_ipc_hop_noimportance)
+
/*
* Test 2: Test multihop priority boosting with ulocks, dispatch sync and sync IPC.
*