1*0f4c859eSApple OSS Distributions /* test that the header doesn't implicitly depend on others */
2*0f4c859eSApple OSS Distributions #include <sys/work_interval.h>
3*0f4c859eSApple OSS Distributions
4*0f4c859eSApple OSS Distributions #include <stdlib.h>
5*0f4c859eSApple OSS Distributions #include <stdio.h>
6*0f4c859eSApple OSS Distributions #include <unistd.h>
7*0f4c859eSApple OSS Distributions #include <errno.h>
8*0f4c859eSApple OSS Distributions #include <err.h>
9*0f4c859eSApple OSS Distributions #include <string.h>
10*0f4c859eSApple OSS Distributions #include <pthread.h>
11*0f4c859eSApple OSS Distributions #include <sys/sysctl.h>
12*0f4c859eSApple OSS Distributions
13*0f4c859eSApple OSS Distributions #include <mach/mach.h>
14*0f4c859eSApple OSS Distributions #include <mach/semaphore.h>
15*0f4c859eSApple OSS Distributions
16*0f4c859eSApple OSS Distributions #include <libkern/OSAtomic.h>
17*0f4c859eSApple OSS Distributions
18*0f4c859eSApple OSS Distributions #include <darwintest.h>
19*0f4c859eSApple OSS Distributions #include "test_utils.h"
20*0f4c859eSApple OSS Distributions
21*0f4c859eSApple OSS Distributions T_GLOBAL_META(T_META_NAMESPACE("xnu.scheduler"),
22*0f4c859eSApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
23*0f4c859eSApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("scheduler"));
24*0f4c859eSApple OSS Distributions
25*0f4c859eSApple OSS Distributions
26*0f4c859eSApple OSS Distributions static mach_timebase_info_data_t timebase_info;
27*0f4c859eSApple OSS Distributions
28*0f4c859eSApple OSS Distributions static uint64_t
nanos_to_abs(uint64_t nanos)29*0f4c859eSApple OSS Distributions nanos_to_abs(uint64_t nanos)
30*0f4c859eSApple OSS Distributions {
31*0f4c859eSApple OSS Distributions mach_timebase_info(&timebase_info);
32*0f4c859eSApple OSS Distributions return nanos * timebase_info.denom / timebase_info.numer;
33*0f4c859eSApple OSS Distributions }
34*0f4c859eSApple OSS Distributions
35*0f4c859eSApple OSS Distributions static uint64_t
abs_to_nanos(uint64_t abs)36*0f4c859eSApple OSS Distributions abs_to_nanos(uint64_t abs)
37*0f4c859eSApple OSS Distributions {
38*0f4c859eSApple OSS Distributions return abs * timebase_info.numer / timebase_info.denom;
39*0f4c859eSApple OSS Distributions }
40*0f4c859eSApple OSS Distributions
41*0f4c859eSApple OSS Distributions static void
set_realtime(pthread_t thread,uint64_t interval_nanos)42*0f4c859eSApple OSS Distributions set_realtime(pthread_t thread, uint64_t interval_nanos)
43*0f4c859eSApple OSS Distributions {
44*0f4c859eSApple OSS Distributions kern_return_t kr;
45*0f4c859eSApple OSS Distributions thread_time_constraint_policy_data_t pol;
46*0f4c859eSApple OSS Distributions
47*0f4c859eSApple OSS Distributions mach_port_t target_thread = pthread_mach_thread_np(thread);
48*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_GT(target_thread, 0, "pthread_mach_thread_np");
49*0f4c859eSApple OSS Distributions
50*0f4c859eSApple OSS Distributions /* 1s 100ms 10ms */
51*0f4c859eSApple OSS Distributions pol.period = (uint32_t)nanos_to_abs(interval_nanos);
52*0f4c859eSApple OSS Distributions pol.constraint = (uint32_t)nanos_to_abs(interval_nanos);
53*0f4c859eSApple OSS Distributions pol.computation = (uint32_t)nanos_to_abs(interval_nanos - 1000000); // 1 ms of leeway
54*0f4c859eSApple OSS Distributions
55*0f4c859eSApple OSS Distributions pol.preemptible = 0; /* Ignored by OS */
56*0f4c859eSApple OSS Distributions kr = thread_policy_set(target_thread, THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &pol,
57*0f4c859eSApple OSS Distributions THREAD_TIME_CONSTRAINT_POLICY_COUNT);
58*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_policy_set(THREAD_TIME_CONSTRAINT_POLICY)");
59*0f4c859eSApple OSS Distributions }
60*0f4c859eSApple OSS Distributions
61*0f4c859eSApple OSS Distributions static void
create_coreaudio_work_interval(work_interval_t * wi_handle,work_interval_instance_t * wi_instance,mach_port_t * wi_port,bool enable_telemetry,uint32_t create_flags)62*0f4c859eSApple OSS Distributions create_coreaudio_work_interval(work_interval_t *wi_handle, work_interval_instance_t *wi_instance,
63*0f4c859eSApple OSS Distributions mach_port_t *wi_port, bool enable_telemetry, uint32_t create_flags)
64*0f4c859eSApple OSS Distributions {
65*0f4c859eSApple OSS Distributions int ret = 0;
66*0f4c859eSApple OSS Distributions create_flags |= WORK_INTERVAL_FLAG_GROUP | WORK_INTERVAL_FLAG_JOINABLE | WORK_INTERVAL_TYPE_COREAUDIO;
67*0f4c859eSApple OSS Distributions if (enable_telemetry) {
68*0f4c859eSApple OSS Distributions create_flags |= WORK_INTERVAL_FLAG_ENABLE_TELEMETRY_DATA;
69*0f4c859eSApple OSS Distributions }
70*0f4c859eSApple OSS Distributions
71*0f4c859eSApple OSS Distributions ret = work_interval_create(wi_handle, create_flags);
72*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "work_interval_create");
73*0f4c859eSApple OSS Distributions
74*0f4c859eSApple OSS Distributions ret = work_interval_copy_port(*wi_handle, wi_port);
75*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "work_interval_copy_port");
76*0f4c859eSApple OSS Distributions
77*0f4c859eSApple OSS Distributions *wi_instance = work_interval_instance_alloc(*wi_handle);
78*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_NE(*wi_instance, NULL, "work_interval_instance_alloc");
79*0f4c859eSApple OSS Distributions }
80*0f4c859eSApple OSS Distributions
81*0f4c859eSApple OSS Distributions static void
join_coreaudio_work_interval(mach_port_t * wi_port,uint64_t interval_nanos)82*0f4c859eSApple OSS Distributions join_coreaudio_work_interval(mach_port_t *wi_port, uint64_t interval_nanos)
83*0f4c859eSApple OSS Distributions {
84*0f4c859eSApple OSS Distributions int ret = 0;
85*0f4c859eSApple OSS Distributions
86*0f4c859eSApple OSS Distributions set_realtime(pthread_self(), interval_nanos);
87*0f4c859eSApple OSS Distributions
88*0f4c859eSApple OSS Distributions ret = work_interval_join_port(*wi_port);
89*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "work_interval_join_port");
90*0f4c859eSApple OSS Distributions }
91*0f4c859eSApple OSS Distributions
92*0f4c859eSApple OSS Distributions static pthread_mutex_t barrier_lock = PTHREAD_MUTEX_INITIALIZER;
93*0f4c859eSApple OSS Distributions static pthread_cond_t barrier_cond = PTHREAD_COND_INITIALIZER;
94*0f4c859eSApple OSS Distributions static uint32_t barrier_count[2];
95*0f4c859eSApple OSS Distributions static unsigned int active_barrier_ind;
96*0f4c859eSApple OSS Distributions static uint32_t total_thread_count;
97*0f4c859eSApple OSS Distributions static uint32_t expected_cond_wakeups;
98*0f4c859eSApple OSS Distributions
99*0f4c859eSApple OSS Distributions /*
100*0f4c859eSApple OSS Distributions * This implementation of a barrier using pthread_cond_t is
101*0f4c859eSApple OSS Distributions * intended to control the number of thread sleeps/wakeups
102*0f4c859eSApple OSS Distributions * that can occur, so that the reported wakeup counts from
103*0f4c859eSApple OSS Distributions * the work interval data can be validated.
104*0f4c859eSApple OSS Distributions * Each call to pthread_mutex_lock can produce 0 or 1 thread
105*0f4c859eSApple OSS Distributions * wakeups, and each call to pthread_cond_wait produces 0 or
106*0f4c859eSApple OSS Distributions * 1 wakeups.
107*0f4c859eSApple OSS Distributions */
108*0f4c859eSApple OSS Distributions static void
thread_barrier(void)109*0f4c859eSApple OSS Distributions thread_barrier(void)
110*0f4c859eSApple OSS Distributions {
111*0f4c859eSApple OSS Distributions int ret = 0;
112*0f4c859eSApple OSS Distributions ret = pthread_mutex_lock(&barrier_lock);
113*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_mutex_lock");
114*0f4c859eSApple OSS Distributions
115*0f4c859eSApple OSS Distributions barrier_count[active_barrier_ind]--;
116*0f4c859eSApple OSS Distributions
117*0f4c859eSApple OSS Distributions if (barrier_count[active_barrier_ind]) {
118*0f4c859eSApple OSS Distributions unsigned int local_active_barrier_ind = active_barrier_ind;
119*0f4c859eSApple OSS Distributions while (barrier_count[local_active_barrier_ind]) {
120*0f4c859eSApple OSS Distributions expected_cond_wakeups++;
121*0f4c859eSApple OSS Distributions ret = pthread_cond_wait(&barrier_cond, &barrier_lock);
122*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_cond_wait");
123*0f4c859eSApple OSS Distributions }
124*0f4c859eSApple OSS Distributions } else {
125*0f4c859eSApple OSS Distributions ret = pthread_cond_broadcast(&barrier_cond);
126*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_cond_broadcast");
127*0f4c859eSApple OSS Distributions active_barrier_ind = (active_barrier_ind + 1) % 2;
128*0f4c859eSApple OSS Distributions barrier_count[active_barrier_ind] = total_thread_count;
129*0f4c859eSApple OSS Distributions }
130*0f4c859eSApple OSS Distributions
131*0f4c859eSApple OSS Distributions ret = pthread_mutex_unlock(&barrier_lock);
132*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_mutex_unlock");
133*0f4c859eSApple OSS Distributions }
134*0f4c859eSApple OSS Distributions
135*0f4c859eSApple OSS Distributions struct thread_data {
136*0f4c859eSApple OSS Distributions work_interval_t wi_handle;
137*0f4c859eSApple OSS Distributions mach_port_t *wi_port;
138*0f4c859eSApple OSS Distributions unsigned int num_iterations;
139*0f4c859eSApple OSS Distributions uint64_t interval_nanos;
140*0f4c859eSApple OSS Distributions };
141*0f4c859eSApple OSS Distributions
142*0f4c859eSApple OSS Distributions static volatile int64_t work_sum;
143*0f4c859eSApple OSS Distributions
144*0f4c859eSApple OSS Distributions /*
145*0f4c859eSApple OSS Distributions * This work performed in the work interval is designed to
146*0f4c859eSApple OSS Distributions * require CPU compute so that CLPC perf-controls the work
147*0f4c859eSApple OSS Distributions * interval as it typically would. It is also designed such that
148*0f4c859eSApple OSS Distributions * the threads agree when the work interval work is done
149*0f4c859eSApple OSS Distributions * (work_sum higher than a specified threshold), so that the
150*0f4c859eSApple OSS Distributions * amount of work performed will be consistent between the
151*0f4c859eSApple OSS Distributions * different work interval instances.
152*0f4c859eSApple OSS Distributions */
153*0f4c859eSApple OSS Distributions static void
contribute_to_work_sum(void)154*0f4c859eSApple OSS Distributions contribute_to_work_sum(void)
155*0f4c859eSApple OSS Distributions {
156*0f4c859eSApple OSS Distributions volatile unsigned int x = 0;
157*0f4c859eSApple OSS Distributions do {
158*0f4c859eSApple OSS Distributions for (int i = 0; i < 1000; i++) {
159*0f4c859eSApple OSS Distributions x = x * x - x - 1;
160*0f4c859eSApple OSS Distributions }
161*0f4c859eSApple OSS Distributions x %= 10;
162*0f4c859eSApple OSS Distributions } while (OSAtomicAdd64(x, &work_sum) < 10000);
163*0f4c859eSApple OSS Distributions }
164*0f4c859eSApple OSS Distributions
165*0f4c859eSApple OSS Distributions static void *
coreaudio_workload_fn(void * arg)166*0f4c859eSApple OSS Distributions coreaudio_workload_fn(void *arg)
167*0f4c859eSApple OSS Distributions {
168*0f4c859eSApple OSS Distributions struct thread_data *info = (struct thread_data *)arg;
169*0f4c859eSApple OSS Distributions
170*0f4c859eSApple OSS Distributions join_coreaudio_work_interval(info->wi_port, info->interval_nanos);
171*0f4c859eSApple OSS Distributions
172*0f4c859eSApple OSS Distributions for (unsigned int i = 0; i < info->num_iterations; i++) {
173*0f4c859eSApple OSS Distributions thread_barrier();
174*0f4c859eSApple OSS Distributions contribute_to_work_sum();
175*0f4c859eSApple OSS Distributions }
176*0f4c859eSApple OSS Distributions
177*0f4c859eSApple OSS Distributions int ret = work_interval_leave();
178*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "work_interval_leave");
179*0f4c859eSApple OSS Distributions
180*0f4c859eSApple OSS Distributions thread_barrier();
181*0f4c859eSApple OSS Distributions
182*0f4c859eSApple OSS Distributions return NULL;
183*0f4c859eSApple OSS Distributions }
184*0f4c859eSApple OSS Distributions
185*0f4c859eSApple OSS Distributions static void
start_helper_threads(unsigned int num_threads,pthread_t * threads,struct thread_data * thread_datas,work_interval_t wi_handle,mach_port_t * wi_port,unsigned int num_iterations,uint64_t interval_nanos)186*0f4c859eSApple OSS Distributions start_helper_threads(unsigned int num_threads, pthread_t *threads, struct thread_data *thread_datas,
187*0f4c859eSApple OSS Distributions work_interval_t wi_handle, mach_port_t *wi_port, unsigned int num_iterations, uint64_t interval_nanos)
188*0f4c859eSApple OSS Distributions {
189*0f4c859eSApple OSS Distributions int ret = 0;
190*0f4c859eSApple OSS Distributions for (unsigned int i = 0; i < num_threads; i++) {
191*0f4c859eSApple OSS Distributions thread_datas[i].wi_handle = wi_handle;
192*0f4c859eSApple OSS Distributions thread_datas[i].wi_port = wi_port;
193*0f4c859eSApple OSS Distributions thread_datas[i].num_iterations = num_iterations;
194*0f4c859eSApple OSS Distributions thread_datas[i].interval_nanos = interval_nanos;
195*0f4c859eSApple OSS Distributions ret = pthread_create(&threads[i], NULL, coreaudio_workload_fn, &thread_datas[i]);
196*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(ret, "pthread_create");
197*0f4c859eSApple OSS Distributions }
198*0f4c859eSApple OSS Distributions }
199*0f4c859eSApple OSS Distributions
200*0f4c859eSApple OSS Distributions static void
start_work_interval_instance(uint64_t interval_length_abs,work_interval_instance_t wi_instance,work_interval_data_t wi_data)201*0f4c859eSApple OSS Distributions start_work_interval_instance(uint64_t interval_length_abs, work_interval_instance_t wi_instance,
202*0f4c859eSApple OSS Distributions work_interval_data_t wi_data)
203*0f4c859eSApple OSS Distributions {
204*0f4c859eSApple OSS Distributions int ret = 0;
205*0f4c859eSApple OSS Distributions uint64_t start = mach_absolute_time();
206*0f4c859eSApple OSS Distributions
207*0f4c859eSApple OSS Distributions work_interval_instance_clear(wi_instance);
208*0f4c859eSApple OSS Distributions work_interval_instance_set_start(wi_instance, start);
209*0f4c859eSApple OSS Distributions work_interval_instance_set_deadline(wi_instance, start + interval_length_abs);
210*0f4c859eSApple OSS Distributions
211*0f4c859eSApple OSS Distributions // Sanity assertions that the work interval creation flags and interval id are as expected
212*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_instance->wi_create_flags & WORK_INTERVAL_FLAG_IGNORED, 0, "ignored flag start");
213*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_instance->wi_create_flags & WORK_INTERVAL_TYPE_MASK, WORK_INTERVAL_TYPE_COREAUDIO, "coreaudio start");
214*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_NE(wi_instance->wi_interval_id, 0ULL, "nonzero wi_interval_id");
215*0f4c859eSApple OSS Distributions
216*0f4c859eSApple OSS Distributions ret = work_interval_instance_start(wi_instance);
217*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(ret, "work_interval_instance_start");
218*0f4c859eSApple OSS Distributions
219*0f4c859eSApple OSS Distributions if (wi_instance->wi_instance_id == 0ULL) {
220*0f4c859eSApple OSS Distributions T_LOG("wi_instance_id is 0, which is an acceptable condition for devices running legacy CLPC");
221*0f4c859eSApple OSS Distributions }
222*0f4c859eSApple OSS Distributions
223*0f4c859eSApple OSS Distributions work_interval_instance_get_telemetry_data(wi_instance, wi_data, sizeof(struct work_interval_data));
224*0f4c859eSApple OSS Distributions }
225*0f4c859eSApple OSS Distributions
226*0f4c859eSApple OSS Distributions static uint64_t
finish_work_interval_instance(work_interval_instance_t wi_instance,work_interval_data_t wi_data)227*0f4c859eSApple OSS Distributions finish_work_interval_instance(work_interval_instance_t wi_instance, work_interval_data_t wi_data)
228*0f4c859eSApple OSS Distributions {
229*0f4c859eSApple OSS Distributions int ret = 0;
230*0f4c859eSApple OSS Distributions uint64_t finish = mach_absolute_time();
231*0f4c859eSApple OSS Distributions work_interval_instance_set_finish(wi_instance, finish);
232*0f4c859eSApple OSS Distributions
233*0f4c859eSApple OSS Distributions // Sanity assertions that the work interval creation flags and interval id are as expected
234*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_instance->wi_create_flags & WORK_INTERVAL_FLAG_IGNORED, 0, "ignored flag");
235*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_instance->wi_create_flags & WORK_INTERVAL_TYPE_MASK, WORK_INTERVAL_TYPE_COREAUDIO, "coreaudio start");
236*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_NE(wi_instance->wi_interval_id, 0ULL, "nonzero wi_interval_id");
237*0f4c859eSApple OSS Distributions
238*0f4c859eSApple OSS Distributions uint64_t remembered_start = wi_instance->wi_start;
239*0f4c859eSApple OSS Distributions
240*0f4c859eSApple OSS Distributions ret = work_interval_instance_finish(wi_instance);
241*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(ret, "work_interval_instance_finish");
242*0f4c859eSApple OSS Distributions
243*0f4c859eSApple OSS Distributions work_interval_instance_get_telemetry_data(wi_instance, wi_data, sizeof(struct work_interval_data));
244*0f4c859eSApple OSS Distributions
245*0f4c859eSApple OSS Distributions return abs_to_nanos(finish - remembered_start);
246*0f4c859eSApple OSS Distributions }
247*0f4c859eSApple OSS Distributions
248*0f4c859eSApple OSS Distributions static void
verify_monotonic_work_interval_data(struct work_interval_data * curr_data,struct work_interval_data * prev_data,bool supports_cpi)249*0f4c859eSApple OSS Distributions verify_monotonic_work_interval_data(struct work_interval_data *curr_data, struct work_interval_data *prev_data, bool supports_cpi)
250*0f4c859eSApple OSS Distributions {
251*0f4c859eSApple OSS Distributions if (prev_data != NULL) {
252*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_GE(curr_data->wid_external_wakeups, prev_data->wid_external_wakeups, "wid_external_wakeups");
253*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_GE(curr_data->wid_total_wakeups, prev_data->wid_total_wakeups, "wid_external_wakeups");
254*0f4c859eSApple OSS Distributions }
255*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_GE(curr_data->wid_user_time_mach, prev_data == NULL ? 1 : prev_data->wid_user_time_mach, "monotonic wid_user_time_mach");
256*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_GE(curr_data->wid_system_time_mach, prev_data == NULL ? 1 : prev_data->wid_system_time_mach, "monotonic wid_system_time_mach");
257*0f4c859eSApple OSS Distributions if (supports_cpi) {
258*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_GE(curr_data->wid_cycles, prev_data == NULL ? 1 : prev_data->wid_cycles, "monotonic wid_cycles");
259*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_GE(curr_data->wid_instructions, prev_data == NULL ? 1 : prev_data->wid_instructions, "monotonic wid_instructions");
260*0f4c859eSApple OSS Distributions }
261*0f4c859eSApple OSS Distributions }
262*0f4c859eSApple OSS Distributions
263*0f4c859eSApple OSS Distributions static void
verify_zero_work_interval_data(struct work_interval_data * wi_data,bool supports_cpi)264*0f4c859eSApple OSS Distributions verify_zero_work_interval_data(struct work_interval_data *wi_data, bool supports_cpi)
265*0f4c859eSApple OSS Distributions {
266*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_data->wid_external_wakeups, 0, "zero wid_external_wakeups");
267*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_data->wid_total_wakeups, 0, "zero wid_total_wakeups");
268*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_data->wid_user_time_mach, 0ULL, "zero wid_user_time_mach");
269*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_data->wid_system_time_mach, 0ULL, "zero wid_system_time_mach");
270*0f4c859eSApple OSS Distributions if (supports_cpi) {
271*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_data->wid_cycles, 0ULL, "zero wid_cycles");
272*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_EQ(wi_data->wid_instructions, 0ULL, "zero wid_instructions");
273*0f4c859eSApple OSS Distributions }
274*0f4c859eSApple OSS Distributions }
275*0f4c859eSApple OSS Distributions
276*0f4c859eSApple OSS Distributions static void
run_work_interval_data_test(unsigned int num_iterations,uint64_t interval_nanos,unsigned int thread_count,bool enable_telemetry,uint32_t flags)277*0f4c859eSApple OSS Distributions run_work_interval_data_test(unsigned int num_iterations, uint64_t interval_nanos, unsigned int thread_count,
278*0f4c859eSApple OSS Distributions bool enable_telemetry, uint32_t flags)
279*0f4c859eSApple OSS Distributions {
280*0f4c859eSApple OSS Distributions T_SETUPBEGIN;
281*0f4c859eSApple OSS Distributions
282*0f4c859eSApple OSS Distributions int ret = 0;
283*0f4c859eSApple OSS Distributions
284*0f4c859eSApple OSS Distributions int supports_cpi = 0;
285*0f4c859eSApple OSS Distributions size_t supports_cpi_size = sizeof(supports_cpi);
286*0f4c859eSApple OSS Distributions ret = sysctlbyname("kern.monotonic.supported", &supports_cpi, &supports_cpi_size, NULL, 0);
287*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.monotonic.supported");
288*0f4c859eSApple OSS Distributions
289*0f4c859eSApple OSS Distributions work_interval_t wi_handle = NULL;
290*0f4c859eSApple OSS Distributions work_interval_instance_t wi_instance = NULL;
291*0f4c859eSApple OSS Distributions mach_port_t wi_port = MACH_PORT_NULL;
292*0f4c859eSApple OSS Distributions
293*0f4c859eSApple OSS Distributions create_coreaudio_work_interval(&wi_handle, &wi_instance, &wi_port, enable_telemetry, flags);
294*0f4c859eSApple OSS Distributions join_coreaudio_work_interval(&wi_port, interval_nanos);
295*0f4c859eSApple OSS Distributions
296*0f4c859eSApple OSS Distributions total_thread_count = thread_count;
297*0f4c859eSApple OSS Distributions expected_cond_wakeups = 0;
298*0f4c859eSApple OSS Distributions unsigned int num_helper_threads = thread_count - 1;
299*0f4c859eSApple OSS Distributions active_barrier_ind = 0;
300*0f4c859eSApple OSS Distributions barrier_count[active_barrier_ind] = thread_count;
301*0f4c859eSApple OSS Distributions pthread_t wi_threads[num_helper_threads];
302*0f4c859eSApple OSS Distributions struct thread_data wi_thread_datas[num_helper_threads];
303*0f4c859eSApple OSS Distributions
304*0f4c859eSApple OSS Distributions start_helper_threads(num_helper_threads, wi_threads, wi_thread_datas, wi_handle, &wi_port, num_iterations, interval_nanos);
305*0f4c859eSApple OSS Distributions
306*0f4c859eSApple OSS Distributions T_SETUPEND;
307*0f4c859eSApple OSS Distributions
308*0f4c859eSApple OSS Distributions uint64_t interval_length_abs = nanos_to_abs(interval_nanos);
309*0f4c859eSApple OSS Distributions uint64_t duration_sum = 0;
310*0f4c859eSApple OSS Distributions struct work_interval_data start_data = {0};
311*0f4c859eSApple OSS Distributions struct work_interval_data finish_data = {0};
312*0f4c859eSApple OSS Distributions
313*0f4c859eSApple OSS Distributions for (unsigned int i = 0; i < num_iterations; i++) {
314*0f4c859eSApple OSS Distributions work_sum = 0;
315*0f4c859eSApple OSS Distributions
316*0f4c859eSApple OSS Distributions usleep(1000);
317*0f4c859eSApple OSS Distributions
318*0f4c859eSApple OSS Distributions start_work_interval_instance(interval_length_abs, wi_instance, &start_data);
319*0f4c859eSApple OSS Distributions if (i == 0 && enable_telemetry) {
320*0f4c859eSApple OSS Distributions verify_monotonic_work_interval_data(&start_data, NULL, supports_cpi);
321*0f4c859eSApple OSS Distributions } else if (!enable_telemetry) {
322*0f4c859eSApple OSS Distributions verify_zero_work_interval_data(&start_data, supports_cpi);
323*0f4c859eSApple OSS Distributions }
324*0f4c859eSApple OSS Distributions
325*0f4c859eSApple OSS Distributions thread_barrier();
326*0f4c859eSApple OSS Distributions contribute_to_work_sum();
327*0f4c859eSApple OSS Distributions
328*0f4c859eSApple OSS Distributions duration_sum += finish_work_interval_instance(wi_instance, &finish_data);
329*0f4c859eSApple OSS Distributions if (enable_telemetry) {
330*0f4c859eSApple OSS Distributions verify_monotonic_work_interval_data(&finish_data, &start_data, supports_cpi);
331*0f4c859eSApple OSS Distributions } else {
332*0f4c859eSApple OSS Distributions verify_zero_work_interval_data(&finish_data, supports_cpi);
333*0f4c859eSApple OSS Distributions }
334*0f4c859eSApple OSS Distributions }
335*0f4c859eSApple OSS Distributions
336*0f4c859eSApple OSS Distributions ret = work_interval_leave();
337*0f4c859eSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "work_interval_leave");
338*0f4c859eSApple OSS Distributions thread_barrier();
339*0f4c859eSApple OSS Distributions
340*0f4c859eSApple OSS Distributions if (enable_telemetry) {
341*0f4c859eSApple OSS Distributions T_ASSERT_TRUE(true, "Overall wid_external_wakeups: %u\n", finish_data.wid_external_wakeups);
342*0f4c859eSApple OSS Distributions // Only the wakeups from usleep() are guaranteed to occur
343*0f4c859eSApple OSS Distributions T_ASSERT_GE(finish_data.wid_total_wakeups, num_iterations, "wid_total_wakeups at least accounts for the usleep() wakeups");
344*0f4c859eSApple OSS Distributions }
345*0f4c859eSApple OSS Distributions T_ASSERT_TRUE(true, "Workload survived %u iterations without failures!!! Avg. work interval duration was %llu ns out of a requested %llu ns", num_iterations, duration_sum / num_iterations, interval_nanos);
346*0f4c859eSApple OSS Distributions }
347*0f4c859eSApple OSS Distributions
348*0f4c859eSApple OSS Distributions static const unsigned int DEFAULT_ITERS = 1000;
349*0f4c859eSApple OSS Distributions static const uint64_t DEFAULT_INTERVAL_NS = 15000000; // 15 ms
350*0f4c859eSApple OSS Distributions static const uint64_t DEFAULT_THREAD_COUNT = 3;
351*0f4c859eSApple OSS Distributions
352*0f4c859eSApple OSS Distributions T_DECL(work_interval_rt_coreaudio_quality_telemetry_data, "receiving accurate telemetry data as a coreaudio work interval",
353*0f4c859eSApple OSS Distributions T_META_ASROOT(YES), XNU_T_META_SOC_SPECIFIC, T_META_ENABLED(TARGET_CPU_ARM64))
354*0f4c859eSApple OSS Distributions {
355*0f4c859eSApple OSS Distributions run_work_interval_data_test(
356*0f4c859eSApple OSS Distributions DEFAULT_ITERS,
357*0f4c859eSApple OSS Distributions DEFAULT_INTERVAL_NS,
358*0f4c859eSApple OSS Distributions DEFAULT_THREAD_COUNT,
359*0f4c859eSApple OSS Distributions true, // enable_telemetry
360*0f4c859eSApple OSS Distributions 0); // no added flags
361*0f4c859eSApple OSS Distributions }
362*0f4c859eSApple OSS Distributions
363*0f4c859eSApple OSS Distributions T_DECL(work_interval_rt_coreaudio_telemetry_disabled, "reading telemetry data should see all zeroes if it isn't enabled",
364*0f4c859eSApple OSS Distributions T_META_ASROOT(YES), XNU_T_META_SOC_SPECIFIC, T_META_ENABLED(TARGET_CPU_ARM64))
365*0f4c859eSApple OSS Distributions {
366*0f4c859eSApple OSS Distributions run_work_interval_data_test(
367*0f4c859eSApple OSS Distributions DEFAULT_ITERS,
368*0f4c859eSApple OSS Distributions DEFAULT_INTERVAL_NS,
369*0f4c859eSApple OSS Distributions DEFAULT_THREAD_COUNT,
370*0f4c859eSApple OSS Distributions false, // enable_telemetry
371*0f4c859eSApple OSS Distributions 0); // no added flags
372*0f4c859eSApple OSS Distributions }
373*0f4c859eSApple OSS Distributions
374*0f4c859eSApple OSS Distributions T_DECL(work_interval_rt_coreaudio_telemetry_data_many_threads, "work interval telemetry data works with many joined threads",
375*0f4c859eSApple OSS Distributions T_META_ASROOT(YES), XNU_T_META_SOC_SPECIFIC, T_META_ENABLED(TARGET_CPU_ARM64))
376*0f4c859eSApple OSS Distributions {
377*0f4c859eSApple OSS Distributions run_work_interval_data_test(
378*0f4c859eSApple OSS Distributions DEFAULT_ITERS,
379*0f4c859eSApple OSS Distributions DEFAULT_INTERVAL_NS,
380*0f4c859eSApple OSS Distributions 20, // threads
381*0f4c859eSApple OSS Distributions true, // enable_telemetry
382*0f4c859eSApple OSS Distributions 0); // no added flags
383*0f4c859eSApple OSS Distributions }
384*0f4c859eSApple OSS Distributions
385*0f4c859eSApple OSS Distributions T_DECL(work_interval_rt_coreaudio_telemetry_supported_with_other_flags, "telemetry supported when the other creation flags used by coreaudio are set",
386*0f4c859eSApple OSS Distributions T_META_ASROOT(YES), XNU_T_META_SOC_SPECIFIC, T_META_ENABLED(TARGET_CPU_ARM64))
387*0f4c859eSApple OSS Distributions {
388*0f4c859eSApple OSS Distributions T_LOG("Coreaudio work interval with auto-join and deferred finish enabled");
389*0f4c859eSApple OSS Distributions run_work_interval_data_test(
390*0f4c859eSApple OSS Distributions DEFAULT_ITERS,
391*0f4c859eSApple OSS Distributions DEFAULT_INTERVAL_NS,
392*0f4c859eSApple OSS Distributions DEFAULT_THREAD_COUNT, // threads
393*0f4c859eSApple OSS Distributions true, // enable_telemetry
394*0f4c859eSApple OSS Distributions WORK_INTERVAL_FLAG_ENABLE_AUTO_JOIN | WORK_INTERVAL_FLAG_ENABLE_DEFERRED_FINISH);
395*0f4c859eSApple OSS Distributions
396*0f4c859eSApple OSS Distributions T_LOG("Coreaudio work interval with auto-join, deferred finish, and unrestricted flags enabled");
397*0f4c859eSApple OSS Distributions run_work_interval_data_test(
398*0f4c859eSApple OSS Distributions DEFAULT_ITERS,
399*0f4c859eSApple OSS Distributions DEFAULT_INTERVAL_NS,
400*0f4c859eSApple OSS Distributions DEFAULT_THREAD_COUNT, // threads
401*0f4c859eSApple OSS Distributions true, // enable_telemetry
402*0f4c859eSApple OSS Distributions WORK_INTERVAL_FLAG_ENABLE_AUTO_JOIN | WORK_INTERVAL_FLAG_ENABLE_DEFERRED_FINISH | WORK_INTERVAL_FLAG_UNRESTRICTED);
403*0f4c859eSApple OSS Distributions }
404