1
0
Fork 0
A test of the feasibility of hardware pipes.
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

#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;
}