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