From f93e66b4dff8b025856aa026b75c3253927f5c3f Mon Sep 17 00:00:00 2001 From: daniel-k Date: Fri, 1 Jul 2016 18:52:28 +0200 Subject: [PATCH 1/8] kernel/tasks: implement function to get process control block by ID --- hermit/include/hermit/tasks.h | 12 ++++++++++++ hermit/kernel/tasks.c | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/hermit/include/hermit/tasks.h b/hermit/include/hermit/tasks.h index d45f18141..6fe4612b4 100644 --- a/hermit/include/hermit/tasks.h +++ b/hermit/include/hermit/tasks.h @@ -190,6 +190,18 @@ int wakeup_task(tid_t); */ int block_current_task(void); +/** @brief Get a process control block + * + * @param id ID of the task to retrieve + * @param task Location to store pointer to task + * @return + * - 0 on success + * - -ENOMEM (-12) if @p task is NULL + * - -ENOENT ( -2) if @p id not in task table + * - -EINVAL (-22) if there's no valid task with @p id + */ +int get_task(tid_t id, task_t** task); + /** @brief Block current task until timer expires * * @param deadline Clock tick, when the timer expires diff --git a/hermit/kernel/tasks.c b/hermit/kernel/tasks.c index 3853930b2..44c04ece7 100644 --- a/hermit/kernel/tasks.c +++ b/hermit/kernel/tasks.c @@ -877,6 +877,26 @@ get_task_out: return NULL; } + +int get_task(tid_t id, task_t** task) +{ + if (BUILTIN_EXPECT(task == NULL, 0)) { + return -ENOMEM; + } + + if (BUILTIN_EXPECT(id >= MAX_TASKS, 0)) { + return -ENOENT; + } + + if (BUILTIN_EXPECT(task_table[id].status == TASK_INVALID, 0)) { + return -EINVAL; + } + + *task = &task_table[id]; + + return 0; +} + void reschedule(void) { size_t** stack; From b7d2dc95c5a191a393ee1ae7cd4bf45fe1aac497 Mon Sep 17 00:00:00 2001 From: daniel-k Date: Fri, 8 Jul 2016 18:23:51 +0200 Subject: [PATCH 2/8] kernel/tasks: initialize task->last_core with initial core instead of 0 Otherwise it's not possible to determine on which core a task is running if it hasn't been rescheduled at least one time. --- hermit/kernel/tasks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hermit/kernel/tasks.c b/hermit/kernel/tasks.c index 44c04ece7..d2d15a087 100644 --- a/hermit/kernel/tasks.c +++ b/hermit/kernel/tasks.c @@ -376,7 +376,7 @@ int clone_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio) if (task_table[i].status == TASK_INVALID) { task_table[i].id = i; task_table[i].status = TASK_READY; - task_table[i].last_core = 0; + task_table[i].last_core = core_id; task_table[i].last_stack_pointer = NULL; task_table[i].stack = stack; task_table[i].prio = prio; @@ -476,7 +476,7 @@ int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio, uint32_t c if (task_table[i].status == TASK_INVALID) { task_table[i].id = i; task_table[i].status = TASK_READY; - task_table[i].last_core = 0; + task_table[i].last_core = core_id; task_table[i].last_stack_pointer = NULL; task_table[i].stack = stack; task_table[i].prio = prio; From e03887a1ab6376038499b88ca071794943cdf070 Mon Sep 17 00:00:00 2001 From: daniel-k Date: Fri, 8 Jul 2016 18:21:24 +0200 Subject: [PATCH 3/8] hermit/dequeue: implement a double-ended queue --- hermit/include/hermit/dequeue.h | 122 ++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 hermit/include/hermit/dequeue.h diff --git a/hermit/include/hermit/dequeue.h b/hermit/include/hermit/dequeue.h new file mode 100644 index 000000000..f56cfe052 --- /dev/null +++ b/hermit/include/hermit/dequeue.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016, Daniel Krebs, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Daniel Krebs + * @file include/hermit/dequeue.h + * @brief Double-ended queue implementation + */ + +#include +#include +#include +#include + +#ifndef __DEQUEUE_H__ +#define __DEQUEUE_H__ + +#define NOT_NULL(dequeue) do { \ + if(BUILTIN_EXPECT(!dequeue, 0)) { \ + return -EINVAL; \ + } \ + } while(0) + +typedef struct _dequeue_t { + size_t front; ///< point to first used entry + size_t back; ///< point to first unused entry + spinlock_t lock; ///< make dequeue thread safe + char* buffer; ///< pointer to buffer that holds elements + size_t buffer_length; ///< number of elements buffer can hold + size_t element_size; ///< size of one element in buffer +} dequeue_t; + +static inline int +dequeue_init(dequeue_t* dequeue, void* buffer, size_t buffer_length, size_t element_size) +{ + NOT_NULL(dequeue); + + dequeue->front = 0; + dequeue->back = 0; + + dequeue->buffer = buffer; + dequeue->buffer_length = buffer_length; + dequeue->element_size = element_size; + + spinlock_init(&dequeue->lock); + + return 0; +} + +static inline int +dequeue_push(dequeue_t* dequeue, void* v) +{ + NOT_NULL(dequeue); + + spinlock_lock(&dequeue->lock); + + size_t new_back = (dequeue->back + 1) % dequeue->buffer_length; + if(new_back == dequeue->front) { + spinlock_unlock(&dequeue->lock); + return -EOVERFLOW; + } + + memcpy(&dequeue->buffer[dequeue->back * dequeue->element_size], + v, + dequeue->element_size); + + dequeue->back = new_back; + + spinlock_unlock(&dequeue->lock); + return 0; +} + +static inline int +dequeue_pop(dequeue_t* dequeue, void* out) +{ + + NOT_NULL(dequeue); + NOT_NULL(out); + + spinlock_lock(&dequeue->lock); + + if(dequeue->front == dequeue->back) { + spinlock_unlock(&dequeue->lock); + return -ENOENT; + } + + memcpy(out, + &dequeue->buffer[dequeue->front * dequeue->element_size], + dequeue->element_size); + + dequeue->front = (dequeue->front + 1) % dequeue->buffer_length; + + spinlock_unlock(&dequeue->lock); + + return 0; +} + +#endif // __DEQUEUE_H__ From 56349176da83a512812055b6215f721421c03c0d Mon Sep 17 00:00:00 2001 From: daniel-k Date: Thu, 25 Aug 2016 17:14:47 +0200 Subject: [PATCH 4/8] hermit/kernel: implement signal handling for communication between tasks When a signal is sent, the operating system interrupts the target task's normal flow of execution to deliver the signal. Execution can be interrupted during any non-atomic instruction. If the process has previously registered a signal handler, that routine is executed when the task's execution would continue. --- hermit/arch/x86/kernel/entry.asm | 32 ++++- hermit/arch/x86/kernel/irq.c | 3 + hermit/include/hermit/signal.h | 76 +++++++++++ hermit/include/hermit/tasks_types.h | 3 + hermit/kernel/Makefile | 2 +- hermit/kernel/main.c | 4 + hermit/kernel/signal.c | 203 ++++++++++++++++++++++++++++ hermit/kernel/tasks.c | 2 + 8 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 hermit/include/hermit/signal.h create mode 100644 hermit/kernel/signal.c diff --git a/hermit/arch/x86/kernel/entry.asm b/hermit/arch/x86/kernel/entry.asm index 37767864d..d3ced9bf9 100644 --- a/hermit/arch/x86/kernel/entry.asm +++ b/hermit/arch/x86/kernel/entry.asm @@ -354,9 +354,9 @@ isrstub_pseudo_error 9 %assign i i+1 %endrep -; Create entries for the interrupts 80 to 81 +; Create entries for the interrupts 80 to 82 %assign i 80 -%rep 2 +%rep 3 irqstub i %assign i i+1 %endrep @@ -674,6 +674,34 @@ is_single_kernel: mov eax, DWORD [single_kernel] ret + +global sighandler_epilog +sighandler_epilog: + ; restore only those registers that might have changed between returning + ; from IRQ and execution of signal handler + add rsp, 2 * 8 ; ignore fs, gs + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rbp + add rsp, 8 ; ignore rsp + pop rbx + pop rdx + pop rcx + pop rax + add rsp, 4 * 8 ; ignore int_no, error, rip, cs + popfq + add rsp, 2 * 8 ; ignore userrsp, ss + + jmp [rsp - 5 * 8] ; jump to rip from saved state + SECTION .data align 4096 diff --git a/hermit/arch/x86/kernel/irq.c b/hermit/arch/x86/kernel/irq.c index 89328c6c5..c22c9ff6f 100644 --- a/hermit/arch/x86/kernel/irq.c +++ b/hermit/arch/x86/kernel/irq.c @@ -74,6 +74,7 @@ extern void irq22(void); extern void irq23(void); extern void irq80(void); extern void irq81(void); +extern void irq82(void); extern void apic_timer(void); extern void apic_lint0(void); extern void apic_lint1(void); @@ -230,6 +231,8 @@ static int irq_install(void) IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP, 1); idt_set_gate(113, (size_t)irq81, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP, 1); + idt_set_gate(114, (size_t)irq82, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP, 1); idt_set_gate(121, (size_t)wakeup, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP, 1); diff --git a/hermit/include/hermit/signal.h b/hermit/include/hermit/signal.h new file mode 100644 index 000000000..9c71de13c --- /dev/null +++ b/hermit/include/hermit/signal.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, Daniel Krebs, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Daniel Krebs + * @file include/hermit/signal.h + * @brief Signal related functions + */ + +#ifndef __SIGNAL_H__ +#define __SIGNAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define MAX_SIGNALS 32 + +typedef void (*signal_handler_t)(int); + +// This is used in deqeue.h (HACK) +typedef struct _sig { + tid_t dest; + int signum; +} sig_t; + +/** @brief Send signal to kernel task + * + * @param dest Send signal to this task + * @param signum Signal number + * @return + * - 0 on success + * - -ENOENT (-2) if task not found + */ +int hermit_kill(tid_t dest, int signum); + +/** @brief Register signal handler + * + * @param handler Signal handler + * @return + * - 0 on success + */ +int hermit_signal(signal_handler_t handler); + +#ifdef __cplusplus +} +#endif + +#endif // __SIGNAL_H__ diff --git a/hermit/include/hermit/tasks_types.h b/hermit/include/hermit/tasks_types.h index 0c63a8ad6..cb4be8b7d 100644 --- a/hermit/include/hermit/tasks_types.h +++ b/hermit/include/hermit/tasks_types.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -104,6 +105,8 @@ typedef struct task { size_t tls_size; /// LwIP error code int lwip_err; + /// Handler for (POSIX) Signals + signal_handler_t signal_handler; /// FPU state union fpu_state fpu; } task_t; diff --git a/hermit/kernel/Makefile b/hermit/kernel/Makefile index 1082e2f15..524b2e44c 100644 --- a/hermit/kernel/Makefile +++ b/hermit/kernel/Makefile @@ -1,4 +1,4 @@ -C_source := main.c tasks.c syscall.c timer.c +C_source := main.c tasks.c syscall.c timer.c signal.c MODULE := kernel include $(TOPDIR)/Makefile.inc diff --git a/hermit/kernel/main.c b/hermit/kernel/main.c index 2a2a52dfe..c7dcf835d 100644 --- a/hermit/kernel/main.c +++ b/hermit/kernel/main.c @@ -98,6 +98,8 @@ uint32_t idle_poll = 1; islelock_t* rcce_lock = NULL; rcce_mpb_t* rcce_mpb = NULL; +extern void signal_init(); + #if 0 static int foo(void* arg) { @@ -130,6 +132,8 @@ static int hermit_init(void) timer_init(); multitasking_init(); memory_init(); + signal_init(); + #ifndef CONFIG_VGA uart_init(); #endif diff --git a/hermit/kernel/signal.c b/hermit/kernel/signal.c new file mode 100644 index 000000000..4c010c72f --- /dev/null +++ b/hermit/kernel/signal.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_DEBUG 0 +#if !ENABLE_DEBUG +#define kprintf(...) +#endif + +#define SIGNAL_IRQ (32 + 82) +#define SIGNAL_BUFFER_SIZE (16) + +// Per-core signal queue and buffer +static dequeue_t signal_queue[MAX_CORES]; +static sig_t signal_buffer[MAX_CORES][SIGNAL_BUFFER_SIZE]; + +static void _signal_irq_handler(struct state* s) +{ + kprintf("Enter _signal_irq_handler() on core %d\n", CORE_ID); + + sig_t signal; + task_t* dest_task; + task_t* curr_task = per_core(current_task); + + while(dequeue_pop(&signal_queue[CORE_ID], &signal) == 0) { + kprintf(" Deliver signal %d\n", signal.signum); + + if(get_task(signal.dest, &dest_task) == 0) { + kprintf(" Found valid task with ID %d\n", dest_task->id); + + // only service signals for tasks on this core + if(dest_task->last_core != CORE_ID) { + kprintf(" Signal dispatched to wrong CPU! Dropping it ...\n"); + continue; + } + + if(dest_task->signal_handler) { + kprintf(" Has signal handler (%p)\n", dest_task->signal_handler); + + /* We will inject the signal handler into the control flow when + * the task will continue it's exection the next time. There are + * 3 cases how the task was interrupted: + * + * 1. call to reschedule() by own intend + * 2. a timer interrupt lead to rescheduling to another task + * 3. this IRQ interrupted the task + * + * Depending on those cases, the state of the task can either be + * saved to it's own stack (1.), it's interrupt stack (IST, 2.) + * or the stack of this interrupt handler (3.). + * + * When the signal handler finishes it's execution, we need to + * restore the task state, so we make the signal handler return + * first to sighandler_epilog() which then restores the original + * state. + * + * For cases 2+3, when task was interrupted by an IRQ, we modify + * the existing state on the interrupt stack to execute the + * signal handler, wherease in case 1, we craft a new state and + * place it on top of the task stack. + * + * The task stack will have the following layout: + * + * | ... | <- task's rsp before interruption + * |----------------------| + * | saved state | + * |----------------------| + * | &sighandler_epilog() | <- rsp after IRQ + * |----------------------| + * |----------------------| Only for case 1: + * | signal handler state | Craft signal handler state, so it + * |----------------------| executes before task is continued + */ + + size_t* task_stackptr; + struct state *task_state, *sighandler_state; + + const int task_is_running = dest_task == curr_task; + kprintf(" Task is%s running\n", task_is_running ? "" : " not"); + + // location of task state depends of type of interruption + task_state = (!task_is_running) ? + /* case 1+2: */ (struct state*) dest_task->last_stack_pointer : + /* case 3: */ s; + + // pseudo state pushed by reschedule() has INT no. 0 + const int state_on_task_stack = task_state->int_no == 0; + + if(state_on_task_stack) { + kprintf(" State is already on task stack\n"); + // stack pointer was saved by switch_context() after saving + // task state to task stack + task_stackptr = dest_task->last_stack_pointer; + } else { + // stack pointer is last rsp, since task state is saved to + // interrupt stack + task_stackptr = (size_t*) task_state->rsp; + + kprintf(" Copy state to task stack\n"); + task_stackptr -= sizeof(struct state) / sizeof(size_t); + memcpy(task_stackptr, task_state, sizeof(struct state)); + } + + // signal handler will return to this function to restore + // register state + extern void sighandler_epilog(); + *(--task_stackptr) = (uint64_t) &sighandler_epilog; + size_t* sighandler_rsp = task_stackptr; + + if(state_on_task_stack) { + kprintf(" Craft state for signal handler on task stack\n"); + + // we actually only care for ss, rflags, cs, fs and gs + task_stackptr -= sizeof(struct state) / sizeof(size_t); + sighandler_state = (struct state*) task_stackptr; + memcpy(sighandler_state, task_state, sizeof(struct state)); + + // advance stack pointer so signal handler state will be + // restored first + dest_task->last_stack_pointer = (size_t*) sighandler_state; + } else { + kprintf(" Reuse state on IST for signal handler\n"); + sighandler_state = task_state; + } + + // update rsp so that sighandler_epilog() will be executed + // after signal handler + sighandler_state->rsp = (uint64_t) sighandler_rsp; + sighandler_state->userrsp = sighandler_state->rsp; + + // call signal handler instead of continuing task's execution + sighandler_state->rdi = (uint64_t) signal.signum; + sighandler_state->rip = (uint64_t) dest_task->signal_handler; + } else { + kprintf(" No signal handler installed\n"); + } + } else { + kprintf(" Task %d has already died\n", signal.dest); + } + } + kprintf("Leave _signal_irq_handler() on core %d\n", CORE_ID); +} + +int hermit_signal(signal_handler_t handler) +{ + task_t* curr_task = per_core(current_task); + curr_task->signal_handler = handler; + + return 0; +} + +int hermit_kill(tid_t dest, int signum) +{ + task_t* task; + if(BUILTIN_EXPECT(get_task(dest, &task), 0)) { + kprintf("Trying to send signal %d to invalid task %d\n", signum, dest); + return -ENOENT; + } + + const tid_t dest_core = task->last_core; + + kprintf("Send signal %d from task %d (core %d) to task %d (core %d)\n", + signum, per_core(current_task)->id, CORE_ID, dest, dest_core); + + if(task == per_core(current_task)) { + kprintf(" Deliver signal to itself, call handler immediately\n"); + + if(task->signal_handler) { + task->signal_handler(signum); + } + return 0; + } + + sig_t signal = {dest, signum}; + if(dequeue_push(&signal_queue[dest_core], &signal)) { + kprintf(" Cannot push signal to task's signal queue, dropping it\n"); + return -ENOMEM; + } + + // send IPI to destination core + kprintf(" Send signal IPI (%d) to core %d\n", SIGNAL_IRQ, dest_core); + apic_send_ipi(dest_core, SIGNAL_IRQ); + + return 0; +} + +void signal_init() +{ + // initialize per-core signal queue + for(int i = 0; i < MAX_CORES; i++) { + dequeue_init(&signal_queue[i], signal_buffer[i], + SIGNAL_BUFFER_SIZE, sizeof(sig_t)); + } + + irq_install_handler(SIGNAL_IRQ, _signal_irq_handler); +} + diff --git a/hermit/kernel/tasks.c b/hermit/kernel/tasks.c index d2d15a087..d5d091358 100644 --- a/hermit/kernel/tasks.c +++ b/hermit/kernel/tasks.c @@ -387,6 +387,7 @@ int clone_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio) task_table[i].tls_size = curr_task->tls_size; task_table[i].ist_addr = ist; task_table[i].lwip_err = 0; + task_table[i].signal_handler = NULL; if (id) *id = i; @@ -487,6 +488,7 @@ int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio, uint32_t c task_table[i].tls_addr = 0; task_table[i].tls_size = 0; task_table[i].lwip_err = 0; + task_table[i].signal_handler = NULL; if (id) *id = i; From 74a56254d629d672f43c1c5d35c75a6b68d43708 Mon Sep 17 00:00:00 2001 From: daniel-k Date: Thu, 25 Aug 2016 17:15:52 +0200 Subject: [PATCH 5/8] hermit/syscall: provide syscall interface for signals --- hermit/include/hermit/syscall.h | 4 ++++ hermit/kernel/syscall.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/hermit/include/hermit/syscall.h b/hermit/include/hermit/syscall.h index b9a93675a..8bb061672 100644 --- a/hermit/include/hermit/syscall.h +++ b/hermit/include/hermit/syscall.h @@ -57,6 +57,8 @@ extern "C" { struct sem; typedef struct sem sem_t; +typedef void (*signal_handler_t)(int); + /* * HermitCore is a libOS. * => classical system calls are realized as normal function @@ -88,6 +90,8 @@ int sys_rcce_init(int session_id); size_t sys_rcce_malloc(int session_id, int ue); int sys_rcce_fini(int session_id); void sys_yield(void); +int sys_kill(tid_t dest, int signum); +int sys_signal(signal_handler_t handler); #define __NR_exit 0 #define __NR_write 1 diff --git a/hermit/kernel/syscall.c b/hermit/kernel/syscall.c index 70040ec12..2d331f6f8 100644 --- a/hermit/kernel/syscall.c +++ b/hermit/kernel/syscall.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -630,6 +631,19 @@ void sys_yield(void) #endif } +int sys_kill(tid_t dest, int signum) +{ + if(signum < 0) { + return -EINVAL; + } + return hermit_kill(dest, signum); +} + +int sys_signal(signal_handler_t handler) +{ + return hermit_signal(handler); +} + static int default_handler(void) { #if 1 From 340fb985b5a29f66424d2a018e5261744f64da94 Mon Sep 17 00:00:00 2001 From: daniel-k Date: Thu, 25 Aug 2016 17:24:30 +0200 Subject: [PATCH 6/8] usr/tests: provide a test for signals (already using newlib) --- hermit/.gitignore | 1 + hermit/usr/tests/Makefile | 15 ++++- hermit/usr/tests/signals.c | 111 +++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 hermit/usr/tests/signals.c diff --git a/hermit/.gitignore b/hermit/.gitignore index f1736183d..a8679f4b2 100644 --- a/hermit/.gitignore +++ b/hermit/.gitignore @@ -17,6 +17,7 @@ usr/tests/hellof usr/tests/jacobi usr/tests/thr_hello usr/tests/RCCE_minimum +usr/tests/signals usr/benchmarks/RCCE_pingping usr/benchmarks/RCCE_pingpong usr/benchmarks/stream diff --git a/hermit/usr/tests/Makefile b/hermit/usr/tests/Makefile index 376305d07..0d33dd97f 100644 --- a/hermit/usr/tests/Makefile +++ b/hermit/usr/tests/Makefile @@ -44,7 +44,7 @@ endif default: all -all: hello hello++ thr_hello jacobi hellof RCCE_minimum +all: hello hello++ thr_hello jacobi hellof RCCE_minimum signals hello++: hello++.o @echo [LD] $@ @@ -85,6 +85,17 @@ thr_hello: thr_hello.o $Q$(OBJCOPY_FOR_TARGET) $(STRIP_DEBUG) $@ $Qchmod a-x $@.sym +signals.o: signals.c + @echo [CC] $@ + $Q$(CC_FOR_TARGET) -c $(CFLAGS_FOR_TARGET) -pthread -o $@ $< + +signals: signals.o + @echo [LD] $@ + $Q$(CC_FOR_TARGET) -o $@ $< $(LDFLAGS_FOR_TARGET) $(CFLAGS_FOR_TARGET) -pthread + $Q$(OBJCOPY_FOR_TARGET) $(KEEP_DEBUG) $@ $@.sym + $Q$(OBJCOPY_FOR_TARGET) $(STRIP_DEBUG) $@ + $Qchmod a-x $@.sym + RCCE_minimum: RCCE_minimum.o @echo [LD] $@ $Q$(CC_FOR_TARGET) -o $@ $< $(LDFLAGS_FOR_TARGET) $(CFLAGS_FOR_TARGET) -lircce @@ -94,7 +105,7 @@ RCCE_minimum: RCCE_minimum.o clean: @echo Cleaning tests - $Q$(RM) hello hello++ hellof jacobi thr_hello RCCE_minimum *.sym *.o *~ + $Q$(RM) hello hello++ hellof jacobi thr_hello RCCE_minimum signals *.sym *.o *~ veryclean: @echo Propper cleaning tests diff --git a/hermit/usr/tests/signals.c b/hermit/usr/tests/signals.c new file mode 100644 index 000000000..2251ca331 --- /dev/null +++ b/hermit/usr/tests/signals.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016, Daniel Krebs, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#define THREAD_COUNT_DEFAULT 2 + +static volatile __thread int alive = 1; +static volatile __thread int thread_id; + +pthread_barrier_t barrier; +pthread_barrierattr_t attr; + +static void sighandler(int sig) +{ + printf("[%d] Received signal %d\n", thread_id, sig); + alive = 0; +} + +void* thread_func(void* arg) +{ + thread_id = *((int*) arg); + + printf("[%d] Hello (task ID: %d)\n", thread_id, sys_getpid()); + + // register signal handler + signal(16, sighandler); + + // make sure all threads are running before main threads starts sending + // signals + pthread_barrier_wait(&barrier); + + // stay here until signal received + while(alive); + + printf("[%d] I'm done\n", thread_id); + + return 0; +} + +int main(int argc, char** argv) +{ + size_t thread_count = THREAD_COUNT_DEFAULT; + if(argc == 2) { + thread_count = strtoul(argv[1], NULL, 10); + } + + pthread_t threads[thread_count]; + unsigned int i, param[thread_count]; + int ret; + + // if we send the signals too early some threads might not have registered + // a signal handler yet + pthread_barrier_init(&barrier, &attr, thread_count + 1); + + for(i = 0; i < thread_count; i++) { + param[i] = i; + ret = pthread_create(threads+i, NULL, thread_func, (void*) ¶m[i]); + if (ret) { + printf("Thread creation failed! error = %d\n", ret); + return ret; + } else printf("Create thread %d\n", i); + } + + pthread_barrier_wait(&barrier); + + for(i = 0; i < thread_count; i++) { + printf("Send signal to thread %d\n", i); + pthread_kill(threads[i], 16); + } + + sys_msleep(500); + + printf("Wait for all threads to finish\n"); + for(i = 0; i < thread_count; i++) { + pthread_join(threads[i], NULL); + printf("Thread %d is done\n", i); + } + + printf("All done\n"); + + return 0; +} From 57569512676fed7c77490ff4790733e288e1b0ce Mon Sep 17 00:00:00 2001 From: daniel-k Date: Fri, 26 Aug 2016 12:22:31 +0200 Subject: [PATCH 7/8] usr/newlib: update submodule to make use of new signal handling --- hermit/usr/newlib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hermit/usr/newlib b/hermit/usr/newlib index 5ef13e91f..d3539f7d5 160000 --- a/hermit/usr/newlib +++ b/hermit/usr/newlib @@ -1 +1 @@ -Subproject commit 5ef13e91f15138ede39e9a8b15c3ef0641108068 +Subproject commit d3539f7d58c1ae43acdd4e67b845103dfcae36c7 From ab88f5770142a7a21849ce23fdea1503c2e3e256 Mon Sep 17 00:00:00 2001 From: daniel-k Date: Thu, 1 Sep 2016 15:53:43 +0200 Subject: [PATCH 8/8] kernel/tasks: set correct timer when waking up tasks with DYNAMIC_TICKS --- hermit/include/hermit/time.h | 2 ++ hermit/kernel/tasks.c | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/hermit/include/hermit/time.h b/hermit/include/hermit/time.h index c599cef7f..c38c5a518 100644 --- a/hermit/include/hermit/time.h +++ b/hermit/include/hermit/time.h @@ -83,6 +83,8 @@ static inline void sleep(unsigned int sec) { timer_wait(sec*TIMER_FREQ); } static inline int timer_deadline(uint32_t t) { return apic_timer_deadline(t); } +static inline void timer_disable() { apic_disable_timer(); } + #ifdef __cplusplus } #endif diff --git a/hermit/kernel/tasks.c b/hermit/kernel/tasks.c index d5d091358..8ace3b7b8 100644 --- a/hermit/kernel/tasks.c +++ b/hermit/kernel/tasks.c @@ -587,8 +587,24 @@ int wakeup_task(tid_t id) task->prev->next = task->next; if (task->next) task->next->prev = task->prev; - if (readyqueues[core_id].timers.first == task) + if (readyqueues[core_id].timers.first == task) { readyqueues[core_id].timers.first = task->next; + +#ifdef DYNAMIC_TICKS + const task_t* first = readyqueues[core_id].timers.first; + if(first) { + if(first->timeout > get_clock_tick()) { + timer_deadline(first->timeout - get_clock_tick()); + } else { + // workaround: start timer so new head will be serviced + timer_deadline(1); + } + } else { + // prevent spurious interrupts + timer_disable(); + } +#endif + } if (readyqueues[core_id].timers.last == task) readyqueues[core_id].timers.last = task->prev; }