9#include <sys/syscall.h>
13int sc_cpus[CPU_SETSIZE], sc_count;
14size_t sc_bytes, sc_page;
15_Thread_local
unsigned sc_tls;
17int sc_init(
int expected) {
18 sc_page = (size_t)sysconf(_SC_PAGESIZE);
19 long bytes = syscall(SYS_sched_getaffinity, 0,
sizeof(sc_allowed), &sc_allowed);
20 if (bytes <= 0 || bytes > (
long)
sizeof(sc_allowed) || bytes %
sizeof(
long)) {
21 fprintf(stderr,
"raw affinity size=%ld errno=%d\n", bytes, errno);
24 sc_bytes = (size_t)bytes;
25 if (sched_getaffinity(0,
sizeof(sc_allowed), &sc_allowed))
27 for (
int cpu = 0; cpu < CPU_SETSIZE; ++cpu)
28 if (CPU_ISSET(cpu, &sc_allowed))
29 sc_cpus[sc_count++] = cpu;
30 if (!sc_count || (expected && expected != sc_count)) {
31 fprintf(stderr,
"topology expected=%d allowed=%d\n", expected, sc_count);
34 printf(
"SCHEDULING-CONTRACT: topology cpus=%d mask-bytes=%zu\n", sc_count, sc_bytes);
36 puts(
"SCHEDULING-CONTRACT: SKIP cross-CPU placement (one allowed CPU)");
39int sc_pin(pid_t tid,
int cpu) {
43 return sched_setaffinity(tid,
sizeof(mask), &mask);
45int sc_mask(pid_t tid,
int cpu) {
47 return sched_getaffinity(tid,
sizeof(mask), &mask) || CPU_COUNT(&mask) != 1 ||
48 !CPU_ISSET(cpu, &mask)
52int sc_sample(
int cpu) {
53 unsigned actual = UINT32_MAX, node = UINT32_MAX;
54 if (syscall(SYS_getcpu, &actual, &node, NULL) || actual != (
unsigned)cpu || node ||
55 sched_getcpu() != cpu) {
56 fprintf(stderr,
"CPU expected=%d actual=%u node=%u errno=%d\n", cpu, actual, node, errno);
61int sc_register_syscall(
long number,
long a,
long b,
long c,
long* result) {
62#if defined(__x86_64__)
63 const uint64_t input[2] = {UINT64_C(0x729bd064a35ecf18), UINT64_C(0xa148f7328d9605eb)};
64 uint64_t output[2] = {};
65 const long double floating = 0x1.23456789abcdefp+19L;
66 long double restored = 0;
70 "movdqu %[input], %%xmm7\n\t"
71 "fldt %[floating]\n\t"
73 "movdqu %%xmm7, %[output]\n\t"
75 :
"+a"(value), [output]
"=m"(output), [restored]
"=m"(restored)
76 :
"D"(a),
"S"(b),
"d"(c), [input]
"m"(input), [floating]
"m"(floating)
77 :
"rcx",
"r11",
"xmm7",
"st",
"memory",
"cc");
79 return memcmp(input, output,
sizeof(input)) || memcmp(&floating, &restored, 10) ? -1 : 0;
87 return clock_gettime(CLOCK_MONOTONIC, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
89int sc_wait(atomic_uint* value,
unsigned expected) {
90 int64_t now = sc_now(), end = now + INT64_C(10000000000);
91 while (now >= 0 && now < end) {
92 if (atomic_load_explicit(value, memory_order_acquire) == expected)
97 fprintf(stderr,
"generation timeout expected=%u actual=%u\n", expected,
98 atomic_load_explicit(value, memory_order_acquire));
101int sc_wait_for_task_retirement(pid_t tid,
const struct timespec* expected_interval) {
103 int64_t now = sc_now(), end = now + INT64_C(10000000000);
104 while (now >= 0 && now < end) {
105 struct timespec value;
107 int result = sched_rr_get_interval(tid, &value);
109 return errno == ESRCH ? 0 : -1;
110 if (result != 0 || value.tv_sec != expected_interval->tv_sec ||
111 value.tv_nsec != expected_interval->tv_nsec) {
112 fprintf(stderr,
"retiring task %d returned inconsistent interval\n", tid);
120 fprintf(stderr,
"task retirement timeout tid=%d errno=%d\n", tid, errno);
123int sc_read(
int fd,
void* buffer,
size_t length) {
124 unsigned char* bytes = buffer;
126 struct pollfd ready = {.fd = fd, .events = POLLIN};
129 count = poll(&ready, 1, 10000);
130 while (count < 0 && errno == EINTR);
133 ssize_t n = read(fd, bytes, length);
134 if (n < 0 && errno == EINTR)
143int sc_write(
int fd,
const void* buffer,
size_t length) {
144 const unsigned char* bytes = buffer;
146 ssize_t n = write(fd, bytes, length);
147 if (n < 0 && errno == EINTR)
156int sc_send(
int fd,
char value) {
157 return sc_write(fd, &value, 1);
159int sc_receive(
int fd,
char value) {
161 return sc_read(fd, &actual, 1) || actual != value ? -1 : 0;
163int sc_reap(pid_t pid,
int milliseconds) {
164 int64_t now = sc_now(), end = now + (int64_t)milliseconds * 1000000;
165 while (now >= 0 && now < end) {
167 pid_t result = waitpid(pid, &status, WNOHANG);
169 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
170 if (result < 0 && errno != EINTR)
172 struct timespec delay = {0, 5000000};
173 nanosleep(&delay, NULL);
177 while (waitpid(pid, NULL, 0) < 0 && errno == EINTR) {
181int sc_spawn(
struct sc_peer* peer,
int (*body)(
int,
int,
void*),
void* argument) {
182 int command[2], report[2];
195 int result = body(command[0], report[1], argument);
197 _exit(result ? 1 : 0);
206 *peer = (
struct sc_peer){pid, command[1], report[0]};
209int sc_join(
struct sc_peer* peer) {
210 int result = sc_reap(peer->pid, 15000);
212 fprintf(stderr,
"scheduling child pid=%d status=%d\n", peer->pid, result);
217void sc_cleanup(
struct sc_peer* peer) {
219 kill(peer->pid, SIGKILL);
220 sc_reap(peer->pid, 1000);
222 if (peer->command >= 0)
223 close(peer->command);
224 if (peer->report >= 0)
226 *peer = (
struct sc_peer)SC_PEER_INITIALIZER;