The Pedigree Project 0.1
scheduling-contract-test/lifecycle.c
1#define _GNU_SOURCE
2#include <signal.h>
3#include <stdlib.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/syscall.h>
8
9struct inheritance {
10 int cpu;
11 pid_t tid;
12};
13static void* inherited_entry(void* argument) {
14 struct inheritance* state = argument;
15 state->tid = (pid_t)syscall(SYS_gettid);
16 int policy;
17 struct sched_param param;
18 return (void*)(intptr_t)(sc_mask(0, state->cpu) || sc_sample(state->cpu) ||
19 pthread_getschedparam(pthread_self(), &policy, &param) ||
20 policy != SCHED_OTHER || param.sched_priority);
21}
22static int check_inherited_thread(int cpu, pid_t* tid) {
23 struct inheritance state = {.cpu = cpu};
24 pthread_t thread;
25 if (pthread_create(&thread, NULL, inherited_entry, &state))
26 return -1;
27 void* result;
28 if (pthread_join(thread, &result) || result)
29 return -1;
30 if (tid)
31 *tid = state.tid;
32 return 0;
33}
34static void* creator_entry(void* argument) {
35 int cpu = *(int*)argument;
36 if (sc_pin(0, cpu) || check_inherited_thread(cpu, NULL))
37 return (void*)1;
38 pid_t child = fork();
39 if (!child)
40 _exit(sc_mask(0, cpu) || sc_sample(cpu) ? 1 : 0);
41 return (void*)(intptr_t)(child < 0 || sc_reap(child, 10000));
42}
43static int inheritance(void) {
44 int failed = 0;
45 CHECK(sc_pin(0, sc_cpus[0]) == 0);
46 pid_t retired;
47 CHECK(check_inherited_thread(sc_cpus[0], &retired) == 0);
48 struct timespec interval;
49 CHECK(sched_rr_get_interval(0, &interval) == 0);
50 CHECK(sc_wait_for_task_retirement(retired, &interval) == 0);
51 cpu_set_t mask;
52 struct sched_param param = {.sched_priority = 0};
53 errno = 0;
54 CHECK(sched_getaffinity(retired, sizeof(mask), &mask) == -1 && errno == ESRCH);
55 errno = 0;
56 CHECK(sc_pin(retired, sc_cpus[0]) == -1 && errno == ESRCH);
57 errno = 0;
58 CHECK(syscall(SYS_sched_getparam, retired, &param) == -1 && errno == ESRCH);
59 errno = 0;
60 CHECK(syscall(SYS_sched_getscheduler, retired) == -1 && errno == ESRCH);
61 errno = 0;
62 CHECK(syscall(SYS_sched_setparam, retired, &param) == -1 && errno == ESRCH);
63 errno = 0;
64 CHECK(syscall(SYS_sched_setscheduler, retired, SCHED_OTHER, &param) == -1 && errno == ESRCH);
65 pthread_t creator;
66 CHECK(pthread_create(&creator, NULL, creator_entry, &sc_cpus[sc_count - 1]) == 0);
67 void* result;
68 CHECK(pthread_join(creator, &result) == 0 && result == NULL);
69 CHECK(sc_mask(0, sc_cpus[0]) == 0 && sc_sample(sc_cpus[0]) == 0);
70out:
71 return failed;
72}
73int sc_exec(int argc, char** argv) {
74 int failed = 0;
75 alarm(15);
76 CHECK(argc == 3);
77 int cpu = atoi(argv[2]);
78 CHECK(cpu >= 0 && cpu < CPU_SETSIZE);
79 CHECK(sc_mask(0, cpu) == 0 && sc_sample(cpu) == 0);
80 CHECK((pid_t)syscall(SYS_gettid) == getpid());
81 CHECK(check_inherited_thread(cpu, NULL) == 0);
82out:
83 return failed;
84}
85static int enter_exec(int cpu) {
86 if (sc_pin(0, cpu))
87 return 1;
88 execl("/applications/scheduling-no-such-image", "absent", (char*)NULL);
89 if (errno != ENOENT || sc_mask(0, cpu) || sc_sample(cpu))
90 return 1;
91 char text[16];
92 snprintf(text, sizeof(text), "%d", cpu);
93 execl(SC_APP, SC_APP, "schedule-exec", text, (char*)NULL);
94 return 1;
95}
96static void* exec_entry(void* argument) {
97 _exit(enter_exec(*(int*)argument));
98}
99static int exec_child(int command, int report, void* argument) {
100 (void)command;
101 (void)report;
102 if (sc_pin(0, sc_cpus[0]))
103 return 1;
104 if (!*(int*)argument)
105 return enter_exec(sc_cpus[sc_count - 1]);
106 pthread_t worker;
107 if (pthread_create(&worker, NULL, exec_entry, &sc_cpus[sc_count - 1]))
108 return 1;
109 for (;;)
110 pause();
111}
112static int exec_inheritance(void) {
113 int failed = 0;
114 struct sc_peer peer = SC_PEER_INITIALIZER;
115 for (int threaded = 0; threaded < 2; ++threaded) {
116 CHECK(sc_spawn(&peer, exec_child, &threaded) == 0);
117 CHECK(sc_join(&peer) == 0);
118 }
119out:
120 sc_cleanup(&peer);
121 return failed;
122}
123
125 atomic_uint ready, start, stop, sample, sampled;
126 pid_t tid;
127 unsigned final_cpu;
128 int failed;
129};
130struct setter {
131 struct competition* state;
132 int reverse, exit_race;
133};
134static void* competition_target(void* argument) {
135 struct competition* state = argument;
136 if (sc_pin(0, sc_cpus[0]))
137 return (void*)1;
138 state->tid = (pid_t)syscall(SYS_gettid);
139 atomic_store_explicit(&state->ready, 1, memory_order_release);
140 while (!atomic_load_explicit(&state->stop, memory_order_acquire)) {
141 if (atomic_load_explicit(&state->sample, memory_order_acquire) &&
142 !atomic_load_explicit(&state->sampled, memory_order_relaxed)) {
143 unsigned cpu, node;
144 state->failed = syscall(SYS_getcpu, &cpu, &node, NULL) || node;
145 state->final_cpu = cpu;
146 atomic_store_explicit(&state->sampled, 1, memory_order_release);
147 }
148 sched_yield();
149 }
150 return NULL;
151}
152static void* competition_setter(void* argument) {
153 struct setter* setter = argument;
154 if (sc_wait(&setter->state->start, 1))
155 return (void*)1;
156 for (int i = 0; i < (setter->exit_race ? 1 : 8); ++i) {
157 int cpu = sc_cpus[(i + setter->reverse) % 2 ? sc_count - 1 : 0];
158 int result = sc_pin(setter->state->tid, cpu);
159 if (result && !(setter->exit_race && errno == ESRCH))
160 return (void*)1;
161 }
162 return NULL;
163}
164static int competing_requests(void) {
165 int failed = 0, target_live = 0, created = 0;
166 struct competition state = {0};
167 pthread_t target, controllers[2];
168 struct setter setters[2] = {{&state, 0, 0}, {&state, 1, 0}};
169 CHECK(pthread_create(&target, NULL, competition_target, &state) == 0);
170 target_live = 1;
171 CHECK(sc_wait(&state.ready, 1) == 0);
172 for (int i = 0; i < 2; ++i) {
173 CHECK(pthread_create(&controllers[i], NULL, competition_setter, &setters[i]) == 0);
174 ++created;
175 }
176 atomic_store_explicit(&state.start, 1, memory_order_release);
177 for (int i = 0; i < created; ++i) {
178 void* result;
179 if (pthread_join(controllers[i], &result) || result)
180 failed = 1;
181 }
182 created = 0;
183 CHECK(!failed);
184 CHECK(sc_pin(state.tid, sc_cpus[sc_count - 1]) == 0);
185 atomic_store_explicit(&state.sample, 1, memory_order_release);
186 CHECK(sc_wait(&state.sampled, 1) == 0 && !state.failed &&
187 state.final_cpu == (unsigned)sc_cpus[sc_count - 1]);
188out:
189 atomic_store_explicit(&state.start, 1, memory_order_release);
190 for (int i = 0; i < created; ++i)
191 pthread_join(controllers[i], NULL);
192 atomic_store_explicit(&state.stop, 1, memory_order_release);
193 if (target_live) {
194 void* result;
195 if (pthread_join(target, &result) || result)
196 failed = 1;
197 }
198 return failed;
199}
200static int exit_requests(void) {
201 int failed = 0;
202 for (int i = 0; i < 4; ++i) {
203 struct competition state = {0};
204 struct setter setter = {&state, 1, 1};
205 pthread_t target, controller;
206 CHECK(pthread_create(&target, NULL, competition_target, &state) == 0);
207 if (sc_wait(&state.ready, 1)) {
208 atomic_store(&state.stop, 1);
209 pthread_join(target, NULL);
210 CHECK(0);
211 }
212 if (pthread_create(&controller, NULL, competition_setter, &setter)) {
213 atomic_store(&state.stop, 1);
214 pthread_join(target, NULL);
215 CHECK(0);
216 }
217 atomic_store_explicit(&state.start, 1, memory_order_release);
218 atomic_store_explicit(&state.stop, 1, memory_order_release);
219 void* changed;
220 void* stopped;
221 int result = pthread_join(controller, &changed);
222 result |= pthread_join(target, &stopped);
223 CHECK(!result && !changed && !stopped);
224 errno = 0;
225 CHECK(sc_pin(state.tid, sc_cpus[0]) == -1 && errno == ESRCH);
226 }
227out:
228 return failed;
229}
230int sc_lifecycle(void) {
231 return inheritance() || exec_inheritance() || competing_requests() || exit_requests();
232}