1*2c2f96dcSApple OSS Distributions // Copyright (c) 2023 Apple Inc. All rights reserved.
2*2c2f96dcSApple OSS Distributions
3*2c2f96dcSApple OSS Distributions #include <unistd.h>
4*2c2f96dcSApple OSS Distributions #include <stdlib.h>
5*2c2f96dcSApple OSS Distributions #include <pthread.h>
6*2c2f96dcSApple OSS Distributions #include <string.h>
7*2c2f96dcSApple OSS Distributions #include <mach/mach.h>
8*2c2f96dcSApple OSS Distributions #include <mach/mach_time.h>
9*2c2f96dcSApple OSS Distributions #include <sys/stat.h>
10*2c2f96dcSApple OSS Distributions #include <sys/sysctl.h>
11*2c2f96dcSApple OSS Distributions #include <sys/time.h>
12*2c2f96dcSApple OSS Distributions #include <stdatomic.h>
13*2c2f96dcSApple OSS Distributions #include <time.h>
14*2c2f96dcSApple OSS Distributions
15*2c2f96dcSApple OSS Distributions #include <machine/cpu_capabilities.h>
16*2c2f96dcSApple OSS Distributions #include <os/tsd.h>
17*2c2f96dcSApple OSS Distributions
18*2c2f96dcSApple OSS Distributions #include <darwintest.h>
19*2c2f96dcSApple OSS Distributions #include <darwintest_utils.h>
20*2c2f96dcSApple OSS Distributions #include "test_utils.h"
21*2c2f96dcSApple OSS Distributions
22*2c2f96dcSApple OSS Distributions T_GLOBAL_META(T_META_NAMESPACE("xnu.scheduler"),
23*2c2f96dcSApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
24*2c2f96dcSApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("scheduler"));
25*2c2f96dcSApple OSS Distributions
26*2c2f96dcSApple OSS Distributions /*
27*2c2f96dcSApple OSS Distributions * As a successor of clpc_disabling_cores_test_21636137, this test ensures that threads
28*2c2f96dcSApple OSS Distributions * are naturally being scheduled on all of the logical cores (without binding). The test
29*2c2f96dcSApple OSS Distributions * fails if CLPC has derecommended any cores.
30*2c2f96dcSApple OSS Distributions */
31*2c2f96dcSApple OSS Distributions
32*2c2f96dcSApple OSS Distributions static mach_timebase_info_data_t timebase_info;
33*2c2f96dcSApple OSS Distributions
34*2c2f96dcSApple OSS Distributions static uint64_t
nanos_to_abs(uint64_t nanos)35*2c2f96dcSApple OSS Distributions nanos_to_abs(uint64_t nanos)
36*2c2f96dcSApple OSS Distributions {
37*2c2f96dcSApple OSS Distributions mach_timebase_info(&timebase_info);
38*2c2f96dcSApple OSS Distributions return nanos * timebase_info.denom / timebase_info.numer;
39*2c2f96dcSApple OSS Distributions }
40*2c2f96dcSApple OSS Distributions
41*2c2f96dcSApple OSS Distributions static _Atomic uint64_t visited_cores_bitmask = 0;
42*2c2f96dcSApple OSS Distributions static uint64_t spin_deadline_timestamp = 0;
43*2c2f96dcSApple OSS Distributions
44*2c2f96dcSApple OSS Distributions static void *
spin_thread_fn(__unused void * arg)45*2c2f96dcSApple OSS Distributions spin_thread_fn(__unused void *arg)
46*2c2f96dcSApple OSS Distributions {
47*2c2f96dcSApple OSS Distributions while (mach_absolute_time() < spin_deadline_timestamp) {
48*2c2f96dcSApple OSS Distributions unsigned int curr_cpu = _os_cpu_number();
49*2c2f96dcSApple OSS Distributions atomic_fetch_or_explicit(&visited_cores_bitmask, (1ULL << curr_cpu), memory_order_relaxed);
50*2c2f96dcSApple OSS Distributions }
51*2c2f96dcSApple OSS Distributions return NULL;
52*2c2f96dcSApple OSS Distributions }
53*2c2f96dcSApple OSS Distributions
54*2c2f96dcSApple OSS Distributions static void
start_threads(pthread_t * threads,void * (* start_routine)(void *),int priority,unsigned int num_threads)55*2c2f96dcSApple OSS Distributions start_threads(pthread_t *threads, void *(*start_routine)(void *), int priority, unsigned int num_threads)
56*2c2f96dcSApple OSS Distributions {
57*2c2f96dcSApple OSS Distributions int rv;
58*2c2f96dcSApple OSS Distributions pthread_attr_t attr;
59*2c2f96dcSApple OSS Distributions
60*2c2f96dcSApple OSS Distributions rv = pthread_attr_init(&attr);
61*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(rv, "pthread_attr_init");
62*2c2f96dcSApple OSS Distributions
63*2c2f96dcSApple OSS Distributions for (unsigned int i = 0; i < num_threads; i++) {
64*2c2f96dcSApple OSS Distributions struct sched_param param = { .sched_priority = (int)priority };
65*2c2f96dcSApple OSS Distributions
66*2c2f96dcSApple OSS Distributions rv = pthread_attr_setschedparam(&attr, ¶m);
67*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(rv, "pthread_attr_setschedparam");
68*2c2f96dcSApple OSS Distributions
69*2c2f96dcSApple OSS Distributions rv = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
70*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(rv, "pthread_attr_setdetachstate");
71*2c2f96dcSApple OSS Distributions
72*2c2f96dcSApple OSS Distributions rv = pthread_create(&threads[i], &attr, start_routine, NULL);
73*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(rv, "pthread_create");
74*2c2f96dcSApple OSS Distributions }
75*2c2f96dcSApple OSS Distributions
76*2c2f96dcSApple OSS Distributions rv = pthread_attr_destroy(&attr);
77*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(rv, "pthread_attr_destroy");
78*2c2f96dcSApple OSS Distributions }
79*2c2f96dcSApple OSS Distributions
80*2c2f96dcSApple OSS Distributions static host_t host;
81*2c2f96dcSApple OSS Distributions static processor_port_array_t cpu_ports;
82*2c2f96dcSApple OSS Distributions static mach_msg_type_number_t cpu_count;
83*2c2f96dcSApple OSS Distributions
84*2c2f96dcSApple OSS Distributions static void
init_host_and_cpu_count(void)85*2c2f96dcSApple OSS Distributions init_host_and_cpu_count(void)
86*2c2f96dcSApple OSS Distributions {
87*2c2f96dcSApple OSS Distributions kern_return_t kr;
88*2c2f96dcSApple OSS Distributions host_t priv_host;
89*2c2f96dcSApple OSS Distributions
90*2c2f96dcSApple OSS Distributions host = mach_host_self();
91*2c2f96dcSApple OSS Distributions
92*2c2f96dcSApple OSS Distributions kr = host_get_host_priv_port(host, &priv_host);
93*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "host_get_host_priv_port");
94*2c2f96dcSApple OSS Distributions
95*2c2f96dcSApple OSS Distributions kr = host_processors(priv_host, &cpu_ports, &cpu_count);
96*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "host_processors");
97*2c2f96dcSApple OSS Distributions
98*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_EQ(cpu_count, (unsigned int)dt_ncpu(), "cpu counts between host_processors() and hw.ncpu don't match");
99*2c2f96dcSApple OSS Distributions }
100*2c2f96dcSApple OSS Distributions
101*2c2f96dcSApple OSS Distributions static void
record_cpu_loads(struct processor_cpu_load_info * cpu_loads)102*2c2f96dcSApple OSS Distributions record_cpu_loads(struct processor_cpu_load_info *cpu_loads)
103*2c2f96dcSApple OSS Distributions {
104*2c2f96dcSApple OSS Distributions kern_return_t kr;
105*2c2f96dcSApple OSS Distributions mach_msg_type_number_t info_count = PROCESSOR_CPU_LOAD_INFO_COUNT;
106*2c2f96dcSApple OSS Distributions for (unsigned int i = 0; i < cpu_count; i++) {
107*2c2f96dcSApple OSS Distributions kr = processor_info(cpu_ports[i], PROCESSOR_CPU_LOAD_INFO, &host, (processor_info_t)&cpu_loads[i], &info_count);
108*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "processor_info");
109*2c2f96dcSApple OSS Distributions }
110*2c2f96dcSApple OSS Distributions }
111*2c2f96dcSApple OSS Distributions
112*2c2f96dcSApple OSS Distributions static void
cpu_loads_delta(struct processor_cpu_load_info * start_loads,struct processor_cpu_load_info * finish_loads,unsigned int * non_idle_ticks)113*2c2f96dcSApple OSS Distributions cpu_loads_delta(struct processor_cpu_load_info *start_loads,
114*2c2f96dcSApple OSS Distributions struct processor_cpu_load_info *finish_loads,
115*2c2f96dcSApple OSS Distributions unsigned int *non_idle_ticks)
116*2c2f96dcSApple OSS Distributions {
117*2c2f96dcSApple OSS Distributions struct processor_cpu_load_info delta_loads[cpu_count];
118*2c2f96dcSApple OSS Distributions T_LOG("Non-idle time per CPU:");
119*2c2f96dcSApple OSS Distributions for (unsigned int i = 0; i < cpu_count; i++) {
120*2c2f96dcSApple OSS Distributions uint64_t delta_sum = 0;
121*2c2f96dcSApple OSS Distributions for (int state = CPU_STATE_USER; state < CPU_STATE_MAX; state++) {
122*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_GE(finish_loads[i].cpu_ticks[state], start_loads[i].cpu_ticks[state], "non-monotonic ticks for state %d", state);
123*2c2f96dcSApple OSS Distributions delta_loads[i].cpu_ticks[state] = finish_loads[i].cpu_ticks[state] - start_loads[i].cpu_ticks[state];
124*2c2f96dcSApple OSS Distributions delta_sum += delta_loads[i].cpu_ticks[state];
125*2c2f96dcSApple OSS Distributions }
126*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_GT(delta_sum, 0ULL, "Failed to read meaningful load data for the core. Was the amfi_get_out_of_my_way=1 boot-arg missing?");
127*2c2f96dcSApple OSS Distributions non_idle_ticks[i] = delta_loads[i].cpu_ticks[CPU_STATE_USER] + delta_loads[i].cpu_ticks[CPU_STATE_SYSTEM];
128*2c2f96dcSApple OSS Distributions T_LOG("\tCore %d non-idle ticks: %d", i, non_idle_ticks[i]);
129*2c2f96dcSApple OSS Distributions }
130*2c2f96dcSApple OSS Distributions }
131*2c2f96dcSApple OSS Distributions
132*2c2f96dcSApple OSS Distributions #define KERNEL_BOOTARGS_MAX_SIZE 1024
133*2c2f96dcSApple OSS Distributions static char kernel_bootargs[KERNEL_BOOTARGS_MAX_SIZE];
134*2c2f96dcSApple OSS Distributions
135*2c2f96dcSApple OSS Distributions static const int DEFAULT_THREAD_PRI = 31;
136*2c2f96dcSApple OSS Distributions
137*2c2f96dcSApple OSS Distributions T_DECL(all_cores_running,
138*2c2f96dcSApple OSS Distributions "Verify that we are using all available cores on the system",
139*2c2f96dcSApple OSS Distributions /* Required to get around the rate limit for processor_info() */
140*2c2f96dcSApple OSS Distributions T_META_BOOTARGS_SET("amfi_get_out_of_my_way=1"),
141*2c2f96dcSApple OSS Distributions T_META_ASROOT(true),
142*2c2f96dcSApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
143*2c2f96dcSApple OSS Distributions {
144*2c2f96dcSApple OSS Distributions T_SETUPBEGIN;
145*2c2f96dcSApple OSS Distributions int rv;
146*2c2f96dcSApple OSS Distributions
147*2c2f96dcSApple OSS Distributions /* Warn if amfi_get_out_of_my_way is not set and fail later on if we actually run into the rate limit */
148*2c2f96dcSApple OSS Distributions size_t kernel_bootargs_size = sizeof(kernel_bootargs);
149*2c2f96dcSApple OSS Distributions rv = sysctlbyname("kern.bootargs", kernel_bootargs, &kernel_bootargs_size, NULL, 0);
150*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.bootargs");
151*2c2f96dcSApple OSS Distributions if (strstr(kernel_bootargs, "amfi_get_out_of_my_way=1") == NULL) {
152*2c2f96dcSApple OSS Distributions T_LOG("WARNING: amfi_get_out_of_my_way=1 boot-arg is missing, required to reliably capture CPU load data");
153*2c2f96dcSApple OSS Distributions }
154*2c2f96dcSApple OSS Distributions
155*2c2f96dcSApple OSS Distributions init_host_and_cpu_count();
156*2c2f96dcSApple OSS Distributions T_LOG("System has %d logical cores", cpu_count);
157*2c2f96dcSApple OSS Distributions
158*2c2f96dcSApple OSS Distributions uint64_t recommended_cores_mask = 0;
159*2c2f96dcSApple OSS Distributions size_t recommended_cores_mask_size = sizeof(recommended_cores_mask);
160*2c2f96dcSApple OSS Distributions rv = sysctlbyname("kern.sched_recommended_cores", &recommended_cores_mask, &recommended_cores_mask_size, NULL, 0);
161*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.sched_recommended_cores");
162*2c2f96dcSApple OSS Distributions T_LOG("Mask kern.sched_recommended_cores: 0x%016llx", recommended_cores_mask);
163*2c2f96dcSApple OSS Distributions
164*2c2f96dcSApple OSS Distributions T_SETUPEND;
165*2c2f96dcSApple OSS Distributions
166*2c2f96dcSApple OSS Distributions struct processor_cpu_load_info start_cpu_loads[cpu_count];
167*2c2f96dcSApple OSS Distributions record_cpu_loads(start_cpu_loads);
168*2c2f96dcSApple OSS Distributions
169*2c2f96dcSApple OSS Distributions /* Wait 100ms for the system to settle down */
170*2c2f96dcSApple OSS Distributions usleep(100000);
171*2c2f96dcSApple OSS Distributions
172*2c2f96dcSApple OSS Distributions const uint64_t spin_seconds = 3;
173*2c2f96dcSApple OSS Distributions spin_deadline_timestamp = mach_absolute_time() + nanos_to_abs(spin_seconds * NSEC_PER_SEC);
174*2c2f96dcSApple OSS Distributions unsigned int num_threads = (unsigned int)dt_ncpu() * 2;
175*2c2f96dcSApple OSS Distributions T_LOG("Launching %u threads to spin for %lld seconds...", num_threads, spin_seconds);
176*2c2f96dcSApple OSS Distributions
177*2c2f96dcSApple OSS Distributions pthread_t threads[num_threads];
178*2c2f96dcSApple OSS Distributions start_threads(threads, &spin_thread_fn, DEFAULT_THREAD_PRI, num_threads);
179*2c2f96dcSApple OSS Distributions
180*2c2f96dcSApple OSS Distributions /* Wait for threads to perform spinning work */
181*2c2f96dcSApple OSS Distributions sleep(spin_seconds);
182*2c2f96dcSApple OSS Distributions T_LOG("...%lld seconds have elapsed", spin_seconds);
183*2c2f96dcSApple OSS Distributions
184*2c2f96dcSApple OSS Distributions struct processor_cpu_load_info finish_cpu_loads[cpu_count];
185*2c2f96dcSApple OSS Distributions record_cpu_loads(finish_cpu_loads);
186*2c2f96dcSApple OSS Distributions
187*2c2f96dcSApple OSS Distributions uint64_t final_visited_cores_bitmask = atomic_load(&visited_cores_bitmask);
188*2c2f96dcSApple OSS Distributions T_LOG("Visited cores bitmask: %llx", final_visited_cores_bitmask);
189*2c2f96dcSApple OSS Distributions
190*2c2f96dcSApple OSS Distributions unsigned int non_idle_ticks[cpu_count];
191*2c2f96dcSApple OSS Distributions cpu_loads_delta(start_cpu_loads, finish_cpu_loads, non_idle_ticks);
192*2c2f96dcSApple OSS Distributions
193*2c2f96dcSApple OSS Distributions /*
194*2c2f96dcSApple OSS Distributions * Now after we have logged all of the relevant information, enforce that each
195*2c2f96dcSApple OSS Distributions * of the cores was recommended and had test threads scheduled on it.
196*2c2f96dcSApple OSS Distributions */
197*2c2f96dcSApple OSS Distributions T_ASSERT_EQ((unsigned int)__builtin_popcountll(final_visited_cores_bitmask), cpu_count, "Each core ran at least one of the test threads");
198*2c2f96dcSApple OSS Distributions for (unsigned int i = 0; i < cpu_count; i++) {
199*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_GT(non_idle_ticks[i], 0, "One or more cores were idle during the work period");
200*2c2f96dcSApple OSS Distributions }
201*2c2f96dcSApple OSS Distributions T_PASS("Each core performed work during the work period");
202*2c2f96dcSApple OSS Distributions
203*2c2f96dcSApple OSS Distributions T_END;
204*2c2f96dcSApple OSS Distributions }
205*2c2f96dcSApple OSS Distributions
206*2c2f96dcSApple OSS Distributions T_DECL(recommended_cores_mask,
207*2c2f96dcSApple OSS Distributions "Tests that the mask of recommended cores includes all logical cores according to hw.ncpu",
208*2c2f96dcSApple OSS Distributions T_META_NAMESPACE("xnu.scheduler"),
209*2c2f96dcSApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
210*2c2f96dcSApple OSS Distributions {
211*2c2f96dcSApple OSS Distributions int ret;
212*2c2f96dcSApple OSS Distributions
213*2c2f96dcSApple OSS Distributions uint32_t ncpu = 0;
214*2c2f96dcSApple OSS Distributions size_t ncpu_size = sizeof(ncpu);
215*2c2f96dcSApple OSS Distributions ret = sysctlbyname("hw.ncpu", &ncpu, &ncpu_size, NULL, 0);
216*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "hw.ncpu");
217*2c2f96dcSApple OSS Distributions T_LOG("hw.ncpu: %d\n", ncpu);
218*2c2f96dcSApple OSS Distributions
219*2c2f96dcSApple OSS Distributions T_ASSERT_LE(ncpu, 64, "Core count isn't too high to reflect in the system's 64-bit wide core masks");
220*2c2f96dcSApple OSS Distributions
221*2c2f96dcSApple OSS Distributions int passed_test = 0;
222*2c2f96dcSApple OSS Distributions int tries = 0;
223*2c2f96dcSApple OSS Distributions int MAX_RETRIES = 3;
224*2c2f96dcSApple OSS Distributions while (!passed_test && tries < MAX_RETRIES) {
225*2c2f96dcSApple OSS Distributions uint64_t recommended_cores_mask = 0;
226*2c2f96dcSApple OSS Distributions size_t recommended_cores_mask_size = sizeof(recommended_cores_mask);
227*2c2f96dcSApple OSS Distributions ret = sysctlbyname("kern.sched_recommended_cores", &recommended_cores_mask, &recommended_cores_mask_size, NULL, 0);
228*2c2f96dcSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.sched_recommended_cores");
229*2c2f96dcSApple OSS Distributions T_LOG("kern.sched_recommended_cores: 0x%016llx", recommended_cores_mask);
230*2c2f96dcSApple OSS Distributions
231*2c2f96dcSApple OSS Distributions uint64_t expected_set_mask = ~0ULL >> (64 - ncpu);
232*2c2f96dcSApple OSS Distributions T_LOG("Expected bits set for all cores: 0x%016llx", expected_set_mask);
233*2c2f96dcSApple OSS Distributions
234*2c2f96dcSApple OSS Distributions if ((recommended_cores_mask & expected_set_mask) == expected_set_mask) {
235*2c2f96dcSApple OSS Distributions passed_test = 1;
236*2c2f96dcSApple OSS Distributions } else {
237*2c2f96dcSApple OSS Distributions /*
238*2c2f96dcSApple OSS Distributions * Maybe some of the cores are derecommended due to thermals.
239*2c2f96dcSApple OSS Distributions * Sleep to give the system a chance to quiesce and try again.
240*2c2f96dcSApple OSS Distributions */
241*2c2f96dcSApple OSS Distributions unsigned int sleep_seconds = 10;
242*2c2f96dcSApple OSS Distributions T_LOG("Missing expected bits. Sleeping for %u seconds before retrying", sleep_seconds);
243*2c2f96dcSApple OSS Distributions sleep(sleep_seconds);
244*2c2f96dcSApple OSS Distributions tries++;
245*2c2f96dcSApple OSS Distributions }
246*2c2f96dcSApple OSS Distributions }
247*2c2f96dcSApple OSS Distributions
248*2c2f96dcSApple OSS Distributions T_ASSERT_EQ(passed_test, 1, "kern.sched_recommended_cores reflects that all expected cores are recommended");
249*2c2f96dcSApple OSS Distributions }
250