19#include <pedigree/log.h>
21#include <sys/reboot.h>
23#include <sys/syscall.h>
35 loopback_accept_ready = 1,
36 loopback_receive_ready = 2,
40 loopback_phase_yields = 4096,
41 loopback_blocking_window_yields = 32,
42 compute_probe_sleep_us = 500000,
43 compute_probe_fallback_ns = 2000000000ULL,
44 compute_probe_latest_wake_ns = 1500000000ULL,
50 uintptr_t parent_self;
51 uintptr_t child_self[2];
52 int child_syscall_ok[2];
68static uint64_t monotonic_nanoseconds(
void) {
70 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
73 return ((uint64_t)now.tv_sec * 1000000000ULL) + now.tv_nsec;
76static void* run_compute_preemption_worker(
void* parameter) {
79 __atomic_store_n(&probe->child_self[
worker->index], (uintptr_t)pthread_self(), __ATOMIC_RELEASE);
80 const uint64_t started = monotonic_nanoseconds();
81 __atomic_store_n(&probe->child_syscall_ok[
worker->index], started != 0, __ATOMIC_RELEASE);
82 const uint64_t deadline = started + compute_probe_fallback_ns;
84 while (!__atomic_load_n(&probe->stop, __ATOMIC_ACQUIRE)) {
85 const uint64_t count = __atomic_add_fetch(&probe->counters[
worker->index], 1, __ATOMIC_RELAXED);
86 if ((count & 0x3fff) == 0) {
87 const uint64_t now = monotonic_nanoseconds();
88 if (!now || now >= deadline) {
89 __atomic_store_n(&probe->fallback, 1, __ATOMIC_RELEASE);
98static int run_compute_preemption_test(
void) {
100 memset(&probe, 0,
sizeof(probe));
101 probe.parent_self = (uintptr_t)pthread_self();
106 pthread_t threads[2];
108 if (pthread_create(&threads[0], 0, run_compute_preemption_worker, &workers[0])) {
111 if (pthread_create(&threads[1], 0, run_compute_preemption_worker, &workers[1])) {
112 __atomic_store_n(&probe.stop, 1, __ATOMIC_RELEASE);
113 pthread_join(threads[0], 0);
116 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: PASS pthread-clone-state-switch");
118 const uint64_t started = monotonic_nanoseconds();
119 const int sleep_result = usleep(compute_probe_sleep_us);
120 const uint64_t elapsed = monotonic_nanoseconds() - started;
121 __atomic_store_n(&probe.stop, 1, __ATOMIC_RELEASE);
123 const uintptr_t first_self = __atomic_load_n(&probe.child_self[0], __ATOMIC_ACQUIRE);
124 const uintptr_t second_self = __atomic_load_n(&probe.child_self[1], __ATOMIC_ACQUIRE);
125 const int child_contract = first_self && second_self && first_self != second_self &&
126 first_self != probe.parent_self && second_self != probe.parent_self &&
127 __atomic_load_n(&probe.child_syscall_ok[0], __ATOMIC_ACQUIRE) &&
128 __atomic_load_n(&probe.child_syscall_ok[1], __ATOMIC_ACQUIRE) &&
129 __atomic_load_n(&probe.counters[0], __ATOMIC_ACQUIRE) &&
130 __atomic_load_n(&probe.counters[1], __ATOMIC_ACQUIRE);
131 if (child_contract) {
132 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: PASS pthread-child-tls-args-syscall");
135 const int first_join = pthread_join(threads[0], 0);
136 const int second_join = pthread_join(threads[1], 0);
137 if (!first_join && !second_join) {
138 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: PASS pthread-clear-tid-join");
140 if (sleep_result || first_join || second_join || !child_contract ||
141 __atomic_load_n(&probe.fallback, __ATOMIC_ACQUIRE) ||
142 elapsed > compute_probe_latest_wake_ns) {
149static void* run_detached_exit_worker(
void* parameter) {
151 const long tid = syscall(SYS_set_tid_address, &probe->tid_word);
153 __atomic_store_n(&probe->failure, 1, __ATOMIC_RELEASE);
154 __atomic_store_n(&probe->ready, 1, __ATOMIC_RELEASE);
158 __atomic_store_n(&probe->tid_word, (
int)tid, __ATOMIC_RELEASE);
159 __atomic_store_n(&probe->ready, 1, __ATOMIC_RELEASE);
160 while (!__atomic_load_n(&probe->go, __ATOMIC_ACQUIRE)) {
164 syscall(SYS_exit, 0);
165 __atomic_store_n(&probe->failure, 2, __ATOMIC_RELEASE);
169static int run_detached_clear_tid_test(
void) {
171 memset(&probe, 0,
sizeof(probe));
173 pthread_attr_t attributes;
174 if (pthread_attr_init(&attributes)) {
177 if (pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED)) {
178 pthread_attr_destroy(&attributes);
183 const int create_result = pthread_create(&thread, &attributes, run_detached_exit_worker, &probe);
184 pthread_attr_destroy(&attributes);
189 for (
int i = 0; i < 100000; ++i) {
190 if (__atomic_load_n(&probe.ready, __ATOMIC_ACQUIRE)) {
195 if (!__atomic_load_n(&probe.ready, __ATOMIC_ACQUIRE) ||
196 __atomic_load_n(&probe.failure, __ATOMIC_ACQUIRE)) {
200 __atomic_store_n(&probe.go, 1, __ATOMIC_RELEASE);
201 for (
int attempt = 0; attempt < 3; ++attempt) {
202 const int observed = __atomic_load_n(&probe.tid_word, __ATOMIC_ACQUIRE);
207 const struct timespec timeout = {1, 0};
208 syscall(SYS_futex, &probe.tid_word, 0, observed, &timeout);
211 if (__atomic_load_n(&probe.tid_word, __ATOMIC_ACQUIRE) ||
212 __atomic_load_n(&probe.failure, __ATOMIC_ACQUIRE)) {
220 for (
int i = 0; i < loopback_phase_yields; ++i) {
221 if (__atomic_load_n(&server->phase, __ATOMIC_ACQUIRE) >= phase) {
229static void yield_blocking_window(
void) {
230 for (
int i = 0; i < loopback_blocking_window_yields; ++i) {
235static void shutdown_and_close(
int descriptor) {
236 if (descriptor >= 0) {
237 shutdown(descriptor, SHUT_RDWR);
242static void* run_loopback_server(
void* parameter) {
244 __atomic_store_n(&server->phase, loopback_accept_ready, __ATOMIC_RELEASE);
245 int connection = accept(server->listener, 0, 0);
246 if (connection < 0) {
251 __atomic_store_n(&server->phase, loopback_receive_ready, __ATOMIC_RELEASE);
252 ssize_t received = recv(connection, &server->received, 1, 0);
259 received = recv(connection, &server->received, 1, 0);
260 server->saw_eof = received == 0;
261 if (!server->saw_eof) {
269static int run_loopback_test(
void) {
270 int listener = socket(AF_INET, SOCK_STREAM, 0);
275 struct sockaddr_in address;
276 memset(&address, 0,
sizeof(address));
277 address.sin_family = AF_INET;
279 if (bind(listener, (
struct sockaddr*)&address,
sizeof(address)) != 0) {
284 socklen_t address_length =
sizeof(address);
285 if (getsockname(listener, (
struct sockaddr*)&address, &address_length) != 0 ||
290 if (listen(listener, 1) != 0) {
295 int client = socket(AF_INET, SOCK_STREAM, 0);
302 memset(&server, 0,
sizeof(server));
303 server.listener = listener;
304 pthread_t server_thread;
305 int thread_error = pthread_create(&server_thread, 0, run_loopback_server, &server);
312 if (!wait_for_phase(&server, loopback_accept_ready)) {
313 shutdown_and_close(client);
314 shutdown_and_close(listener);
315 pthread_join(server_thread, 0);
320 yield_blocking_window();
321 if (connect(client, (
struct sockaddr*)&address, address_length) != 0) {
322 shutdown_and_close(client);
323 shutdown_and_close(listener);
324 pthread_join(server_thread, 0);
328 if (!wait_for_phase(&server, loopback_receive_ready)) {
329 shutdown_and_close(client);
330 shutdown_and_close(listener);
331 pthread_join(server_thread, 0);
336 yield_blocking_window();
337 const uint8_t expected = 0xA5;
338 if (send(client, &expected,
sizeof(expected), 0) != (ssize_t)
sizeof(expected) ||
339 shutdown(client, SHUT_WR) != 0) {
340 shutdown_and_close(client);
341 shutdown_and_close(listener);
342 pthread_join(server_thread, 0);
347 if (pthread_join(server_thread, 0) != 0) {
353 if (server.failure || server.received != expected || !server.saw_eof) {
354 return 30 + server.failure;
359static int run_udp_message_test(
void) {
363 int epoll_descriptor = -1;
364 struct sockaddr_in server_address;
365 struct sockaddr_in client_address;
366 socklen_t server_address_length =
sizeof(server_address);
367 socklen_t client_address_length =
sizeof(client_address);
369 memset(&server_address, 0,
sizeof(server_address));
370 server_address.sin_family = AF_INET;
372 server = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
373 if (server < 0 || bind(server, (
struct sockaddr*)&server_address,
sizeof(server_address)) ||
374 getsockname(server, (
struct sockaddr*)&server_address, &server_address_length) ||
375 server_address_length !=
sizeof(server_address) || !server_address.sin_port) {
380 memset(&client_address, 0,
sizeof(client_address));
381 client_address.sin_family = AF_INET;
383 client = socket(AF_INET, SOCK_DGRAM, 0);
384 if (client < 0 || bind(client, (
struct sockaddr*)&client_address,
sizeof(client_address)) ||
385 getsockname(client, (
struct sockaddr*)&client_address, &client_address_length) ||
386 client_address_length !=
sizeof(client_address) || !client_address.sin_port) {
391 epoll_descriptor = epoll_create1(EPOLL_CLOEXEC);
392 struct epoll_event watch;
393 memset(&watch, 0,
sizeof(watch));
394 watch.events = EPOLLIN | EPOLLET;
395 watch.data.fd = server;
396 if (epoll_descriptor < 0 || epoll_ctl(epoll_descriptor, EPOLL_CTL_ADD, server, &watch)) {
401 const uint8_t first_prefix[] = {0x10, 0x11};
402 const uint8_t first_suffix[] = {0x12, 0x13, 0x14, 0x15};
403 struct iovec outgoing_vectors[2] = {
404 {(
void*)first_prefix,
sizeof(first_prefix)},
405 {(
void*)first_suffix,
sizeof(first_suffix)},
407 struct msghdr outgoing;
408 memset(&outgoing, 0,
sizeof(outgoing));
409 outgoing.msg_name = &server_address;
410 outgoing.msg_namelen = server_address_length;
411 outgoing.msg_iov = outgoing_vectors;
412 outgoing.msg_iovlen = 2;
413 if (sendmsg(client, &outgoing, 0) != (ssize_t)(
sizeof(first_prefix) +
sizeof(first_suffix))) {
418 struct epoll_event ready;
419 memset(&ready, 0,
sizeof(ready));
420 if (epoll_wait(epoll_descriptor, &ready, 1, 1000) != 1 || !(ready.events & EPOLLIN) ||
421 ready.data.fd != server) {
426 uint8_t first_received[3] = {0};
427 struct iovec incoming_vectors[2] = {
428 {&first_received[0], 2},
429 {&first_received[2], 1},
431 struct sockaddr_in first_source;
432 memset(&first_source, 0,
sizeof(first_source));
433 struct msghdr incoming;
434 memset(&incoming, 0,
sizeof(incoming));
435 incoming.msg_name = &first_source;
436 incoming.msg_namelen =
sizeof(first_source);
437 incoming.msg_iov = incoming_vectors;
438 incoming.msg_iovlen = 2;
439 if (recvmsg(server, &incoming, 0) != (ssize_t)
sizeof(first_received) ||
440 memcmp(first_received, first_prefix,
sizeof(first_prefix)) || first_received[2] != 0x12 ||
441 !(incoming.msg_flags & MSG_TRUNC) || incoming.msg_namelen !=
sizeof(first_source) ||
442 first_source.sin_family != AF_INET || first_source.sin_port != client_address.sin_port ||
443 first_source.sin_addr.s_addr != client_address.sin_addr.s_addr) {
450 if (recvfrom(server, &extra,
sizeof(extra), 0, 0, 0) != -1 || errno != EAGAIN ||
451 epoll_wait(epoll_descriptor, &ready, 1, 0) != 0) {
456 const uint8_t second_payload[] = {0x20, 0x21, 0x22, 0x23, 0x24};
457 if (sendto(client, second_payload,
sizeof(second_payload), 0, (
struct sockaddr*)&server_address,
458 server_address_length) != (ssize_t)
sizeof(second_payload)) {
462 memset(&ready, 0,
sizeof(ready));
463 if (epoll_wait(epoll_descriptor, &ready, 1, 1000) != 1 || !(ready.events & EPOLLIN) ||
464 ready.data.fd != server) {
469 uint8_t second_received[2] = {0};
470 struct sockaddr_in second_source;
471 memset(&second_source, 0,
sizeof(second_source));
472 socklen_t second_source_length =
sizeof(second_source);
473 if (recvfrom(server, second_received,
sizeof(second_received), MSG_TRUNC,
474 (
struct sockaddr*)&second_source,
475 &second_source_length) != (ssize_t)
sizeof(second_payload) ||
476 memcmp(second_received, second_payload,
sizeof(second_received)) ||
477 second_source_length !=
sizeof(second_source) || second_source.sin_family != AF_INET ||
478 second_source.sin_port != client_address.sin_port ||
479 second_source.sin_addr.s_addr != client_address.sin_addr.s_addr) {
485 if (recvfrom(server, &extra,
sizeof(extra), 0, 0, 0) != -1 || errno != EAGAIN ||
486 epoll_wait(epoll_descriptor, &ready, 1, 0) != 0) {
491 if (epoll_descriptor >= 0) {
492 close(epoll_descriptor);
503int main(
int argc,
char** argv) {
504 const char* stage = argc > 1 ? argv[1] :
"shutdown";
506 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: simple userspace command ran");
507 if (!strcmp(stage,
"command")) {
508 const int preemption_result = run_compute_preemption_test();
509 if (preemption_result) {
510 pedigree_log(LOG_ERR,
"HOSTED-SMOKE: FAIL userspace-compute-preemption: %d",
513 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: PASS userspace-compute-preemption");
516 const int detached_result = run_detached_clear_tid_test();
517 if (detached_result) {
518 pedigree_log(LOG_ERR,
"HOSTED-SMOKE: FAIL pthread-clear-tid-detached: %d", detached_result);
520 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: PASS pthread-clear-tid-detached");
523 const int loopback_result = run_loopback_test();
524 if (loopback_result) {
525 pedigree_log(LOG_ERR,
526 "HOSTED-SMOKE: FAIL posix-lwip-loopback-roundtrip: %d "
528 loopback_result, errno);
530 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: PASS posix-lwip-loopback-roundtrip");
533 const int udp_result = run_udp_message_test();
535 pedigree_log(LOG_ERR,
536 "HOSTED-SMOKE: FAIL posix-lwip-udp-message-semantics: %d "
540 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: PASS posix-lwip-udp-message-semantics");
543 if (!strcmp(stage,
"shutdown")) {
544 pedigree_log(LOG_INFO,
"HOSTED-SMOKE: requesting clean shutdown");
547 if (reboot(0) != 0) {
548 pedigree_log(LOG_ERR,
"HOSTED-SMOKE: shutdown request failed: %d", errno);