Skip to content

Commit

Permalink
lkl: add support for multiple system call threads
Browse files Browse the repository at this point in the history
One of the current limitation of LKL is that all system calls are
serialized which limits the usability of LKL with threading applications.

As an example, lets take an application that waits for data on a
network socket using a blocking call to read() and occasionally
write()s data from another application thread. Once the read() syscall
has been issued, no write()s can be performed until the socket
receives some data and returns from the read().

This patch adds support for multiple system call threads so that the
application can issue multiple system calls. Note that only one system
call will be executed (i.e. LKL is still non SMP) at a time, but if
one system call blocks, another one can be executed.

In order to do so, the application must issue
lkl_create_syscall_thread from the context of a host thread. A new
kernel thread will be created and all subsequent system calls from the
host thread will be queued to the newly created kernel thread.

All the host threads that call lkl_create_syscall_thread must call
lkl_stop_syscall_thread and this must happen before lkl_halt is
called.

Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
  • Loading branch information
Octavian Purdila committed Feb 23, 2016
1 parent 84ec4e0 commit 2fa4b13
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 46 deletions.
3 changes: 2 additions & 1 deletion arch/lkl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ endif

LDFLAGS_vmlinux += -r
LKL_ENTRY_POINTS := lkl_start_kernel lkl_sys_halt lkl_syscall lkl_trigger_irq \
lkl_get_free_irq lkl_put_irq
lkl_get_free_irq lkl_put_irq lkl_create_syscall_thread \
lkl_stop_syscall_thread

core-y += arch/lkl/kernel/

Expand Down
2 changes: 1 addition & 1 deletion arch/lkl/include/asm/syscalls.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef _ASM_LKL_SYSCALLS_H
#define _ASM_LKL_SYSCALLS_H

int run_syscalls(void);
int initial_syscall_thread(void *);
long lkl_syscall(long no, long *params);

#define sys_mmap sys_ni_syscall
Expand Down
4 changes: 4 additions & 0 deletions arch/lkl/include/asm/unistd.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include <uapi/asm/unistd.h>

#define __NR_create_syscall_thread __NR_arch_specific_syscall

__SYSCALL(__NR_create_syscall_thread, sys_create_syscall_thread)

#define __SC_ASCII(t, a) #t "," #a

#define __ASCII_MAP0(m,...)
Expand Down
10 changes: 10 additions & 0 deletions arch/lkl/include/uapi/asm/host_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ struct lkl_sem_t;
* thread handle or NULL if the thread could not be created
* @thread_exit - terminates the current thread
*
* @tls_alloc - allocate a thread local storage key; returns 0 if succesful
* @tls_free - frees a thread local storage key; returns 0 if succesful
* @tls_set - associate data to the thread local storage key; returns 0 if succesful
* @tls_get - return data associated with the thread local storage key or NULL on error
*
* @mem_alloc - allocate memory
* @mem_free - free memory
*
Expand Down Expand Up @@ -70,6 +75,11 @@ struct lkl_host_operations {
int (*thread_create)(void (*f)(void *), void *arg);
void (*thread_exit)(void);

int (*tls_alloc)(unsigned int *key);
int (*tls_free)(unsigned int key);
int (*tls_set)(unsigned int key, void *data);
void *(*tls_get)(unsigned int key);

void* (*mem_alloc)(unsigned long);
void (*mem_free)(void *);

Expand Down
8 changes: 1 addition & 7 deletions arch/lkl/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,10 @@ void __init setup_arch(char **cl)

int run_init_process(const char *init_filename)
{
lkl_ops->sem_up(init_sem);

run_syscalls();
initial_syscall_thread(init_sem);

kernel_halt();

/* We want to kill init without panic()ing */
init_pid_ns.child_reaper = 0;
do_exit(0);

return 0;
}

Expand Down
233 changes: 196 additions & 37 deletions arch/lkl/kernel/syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
#include <linux/net.h>
#include <linux/task_work.h>
#include <linux/syscalls.h>
#include <linux/kthread.h>
#include <asm/host_ops.h>
#include <asm/syscalls.h>

struct syscall_thread_data;
static asmlinkage long sys_create_syscall_thread(struct syscall_thread_data *);

typedef long (*syscall_handler_t)(long arg1, ...);

#undef __SYSCALL
Expand All @@ -23,15 +27,15 @@ syscall_handler_t syscall_table[__NR_syscalls] = {

struct syscall {
long no, *params, ret;
void *sem;
};

static struct syscall_thread_data {
wait_queue_head_t wqh;
struct syscall *s;
void *mutex, *completion;
} syscall_thread_data;

int irq;
/* to be accessed from Linux context only */
wait_queue_head_t wqh;
} default_syscall_thread_data;

static struct syscall *dequeue_syscall(struct syscall_thread_data *data)
{
Expand All @@ -53,19 +57,52 @@ static long run_syscall(struct syscall *s)

task_work_run();

if (s->sem)
lkl_ops->sem_up(s->sem);
return ret;
}

int run_syscalls(void)
static irqreturn_t syscall_irq_handler(int irq, void *dev_id)
{
struct syscall_thread_data *data = (struct syscall_thread_data *)dev_id;

wake_up(&data->wqh);

return IRQ_HANDLED;
}

int syscall_thread(void *_data)
{
struct syscall_thread_data *data = &syscall_thread_data;
struct syscall_thread_data *data;
struct syscall *s;
int ret;
static int count;

data = (struct syscall_thread_data *)_data;
init_waitqueue_head(&data->wqh);

snprintf(current->comm, sizeof(current->comm), "ksyscalld%d", count++);

current->flags &= ~PF_KTHREAD;
data->irq = lkl_get_free_irq("syscall");
if (data->irq < 0) {
pr_err("lkl: %s: failed to allocate irq: %d\n", __func__,
data->irq);
return data->irq;
}

ret = request_irq(data->irq, syscall_irq_handler, 0, current->comm,
data);
if (ret) {
pr_err("lkl: %s: failed to request irq %d: %d\n", __func__,
data->irq, ret);
lkl_put_irq(data->irq, "syscall");
data->irq = -1;
return ret;
}

snprintf(current->comm, sizeof(current->comm), "init");
pr_info("lkl: syscall thread %s initialized (irq%d)\n", current->comm,
data->irq);

/* system call thread is ready */
lkl_ops->sem_up(data->completion);

while (1) {
wait_event(data->wqh, (s = dequeue_syscall(data)) != NULL);
Expand All @@ -74,48 +111,146 @@ int run_syscalls(void)
break;

run_syscall(s);

lkl_ops->sem_up(data->completion);
}

free_irq(data->irq, data);
lkl_put_irq(data->irq, "syscall");

s->ret = 0;
lkl_ops->sem_up(s->sem);
lkl_ops->sem_up(data->completion);

return 0;
}

static irqreturn_t syscall_irq_handler(int irq, void *dev_id)
{
wake_up(&syscall_thread_data.wqh);
static unsigned int syscall_thread_data_key;

return IRQ_HANDLED;
}
static int syscall_thread_data_init(struct syscall_thread_data *data,
void *completion)
{
data->mutex = lkl_ops->sem_alloc(1);
if (!data->mutex)
return -ENOMEM;

static struct irqaction syscall_irqaction = {
.handler = syscall_irq_handler,
.flags = IRQF_NOBALANCING,
.dev_id = &syscall_irqaction,
.name = "syscall"
};
if (!completion)
data->completion = lkl_ops->sem_alloc(0);
else
data->completion = completion;
if (!data->completion) {
lkl_ops->sem_free(data->mutex);
return -ENOMEM;
}

static int syscall_irq;
return 0;
}

long lkl_syscall(long no, long *params)
{
struct syscall_thread_data *data = &syscall_thread_data;
struct syscall_thread_data *data = NULL;
struct syscall s;

if (lkl_ops->tls_get)
data = lkl_ops->tls_get(syscall_thread_data_key);
if (!data)
data = &default_syscall_thread_data;

s.no = no;
s.params = params;
s.sem = data->completion;

lkl_ops->sem_down(data->mutex);
data->s = &s;
lkl_trigger_irq(syscall_irq);
lkl_trigger_irq(data->irq);
lkl_ops->sem_down(data->completion);
lkl_ops->sem_up(data->mutex);

if (no == __NR_reboot) {
lkl_ops->sem_free(data->completion);
lkl_ops->sem_free(data->mutex);
if (data != &default_syscall_thread_data)
lkl_ops->mem_free(data);
}

return s.ret;
}

static int syscall_threads;

int lkl_create_syscall_thread(void)
{
struct syscall_thread_data *data;
long params[6], ret;

if (!lkl_ops->tls_set)
return -ENOTSUPP;

data = lkl_ops->mem_alloc(sizeof(*data));
if (!data)
return -ENOMEM;

ret = syscall_thread_data_init(data, NULL);
if (ret < 0) {
lkl_ops->mem_free(data);
return ret;
}

params[0] = (long)data;
ret = lkl_syscall(__NR_create_syscall_thread, params);
if (ret < 0) {
lkl_ops->sem_free(data->completion);
lkl_ops->sem_free(data->mutex);
lkl_put_irq(data->irq, "syscall");
lkl_ops->mem_free(data);
return ret;
}

lkl_ops->sem_down(data->completion);

ret = lkl_ops->tls_set(syscall_thread_data_key, data);
if (ret < 0) {
lkl_ops->sem_free(data->completion);
lkl_ops->sem_free(data->mutex);
lkl_put_irq(data->irq, "syscall");
lkl_ops->mem_free(data);
return ret;
}

__sync_fetch_and_add(&syscall_threads, 1);

return 0;
}

int lkl_stop_syscall_thread(void)
{
struct syscall_thread_data *data;
long params[6] = { 0, };
int ret;

if (!lkl_ops->tls_get || !lkl_ops->tls_set)
return -ENOTSUPP;

data = lkl_ops->tls_get(syscall_thread_data_key);
if (!data || data == &default_syscall_thread_data)
return -EINVAL;

ret = lkl_syscall(__NR_reboot, params);
if (ret < 0)
return ret;

ret = lkl_ops->tls_set(syscall_thread_data_key, NULL);
if (ret)
return ret;

params[0] = 0;
params[3] = WEXITED;
ret = lkl_syscall(__NR_waitid, params);
if (ret < 0)
return ret;

__sync_fetch_and_sub(&syscall_threads, 1);
return 0;
}

asmlinkage
ssize_t sys_lkl_pwrite64(unsigned int fd, const char *buf, size_t count,
off_t pos_hi, off_t pos_lo)
Expand All @@ -130,19 +265,43 @@ ssize_t sys_lkl_pread64(unsigned int fd, char *buf, size_t count,
return sys_pread64(fd, buf, count, ((loff_t)pos_hi << 32) + pos_lo);
}

int __init syscall_init(void)
static asmlinkage long
sys_create_syscall_thread(struct syscall_thread_data *data)
{
struct syscall_thread_data *data = &syscall_thread_data;

init_waitqueue_head(&data->wqh);
data->mutex = lkl_ops->sem_alloc(1);
data->completion = lkl_ops->sem_alloc(0);
BUG_ON(!data->mutex || !data->completion);
pid_t pid;

syscall_irq = lkl_get_free_irq("syscall");
setup_irq(syscall_irq, &syscall_irqaction);
pid = kernel_thread(syscall_thread, data, CLONE_VM | CLONE_FS |
CLONE_FILES | SIGCHLD);
if (pid < 0)
return pid;

pr_info("lkl: syscall interface initialized (irq%d)\n", syscall_irq);
return 0;
}
late_initcall(syscall_init);

int initial_syscall_thread(void *sem)
{
int ret = 0;

if (lkl_ops->tls_alloc)
ret = lkl_ops->tls_alloc(&syscall_thread_data_key);
if (ret)
return ret;

init_pid_ns.child_reaper = 0;

ret = syscall_thread_data_init(&default_syscall_thread_data, sem);
if (ret) {
lkl_ops->tls_free(syscall_thread_data_key);
return ret;
}

ret = syscall_thread(&default_syscall_thread_data);
if (lkl_ops->tls_alloc) {
lkl_ops->tls_free(syscall_thread_data_key);
__sync_synchronize();
BUG_ON(syscall_threads);
}

return ret;
}

Loading

0 comments on commit 2fa4b13

Please sign in to comment.