From d28d181801d83c56dd45028f7e436d9cf6d078ff Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 23 Feb 2016 21:44:05 +0200 Subject: [PATCH 1/6] lkl: fix NR_IRQS NR_IRQS is defined by the number of bits, not bytes, which fit in the long datatype. Signed-off-by: Octavian Purdila --- arch/lkl/include/asm/irq.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0063276716a49aba9fa06d594105f349360bb402 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 23 Feb 2016 21:47:34 +0200 Subject: [PATCH 2/6] lkl tools: virtio: fix mingw warning lib/virtio.c:107:14: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] buf->addr = (void *)le64toh(vring_desc->addr); Signed-off-by: Octavian Purdila --- tools/lkl/lib/virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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)) From 8d11cc98e45638f7b7c13135e49f29dab42a34b1 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 23 Feb 2016 21:49:59 +0200 Subject: [PATCH 3/6] lkl: fix lkl_sys_fcntl for 32bit systems Signed-off-by: Octavian Purdila --- tools/lkl/include/lkl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h index 46131c230aaa53..10d4d910c38690 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 From c4f10729c86f85919477fb5b61ecaeb1c3e013c0 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 23 Feb 2016 21:52:43 +0200 Subject: [PATCH 4/6] lkl tools: nt host lib: fix warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lib/nt-host.c:179:2: warning: initialization from incompatible pointer type [enabled by default] lib/nt-host.c:179:2: warning: (near initialization for ‘lkl_host_ops.sem_alloc’) [enabled by default] Signed-off-by: Octavian Purdila --- tools/lkl/lib/nt-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lkl/lib/nt-host.c b/tools/lkl/lib/nt-host.c index 6a99ff1f6b911a..92e69735572992 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)); From a1766656101b0ddbccf2b9eb083540f57c344709 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 23 Feb 2016 21:57:13 +0200 Subject: [PATCH 5/6] lkl: host ops: add support for local thread storage Signed-off-by: Octavian Purdila --- arch/lkl/include/uapi/asm/host_ops.h | 10 ++++++++++ tools/lkl/lib/nt-host.c | 25 +++++++++++++++++++++++++ tools/lkl/lib/posix-host.c | 25 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+) 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/tools/lkl/lib/nt-host.c b/tools/lkl/lib/nt-host.c index 92e69735572992..f9426b762e44bd 100644 --- a/tools/lkl/lib/nt-host.c +++ b/tools/lkl/lib/nt-host.c @@ -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, From 8fd682466664a714534cb264221b7f9c04104658 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 23 Feb 2016 07:50:36 +0200 Subject: [PATCH 6/6] lkl: add support for multiple system call threads 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 --- arch/lkl/Makefile | 3 +- arch/lkl/include/asm/syscalls.h | 2 +- arch/lkl/include/asm/unistd.h | 4 + arch/lkl/kernel/setup.c | 8 +- arch/lkl/kernel/syscalls.c | 234 +++++++++++++++++++++++++++----- tools/lkl/include/lkl.h | 18 +++ tools/lkl/tests/boot.c | 64 +++++++++ 7 files changed, 288 insertions(+), 45 deletions(-) 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/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/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 10d4d910c38690..5f343e24e34a9f 100644 --- a/tools/lkl/include/lkl.h +++ b/tools/lkl/include/lkl.h @@ -232,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/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();