1*fdd8201dSApple OSS Distributions /*
2*fdd8201dSApple OSS Distributions * Test to validate that we can schedule threads on all hw.ncpus cores according to _os_cpu_number
3*fdd8201dSApple OSS Distributions *
4*fdd8201dSApple OSS Distributions * <rdar://problem/29545645>
5*fdd8201dSApple OSS Distributions * <rdar://problem/30445216>
6*fdd8201dSApple OSS Distributions *
7*fdd8201dSApple OSS Distributions * xcrun -sdk macosx.internal clang -o cpucount cpucount.c -ldarwintest -g -Weverything
8*fdd8201dSApple OSS Distributions * xcrun -sdk iphoneos.internal clang -arch arm64 -o cpucount-ios cpucount.c -ldarwintest -g -Weverything
9*fdd8201dSApple OSS Distributions * xcrun -sdk macosx.internal clang -o cpucount cpucount.c -ldarwintest -arch arm64e -Weverything
10*fdd8201dSApple OSS Distributions */
11*fdd8201dSApple OSS Distributions
12*fdd8201dSApple OSS Distributions #include <darwintest.h>
13*fdd8201dSApple OSS Distributions
14*fdd8201dSApple OSS Distributions #include <stdio.h>
15*fdd8201dSApple OSS Distributions #include <stdlib.h>
16*fdd8201dSApple OSS Distributions #include <unistd.h>
17*fdd8201dSApple OSS Distributions #include <pthread.h>
18*fdd8201dSApple OSS Distributions #include <sys/sysctl.h>
19*fdd8201dSApple OSS Distributions #include <sys/proc_info.h>
20*fdd8201dSApple OSS Distributions #include <libproc.h>
21*fdd8201dSApple OSS Distributions
22*fdd8201dSApple OSS Distributions #include <mach/mach.h>
23*fdd8201dSApple OSS Distributions #include <mach/mach_time.h>
24*fdd8201dSApple OSS Distributions
25*fdd8201dSApple OSS Distributions #include <os/tsd.h> /* private header for _os_cpu_number */
26*fdd8201dSApple OSS Distributions
27*fdd8201dSApple OSS Distributions T_GLOBAL_META(
28*fdd8201dSApple OSS Distributions T_META_RUN_CONCURRENTLY(false),
29*fdd8201dSApple OSS Distributions T_META_BOOTARGS_SET("enable_skstb=1"),
30*fdd8201dSApple OSS Distributions T_META_CHECK_LEAKS(false),
31*fdd8201dSApple OSS Distributions T_META_ASROOT(true),
32*fdd8201dSApple OSS Distributions T_META_ALL_VALID_ARCHS(true),
33*fdd8201dSApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
34*fdd8201dSApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("scheduler")
35*fdd8201dSApple OSS Distributions );
36*fdd8201dSApple OSS Distributions
37*fdd8201dSApple OSS Distributions #define KERNEL_BOOTARGS_MAX_SIZE 1024
38*fdd8201dSApple OSS Distributions static char kernel_bootargs[KERNEL_BOOTARGS_MAX_SIZE];
39*fdd8201dSApple OSS Distributions
40*fdd8201dSApple OSS Distributions #define KERNEL_VERSION_MAX_SIZE 1024
41*fdd8201dSApple OSS Distributions static char kernel_version[KERNEL_VERSION_MAX_SIZE];
42*fdd8201dSApple OSS Distributions
43*fdd8201dSApple OSS Distributions static mach_timebase_info_data_t timebase_info;
44*fdd8201dSApple OSS Distributions
45*fdd8201dSApple OSS Distributions static uint64_t
abs_to_nanos(uint64_t abs)46*fdd8201dSApple OSS Distributions abs_to_nanos(uint64_t abs)
47*fdd8201dSApple OSS Distributions {
48*fdd8201dSApple OSS Distributions return abs * timebase_info.numer / timebase_info.denom;
49*fdd8201dSApple OSS Distributions }
50*fdd8201dSApple OSS Distributions
51*fdd8201dSApple OSS Distributions static int32_t
get_csw_count()52*fdd8201dSApple OSS Distributions get_csw_count()
53*fdd8201dSApple OSS Distributions {
54*fdd8201dSApple OSS Distributions struct proc_taskinfo taskinfo;
55*fdd8201dSApple OSS Distributions int rv;
56*fdd8201dSApple OSS Distributions
57*fdd8201dSApple OSS Distributions rv = proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &taskinfo, sizeof(taskinfo));
58*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "PROC_PIDTASKINFO");
59*fdd8201dSApple OSS Distributions
60*fdd8201dSApple OSS Distributions return taskinfo.pti_csw;
61*fdd8201dSApple OSS Distributions }
62*fdd8201dSApple OSS Distributions
63*fdd8201dSApple OSS Distributions // noinline hopefully keeps the optimizer from hoisting it out of the loop
64*fdd8201dSApple OSS Distributions // until rdar://68253516 is fixed.
65*fdd8201dSApple OSS Distributions __attribute__((noinline))
66*fdd8201dSApple OSS Distributions static uint32_t
fixed_os_cpu_number(void)67*fdd8201dSApple OSS Distributions fixed_os_cpu_number(void)
68*fdd8201dSApple OSS Distributions {
69*fdd8201dSApple OSS Distributions uint32_t cpu_number = _os_cpu_number();
70*fdd8201dSApple OSS Distributions
71*fdd8201dSApple OSS Distributions return cpu_number;
72*fdd8201dSApple OSS Distributions }
73*fdd8201dSApple OSS Distributions
74*fdd8201dSApple OSS Distributions
75*fdd8201dSApple OSS Distributions T_DECL(count_cpus, "Tests we can schedule bound threads on all hw.ncpus cores and that _os_cpu_number matches")
76*fdd8201dSApple OSS Distributions {
77*fdd8201dSApple OSS Distributions int rv;
78*fdd8201dSApple OSS Distributions
79*fdd8201dSApple OSS Distributions setvbuf(stdout, NULL, _IONBF, 0);
80*fdd8201dSApple OSS Distributions setvbuf(stderr, NULL, _IONBF, 0);
81*fdd8201dSApple OSS Distributions
82*fdd8201dSApple OSS Distributions /* Validate what kind of kernel we're on */
83*fdd8201dSApple OSS Distributions size_t kernel_version_size = sizeof(kernel_version);
84*fdd8201dSApple OSS Distributions rv = sysctlbyname("kern.version", kernel_version, &kernel_version_size, NULL, 0);
85*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.version");
86*fdd8201dSApple OSS Distributions
87*fdd8201dSApple OSS Distributions T_LOG("kern.version: %s\n", kernel_version);
88*fdd8201dSApple OSS Distributions
89*fdd8201dSApple OSS Distributions /* Double check that darwintest set the boot arg we requested */
90*fdd8201dSApple OSS Distributions size_t kernel_bootargs_size = sizeof(kernel_bootargs);
91*fdd8201dSApple OSS Distributions rv = sysctlbyname("kern.bootargs", kernel_bootargs, &kernel_bootargs_size, NULL, 0);
92*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.bootargs");
93*fdd8201dSApple OSS Distributions
94*fdd8201dSApple OSS Distributions T_LOG("kern.bootargs: %s\n", kernel_bootargs);
95*fdd8201dSApple OSS Distributions
96*fdd8201dSApple OSS Distributions if (NULL == strstr(kernel_bootargs, "enable_skstb=1")) {
97*fdd8201dSApple OSS Distributions T_FAIL("enable_skstb=1 boot-arg is missing");
98*fdd8201dSApple OSS Distributions }
99*fdd8201dSApple OSS Distributions
100*fdd8201dSApple OSS Distributions kern_return_t kr;
101*fdd8201dSApple OSS Distributions kr = mach_timebase_info(&timebase_info);
102*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_timebase_info");
103*fdd8201dSApple OSS Distributions
104*fdd8201dSApple OSS Distributions int bound_cpu_out = 0;
105*fdd8201dSApple OSS Distributions size_t bound_cpu_out_size = sizeof(bound_cpu_out);
106*fdd8201dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", &bound_cpu_out, &bound_cpu_out_size, NULL, 0);
107*fdd8201dSApple OSS Distributions
108*fdd8201dSApple OSS Distributions if (rv == -1) {
109*fdd8201dSApple OSS Distributions if (errno == ENOENT) {
110*fdd8201dSApple OSS Distributions T_FAIL("kern.sched_thread_bind_cpu doesn't exist, must set enable_skstb=1 boot-arg on development kernel");
111*fdd8201dSApple OSS Distributions }
112*fdd8201dSApple OSS Distributions if (errno == EPERM) {
113*fdd8201dSApple OSS Distributions T_FAIL("must run as root");
114*fdd8201dSApple OSS Distributions }
115*fdd8201dSApple OSS Distributions }
116*fdd8201dSApple OSS Distributions
117*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cpu");
118*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(bound_cpu_out, -1, "kern.sched_thread_bind_cpu should exist, start unbound");
119*fdd8201dSApple OSS Distributions
120*fdd8201dSApple OSS Distributions struct sched_param param = {.sched_priority = 63};
121*fdd8201dSApple OSS Distributions
122*fdd8201dSApple OSS Distributions rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
123*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_setschedparam");
124*fdd8201dSApple OSS Distributions
125*fdd8201dSApple OSS Distributions uint32_t sysctl_ncpu = 0;
126*fdd8201dSApple OSS Distributions size_t ncpu_size = sizeof(sysctl_ncpu);
127*fdd8201dSApple OSS Distributions rv = sysctlbyname("hw.ncpu", &sysctl_ncpu, &ncpu_size, NULL, 0);
128*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sysctlbyname(hw.ncpu)");
129*fdd8201dSApple OSS Distributions
130*fdd8201dSApple OSS Distributions T_LOG("hw.ncpu: %2d\n", sysctl_ncpu);
131*fdd8201dSApple OSS Distributions
132*fdd8201dSApple OSS Distributions T_ASSERT_GT(sysctl_ncpu, 0, "at least one CPU exists");
133*fdd8201dSApple OSS Distributions
134*fdd8201dSApple OSS Distributions for (uint32_t cpu_to_bind = 0; cpu_to_bind < sysctl_ncpu; cpu_to_bind++) {
135*fdd8201dSApple OSS Distributions int32_t before_csw_count = get_csw_count();
136*fdd8201dSApple OSS Distributions T_LOG("(csw %4d) attempting to bind to cpu %2d\n", before_csw_count, cpu_to_bind);
137*fdd8201dSApple OSS Distributions
138*fdd8201dSApple OSS Distributions uint64_t start = mach_absolute_time();
139*fdd8201dSApple OSS Distributions
140*fdd8201dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", NULL, 0, &cpu_to_bind, sizeof(cpu_to_bind));
141*fdd8201dSApple OSS Distributions
142*fdd8201dSApple OSS Distributions uint64_t end = mach_absolute_time();
143*fdd8201dSApple OSS Distributions
144*fdd8201dSApple OSS Distributions if (rv == -1 && errno == ENOTSUP) {
145*fdd8201dSApple OSS Distributions T_SKIP("Binding is available, but this process doesn't support binding (e.g. Rosetta on Aruba)");
146*fdd8201dSApple OSS Distributions }
147*fdd8201dSApple OSS Distributions
148*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.sched_thread_bind_cpu(%u)", cpu_to_bind);
149*fdd8201dSApple OSS Distributions
150*fdd8201dSApple OSS Distributions uint32_t os_cpu_number_reported = fixed_os_cpu_number();
151*fdd8201dSApple OSS Distributions
152*fdd8201dSApple OSS Distributions bound_cpu_out = 0;
153*fdd8201dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", &bound_cpu_out, &bound_cpu_out_size, NULL, 0);
154*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cpu");
155*fdd8201dSApple OSS Distributions
156*fdd8201dSApple OSS Distributions T_QUIET; T_EXPECT_EQ((int)cpu_to_bind, bound_cpu_out,
157*fdd8201dSApple OSS Distributions "should report bound cpu id matching requested bind target");
158*fdd8201dSApple OSS Distributions
159*fdd8201dSApple OSS Distributions uint64_t delta_abs = end - start;
160*fdd8201dSApple OSS Distributions uint64_t delta_ns = abs_to_nanos(delta_abs);
161*fdd8201dSApple OSS Distributions
162*fdd8201dSApple OSS Distributions int32_t after_csw_count = get_csw_count();
163*fdd8201dSApple OSS Distributions
164*fdd8201dSApple OSS Distributions T_LOG("(csw %4d) bound to cpu %2d in %f milliseconds\n",
165*fdd8201dSApple OSS Distributions after_csw_count, cpu_to_bind,
166*fdd8201dSApple OSS Distributions ((double)delta_ns / 1000000.0));
167*fdd8201dSApple OSS Distributions
168*fdd8201dSApple OSS Distributions if (cpu_to_bind > 0) {
169*fdd8201dSApple OSS Distributions T_QUIET; T_EXPECT_LT(before_csw_count, after_csw_count,
170*fdd8201dSApple OSS Distributions "should have had to context switch to execute the bind");
171*fdd8201dSApple OSS Distributions }
172*fdd8201dSApple OSS Distributions
173*fdd8201dSApple OSS Distributions T_LOG("cpu %2d reported id %2d\n",
174*fdd8201dSApple OSS Distributions cpu_to_bind, os_cpu_number_reported);
175*fdd8201dSApple OSS Distributions
176*fdd8201dSApple OSS Distributions T_QUIET;
177*fdd8201dSApple OSS Distributions T_EXPECT_EQ(cpu_to_bind, os_cpu_number_reported,
178*fdd8201dSApple OSS Distributions "should report same CPU number as was bound to");
179*fdd8201dSApple OSS Distributions }
180*fdd8201dSApple OSS Distributions
181*fdd8201dSApple OSS Distributions int unbind = -1; /* pass -1 in order to unbind the thread */
182*fdd8201dSApple OSS Distributions
183*fdd8201dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", NULL, 0, &unbind, sizeof(unbind));
184*fdd8201dSApple OSS Distributions
185*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "kern.sched_thread_bind_cpu(%u)", unbind);
186*fdd8201dSApple OSS Distributions
187*fdd8201dSApple OSS Distributions rv = sysctlbyname("kern.sched_thread_bind_cpu", &bound_cpu_out, &bound_cpu_out_size, NULL, 0);
188*fdd8201dSApple OSS Distributions
189*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "read kern.sched_thread_bind_cpu");
190*fdd8201dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(bound_cpu_out, -1, "thread should be unbound at the end");
191*fdd8201dSApple OSS Distributions
192*fdd8201dSApple OSS Distributions T_PASS("test has run threads on all CPUS");
193*fdd8201dSApple OSS Distributions }
194