The Pedigree Project 0.1
placement.c
1#define _GNU_SOURCE
2#include <string.h>
3#include <unistd.h>
4
5#include "contract.h"
6#include <sys/syscall.h>
7
8static int self_placement(void) {
9 int failed = 0;
10 sc_tls = 0xb1759e2;
11 for (int round = 0; round < 3 * sc_count; ++round) {
12 int cpu = sc_cpus[round % sc_count];
13 cpu_set_t mask;
14 CPU_ZERO(&mask);
15 CPU_SET(cpu, &mask);
16 long result = -1;
17 errno = 271;
18 CHECK(sc_register_syscall(SYS_sched_setaffinity, 0, sizeof(mask), (long)&mask, &result) == 0);
19 CHECK(result == 0 && sc_tls == 0xb1759e2 && errno == 271);
20 CHECK(sc_sample(cpu) == 0 && sc_mask(0, cpu) == 0);
21 sched_yield();
22 CHECK(sc_sample(cpu) == 0 && sc_tls == 0xb1759e2);
23 }
24out:
25 if (sched_setaffinity(0, sizeof(sc_allowed), &sc_allowed))
26 failed = 1;
27 return failed;
28}
30 atomic_uint ready, command, ack, stop;
31 pid_t tid;
32 unsigned cpu, node, identity;
33 int failed;
34};
35static void* running_entry(void* argument) {
36 struct running_peer* peer = argument;
37 peer->tid = (pid_t)syscall(SYS_gettid);
38 sc_tls = peer->identity;
39 errno = (int)peer->identity;
40 atomic_store_explicit(&peer->ready, 1, memory_order_release);
41 unsigned acknowledged = 0;
42 int damaged = 0;
43 while (!atomic_load_explicit(&peer->stop, memory_order_acquire)) {
44 unsigned generation = atomic_load_explicit(&peer->command, memory_order_acquire);
45 unsigned cpu = UINT32_MAX, node = UINT32_MAX;
46 long result = -1;
47 damaged |= sc_register_syscall(SYS_getcpu, (long)&cpu, (long)&node, 0, &result) != 0 ||
48 result || node || sc_tls != peer->identity || errno != (int)peer->identity;
49 if (generation != acknowledged) {
50 peer->cpu = cpu;
51 peer->node = node;
52 peer->failed = damaged;
53 acknowledged = generation;
54 atomic_store_explicit(&peer->ack, generation, memory_order_release);
55 }
56 }
57 return (void*)(intptr_t)damaged;
58}
59static int peer_placement(void) {
60 int failed = 0, created = 0;
61 struct running_peer peers[2] = {{.identity = 731}, {.identity = 829}};
62 pthread_t threads[2];
63 for (int i = 0; i < 2; ++i) {
64 CHECK(pthread_create(&threads[i], NULL, running_entry, &peers[i]) == 0);
65 ++created;
66 CHECK(sc_wait(&peers[i].ready, 1) == 0);
67 CHECK(peers[i].tid > 0 && peers[i].tid != (pid_t)syscall(SYS_gettid));
68 }
69 for (unsigned round = 1; round <= (unsigned)(6 * sc_count); ++round) {
70 int expected[2] = {sc_cpus[round % (unsigned)sc_count],
71 sc_cpus[(unsigned)(sc_count - 1) - round % (unsigned)sc_count]};
72 for (int i = 0; i < 2; ++i) {
73 CHECK(sc_pin(peers[i].tid, expected[i]) == 0);
74 CHECK(sc_mask(peers[i].tid, expected[i]) == 0);
75 // This command follows kernel acknowledgement, so the sample cannot
76 // accidentally describe the old placement before the setter completed.
77 atomic_store_explicit(&peers[i].command, round, memory_order_release);
78 }
79 for (int i = 0; i < 2; ++i) {
80 CHECK(sc_wait(&peers[i].ack, round) == 0);
81 CHECK(!peers[i].failed && peers[i].cpu == (unsigned)expected[i] && !peers[i].node);
82 }
83 }
84 for (int i = 0; i < 2; ++i) {
85 cpu_set_t got;
86 CHECK(pthread_setaffinity_np(threads[i], sizeof(sc_allowed), &sc_allowed) == 0);
87 CHECK(pthread_getaffinity_np(threads[i], sizeof(got), &got) == 0 &&
88 CPU_EQUAL(&got, &sc_allowed));
89 }
90out:
91 for (int i = 0; i < created; ++i)
92 atomic_store_explicit(&peers[i].stop, 1, memory_order_release);
93 for (int i = 0; i < created; ++i) {
94 void* result;
95 if (pthread_join(threads[i], &result) || result)
96 failed = 1;
97 }
98 return failed;
99}
100int sc_placement(void) {
101 return self_placement() || peer_placement();
102}