You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
6.2 KiB
388 lines
6.2 KiB
#define _GNU_SOURCE |
|
#define _XOPEN_SOURCE (700) |
|
|
|
#include <assert.h> |
|
#include <stdlib.h> |
|
#include <sched.h> |
|
#include <unistd.h> |
|
#include <signal.h> |
|
#include <errno.h> |
|
#include <sys/mman.h> |
|
#include <fcntl.h> |
|
#include <sys/types.h> |
|
#include <sys/wait.h> |
|
#include <pthread.h> |
|
#include <stdbool.h> |
|
#include <linux/futex.h> |
|
#include <stdint.h> |
|
#include <errno.h> |
|
#include <limits.h> |
|
#include <sched.h> |
|
#include <sys/syscall.h> |
|
|
|
#include "time.c" |
|
|
|
#ifdef AFFINITY |
|
#define STR "AFFINITY " |
|
#else // AFFINITY |
|
#define STR "" |
|
#endif // AFFINITY |
|
|
|
#ifndef KERNEL |
|
|
|
#ifndef SHMEM |
|
#error Must define one of SHMEM or KERNEL |
|
#endif // SHMEM |
|
|
|
#ifdef __STDC_NO_ATOMICS__ |
|
#error must have atomics |
|
#endif // __STDC_NO_ATOMICS__ |
|
|
|
#include <stdatomic.h> |
|
|
|
#else //KERNEL |
|
|
|
#ifdef SHMEM |
|
#error Must define only one of SHMEM or KERNEL |
|
#endif // SHMEM |
|
|
|
#endif // KERNEL |
|
|
|
#define LIMIT ((size_t) 100000000) |
|
|
|
#define CONSUMER(p) ((p) == 0) |
|
|
|
#define SIZE (1 << 12) |
|
|
|
#define MASK (SIZE / sizeof(size_t) - 1) |
|
|
|
#ifdef SHMEM |
|
|
|
#define ARRAY_SIZE \ |
|
(SIZE - sizeof(pthread_mutex_t) - sizeof(size_t) - 2 * sizeof(atomic_uint)) |
|
|
|
#define ARRAY_LEN (ARRAY_SIZE / sizeof(size_t)) |
|
|
|
typedef struct shmem |
|
{ |
|
size_t size; |
|
uint32_t sent; |
|
uint32_t read; |
|
char* a; |
|
uint32_t pos; |
|
|
|
} Shmem; |
|
|
|
Shmem s; |
|
|
|
#else // SHMEM |
|
|
|
int in; |
|
int out; |
|
|
|
typedef union size |
|
{ |
|
|
|
size_t s; |
|
char a[sizeof(size_t)]; |
|
|
|
} Size; |
|
|
|
#endif // SHMEM |
|
|
|
#ifdef SHMEM |
|
static void shmem_create(void) |
|
{ |
|
// Our memory buffer will be readable and writable: |
|
int protection = PROT_READ | PROT_WRITE; |
|
|
|
// The buffer will be shared (meaning other processes can access it), but |
|
// anonymous (meaning third-party processes cannot obtain an address for |
|
// it), so only this process and its children will be able to use it: |
|
int visibility = MAP_SHARED | MAP_ANONYMOUS; |
|
|
|
// The remaining parameters to `mmap()` are not important for this use case, |
|
// but the manpage for `mmap` explains their purpose. |
|
s.a = mmap(NULL, SIZE, protection, visibility, -1, 0); |
|
if (s.a == NULL) err("mmap of array failed", 5); |
|
|
|
s.size = SIZE; |
|
s.sent = 0; |
|
s.read = 0; |
|
s.pos = 0; |
|
} |
|
|
|
static void shmem_destroy(Shmem* s) |
|
{ |
|
if (munmap(s->a, SIZE)) err("could not unmap memory", 1); |
|
} |
|
|
|
static inline long futex(uint32_t* uaddr, int futex_op, uint32_t val, |
|
const struct timespec* timeout, |
|
uint32_t* uaddr2, uint32_t val3) |
|
{ |
|
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); |
|
} |
|
|
|
static inline uint32_t do_wait(uint32_t* f, uint32_t val) |
|
{ |
|
long res; |
|
|
|
do { |
|
|
|
errno = 0; |
|
|
|
res = futex(f, FUTEX_WAIT, val, NULL, NULL, 0); |
|
|
|
} while (res == -1 && errno != EAGAIN); |
|
|
|
return *f; |
|
} |
|
|
|
static inline void do_wake(uint32_t* f, uint32_t val) |
|
{ |
|
long res; |
|
|
|
assert(*f != val); |
|
|
|
*f = val; |
|
|
|
res = futex(f, FUTEX_WAKE, INT_MAX, NULL, NULL, 0); |
|
} |
|
|
|
__attribute__((always_inline)) |
|
static inline uint32_t get_val(bool read, uint32_t val) |
|
{ |
|
return do_wait(read ? &s.read : &s.sent, val); |
|
} |
|
|
|
__attribute__((always_inline)) |
|
static inline void set_val(unsigned int val, bool read) |
|
{ |
|
do_wake(read ? &s.read : &s.sent, val); |
|
} |
|
|
|
__attribute__((always_inline)) |
|
static inline uint32_t* get_write_ptr(void) |
|
{ |
|
return ((uint32_t*) s.a + ((s.pos + sizeof(uint32_t)) & MASK)); |
|
} |
|
|
|
__attribute__((always_inline)) |
|
static inline uint32_t* get_read_ptr(void) |
|
{ |
|
return ((uint32_t*) s.a + (s.pos & MASK)); |
|
} |
|
|
|
__attribute__((always_inline)) |
|
static inline size_t* get_data_ptr(void) |
|
{ |
|
return ((size_t*) s.a + ((s.pos + 2 * sizeof(uint32_t)) & MASK)); |
|
} |
|
#endif // SHMEM |
|
|
|
static size_t receive(unsigned int* rd) |
|
{ |
|
size_t idx; |
|
|
|
#ifdef KERNEL |
|
|
|
Size s; |
|
ssize_t r; |
|
|
|
r = read(in, s.a, sizeof(size_t)); |
|
if (r != sizeof(size_t) && r != 0) err("could not read", 13); |
|
|
|
idx = s.s; |
|
|
|
#else // KERNEL |
|
|
|
unsigned int val; |
|
|
|
uint32_t* read_ptr; |
|
uint32_t* read_ptr2; |
|
uint32_t* write_ptr; |
|
size_t* data_ptr; |
|
|
|
read_ptr = get_read_ptr(); |
|
write_ptr = get_write_ptr(); |
|
data_ptr = get_data_ptr(); |
|
|
|
s.pos += 2 * sizeof(uint32_t) + sizeof(size_t); |
|
|
|
do_wait(write_ptr, 0); |
|
|
|
idx = *data_ptr; |
|
|
|
*rd += 1; |
|
|
|
do_wake(read_ptr, 8); |
|
|
|
#endif // KERNEL |
|
|
|
return idx; |
|
} |
|
|
|
static void send(size_t idx, unsigned int* st) |
|
{ |
|
#ifdef KERNEL |
|
|
|
Size s; |
|
ssize_t w; |
|
|
|
*st += 1; |
|
|
|
s.s = idx; |
|
|
|
errno = 0; |
|
w = write(out, s.a, sizeof(size_t)); |
|
if (w != sizeof(size_t)) err("could not write", 14); |
|
|
|
#else // KERNEL |
|
|
|
uint32_t* read_ptr; |
|
uint32_t* read_ptr2; |
|
uint32_t* write_ptr; |
|
uint32_t* write_ptr2; |
|
size_t* data_ptr; |
|
|
|
read_ptr = get_read_ptr(); |
|
write_ptr = get_write_ptr(); |
|
data_ptr = get_data_ptr(); |
|
|
|
*data_ptr = idx + 1; |
|
|
|
s.pos += 2 * sizeof(uint32_t) + sizeof(size_t); |
|
|
|
read_ptr2 = get_read_ptr(); |
|
write_ptr2 = get_write_ptr(); |
|
|
|
*read_ptr2 = 0; |
|
*write_ptr2 = 0; |
|
|
|
*st += 1; |
|
|
|
do_wake(write_ptr, 8); |
|
do_wait(read_ptr, 0); |
|
|
|
#endif // KERNEL |
|
} |
|
|
|
int main(int argc, char* argv[]) |
|
{ |
|
size_t i, sum = 0; |
|
unsigned int processed = 0; |
|
cpu_set_t set; |
|
pid_t pid; |
|
int pipefds[2]; |
|
TimeSpec start, end, diff; |
|
int status; |
|
|
|
nice(-20); |
|
|
|
#ifdef SHMEM |
|
|
|
shmem_create(); |
|
|
|
#else |
|
|
|
signal(SIGPIPE, SIG_IGN); |
|
|
|
if (pipe2(pipefds, 0) < 0) |
|
{ |
|
err("couldn't create pipe", 8); |
|
} |
|
|
|
#endif // SHMEM |
|
|
|
pid = fork(); |
|
|
|
if (pid < 0) |
|
{ |
|
err("fork failed", 1); |
|
} |
|
|
|
#ifdef KERNEL |
|
|
|
if (pid == 0) |
|
{ |
|
close(pipefds[1]); |
|
in = pipefds[0]; |
|
} |
|
else |
|
{ |
|
close(pipefds[0]); |
|
out = pipefds[1]; |
|
} |
|
|
|
#endif // KERNEL |
|
|
|
#ifdef AFFINITY |
|
CPU_ZERO(&set); |
|
|
|
if (CONSUMER(pid)) CPU_SET(1, &set); |
|
else CPU_SET(0, &set); |
|
|
|
if (sched_setaffinity(0, sizeof(cpu_set_t), &set) < 0) |
|
{ |
|
err("could not set cpu affinity", 2); |
|
} |
|
#endif // AFFINITY |
|
|
|
if (!CONSUMER(pid)) ts_time(&start); |
|
|
|
if (CONSUMER(pid)) |
|
{ |
|
for (i = 0; i < LIMIT; ++i) |
|
{ |
|
sum += receive(&processed); |
|
} |
|
|
|
#ifndef NDEBUG |
|
#ifdef KERNEL |
|
printf("KERNEL Sum: %zu\n", sum); |
|
#else // KERNEL |
|
printf("SHMEM Sum: %zu\n", sum); |
|
#endif // KERNEL |
|
#endif // NDEBUG |
|
|
|
if (sum != ((LIMIT * (LIMIT + 1)) / 2)) err("wrong sum", 15); |
|
} |
|
else |
|
{ |
|
for (i = 0; i < LIMIT; ++i) |
|
{ |
|
send(i, &processed); |
|
} |
|
} |
|
|
|
if (!CONSUMER(pid)) |
|
{ |
|
waitpid(pid, &status, 0); |
|
|
|
ts_time(&end); |
|
|
|
if (WEXITSTATUS(status) != 0) err("child exited bad", 11); |
|
|
|
diff = ts_diff(end, start); |
|
|
|
#ifdef KERNEL |
|
printf(STR "KERNEL: "); |
|
#else // KERNEL |
|
printf(STR "SHMEM: "); |
|
#endif |
|
|
|
ts_print(diff, LIMIT); |
|
|
|
printf("\n"); |
|
} |
|
|
|
#ifdef KERNEL |
|
if (CONSUMER(pid)) close(in); |
|
else close(out); |
|
#else // KERNEL |
|
shmem_destroy(&s); |
|
#endif // KERNEL |
|
|
|
return 0; |
|
}
|
|
|