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