1*8d741a5dSApple OSS Distributions /*
2*8d741a5dSApple OSS Distributions * Tests to validate that:
3*8d741a5dSApple OSS Distributions * - we can schedule threads on all hw.ncpus cores according to _os_cpu_number
4*8d741a5dSApple OSS Distributions * - we can schedule threads on all hw.cpuclusters clusters according to _os_cpu_cluster_number
5*8d741a5dSApple OSS Distributions * - the cluster id returned by _os_cpu_cluster_number aligns with mappings from IORegistry
6*8d741a5dSApple OSS Distributions *
7*8d741a5dSApple OSS Distributions * <rdar://problem/29545645>
8*8d741a5dSApple OSS Distributions * <rdar://problem/30445216>
9*8d741a5dSApple OSS Distributions *
10*8d741a5dSApple OSS Distributions * xcrun -sdk macosx.internal clang -o cpucount cpucount.c -ldarwintest -framework IOKit -framework CoreFoundation -g -Weverything
11*8d741a5dSApple OSS Distributions * xcrun -sdk iphoneos.internal clang -arch arm64 -o cpucount-ios cpucount.c -ldarwintest -framework IOKit -framework CoreFoundation -g -Weverything
12*8d741a5dSApple OSS Distributions * xcrun -sdk macosx.internal clang -o cpucount cpucount.c -ldarwintest -framework IOKit -framework CoreFoundation -arch arm64e -Weverything
13*8d741a5dSApple OSS Distributions */
14*8d741a5dSApple OSS Distributions
15*8d741a5dSApple OSS Distributions #include <darwintest.h>
16*8d741a5dSApple OSS Distributions #include "test_utils.h"
17*8d741a5dSApple OSS Distributions
18*8d741a5dSApple OSS Distributions #include <stdio.h>
19*8d741a5dSApple OSS Distributions #include <stdlib.h>
20*8d741a5dSApple OSS Distributions #include <unistd.h>
21*8d741a5dSApple OSS Distributions #include <pthread.h>
22*8d741a5dSApple OSS Distributions #include <sys/commpage.h>
23*8d741a5dSApple OSS Distributions #include <sys/sysctl.h>
24*8d741a5dSApple OSS Distributions #include <sys/proc_info.h>
25*8d741a5dSApple OSS Distributions #include <libproc.h>
26*8d741a5dSApple OSS Distributions
27*8d741a5dSApple OSS Distributions #include <CoreFoundation/CoreFoundation.h>
28*8d741a5dSApple OSS Distributions #include <IOKit/IOKitLib.h>
29*8d741a5dSApple OSS Distributions
30*8d741a5dSApple OSS Distributions #include <mach/mach.h>
31*8d741a5dSApple OSS Distributions #include <mach/mach_time.h>
32*8d741a5dSApple OSS Distributions #include <machine/cpu_capabilities.h>
33*8d741a5dSApple OSS Distributions
34*8d741a5dSApple OSS Distributions #include <os/tsd.h> /* private header for _os_cpu_number, _os_cpu_cluster_number */
35*8d741a5dSApple OSS Distributions
36*8d741a5dSApple OSS Distributions T_GLOBAL_META(
37*8d741a5dSApple OSS Distributions T_META_RUN_CONCURRENTLY(false),
38*8d741a5dSApple OSS Distributions T_META_BOOTARGS_SET("enable_skstb=1"),
39*8d741a5dSApple OSS Distributions T_META_CHECK_LEAKS(false),
40*8d741a5dSApple OSS Distributions T_META_ASROOT(true),
41*8d741a5dSApple OSS Distributions T_META_ALL_VALID_ARCHS(true),
42*8d741a5dSApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
43*8d741a5dSApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("scheduler"),
44*8d741a5dSApple OSS Distributions T_META_OWNER("jarrad"),
45*8d741a5dSApple OSS Distributions T_META_TAG_VM_NOT_PREFERRED
46*8d741a5dSApple OSS Distributions );
47*8d741a5dSApple OSS Distributions
48*8d741a5dSApple OSS Distributions #define KERNEL_BOOTARGS_MAX_SIZE 1024
49*8d741a5dSApple OSS Distributions static char kernel_bootargs[KERNEL_BOOTARGS_MAX_SIZE];
50*8d741a5dSApple OSS Distributions
51*8d741a5dSApple OSS Distributions #define KERNEL_VERSION_MAX_SIZE 1024
52*8d741a5dSApple OSS Distributions static char kernel_version[KERNEL_VERSION_MAX_SIZE];
53*8d741a5dSApple OSS Distributions
54*8d741a5dSApple OSS Distributions static mach_timebase_info_data_t timebase_info;
55*8d741a5dSApple OSS Distributions
56*8d741a5dSApple OSS Distributions // Source: libktrace:corefoundation_helpers.c
57*8d741a5dSApple OSS Distributions
58*8d741a5dSApple OSS Distributions static void
dict_number_internal(CFDictionaryRef dict,CFStringRef key,void * dst_out,CFNumberType nbr_type)59*8d741a5dSApple OSS Distributions dict_number_internal(CFDictionaryRef dict, CFStringRef key, void *dst_out, CFNumberType nbr_type)
60*8d741a5dSApple OSS Distributions {
61*8d741a5dSApple OSS Distributions bool success;
62*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(dict, "dict must not be null");
63*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(key, " key must not be null");
64*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(dst_out, "dst out must not be null");
65*8d741a5dSApple OSS Distributions
66*8d741a5dSApple OSS Distributions CFTypeRef val = CFDictionaryGetValue(dict, key);
67*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(val, "unable to get value for key %s", CFStringGetCStringPtr(key, kCFStringEncodingASCII));
68*8d741a5dSApple OSS Distributions
69*8d741a5dSApple OSS Distributions CFTypeID type = CFGetTypeID(val);
70*8d741a5dSApple OSS Distributions if (type == CFNumberGetTypeID()) {
71*8d741a5dSApple OSS Distributions CFNumberRef val_nbr = (CFNumberRef)val;
72*8d741a5dSApple OSS Distributions success = CFNumberGetValue(val_nbr, nbr_type, dst_out);
73*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_TRUE(success, "dictionary number at key '%s' is not the right type", CFStringGetCStringPtr(key, kCFStringEncodingASCII));
74*8d741a5dSApple OSS Distributions } else if (type == CFDataGetTypeID()) {
75*8d741a5dSApple OSS Distributions CFDataRef val_data = (CFDataRef)val;
76*8d741a5dSApple OSS Distributions size_t raw_size = (size_t)CFDataGetLength(val_data);
77*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(raw_size, (size_t)4, "cannot convert CFData of size %zu to number", raw_size);
78*8d741a5dSApple OSS Distributions CFDataGetBytes(val_data, CFRangeMake(0, (CFIndex)raw_size), dst_out);
79*8d741a5dSApple OSS Distributions } else {
80*8d741a5dSApple OSS Distributions T_ASSERT_FAIL("dictionary value at key '%s' should be a number or data", CFStringGetCStringPtr(key, kCFStringEncodingASCII));
81*8d741a5dSApple OSS Distributions }
82*8d741a5dSApple OSS Distributions }
83*8d741a5dSApple OSS Distributions
84*8d741a5dSApple OSS Distributions static void
dict_uint32(CFDictionaryRef dict,CFStringRef key,uint32_t * dst_out)85*8d741a5dSApple OSS Distributions dict_uint32(CFDictionaryRef dict, CFStringRef key, uint32_t *dst_out)
86*8d741a5dSApple OSS Distributions {
87*8d741a5dSApple OSS Distributions dict_number_internal(dict, key, dst_out, kCFNumberSInt32Type);
88*8d741a5dSApple OSS Distributions }
89*8d741a5dSApple OSS Distributions
90*8d741a5dSApple OSS Distributions static uint64_t
abs_to_nanos(uint64_t abs)91*8d741a5dSApple OSS Distributions abs_to_nanos(uint64_t abs)
92*8d741a5dSApple OSS Distributions {
93*8d741a5dSApple OSS Distributions return abs * timebase_info.numer / timebase_info.denom;
94*8d741a5dSApple OSS Distributions }
95*8d741a5dSApple OSS Distributions
96*8d741a5dSApple OSS Distributions static int32_t
get_csw_count(void)97*8d741a5dSApple OSS Distributions get_csw_count(void)
98*8d741a5dSApple OSS Distributions {
99*8d741a5dSApple OSS Distributions struct proc_taskinfo taskinfo;
100*8d741a5dSApple OSS Distributions int rv;
101*8d741a5dSApple OSS Distributions
102*8d741a5dSApple OSS Distributions rv = proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &taskinfo, sizeof(taskinfo));
103*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "PROC_PIDTASKINFO");
104*8d741a5dSApple OSS Distributions
105*8d741a5dSApple OSS Distributions return taskinfo.pti_csw;
106*8d741a5dSApple OSS Distributions }
107*8d741a5dSApple OSS Distributions
108*8d741a5dSApple OSS Distributions // noinline hopefully keeps the optimizer from hoisting it out of the loop
109*8d741a5dSApple OSS Distributions // until rdar://68253516 is fixed.
110*8d741a5dSApple OSS Distributions __attribute__((noinline))
111*8d741a5dSApple OSS Distributions static uint32_t
fixed_os_cpu_number(void)112*8d741a5dSApple OSS Distributions fixed_os_cpu_number(void)
113*8d741a5dSApple OSS Distributions {
114*8d741a5dSApple OSS Distributions uint32_t cpu_number = _os_cpu_number();
115*8d741a5dSApple OSS Distributions return cpu_number;
116*8d741a5dSApple OSS Distributions }
117*8d741a5dSApple OSS Distributions
118*8d741a5dSApple OSS Distributions static unsigned int
commpage_cpu_cluster_number(void)119*8d741a5dSApple OSS Distributions commpage_cpu_cluster_number(void)
120*8d741a5dSApple OSS Distributions {
121*8d741a5dSApple OSS Distributions uint8_t cpu_number = (uint8_t)fixed_os_cpu_number();
122*8d741a5dSApple OSS Distributions volatile uint8_t *cpu_to_cluster = COMM_PAGE_SLOT(uint8_t, CPU_TO_CLUSTER);
123*8d741a5dSApple OSS Distributions return (unsigned int)*(cpu_to_cluster + cpu_number);
124*8d741a5dSApple OSS Distributions }
125*8d741a5dSApple OSS Distributions
126*8d741a5dSApple OSS Distributions static void
cpucount_setup(void)127*8d741a5dSApple OSS Distributions cpucount_setup(void)
128*8d741a5dSApple OSS Distributions {
129*8d741a5dSApple OSS Distributions int rv;
130*8d741a5dSApple OSS Distributions kern_return_t kr;
131*8d741a5dSApple OSS Distributions
132*8d741a5dSApple OSS Distributions T_SETUPBEGIN;
133*8d741a5dSApple OSS Distributions
134*8d741a5dSApple OSS Distributions setvbuf(stdout, NULL, _IONBF, 0);
135*8d741a5dSApple OSS Distributions setvbuf(stderr, NULL, _IONBF, 0);
136*8d741a5dSApple OSS Distributions
137*8d741a5dSApple OSS Distributions /* Validate what kind of kernel we're on */
138*8d741a5dSApple OSS Distributions size_t kernel_version_size = sizeof(kernel_version);
139*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.version", kernel_version, &kernel_version_size, NULL, 0);
140*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.version");
141*8d741a5dSApple OSS Distributions
142*8d741a5dSApple OSS Distributions T_LOG("kern.version: %s\n", kernel_version);
143*8d741a5dSApple OSS Distributions
144*8d741a5dSApple OSS Distributions /* Double check that darwintest set the boot arg we requested */
145*8d741a5dSApple OSS Distributions size_t kernel_bootargs_size = sizeof(kernel_bootargs);
146*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.bootargs", kernel_bootargs, &kernel_bootargs_size, NULL, 0);
147*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.bootargs");
148*8d741a5dSApple OSS Distributions
149*8d741a5dSApple OSS Distributions T_LOG("kern.bootargs: %s\n", kernel_bootargs);
150*8d741a5dSApple OSS Distributions
151*8d741a5dSApple OSS Distributions if (NULL == strstr(kernel_bootargs, "enable_skstb=1")) {
152*8d741a5dSApple OSS Distributions T_ASSERT_FAIL("enable_skstb=1 boot-arg is missing");
153*8d741a5dSApple OSS Distributions }
154*8d741a5dSApple OSS Distributions
155*8d741a5dSApple OSS Distributions kr = mach_timebase_info(&timebase_info);
156*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_timebase_info");
157*8d741a5dSApple OSS Distributions
158*8d741a5dSApple OSS Distributions struct sched_param param = {.sched_priority = 63};
159*8d741a5dSApple OSS Distributions
160*8d741a5dSApple OSS Distributions rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
161*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_setschedparam");
162*8d741a5dSApple OSS Distributions
163*8d741a5dSApple OSS Distributions T_SETUPEND;
164*8d741a5dSApple OSS Distributions }
165*8d741a5dSApple OSS Distributions
166*8d741a5dSApple OSS Distributions
167*8d741a5dSApple OSS Distributions T_DECL(count_cpus,
168*8d741a5dSApple OSS Distributions "Tests we can schedule bound threads on all hw.ncpus cores and that _os_cpu_number matches",
169*8d741a5dSApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
170*8d741a5dSApple OSS Distributions {
171*8d741a5dSApple OSS Distributions int rv;
172*8d741a5dSApple OSS Distributions
173*8d741a5dSApple OSS Distributions cpucount_setup();
174*8d741a5dSApple OSS Distributions
175*8d741a5dSApple OSS Distributions int bound_cpu_out = 0;
176*8d741a5dSApple OSS Distributions size_t bound_cpu_out_size = sizeof(bound_cpu_out);
177*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", &bound_cpu_out, &bound_cpu_out_size, NULL, 0);
178*8d741a5dSApple OSS Distributions
179*8d741a5dSApple OSS Distributions if (rv == -1) {
180*8d741a5dSApple OSS Distributions if (errno == ENOENT) {
181*8d741a5dSApple OSS Distributions T_ASSERT_FAIL("kern.sched_thread_bind_cpu doesn't exist, must set enable_skstb=1 boot-arg on development kernel");
182*8d741a5dSApple OSS Distributions }
183*8d741a5dSApple OSS Distributions if (errno == EPERM) {
184*8d741a5dSApple OSS Distributions T_ASSERT_FAIL("must run as root");
185*8d741a5dSApple OSS Distributions }
186*8d741a5dSApple OSS Distributions }
187*8d741a5dSApple OSS Distributions
188*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cpu");
189*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(bound_cpu_out, -1, "kern.sched_thread_bind_cpu should exist, start unbound");
190*8d741a5dSApple OSS Distributions
191*8d741a5dSApple OSS Distributions uint32_t sysctl_ncpu = 0;
192*8d741a5dSApple OSS Distributions size_t ncpu_size = sizeof(sysctl_ncpu);
193*8d741a5dSApple OSS Distributions rv = sysctlbyname("hw.ncpu", &sysctl_ncpu, &ncpu_size, NULL, 0);
194*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sysctlbyname(hw.ncpu)");
195*8d741a5dSApple OSS Distributions
196*8d741a5dSApple OSS Distributions T_LOG("hw.ncpu: %2d\n", sysctl_ncpu);
197*8d741a5dSApple OSS Distributions
198*8d741a5dSApple OSS Distributions T_ASSERT_GT(sysctl_ncpu, 0, "at least one CPU exists");
199*8d741a5dSApple OSS Distributions
200*8d741a5dSApple OSS Distributions for (uint32_t cpu_to_bind = 0; cpu_to_bind < sysctl_ncpu; cpu_to_bind++) {
201*8d741a5dSApple OSS Distributions int32_t before_csw_count = get_csw_count();
202*8d741a5dSApple OSS Distributions T_LOG("(csw %4d) attempting to bind to cpu %2d\n", before_csw_count, cpu_to_bind);
203*8d741a5dSApple OSS Distributions
204*8d741a5dSApple OSS Distributions uint64_t start = mach_absolute_time();
205*8d741a5dSApple OSS Distributions
206*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", NULL, 0, &cpu_to_bind, sizeof(cpu_to_bind));
207*8d741a5dSApple OSS Distributions
208*8d741a5dSApple OSS Distributions uint64_t end = mach_absolute_time();
209*8d741a5dSApple OSS Distributions
210*8d741a5dSApple OSS Distributions if (rv == -1 && errno == ENOTSUP) {
211*8d741a5dSApple OSS Distributions T_SKIP("Binding is available, but this process doesn't support binding (e.g. Rosetta on Aruba)");
212*8d741a5dSApple OSS Distributions }
213*8d741a5dSApple OSS Distributions
214*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.sched_thread_bind_cpu(%u)", cpu_to_bind);
215*8d741a5dSApple OSS Distributions
216*8d741a5dSApple OSS Distributions uint32_t os_cpu_number_reported = fixed_os_cpu_number();
217*8d741a5dSApple OSS Distributions
218*8d741a5dSApple OSS Distributions bound_cpu_out = 0;
219*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", &bound_cpu_out, &bound_cpu_out_size, NULL, 0);
220*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cpu");
221*8d741a5dSApple OSS Distributions
222*8d741a5dSApple OSS Distributions T_QUIET; T_EXPECT_EQ((int)cpu_to_bind, bound_cpu_out,
223*8d741a5dSApple OSS Distributions "should report bound cpu id matching requested bind target");
224*8d741a5dSApple OSS Distributions
225*8d741a5dSApple OSS Distributions uint64_t delta_abs = end - start;
226*8d741a5dSApple OSS Distributions uint64_t delta_ns = abs_to_nanos(delta_abs);
227*8d741a5dSApple OSS Distributions
228*8d741a5dSApple OSS Distributions int32_t after_csw_count = get_csw_count();
229*8d741a5dSApple OSS Distributions
230*8d741a5dSApple OSS Distributions T_LOG("(csw %4d) bound to cpu %2d in %f milliseconds\n",
231*8d741a5dSApple OSS Distributions after_csw_count, cpu_to_bind,
232*8d741a5dSApple OSS Distributions ((double)delta_ns / 1000000.0));
233*8d741a5dSApple OSS Distributions
234*8d741a5dSApple OSS Distributions if (cpu_to_bind > 0) {
235*8d741a5dSApple OSS Distributions T_QUIET; T_EXPECT_LT(before_csw_count, after_csw_count,
236*8d741a5dSApple OSS Distributions "should have had to context switch to execute the bind");
237*8d741a5dSApple OSS Distributions }
238*8d741a5dSApple OSS Distributions
239*8d741a5dSApple OSS Distributions T_LOG("cpu %2d reported id %2d\n",
240*8d741a5dSApple OSS Distributions cpu_to_bind, os_cpu_number_reported);
241*8d741a5dSApple OSS Distributions
242*8d741a5dSApple OSS Distributions T_QUIET;
243*8d741a5dSApple OSS Distributions T_EXPECT_EQ(cpu_to_bind, os_cpu_number_reported,
244*8d741a5dSApple OSS Distributions "should report same CPU number as was bound to");
245*8d741a5dSApple OSS Distributions }
246*8d741a5dSApple OSS Distributions
247*8d741a5dSApple OSS Distributions int unbind = -1; /* pass -1 in order to unbind the thread */
248*8d741a5dSApple OSS Distributions
249*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", NULL, 0, &unbind, sizeof(unbind));
250*8d741a5dSApple OSS Distributions
251*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.sched_thread_bind_cpu(%u)", unbind);
252*8d741a5dSApple OSS Distributions
253*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", &bound_cpu_out, &bound_cpu_out_size, NULL, 0);
254*8d741a5dSApple OSS Distributions
255*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cpu");
256*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(bound_cpu_out, -1, "thread should be unbound at the end");
257*8d741a5dSApple OSS Distributions
258*8d741a5dSApple OSS Distributions T_PASS("test has run threads on all CPUS");
259*8d741a5dSApple OSS Distributions }
260*8d741a5dSApple OSS Distributions
261*8d741a5dSApple OSS Distributions T_DECL(count_clusters,
262*8d741a5dSApple OSS Distributions "Tests we can schedule bound threads on all cpu clusters and that _os_cpu_cluster_number matches",
263*8d741a5dSApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
264*8d741a5dSApple OSS Distributions {
265*8d741a5dSApple OSS Distributions int rv;
266*8d741a5dSApple OSS Distributions
267*8d741a5dSApple OSS Distributions cpucount_setup();
268*8d741a5dSApple OSS Distributions
269*8d741a5dSApple OSS Distributions uint8_t cpuclusters = COMM_PAGE_READ(uint8_t, CPU_CLUSTERS);
270*8d741a5dSApple OSS Distributions T_LOG("cpuclusters: %2d\n", cpuclusters);
271*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_GT(cpuclusters, 0, "at least one CPU cluster exists");
272*8d741a5dSApple OSS Distributions if (cpuclusters == 1) {
273*8d741a5dSApple OSS Distributions T_SKIP("Test is unsupported on non-AMP platforms");
274*8d741a5dSApple OSS Distributions }
275*8d741a5dSApple OSS Distributions
276*8d741a5dSApple OSS Distributions uint32_t sysctl_ncpu = 0;
277*8d741a5dSApple OSS Distributions size_t ncpu_size = sizeof(sysctl_ncpu);
278*8d741a5dSApple OSS Distributions rv = sysctlbyname("hw.ncpu", &sysctl_ncpu, &ncpu_size, NULL, 0);
279*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sysctlbyname(hw.ncpu)");
280*8d741a5dSApple OSS Distributions T_LOG("hw.ncpu: %2d\n", sysctl_ncpu);
281*8d741a5dSApple OSS Distributions
282*8d741a5dSApple OSS Distributions uint64_t recommended_cores = 0;
283*8d741a5dSApple OSS Distributions size_t recommended_cores_size = sizeof(recommended_cores);
284*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_recommended_cores", &recommended_cores, &recommended_cores_size, NULL, 0);
285*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sysctlbyname(kern.sched_recommended_cores)");
286*8d741a5dSApple OSS Distributions T_LOG("kern.sched_recommended_cores: %llu", recommended_cores);
287*8d741a5dSApple OSS Distributions if ((uint32_t)__builtin_popcountll(recommended_cores) != sysctl_ncpu) {
288*8d741a5dSApple OSS Distributions T_SKIP("Missing recommended cores");
289*8d741a5dSApple OSS Distributions }
290*8d741a5dSApple OSS Distributions
291*8d741a5dSApple OSS Distributions int bound_cluster_out = 0;
292*8d741a5dSApple OSS Distributions size_t bound_cluster_out_size = sizeof(bound_cluster_out);
293*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cluster_id", &bound_cluster_out, &bound_cluster_out_size, NULL, 0);
294*8d741a5dSApple OSS Distributions
295*8d741a5dSApple OSS Distributions if (rv == -1) {
296*8d741a5dSApple OSS Distributions if (errno == ENOENT) {
297*8d741a5dSApple OSS Distributions T_ASSERT_FAIL("kern.sched_thread_bind_cluster_id doesn't exist, must set enable_skstb=1 boot-arg on development kernel");
298*8d741a5dSApple OSS Distributions }
299*8d741a5dSApple OSS Distributions if (errno == EPERM) {
300*8d741a5dSApple OSS Distributions T_ASSERT_FAIL("must run as root");
301*8d741a5dSApple OSS Distributions }
302*8d741a5dSApple OSS Distributions }
303*8d741a5dSApple OSS Distributions
304*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cluster_id");
305*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(bound_cluster_out, -1, "kern.sched_thread_bind_cluster_id should exist, start unbound");
306*8d741a5dSApple OSS Distributions
307*8d741a5dSApple OSS Distributions for (uint32_t cluster_to_bind = 0; cluster_to_bind < cpuclusters; cluster_to_bind++) {
308*8d741a5dSApple OSS Distributions int32_t before_csw_count = get_csw_count();
309*8d741a5dSApple OSS Distributions T_LOG("(csw %4d) attempting to bind to cluster %2d\n", before_csw_count, cluster_to_bind);
310*8d741a5dSApple OSS Distributions
311*8d741a5dSApple OSS Distributions uint64_t start = mach_absolute_time();
312*8d741a5dSApple OSS Distributions
313*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cluster_id", NULL, 0, &cluster_to_bind, sizeof(cluster_to_bind));
314*8d741a5dSApple OSS Distributions
315*8d741a5dSApple OSS Distributions uint64_t end = mach_absolute_time();
316*8d741a5dSApple OSS Distributions
317*8d741a5dSApple OSS Distributions if (rv == -1 && errno == ENOTSUP) {
318*8d741a5dSApple OSS Distributions T_SKIP("Binding is available, but this process doesn't support binding (e.g. Rosetta on Aruba)");
319*8d741a5dSApple OSS Distributions }
320*8d741a5dSApple OSS Distributions
321*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.sched_thread_bind_cluster_id(%u)", cluster_to_bind);
322*8d741a5dSApple OSS Distributions
323*8d741a5dSApple OSS Distributions T_LOG("CPU ID: %d", fixed_os_cpu_number());
324*8d741a5dSApple OSS Distributions
325*8d741a5dSApple OSS Distributions #if TARGET_CPU_X86_64
326*8d741a5dSApple OSS Distributions T_LOG("_os_cpu_cluster_number unsupported under x86.");
327*8d741a5dSApple OSS Distributions #else
328*8d741a5dSApple OSS Distributions unsigned int os_cluster_number_reported = _os_cpu_cluster_number();
329*8d741a5dSApple OSS Distributions T_LOG("OS reported cluster number: %2d\n",
330*8d741a5dSApple OSS Distributions os_cluster_number_reported);
331*8d741a5dSApple OSS Distributions T_QUIET; T_EXPECT_EQ(cluster_to_bind, os_cluster_number_reported,
332*8d741a5dSApple OSS Distributions "_os_cpu_cluster_number should report same cluster number as was bound to");
333*8d741a5dSApple OSS Distributions #endif
334*8d741a5dSApple OSS Distributions
335*8d741a5dSApple OSS Distributions unsigned int commpage_cluster_number_reported = commpage_cpu_cluster_number();
336*8d741a5dSApple OSS Distributions T_LOG("Comm Page reported cluster number: %u", commpage_cluster_number_reported);
337*8d741a5dSApple OSS Distributions T_EXPECT_EQ(commpage_cluster_number_reported, cluster_to_bind, "comm page cluster number matches commpage for this CPU");
338*8d741a5dSApple OSS Distributions
339*8d741a5dSApple OSS Distributions bound_cluster_out = 0;
340*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cluster_id", &bound_cluster_out, &bound_cluster_out_size, NULL, 0);
341*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cluster_id");
342*8d741a5dSApple OSS Distributions
343*8d741a5dSApple OSS Distributions T_QUIET; T_EXPECT_EQ((int)cluster_to_bind, bound_cluster_out,
344*8d741a5dSApple OSS Distributions "bound cluster id matches requested bind target");
345*8d741a5dSApple OSS Distributions
346*8d741a5dSApple OSS Distributions uint64_t delta_abs = end - start;
347*8d741a5dSApple OSS Distributions uint64_t delta_ns = abs_to_nanos(delta_abs);
348*8d741a5dSApple OSS Distributions
349*8d741a5dSApple OSS Distributions int32_t after_csw_count = get_csw_count();
350*8d741a5dSApple OSS Distributions
351*8d741a5dSApple OSS Distributions T_LOG("(csw %4d) bound to cluster %2d in %f milliseconds\n",
352*8d741a5dSApple OSS Distributions after_csw_count, cluster_to_bind,
353*8d741a5dSApple OSS Distributions ((double)delta_ns / 1000000.0));
354*8d741a5dSApple OSS Distributions
355*8d741a5dSApple OSS Distributions if (cluster_to_bind > 0) {
356*8d741a5dSApple OSS Distributions T_QUIET; T_EXPECT_LT(before_csw_count, after_csw_count,
357*8d741a5dSApple OSS Distributions "should have had to context switch to execute the bind");
358*8d741a5dSApple OSS Distributions }
359*8d741a5dSApple OSS Distributions }
360*8d741a5dSApple OSS Distributions
361*8d741a5dSApple OSS Distributions int unbind = -1; /* pass -1 in order to unbind the thread */
362*8d741a5dSApple OSS Distributions
363*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cluster_id", NULL, 0, &unbind, sizeof(unbind));
364*8d741a5dSApple OSS Distributions
365*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.sched_thread_bind_cluster_id(%u)", unbind);
366*8d741a5dSApple OSS Distributions
367*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cluster_id", &bound_cluster_out, &bound_cluster_out_size, NULL, 0);
368*8d741a5dSApple OSS Distributions
369*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cluster_id");
370*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(bound_cluster_out, -1, "thread should be unbound at the end");
371*8d741a5dSApple OSS Distributions
372*8d741a5dSApple OSS Distributions T_PASS("test has run threads on all clusters");
373*8d741a5dSApple OSS Distributions }
374*8d741a5dSApple OSS Distributions
375*8d741a5dSApple OSS Distributions T_DECL(check_cpu_topology,
376*8d741a5dSApple OSS Distributions "Verify _os_cpu_cluster_number(), _os_cpu_number() against IORegistry",
377*8d741a5dSApple OSS Distributions XNU_T_META_SOC_SPECIFIC,
378*8d741a5dSApple OSS Distributions T_META_ENABLED(TARGET_CPU_ARM || TARGET_CPU_ARM64))
379*8d741a5dSApple OSS Distributions {
380*8d741a5dSApple OSS Distributions int rv;
381*8d741a5dSApple OSS Distributions uint32_t cpu_id, cluster_id;
382*8d741a5dSApple OSS Distributions kern_return_t kr;
383*8d741a5dSApple OSS Distributions io_iterator_t cpus_iter = 0;
384*8d741a5dSApple OSS Distributions io_service_t cpus_service = 0;
385*8d741a5dSApple OSS Distributions io_service_t cpu_service = 0;
386*8d741a5dSApple OSS Distributions CFDictionaryRef match = NULL;
387*8d741a5dSApple OSS Distributions
388*8d741a5dSApple OSS Distributions cpucount_setup();
389*8d741a5dSApple OSS Distributions
390*8d741a5dSApple OSS Distributions int bound_cpu_out = 0;
391*8d741a5dSApple OSS Distributions size_t bound_cpu_out_size = sizeof(bound_cpu_out);
392*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", &bound_cpu_out, &bound_cpu_out_size, NULL, 0);
393*8d741a5dSApple OSS Distributions
394*8d741a5dSApple OSS Distributions if (rv == -1) {
395*8d741a5dSApple OSS Distributions if (errno == ENOENT) {
396*8d741a5dSApple OSS Distributions T_FAIL("kern.sched_thread_bind_cpu doesn't exist, must set enable_skstb=1 boot-arg on development kernel");
397*8d741a5dSApple OSS Distributions }
398*8d741a5dSApple OSS Distributions if (errno == EPERM) {
399*8d741a5dSApple OSS Distributions T_FAIL("must run as root");
400*8d741a5dSApple OSS Distributions }
401*8d741a5dSApple OSS Distributions }
402*8d741a5dSApple OSS Distributions
403*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cpu");
404*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(bound_cpu_out, -1, "kern.sched_thread_bind_cpu should exist, start unbound");
405*8d741a5dSApple OSS Distributions
406*8d741a5dSApple OSS Distributions match = IOServiceNameMatching("cpus");
407*8d741a5dSApple OSS Distributions cpus_service = IOServiceGetMatchingService(kIOMainPortDefault, match);
408*8d741a5dSApple OSS Distributions match = NULL; // consumes reference to match
409*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_NE(cpus_service, (io_service_t)0, "Failed get cpus IOService");
410*8d741a5dSApple OSS Distributions
411*8d741a5dSApple OSS Distributions kr = IORegistryEntryGetChildIterator(cpus_service, "IODeviceTree", &cpus_iter);
412*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "IORegistryEntryGetChildIterator");
413*8d741a5dSApple OSS Distributions
414*8d741a5dSApple OSS Distributions while ((cpu_service = IOIteratorNext(cpus_iter)) != 0) {
415*8d741a5dSApple OSS Distributions CFMutableDictionaryRef props = NULL;
416*8d741a5dSApple OSS Distributions kr = IORegistryEntryCreateCFProperties(cpu_service, &props, kCFAllocatorDefault, 0);
417*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "IORegistryEntryCreateCFProperties");
418*8d741a5dSApple OSS Distributions
419*8d741a5dSApple OSS Distributions dict_uint32(props, CFSTR("logical-cpu-id"), &cpu_id);
420*8d741a5dSApple OSS Distributions T_LOG("IORegistry logical cpu id: %u", cpu_id);
421*8d741a5dSApple OSS Distributions dict_uint32(props, CFSTR("logical-cluster-id"), &cluster_id);
422*8d741a5dSApple OSS Distributions T_LOG("IORegistry logical cpu cluster id: %u", cluster_id);
423*8d741a5dSApple OSS Distributions
424*8d741a5dSApple OSS Distributions T_LOG("Binding thread to cpu %u", cpu_id);
425*8d741a5dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", NULL, 0, &cpu_id, sizeof(cpu_id));
426*8d741a5dSApple OSS Distributions if (rv == -1 && errno == ENOTSUP) {
427*8d741a5dSApple OSS Distributions T_SKIP("Binding is available, but this process doesn't support binding (e.g. Rosetta on Aruba)");
428*8d741a5dSApple OSS Distributions }
429*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.sched_thread_bind_cpu(%u)", cpu_id);
430*8d741a5dSApple OSS Distributions
431*8d741a5dSApple OSS Distributions unsigned int os_cpu_number_reported = fixed_os_cpu_number();
432*8d741a5dSApple OSS Distributions T_EXPECT_EQ(os_cpu_number_reported, cpu_id, "_os_cpu_number matches IORegistry entry for this CPU");
433*8d741a5dSApple OSS Distributions unsigned int os_cluster_number_reported = _os_cpu_cluster_number();
434*8d741a5dSApple OSS Distributions T_EXPECT_EQ(os_cluster_number_reported, cluster_id, "_os_cpu_cluster_number matches IORegistry entry for this CPU");
435*8d741a5dSApple OSS Distributions unsigned int commpage_cluster_number_reported = commpage_cpu_cluster_number();
436*8d741a5dSApple OSS Distributions T_EXPECT_EQ(commpage_cluster_number_reported, cluster_id, "comm page cluster number matches IORegistry entry for this CPU");
437*8d741a5dSApple OSS Distributions
438*8d741a5dSApple OSS Distributions CFRelease(props);
439*8d741a5dSApple OSS Distributions IOObjectRelease(cpu_service);
440*8d741a5dSApple OSS Distributions }
441*8d741a5dSApple OSS Distributions T_PASS("All cluster IDs match with IORegistry");
442*8d741a5dSApple OSS Distributions }
443*8d741a5dSApple OSS Distributions
444*8d741a5dSApple OSS Distributions T_DECL(hw_perflevels_order_and_cpu_counts,
445*8d741a5dSApple OSS Distributions "check that perflevel sysctls return the correct order and with expected cpu counts",
446*8d741a5dSApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
447*8d741a5dSApple OSS Distributions {
448*8d741a5dSApple OSS Distributions int ret;
449*8d741a5dSApple OSS Distributions char sysctlname[256];
450*8d741a5dSApple OSS Distributions
451*8d741a5dSApple OSS Distributions /* Check perflevel count */
452*8d741a5dSApple OSS Distributions int level_count = 0;
453*8d741a5dSApple OSS Distributions ret = sysctlbyname("hw.nperflevels", &level_count, &(size_t){ sizeof(level_count) }, NULL, 0);
454*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "hw.nperflevels");
455*8d741a5dSApple OSS Distributions T_EXPECT_GE(level_count, 1, "valid hw.nperflevels: %d", level_count);
456*8d741a5dSApple OSS Distributions
457*8d741a5dSApple OSS Distributions /* Check perflevel names */
458*8d741a5dSApple OSS Distributions char perflevel_name[level_count][128];
459*8d741a5dSApple OSS Distributions int efficient_pos = -1;
460*8d741a5dSApple OSS Distributions int performance_pos = -1;
461*8d741a5dSApple OSS Distributions int standard_pos = -1;
462*8d741a5dSApple OSS Distributions for (int p = 0; p < level_count; p++) {
463*8d741a5dSApple OSS Distributions snprintf(sysctlname, sizeof(sysctlname), "hw.perflevel%d.name", p);
464*8d741a5dSApple OSS Distributions ret = sysctlbyname(sysctlname, perflevel_name[p], &(size_t){ sizeof(perflevel_name[p]) }, NULL, 0);
465*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, sysctlname);
466*8d741a5dSApple OSS Distributions if (strcmp(perflevel_name[p], "Efficiency") == 0) {
467*8d741a5dSApple OSS Distributions efficient_pos = p;
468*8d741a5dSApple OSS Distributions } else if (strcmp(perflevel_name[p], "Performance") == 0) {
469*8d741a5dSApple OSS Distributions performance_pos = p;
470*8d741a5dSApple OSS Distributions } else if (strcmp(perflevel_name[p], "Standard") == 0) {
471*8d741a5dSApple OSS Distributions standard_pos = p;
472*8d741a5dSApple OSS Distributions }
473*8d741a5dSApple OSS Distributions }
474*8d741a5dSApple OSS Distributions T_ASSERT_TRUE((efficient_pos >= 0) || (performance_pos >= 0) || (standard_pos >= 0),
475*8d741a5dSApple OSS Distributions "valid perflevels detected (\"Efficient\" %d, \"Performance\" %d, \"Standard\" %d)",
476*8d741a5dSApple OSS Distributions efficient_pos, performance_pos, standard_pos);
477*8d741a5dSApple OSS Distributions if (standard_pos >= 0) {
478*8d741a5dSApple OSS Distributions T_ASSERT_EQ(level_count, 1, "single \"Standard\" perflevel");
479*8d741a5dSApple OSS Distributions }
480*8d741a5dSApple OSS Distributions if (efficient_pos >= 0) {
481*8d741a5dSApple OSS Distributions T_ASSERT_EQ(efficient_pos, level_count - 1, "\"Efficiency\" is the highest index perflevel");
482*8d741a5dSApple OSS Distributions }
483*8d741a5dSApple OSS Distributions if (performance_pos >= 0) {
484*8d741a5dSApple OSS Distributions T_ASSERT_EQ(performance_pos, 0, "\"Performance\" is the lowest index perflevel");
485*8d741a5dSApple OSS Distributions }
486*8d741a5dSApple OSS Distributions
487*8d741a5dSApple OSS Distributions /*
488*8d741a5dSApple OSS Distributions * Check that certain variants of CPU counts sum up to the expected total
489*8d741a5dSApple OSS Distributions * across all perflevels.
490*8d741a5dSApple OSS Distributions */
491*8d741a5dSApple OSS Distributions const int num_cpu_count_variants = 2;
492*8d741a5dSApple OSS Distributions char *cpu_count_variants[num_cpu_count_variants] = {"physicalcpu_max", "logicalcpu_max"};
493*8d741a5dSApple OSS Distributions for (int v = 0; v < num_cpu_count_variants; v++) {
494*8d741a5dSApple OSS Distributions unsigned int total_amount = 0;
495*8d741a5dSApple OSS Distributions snprintf(sysctlname, sizeof(sysctlname), "hw.%s", cpu_count_variants[v]);
496*8d741a5dSApple OSS Distributions ret = sysctlbyname(sysctlname, &total_amount, &(size_t){ sizeof(total_amount) }, NULL, 0);
497*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, sysctlname);
498*8d741a5dSApple OSS Distributions unsigned int amount_from_perflevels = 0;
499*8d741a5dSApple OSS Distributions for (int p = 0; p < level_count; p++) {
500*8d741a5dSApple OSS Distributions unsigned int perflevel_amount = 0;
501*8d741a5dSApple OSS Distributions snprintf(sysctlname, sizeof(sysctlname), "hw.perflevel%d.%s", p, cpu_count_variants[v]);
502*8d741a5dSApple OSS Distributions ret = sysctlbyname(sysctlname, &perflevel_amount, &(size_t){ sizeof(perflevel_amount) }, NULL, 0);
503*8d741a5dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, sysctlname);
504*8d741a5dSApple OSS Distributions amount_from_perflevels += perflevel_amount;
505*8d741a5dSApple OSS Distributions }
506*8d741a5dSApple OSS Distributions T_EXPECT_EQ(total_amount, amount_from_perflevels, "all %u %s accounted for", total_amount, cpu_count_variants[v]);
507*8d741a5dSApple OSS Distributions }
508*8d741a5dSApple OSS Distributions }
509