Skip to content

Commit

Permalink
Add basic futex implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Apr 6, 2018
1 parent 498ecfe commit b9c0f6a
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 3 deletions.
2 changes: 1 addition & 1 deletion emu/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static __no_instrument void set_dirty_page(struct mem *mem, page_t page) {
mem->dirty_page = page;
}

char *mem_ptr(struct mem *mem, addr_t addr, int type) {
void *mem_ptr(struct mem *mem, addr_t addr, int type) {
page_t page = PAGE(addr);
struct pt_entry *entry = &mem->pt[page];

Expand Down
2 changes: 1 addition & 1 deletion emu/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ int pt_copy_on_write(struct mem *src, page_t src_start, struct mem *dst, page_t

#define MEM_READ 0
#define MEM_WRITE 1
char *mem_ptr(struct mem *mem, addr_t addr, int type);
void *mem_ptr(struct mem *mem, addr_t addr, int type);

extern size_t real_page_size;

Expand Down
1 change: 1 addition & 0 deletions kernel/calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ syscall_t syscall_table[] = {
[228] = (syscall_t) sys_fsetxattr,
[238] = (syscall_t) sys_tkill,
[239] = (syscall_t) sys_sendfile64,
[240] = (syscall_t) sys_futex,
[243] = (syscall_t) sys_set_thread_area,
[252] = (syscall_t) sys_exit_group,
[258] = (syscall_t) sys_set_tid_address,
Expand Down
3 changes: 2 additions & 1 deletion kernel/calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ struct sys_info {
};
dword_t sys_sysinfo(addr_t info_addr);

// time
// futexes
dword_t sys_futex(addr_t uaddr, dword_t op, dword_t val);

// crap that ideally shouldn't exist
struct pollfd_ {
Expand Down
115 changes: 115 additions & 0 deletions kernel/futex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "kernel/calls.h"

struct futex {
atomic_uint refcount;
struct mem *mem;
addr_t addr;
lock_t lock;
pthread_cond_t cond;
struct list chain; // locked by futex_hash_lock
};

#define FUTEX_HASH_BITS 12
#define FUTEX_HASH_SIZE (1 << FUTEX_HASH_BITS)
static lock_t futex_hash_lock;
static struct list futex_hash[FUTEX_HASH_SIZE];

static void __attribute__((constructor)) init_futex_hash() {
for (int i = 0; i < FUTEX_HASH_SIZE; i++)
list_init(&futex_hash[i]);
}

// returns the futex for the current process at the given addr, and locks it
static struct futex *futex_get(addr_t addr) {
int hash = (addr ^ (unsigned long) current->cpu.mem) % FUTEX_HASH_SIZE;
lock(&futex_hash_lock);
struct list *bucket = &futex_hash[hash];
struct futex *futex;
list_for_each_entry(bucket, futex, chain) {
if (futex->addr == addr) {
futex->refcount++;
goto have_futex;
}
}

futex = malloc(sizeof(struct futex));
if (futex == NULL)
return NULL;
futex->refcount = 0;
futex->mem = current->cpu.mem;
futex->addr = addr;
lock_init(&futex->lock);
pthread_cond_init(&futex->cond, NULL);
list_add(bucket, &futex->chain);

have_futex:
lock(&futex->lock);
unlock(&futex_hash_lock);
return futex;
}

// must be called on the result of futex_get when you're done with it
static void futex_put(struct futex *futex) {
unlock(&futex->lock);
if (--futex->refcount == 0) {
lock(&futex_hash_lock);
list_remove(&futex->chain);
unlock(&futex_hash_lock);
}
}

static int futex_load(struct futex *futex, dword_t *out) {
dword_t *ptr = mem_ptr(futex->mem, futex->addr, MEM_READ);
if (ptr == NULL)
return 1;
*out = *ptr;
return 0;
}

int futex_wait(addr_t uaddr, dword_t val) {
struct futex *futex = futex_get(uaddr);
int err = 0;
dword_t tmp;
if (futex_load(futex, &tmp))
err = _EFAULT;
else if (tmp != val)
err = _EAGAIN;
else
pthread_cond_wait(&futex->cond, &futex->lock);
futex_put(futex);
return err;
}

int futex_wake(addr_t uaddr, dword_t val) {
struct futex *futex = futex_get(uaddr);
if (val == 1)
pthread_cond_signal(&futex->cond);
else if (val != 0x7fffffff)
pthread_cond_broadcast(&futex->cond);
else
TODO("invalid number of futex wakes %d", val);
futex_put(futex);
return val; // FIXME wrong if val is INT_MAX
}

#define FUTEX_WAIT_ 0
#define FUTEX_WAKE_ 1
#define FUTEX_PRIVATE_FLAG_ 128
#define FUTEX_CMD_MASK_ ~(FUTEX_PRIVATE_FLAG_)

dword_t sys_futex(addr_t uaddr, dword_t op, dword_t val) {
if (!(op & FUTEX_PRIVATE_FLAG_)) {
FIXME("no support for shared futexes");
return _ENOSYS;
}
switch (op & FUTEX_CMD_MASK_) {
case FUTEX_WAIT_:
STRACE("futex_wait(0x%x, %d)", uaddr, val);
return futex_wait(uaddr, val);
case FUTEX_WAKE_:
STRACE("futex_wake(0x%x, %d)", uaddr, val);
return futex_wake(uaddr, val);
}
FIXME("unsupported futex operation %d", op);
return _ENOSYS;
}
1 change: 1 addition & 0 deletions kernel/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ int task_set_thread_area(struct task *task, addr_t u_info) {
}

int sys_set_thread_area(addr_t u_info) {
STRACE("set_thread_area(0x%x)", u_info);
return task_set_thread_area(current, u_info);
}

Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ src = [
'kernel/mmap.c',
'kernel/uname.c',
'kernel/tls.c',
'kernel/futex.c',
'kernel/getset.c',
'kernel/signal.c',
'kernel/resource.c',
Expand Down

0 comments on commit b9c0f6a

Please sign in to comment.