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