diff --git a/arch/lkl/Makefile b/arch/lkl/Makefile index cea544d46d408b..2fdf309e2a926b 100644 --- a/arch/lkl/Makefile +++ b/arch/lkl/Makefile @@ -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/ diff --git a/arch/lkl/include/asm/irq.h b/arch/lkl/include/asm/irq.h index e1868c4af92af6..291dd43c3f2301 100644 --- a/arch/lkl/include/asm/irq.h +++ b/arch/lkl/include/asm/irq.h @@ -1,7 +1,7 @@ #ifndef _ASM_LKL_IRQ_H #define _ASM_LKL_IRQ_H -#define NR_IRQS ((int)sizeof(long)) +#define NR_IRQS ((int)sizeof(long) * 8) #include diff --git a/arch/lkl/include/asm/syscalls.h b/arch/lkl/include/asm/syscalls.h index 54e4310bad1a5c..72779c285d4b10 100644 --- a/arch/lkl/include/asm/syscalls.h +++ b/arch/lkl/include/asm/syscalls.h @@ -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 diff --git a/arch/lkl/include/asm/unistd.h b/arch/lkl/include/asm/unistd.h index 234f93d8e8478c..22e435e5577580 100644 --- a/arch/lkl/include/asm/unistd.h +++ b/arch/lkl/include/asm/unistd.h @@ -1,5 +1,9 @@ #include +#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,...) diff --git a/arch/lkl/include/uapi/asm/host_ops.h b/arch/lkl/include/uapi/asm/host_ops.h index 1fe46589433ab4..2697af38527356 100644 --- a/arch/lkl/include/uapi/asm/host_ops.h +++ b/arch/lkl/include/uapi/asm/host_ops.h @@ -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 * @@ -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 *); diff --git a/arch/lkl/kernel/setup.c b/arch/lkl/kernel/setup.c index 3bce11e0b97bf2..77f095283ed5a5 100644 --- a/arch/lkl/kernel/setup.c +++ b/arch/lkl/kernel/setup.c @@ -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; } diff --git a/arch/lkl/kernel/syscalls.c b/arch/lkl/kernel/syscalls.c index cc7decc5c3d18c..7cc5a4358d99cf 100644 --- a/arch/lkl/kernel/syscalls.c +++ b/arch/lkl/kernel/syscalls.c @@ -8,9 +8,13 @@ #include #include #include +#include #include #include +struct syscall_thread_data; +static asmlinkage long sys_create_syscall_thread(struct syscall_thread_data *); + typedef long (*syscall_handler_t)(long arg1, ...); #undef __SYSCALL @@ -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) { @@ -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); @@ -74,48 +111,148 @@ 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) +static unsigned int syscall_thread_data_key; + +static int syscall_thread_data_init(struct syscall_thread_data *data, + void *completion) { - wake_up(&syscall_thread_data.wqh); + data->s = NULL; - return IRQ_HANDLED; -} + 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) @@ -130,19 +267,44 @@ 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) { + if (lkl_ops->tls_free) + lkl_ops->tls_free(syscall_thread_data_key); + return ret; + } + + ret = syscall_thread(&default_syscall_thread_data); + if (lkl_ops->tls_free) { + lkl_ops->tls_free(syscall_thread_data_key); + __sync_synchronize(); + BUG_ON(syscall_threads); + } + + return ret; +} + diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h index 46131c230aaa53..5f343e24e34a9f 100644 --- a/tools/lkl/include/lkl.h +++ b/tools/lkl/include/lkl.h @@ -23,6 +23,7 @@ extern "C" { #define lkl_sys_sendfile lkl_sys_sendfile64 #define lkl_sys_fstatat lkl_sys_fstatat64 #define lkl_sys_fstat lkl_sys_fstat64 +#define lkl_sys_fcntl lkl_sys_fcntl64 #define lkl_statfs lkl_statfs64 @@ -231,6 +232,24 @@ int lkl_netdev_add(union lkl_netdev nd, void *mac); */ int lkl_netdev_get_ifindex(int id); +/** + * lkl_create_syscall_thread - create an additional system call thread + * + * Create a new system call thread. All subsequent system calls issued from this + * host thread are queued to the newly created system call thread. + * + * System call threads must be stopped up by calling @lkl_stop_syscall_thread + * before @lkl_halt is called. + */ +int lkl_create_syscall_thread(void); + +/** + * lkl_stop_syscall_thread - stop the associated system call thread + * + * Stop the system call thread associated with this host thread, if any. + */ +int lkl_stop_syscall_thread(); + #ifdef __cplusplus } #endif diff --git a/tools/lkl/lib/nt-host.c b/tools/lkl/lib/nt-host.c index 6a99ff1f6b911a..f9426b762e44bd 100644 --- a/tools/lkl/lib/nt-host.c +++ b/tools/lkl/lib/nt-host.c @@ -13,7 +13,7 @@ struct lkl_sem_t { HANDLE sem; }; -static void *sem_alloc(int count) +static struct lkl_sem_t *sem_alloc(int count) { struct lkl_sem_t *sem = malloc(sizeof(struct lkl_sem_t)); @@ -75,6 +75,27 @@ static void thread_exit(void) ExitThread(0); } +static int tls_alloc(unsigned int *key) +{ + *key = TlsAlloc(); + return *key == TLS_OUT_OF_INDEXES ? -1 : 0; +} + +static int tls_free(unsigned int key) +{ + return TlsFree(key) ? 0 : -1; +} + +static int tls_set(unsigned int key, void *data) +{ + return TlsSetValue(key, data) ? 0 : -1; +} + +static void *tls_get(unsigned int key) +{ + return TlsGetValue(key); +} + /* * With 64 bits, we can cover about 583 years at a nanosecond resolution. @@ -184,6 +205,10 @@ struct lkl_host_operations lkl_host_ops = { .mutex_free = mutex_free, .mutex_lock = mutex_lock, .mutex_unlock = mutex_unlock, + .tls_alloc = tls_alloc, + .tls_free = tls_free, + .tls_set = tls_set, + .tls_get = tls_get, .time = time_ns, .timer_alloc = timer_alloc, .timer_set_oneshot = timer_set_oneshot, diff --git a/tools/lkl/lib/posix-host.c b/tools/lkl/lib/posix-host.c index 4e146c1a20f134..32b4bcbd047435 100644 --- a/tools/lkl/lib/posix-host.c +++ b/tools/lkl/lib/posix-host.c @@ -178,6 +178,27 @@ static void thread_exit(void) pthread_exit(NULL); } +static int tls_alloc(unsigned int *key) +{ + return pthread_key_create((pthread_key_t*)key, NULL); +} + +static int tls_free(unsigned int key) +{ + return pthread_key_delete(key); +} + +static int tls_set(unsigned int key, void *data) +{ + return pthread_setspecific(key, data); +} + +static void *tls_get(unsigned int key) +{ + return pthread_getspecific(key); +} + + static unsigned long long time_ns(void) { struct timeval tv; @@ -251,6 +272,10 @@ struct lkl_host_operations lkl_host_ops = { .mutex_free = mutex_free, .mutex_lock = mutex_lock, .mutex_unlock = mutex_unlock, + .tls_alloc = tls_alloc, + .tls_free = tls_free, + .tls_set = tls_set, + .tls_get = tls_get, .time = time_ns, .timer_alloc = timer_alloc, .timer_set_oneshot = timer_set_oneshot, diff --git a/tools/lkl/lib/virtio.c b/tools/lkl/lib/virtio.c index 6313e14733741c..6a92a22b9eb4d0 100644 --- a/tools/lkl/lib/virtio.c +++ b/tools/lkl/lib/virtio.c @@ -104,7 +104,7 @@ static inline struct lkl_vring_desc *vring_desc_at_le_idx(struct virtio_queue *q static void init_dev_buf_from_vring_desc(struct lkl_dev_buf *buf, struct lkl_vring_desc *vring_desc) { - buf->addr = (void *)le64toh(vring_desc->addr); + buf->addr = (void *)(uintptr_t)le64toh(vring_desc->addr); buf->len = le32toh(vring_desc->len); if (!(buf->addr && buf->len)) diff --git a/tools/lkl/tests/boot.c b/tools/lkl/tests/boot.c index e68574c65bd313..71cb19dc0a82cd 100644 --- a/tools/lkl/tests/boot.c +++ b/tools/lkl/tests/boot.c @@ -666,6 +666,69 @@ static int test_gettid(char *str, int len) } } +static void test_thread(void *data) +{ + int *pipe_fds = (int*) 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) +{ + int pipe_fds[2]; + char tmp[LKL_PIPE_BUF+1]; + long ret; + + ret = lkl_sys_pipe2(pipe_fds, 0); + if (ret) { + snprintf(str, len, "pipe2: %s", lkl_strerror(ret)); + return TEST_FAILURE; + } + + ret = lkl_sys_fcntl(pipe_fds[0], LKL_F_SETPIPE_SZ, 1); + if (ret < 0) { + snprintf(str, len, "fcntl setpipe_sz: %s", lkl_strerror(ret)); + return TEST_FAILURE; + } + + ret = lkl_host_ops.thread_create(test_thread, pipe_fds); + if (ret) { + snprintf(str, len, "failed to create thread"); + return TEST_FAILURE; + } + + sleep(1); + + ret = lkl_sys_write(pipe_fds[1], tmp, sizeof(tmp)); + if (ret != sizeof(tmp)) { + if (ret < 0) + snprintf(str, len, "write: %s", lkl_strerror(ret)); + else + snprintf(str, len, "write: short write: %ld", ret); + return TEST_FAILURE; + } + + sleep(1); + + return TEST_SUCCESS; +} + static struct cl_option *find_short_opt(char name) { struct cl_option *opt; @@ -773,6 +836,7 @@ int main(int argc, char **argv) TEST(mutex); TEST(semaphore); TEST(gettid); + TEST(syscall_thread); lkl_sys_halt();