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