Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lkl: automatic syscall threads creation #97

Merged
1 commit merged into from Mar 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 132 additions & 87 deletions arch/lkl/kernel/syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@ static struct syscall_thread_data {
int irq;
/* to be accessed from Linux context only */
wait_queue_head_t wqh;
struct list_head list;
bool stop;
struct completion stopped;
} default_syscall_thread_data;

static LIST_HEAD(syscall_threads);

static struct syscall *dequeue_syscall(struct syscall_thread_data *data)
{

Expand Down Expand Up @@ -69,6 +74,8 @@ static irqreturn_t syscall_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}

static int __lkl_stop_syscall_thread(struct syscall_thread_data *data,
bool host);
int syscall_thread(void *_data)
{
struct syscall_thread_data *data;
Expand All @@ -78,6 +85,8 @@ int syscall_thread(void *_data)

data = (struct syscall_thread_data *)_data;
init_waitqueue_head(&data->wqh);
list_add(&data->list, &syscall_threads);
init_completion(&data->stopped);

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

Expand Down Expand Up @@ -105,21 +114,40 @@ int syscall_thread(void *_data)
lkl_ops->sem_up(data->completion);

while (1) {
wait_event(data->wqh, (s = dequeue_syscall(data)) != NULL);
wait_event(data->wqh,
(s = dequeue_syscall(data)) != NULL || data->stop);

if (s->no == __NR_reboot)
if (data->stop || s->no == __NR_reboot)
break;

run_syscall(s);

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

if (data == &default_syscall_thread_data) {
struct syscall_thread_data *i = NULL, *aux;

list_for_each_entry_safe(i, aux, &syscall_threads, list) {
if (i == &default_syscall_thread_data)
continue;
__lkl_stop_syscall_thread(i, false);
}
}

pr_info("lkl: exiting syscall thread %s\n", current->comm);

list_del(&data->list);

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

s->ret = 0;
lkl_ops->sem_up(data->completion);
if (data->stop) {
complete(&data->stopped);
} else {
s->ret = 0;
lkl_ops->sem_up(data->completion);
}

return 0;
}
Expand All @@ -129,8 +157,6 @@ static unsigned int syscall_thread_data_key;
static int syscall_thread_data_init(struct syscall_thread_data *data,
void *completion)
{
data->s = NULL;

data->mutex = lkl_ops->sem_alloc(1);
if (!data->mutex)
return -ENOMEM;
Expand All @@ -141,136 +167,158 @@ static int syscall_thread_data_init(struct syscall_thread_data *data,
data->completion = completion;
if (!data->completion) {
lkl_ops->sem_free(data->mutex);
data->mutex = NULL;
return -ENOMEM;
}

return 0;
}

static struct syscall_thread_data *lkl_syscall_data(void) {
struct syscall_thread_data *data = NULL;

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

return data;
}

static int lkl_syscall_wouldblock(void) {
struct syscall_thread_data *data = lkl_syscall_data();

static int lkl_syscall_wouldblock(struct syscall_thread_data *data)
{
if (!lkl_ops->sem_get)
return 0;

return !lkl_ops->sem_get(data->mutex);
}

long lkl_syscall(long no, long *params)
static long __lkl_syscall(struct syscall_thread_data *data, long no,
long *params)
{
struct syscall_thread_data *data = lkl_syscall_data();
struct syscall s;

s.no = no;
s.params = params;

if (lkl_syscall_wouldblock(data))
lkl_puts("syscall would block");

lkl_ops->sem_down(data->mutex);
data->s = &s;
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)
static struct syscall_thread_data *__lkl_create_syscall_thread(void)
{
struct syscall_thread_data *data;
long params[6], ret;

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

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

memset(data, 0, sizeof(*data));

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

if (lkl_syscall_wouldblock() && lkl_ops->print)
lkl_puts("lkl: lkl_create_syscall_thread will block\n");
ret = lkl_ops->tls_set(syscall_thread_data_key, data);
if (ret < 0)
goto out_free;

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;
}
ret = __lkl_syscall(&default_syscall_thread_data,
__NR_create_syscall_thread, params);
if (ret < 0)
goto out_free;

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;
}
return data;

__sync_fetch_and_add(&syscall_threads, 1);
out_free:
lkl_ops->sem_free(data->completion);
lkl_ops->sem_free(data->mutex);
lkl_ops->mem_free(data);

return 0;
return ERR_PTR(ret);
}

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

if (!lkl_ops->tls_get || !lkl_ops->tls_set)
return -ENOTSUPP;
if (IS_ERR(data))
return PTR_ERR(data);
return 0;
}

data = lkl_ops->tls_get(syscall_thread_data_key);
if (!data || data == &default_syscall_thread_data)
return -EINVAL;
static int kernel_stop_syscall_thread(struct syscall_thread_data *data)
{
data->stop = true;
wake_up(&data->wqh);
wait_for_completion(&data->stopped);

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

static int __lkl_stop_syscall_thread(struct syscall_thread_data *data,
bool host)
{
long ret, params[6];

ret = lkl_ops->tls_set(syscall_thread_data_key, NULL);
if (host)
ret = __lkl_syscall(data, __NR_reboot, params);
else
ret = kernel_stop_syscall_thread(data);
if (ret)
return ret;

params[0] = 0;
params[3] = WEXITED;
ret = lkl_syscall(__NR_waitid, params);
if (ret < 0)
return ret;
lkl_ops->sem_free(data->completion);
lkl_ops->sem_free(data->mutex);
lkl_ops->mem_free(data);

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

int lkl_stop_syscall_thread(void)
{
struct syscall_thread_data *data = NULL;

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

return __lkl_stop_syscall_thread(data, true);
}

static int auto_syscall_threads = true;
static int __init setup_auto_syscall_threads(char *str)
{
get_option (&str, &auto_syscall_threads);

return 1;
}
__setup("lkl_auto_syscall_threads=", setup_auto_syscall_threads);


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

if (auto_syscall_threads && lkl_ops->tls_get) {
data = lkl_ops->tls_get(syscall_thread_data_key);
if (!data) {
data = __lkl_create_syscall_thread();
if (!data)
lkl_puts("failed to create syscall thread\n");
}
}
if (!data || no == __NR_reboot)
data = &default_syscall_thread_data;

return __lkl_syscall(data, no, params);
}

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 @@ -291,7 +339,7 @@ sys_create_syscall_thread(struct syscall_thread_data *data)
pid_t pid;

pid = kernel_thread(syscall_thread, data, CLONE_VM | CLONE_FS |
CLONE_FILES | SIGCHLD);
CLONE_FILES | CLONE_THREAD | CLONE_SIGHAND | SIGCHLD);
if (pid < 0)
return pid;

Expand All @@ -310,18 +358,15 @@ int initial_syscall_thread(void *sem)
init_pid_ns.child_reaper = 0;

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

ret = syscall_thread(&default_syscall_thread_data);
if (lkl_ops->tls_free) {

out:
if (lkl_ops->tls_free)
lkl_ops->tls_free(syscall_thread_data_key);
__sync_synchronize();
BUG_ON(syscall_threads);
}


return ret;
}
Expand Down
2 changes: 1 addition & 1 deletion tools/lkl/include/lkl.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ int lkl_create_syscall_thread(void);
*
* Stop the system call thread associated with this host thread, if any.
*/
int lkl_stop_syscall_thread();
int lkl_stop_syscall_thread(void);

#ifdef __cplusplus
}
Expand Down
14 changes: 0 additions & 14 deletions tools/lkl/tests/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -672,21 +672,11 @@ static void test_thread(void *data)
char tmp[LKL_PIPE_BUF+1];
int ret;

ret = lkl_create_syscall_thread();
if (ret < 0) {
fprintf(stderr, "%s: %s\n", __func__, lkl_strerror(ret));
}

ret = lkl_sys_read(pipe_fds[0], tmp, sizeof(tmp));
if (ret < 0) {
fprintf(stderr, "%s: %s\n", __func__, lkl_strerror(ret));
}

ret = lkl_stop_syscall_thread();
if (ret < 0) {
fprintf(stderr, "%s: %s %d\n", __func__, lkl_strerror(ret), ret);
}

}

static int test_syscall_thread(char *str, int len)
Expand All @@ -713,8 +703,6 @@ static int test_syscall_thread(char *str, int len)
return TEST_FAILURE;
}

sleep(1);

ret = lkl_sys_write(pipe_fds[1], tmp, sizeof(tmp));
if (ret != sizeof(tmp)) {
if (ret < 0)
Expand All @@ -724,8 +712,6 @@ static int test_syscall_thread(char *str, int len)
return TEST_FAILURE;
}

sleep(1);

return TEST_SUCCESS;
}

Expand Down