7#include <sys/syscall.h>
8#include <sys/sysinfo.h>
10static int affinity_abi(
void) {
12 cpu_set_t raw,
public, original, singleton;
13 void* bad = mmap(NULL, sc_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
14 CHECK(bad != MAP_FAILED);
15 CHECK(sched_getaffinity(0,
sizeof(original), &original) == 0);
16 CHECK(sysconf(_SC_NPROCESSORS_CONF) == sc_count && sysconf(_SC_NPROCESSORS_ONLN) == sc_count);
17 CHECK(get_nprocs_conf() == sc_count && get_nprocs() == sc_count);
18 memset(&raw, 0xa7,
sizeof(raw));
19 CHECK(syscall(SYS_sched_getaffinity, 0,
sizeof(raw), &raw) == (
long)sc_bytes);
20 for (
size_t n = sc_bytes; n <
sizeof(raw); ++n)
21 CHECK(((
unsigned char*)&raw)[n] == 0xa7);
22 memset(&
public, 0xa7,
sizeof(
public));
23 CHECK(sched_getaffinity(0,
sizeof(
public), &
public) == 0);
24 CHECK(CPU_EQUAL(&original, &
public));
25 for (
size_t n = sc_bytes; n <
sizeof(
public); ++n)
26 CHECK(((
unsigned char*)&
public)[n] == 0);
27 CHECK(pthread_getaffinity_np(pthread_self(),
sizeof(
public), &
public) == 0);
28 CHECK(CPU_EQUAL(&original, &
public));
29 CHECK(syscall(SYS_sched_getaffinity, syscall(SYS_gettid), sc_bytes, &raw) == (
long)sc_bytes);
31 CHECK(syscall(SYS_sched_getaffinity, 0, sc_bytes - 1, &raw) == -1 && errno == EINVAL);
33 CHECK(syscall(SYS_sched_getaffinity, 0, sc_bytes + 1, &raw) == -1 && errno == EINVAL);
35 CHECK(syscall(SYS_sched_getaffinity, -1,
sizeof(raw), &raw) == -1 && errno == ESRCH);
37 CHECK(syscall(SYS_sched_getaffinity, 0, sc_bytes, bad) == -1 && errno == EFAULT);
38 CHECK(pthread_getaffinity_np(pthread_self(), sc_bytes - 1, &raw) == EINVAL);
40 CPU_SET(sc_cpus[0], &singleton);
41 size_t short_size = (size_t)sc_cpus[0] / 8 + 1;
42 CHECK(syscall(SYS_sched_setaffinity, 0, short_size, &singleton) == 0);
43 CHECK(sc_mask(0, sc_cpus[0]) == 0 && sc_sample(sc_cpus[0]) == 0);
44 CHECK(sysconf(_SC_NPROCESSORS_CONF) == 1 && sysconf(_SC_NPROCESSORS_ONLN) == 1);
45 CHECK(get_nprocs_conf() == 1 && get_nprocs() == 1);
48 mmap(NULL, 2 * sc_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
49 CHECK(edge != MAP_FAILED);
50 if (mprotect(edge + sc_page, sc_page, PROT_NONE)) {
51 munmap(edge, 2 * sc_page);
54 memcpy(edge + sc_page - sc_bytes, &singleton, sc_bytes);
55 long copied = syscall(SYS_sched_setaffinity, 0, 128, edge + sc_page - sc_bytes);
56 munmap(edge, 2 * sc_page);
59 CHECK(syscall(SYS_sched_setaffinity, 0, sc_bytes, bad) == -1 && errno == EFAULT);
60 CHECK(sc_mask(0, sc_cpus[0]) == 0);
63 CHECK(sched_setaffinity(0,
sizeof(raw), &raw) == -1 && errno == EINVAL);
64 CPU_SET(CPU_SETSIZE - 1, &raw);
65 CHECK(!CPU_ISSET(CPU_SETSIZE - 1, &original));
67 CHECK(sched_setaffinity(0,
sizeof(raw), &raw) == -1 && errno == EINVAL);
69 CHECK(syscall(SYS_sched_setaffinity, 0, 0, NULL) == -1 && errno == EINVAL);
71 CHECK(syscall(SYS_sched_setaffinity, -1,
sizeof(singleton), &singleton) == -1 && errno == ESRCH);
72 CHECK(sc_mask(0, sc_cpus[0]) == 0);
73 CHECK(pthread_setaffinity_np(pthread_self(),
sizeof(original), &original) == 0);
74 CHECK(sched_getaffinity(0,
sizeof(
public), &
public) == 0 && CPU_EQUAL(&
public, &original));
76 if (bad != MAP_FAILED)
81static int cpu_abi(
void) {
83 unsigned cpu = UINT32_MAX, node = UINT32_MAX;
84 void* bad = mmap(NULL, sc_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
85 CHECK(bad != MAP_FAILED && sc_pin(0, sc_cpus[0]) == 0);
86 CHECK(syscall(SYS_getcpu, &cpu, &node, bad) == 0 && cpu == (
unsigned)sc_cpus[0] && node == 0);
87 CHECK(syscall(SYS_getcpu, NULL, NULL, bad) == 0);
89 CHECK(syscall(SYS_getcpu, NULL, &node, NULL) == 0 && node == 0);
92 CHECK(syscall(SYS_getcpu, &cpu, bad, NULL) == -1 && errno == EFAULT &&
93 cpu == (
unsigned)sc_cpus[0]);
96 CHECK(syscall(SYS_getcpu, bad, &node, NULL) == -1 && errno == EFAULT && node == 0);
97 CHECK(sc_sample(sc_cpus[0]) == 0);
99 if (bad != MAP_FAILED)
100 munmap(bad, sc_page);
104static atomic_uint entries;
105static void* explicit_entry(
void* argument) {
106 atomic_fetch_add_explicit(&entries, 1, memory_order_relaxed);
108 struct sched_param param;
109 int error = pthread_getschedparam(pthread_self(), &policy, ¶m);
110 return (
void*)(intptr_t)(error || policy != SCHED_OTHER || param.sched_priority ||
111 sc_mask(0, *(
int*)argument) || sc_sample(*(
int*)argument));
113static int policy_abi(
void) {
114 int failed = 0, attr_ready = 0;
116 struct sched_param param = {.sched_priority = 0};
117 void* bad = mmap(NULL, sc_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
118 CHECK(bad != MAP_FAILED);
119 int words[4] = {-1, 0x126abc, 0x35c9ed, 0x47ad8b};
120 CHECK(syscall(SYS_sched_getparam, 0, words) == 0 && words[0] == 0 && words[1] == 0x126abc &&
121 words[2] == 0x35c9ed && words[3] == 0x47ad8b);
122 CHECK(syscall(SYS_sched_getscheduler, 0) == SCHED_OTHER);
123 CHECK(syscall(SYS_sched_setparam, 0, ¶m) == 0);
124 CHECK(syscall(SYS_sched_setscheduler, 0, SCHED_OTHER, ¶m) == 0);
126 CHECK(syscall(SYS_sched_getscheduler, -1) == -1 && errno == EINVAL);
127 for (
size_t i = 0; i < 2; ++i) {
128 long number = i ? SYS_sched_setparam : SYS_sched_getparam;
130 CHECK(syscall(number, 0, NULL) == -1 && errno == EINVAL);
132 CHECK(syscall(number, -1, ¶m) == -1 && errno == EINVAL);
134 CHECK(syscall(number, 0, bad) == -1 && errno == EFAULT);
137 CHECK(syscall(SYS_sched_setscheduler, 0, -1, bad) == -1 && errno == EINVAL);
138 param.sched_priority = 1;
140 CHECK(syscall(SYS_sched_setparam, 0, ¶m) == -1 && errno == EINVAL);
141 CHECK(pthread_setschedprio(pthread_self(), 1) == EINVAL);
142 param.sched_priority = 0;
143 CHECK(pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m) == 0);
144 CHECK(pthread_setschedprio(pthread_self(), 0) == 0);
145 const int policies[] = {SCHED_OTHER, SCHED_FIFO, SCHED_RR,
146 SCHED_BATCH, SCHED_IDLE, SCHED_DEADLINE};
147 for (
size_t i = 0; i <
sizeof(policies) /
sizeof(policies[0]); ++i) {
148 const int realtime = policies[i] == SCHED_FIFO || policies[i] == SCHED_RR;
149 CHECK(sched_get_priority_min(policies[i]) == (realtime ? 1 : 0));
150 CHECK(sched_get_priority_max(policies[i]) == (realtime ? 99 : 0));
151 if (policies[i] == SCHED_OTHER)
153 param.sched_priority = realtime ? 1 : 0;
155 CHECK(syscall(SYS_sched_setscheduler, 0, policies[i], ¶m) == -1 && errno == EOPNOTSUPP);
156 param.sched_priority = realtime ? 0 : 1;
158 CHECK(syscall(SYS_sched_setscheduler, 0, policies[i], ¶m) == -1 && errno == EINVAL);
160 param.sched_priority = 0;
162 CHECK(syscall(SYS_sched_setscheduler, 0, SCHED_OTHER | SCHED_RESET_ON_FORK, ¶m) == -1 &&
163 errno == EOPNOTSUPP);
165 CHECK(syscall(SYS_sched_setscheduler, 0, 0x20000000, ¶m) == -1 && errno == EINVAL);
167 CHECK(sched_get_priority_min(77) == -1 && errno == EINVAL);
169 CHECK(sched_get_priority_max(77) == -1 && errno == EINVAL);
170 CHECK(syscall(SYS_sched_getscheduler, 0) == SCHED_OTHER);
171 CHECK(sc_pin(0, sc_cpus[0]) == 0 && pthread_attr_init(&attr) == 0);
173 CHECK(pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0);
174 CHECK(pthread_attr_setschedpolicy(&attr, SCHED_OTHER) == 0);
175 CHECK(pthread_attr_setschedparam(&attr, ¶m) == 0);
177 CHECK(pthread_create(&thread, &attr, explicit_entry, &sc_cpus[0]) == 0);
179 CHECK(pthread_join(thread, &result) == 0 && result == NULL && atomic_load(&entries) == 1);
180 param.sched_priority = 1;
181 CHECK(pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0);
182 CHECK(pthread_attr_setschedparam(&attr, ¶m) == 0);
183 CHECK(pthread_create(&thread, &attr, explicit_entry, &sc_cpus[0]) == EOPNOTSUPP);
184 CHECK(atomic_load(&entries) == 1);
187 pthread_attr_destroy(&attr);
188 if (bad != MAP_FAILED)
189 munmap(bad, sc_page);
194 atomic_uint ready, stop;
199 struct timespec expected;
201static void* interval_entry(
void* argument) {
203 peer->tid = (pid_t)syscall(SYS_gettid);
204 atomic_store_explicit(&peer->ready, 1, memory_order_release);
205 while (!atomic_load_explicit(&peer->stop, memory_order_acquire))
209static int interval_unprivileged(
int command,
int report,
void* argument) {
213 struct timespec value;
214 return setresuid(51991, 51991, 51991) || sched_rr_get_interval(query->tid, &value) ||
215 value.tv_sec != query->expected.tv_sec || value.tv_nsec != query->expected.tv_nsec;
217static int interval_abi(
void) {
218 _Static_assert(
sizeof(
struct timespec) == 16,
"amd64 Linux timespec ABI");
219 int failed = 0, created = 0;
222 struct sc_peer child = SC_PEER_INITIALIZER;
223 struct timespec baseline, value;
224 void* bad = mmap(NULL, sc_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
225 CHECK(bad != MAP_FAILED);
226 CHECK(sched_rr_get_interval(0, &baseline) == 0);
227 CHECK(baseline.tv_sec >= 0 && baseline.tv_nsec >= 0 && baseline.tv_nsec < 1000000000 &&
228 (baseline.tv_sec || baseline.tv_nsec));
229 printf(
"SCHEDULING-CONTRACT: nominal interval=%llds+%lldns\n", (
long long)baseline.tv_sec,
230 (
long long)baseline.tv_nsec);
231 CHECK(pthread_create(&thread, NULL, interval_entry, &peer) == 0);
233 CHECK(sc_wait(&peer.ready, 1) == 0);
234 for (
int i = 0; i < sc_count; ++i) {
235 CHECK(sc_pin(0, sc_cpus[i]) == 0 && sc_pin(peer.tid, sc_cpus[i]) == 0);
236 CHECK(sc_sample(sc_cpus[i]) == 0);
237 CHECK(sched_rr_get_interval(0, &value) == 0 && value.tv_sec == baseline.tv_sec &&
238 value.tv_nsec == baseline.tv_nsec);
239 CHECK(sched_rr_get_interval(peer.tid, &value) == 0 && value.tv_sec == baseline.tv_sec &&
240 value.tv_nsec == baseline.tv_nsec);
242 int64_t seconds, nanoseconds;
244 } raw = {-1, -1, {UINT64_C(0x48fd8270ab65139c), UINT64_C(0x95814aece263bf70)}};
245 CHECK(syscall(SYS_sched_rr_get_interval, peer.tid, &raw) == 0);
246 CHECK(raw.seconds == baseline.tv_sec && raw.nanoseconds == baseline.tv_nsec &&
247 raw.guard[0] == UINT64_C(0x48fd8270ab65139c) &&
248 raw.guard[1] == UINT64_C(0x95814aece263bf70));
251 CHECK(sc_spawn(&child, interval_unprivileged, &query) == 0 && sc_join(&child) == 0);
253 CHECK(sched_rr_get_interval(-1, &value) == -1 && errno == EINVAL);
255 CHECK(syscall(SYS_sched_rr_get_interval, 0, bad) == -1 && errno == EFAULT);
257 CHECK(syscall(SYS_sched_rr_get_interval, 0, NULL) == -1 && errno == EFAULT);
258 atomic_store_explicit(&peer.stop, 1, memory_order_release);
259 CHECK(pthread_join(thread, NULL) == 0);
261 CHECK(sc_wait_for_task_retirement(peer.tid, &baseline) == 0);
263 CHECK(sched_rr_get_interval(peer.tid, &value) == -1 && errno == ESRCH);
267 atomic_store_explicit(&peer.stop, 1, memory_order_release);
268 pthread_join(thread, NULL);
270 if (bad != MAP_FAILED)
271 munmap(bad, sc_page);
276 return affinity_abi() || cpu_abi() || policy_abi() || interval_abi();