diff --git a/kernel/Makefile.build b/kernel/Makefile.build index d82e683a..48e9670f 100644 --- a/kernel/Makefile.build +++ b/kernel/Makefile.build @@ -1,6 +1,6 @@ AALDIR=$(AALBASE)/$(TARGET) OBJS = init.o mem.o debug.o mikc.o listeners.o ap.o syscall.o cls.o host.o -OBJS += process.o copy.o +OBJS += process.o copy.o waitq.o DEPSRCS=$(wildcard $(SRC)/*.c) CFLAGS += -I$(SRC)/include -mcmodel=kernel -D__KERNEL__ diff --git a/kernel/include/process.h b/kernel/include/process.h index cd8d854d..bbe346df 100644 --- a/kernel/include/process.h +++ b/kernel/include/process.h @@ -14,9 +14,11 @@ #define PS_RUNNING 0x1 #define PS_INTERRUPTIBLE 0x2 -#define PS_UNINTERRUPTIBLE 0x3 -#define PS_ZOMBIE 0x4 -#define PS_EXITED 0x5 +#define PS_UNINTERRUPTIBLE 0x4 +#define PS_ZOMBIE 0x8 +#define PS_EXITED 0x10 + +#define PS_NORMAL (PS_INTERRUPTIBLE | PS_UNINTERRUPTIBLE) struct vm_range { struct list_head list; @@ -78,5 +80,6 @@ unsigned long extend_process_region(struct process *proc, void schedule(void); void runq_add_proc(struct process *proc, int cpu_id); void runq_del_proc(struct process *proc, int cpu_id); +int sched_wakeup_process(struct process *proc, int valid_states); #endif diff --git a/kernel/include/waitq.h b/kernel/include/waitq.h new file mode 100644 index 00000000..066c9540 --- /dev/null +++ b/kernel/include/waitq.h @@ -0,0 +1,135 @@ +#ifndef _LWK_WAITQ_H +#define _LWK_WAITQ_H + +/* Kitten waitqueue adaptation */ + +#include +#include +#include + +struct waitq_entry; + +typedef int (*waitq_func_t)(struct waitq_entry *wait, unsigned mode, + int flags, void *key); + +int default_wake_function(struct waitq_entry *wait, unsigned mode, int flags, + void *key); + +typedef struct waitq { + aal_spinlock_t lock; + struct list_head waitq; +} waitq_t; + +#define WQ_FLAG_EXCLUSIVE 0x01 + +typedef struct waitq_entry { + struct list_head link; + void *private; + unsigned int flags; + waitq_func_t func; +} waitq_entry_t; + +#define DECLARE_WAITQ(name) \ + waitq_t name = __WAITQ_INITIALIZER(name); + +#define __WAITQ_INITIALIZER(name) { \ + .lock = SPIN_LOCK_UNLOCKED, \ + .waitq = { &(name).waitq, &(name).waitq } \ + } + +#define DECLARE_WAITQ_ENTRY(name, tsk) \ + waitq_entry_t name = { \ + .private = tsk, \ + .func = default_wake_function, \ + .link = { &(name).link, &(name).link } \ + } + +extern void waitq_init(waitq_t *waitq); +extern void waitq_init_entry(waitq_entry_t *entry, struct process *proc); +extern int waitq_active(waitq_t *waitq); +extern void waitq_add_entry(waitq_t *waitq, waitq_entry_t *entry); +extern void waitq_add_entry_locked(waitq_t *waitq, waitq_entry_t *entry); +extern void waitq_prepare_to_wait(waitq_t *waitq, + waitq_entry_t *entry, int state); +extern void waitq_finish_wait(waitq_t *waitq, waitq_entry_t *entry); +extern void waitq_wakeup(waitq_t *waitq); +extern int waitq_wake_nr(waitq_t *waitq, int nr); +extern int waitq_wake_nr_locked(waitq_t *waitq, int nr); +extern void waitq_remove_entry(waitq_t *waitq, waitq_entry_t *entry); +extern void waitq_remove_entry_locked(waitq_t *waitq, waitq_entry_t *entry); + +#define __wait_event(waitq, condition) \ +do { \ + DECLARE_WAITQ_ENTRY(__entry, current); \ + for (;;) { \ + waitq_prepare_to_wait(&waitq, &__entry, \ + TASK_UNINTERRUPTIBLE); \ + if (condition) \ + break; \ + schedule(); \ + } \ + waitq_finish_wait(&waitq, &__entry); \ +} while (0) + +/** + * wait_event - sleep until a condition becomes true + * @waitq: the waitqueue to wait on + * @condition: a C expression for the event to wait for + * + * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the + * @condition evaluates to true. The @condition is checked each time + * the waitqueue @waitq is woken up. + * + * wake_up() has to be called after changing any variable that could + * change the result of the wait condition. + */ +#define wait_event(waitq, condition) \ +do { \ + if (condition) \ + break; \ + __wait_event(waitq, condition); \ +} while (0) + +#define __wait_event_interruptible(waitq, condition, ret) \ +do { \ + DECLARE_WAITQ_ENTRY(__entry, current); \ + for (;;) { \ + waitq_prepare_to_wait(&waitq, &__entry, \ + TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (1 /* TODO: !signal_pending(current) */) { \ + schedule(); \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + waitq_finish_wait(&waitq, &__entry); \ +} while (0) + +/** + * wait_event_interruptible - sleep until a condition becomes true + * @waitq: the waitqueue to wait on + * @condition: a C expression for the event to wait for + * + * The process is put to sleep (TASK_INTERRUPTIBLE) until the + * @condition evaluates to true or a signal is received. The + * @condition is checked each time the waitqueue @waitq is woken up. + * + * wake_up() has to be called after changing any variable that could + * change the result of the wait condition. + * + * The function will return -ERESTARTSYS if it was interrupted by a + * signal and 0 if @condition evaluated to true. + */ +#define wait_event_interruptible(waitq, condition) \ +({ \ + int __ret = 0; \ + if (!(condition)) \ + __wait_event_interruptible(waitq, condition, __ret); \ + __ret; \ +}) + +#endif + diff --git a/kernel/process.c b/kernel/process.c index add93639..7c525f3d 100644 --- a/kernel/process.c +++ b/kernel/process.c @@ -5,6 +5,7 @@ #include #include #include +#include #define DEBUG_PRINT_PROCESS @@ -307,6 +308,7 @@ void schedule(void) aal_mc_load_page_table(next->vm->page_table); do_arch_prctl(ARCH_SET_FS, next->vm->region.tlsblock_base); + cpu_local_var(status) = CPU_STATUS_RUNNING; if (prev) { aal_mc_switch_context(&prev->ctx, &next->ctx); @@ -316,6 +318,35 @@ void schedule(void) } } + +int sched_wakeup_process(struct process *proc, int valid_states) +{ + int status; + unsigned long irqstate; + struct cpu_local_var *v = get_cpu_local_var(proc->cpu_id); + + irqstate = aal_mc_spinlock_lock(&(v->runq_lock)); + + if (proc->status & valid_states) { + proc->status = PS_RUNNING; + status = 0; + } + else { + status = -EINVAL; + } + + aal_mc_spinlock_unlock(&(v->runq_lock), irqstate); + + if (!status && (proc->cpu_id != aal_mc_get_processor_id())) { + aal_mc_interrupt_cpu(get_x86_cpu_local_variable(proc->cpu_id)->apic_id, + 0xd1); + } + + return status; +} + + + /* Runq lock must be held here */ void __runq_add_proc(struct process *proc, int cpu_id) { diff --git a/kernel/waitq.c b/kernel/waitq.c new file mode 100644 index 00000000..fca0fe9f --- /dev/null +++ b/kernel/waitq.c @@ -0,0 +1,147 @@ + +/* Kitten waitqueue adaptation */ + +#include +#include +#include + +int +default_wake_function(waitq_entry_t *entry, unsigned mode, + int flags, void *key) +{ + return sched_wakeup_process(entry->private, PS_NORMAL); +} + +void +waitq_init(waitq_t *waitq) +{ + aal_mc_spinlock_init(&waitq->lock); + INIT_LIST_HEAD(&waitq->waitq); +} + +void +waitq_init_entry(waitq_entry_t *entry, struct process *proc) +{ + entry->private = proc; + entry->func = default_wake_function; + INIT_LIST_HEAD(&entry->link); +} + +int +waitq_active(waitq_t *waitq) +{ + int active; + unsigned long irqstate; + + irqstate = aal_mc_spinlock_lock(&waitq->lock); + active = !list_empty(&waitq->waitq); + aal_mc_spinlock_unlock(&waitq->lock, irqstate); + + return active; +} + +void +waitq_add_entry(waitq_t *waitq, waitq_entry_t *entry) +{ + unsigned long irqstate; + + irqstate = aal_mc_spinlock_lock(&waitq->lock); + waitq_add_entry_locked(waitq, entry); + aal_mc_spinlock_unlock(&waitq->lock, irqstate); +} + + +void +waitq_add_entry_locked(waitq_t *waitq, waitq_entry_t *entry) +{ + //BUG_ON(!list_empty(&entry->link)); + list_add_tail(&entry->link, &waitq->waitq); +} + + +void +waitq_remove_entry(waitq_t *waitq, waitq_entry_t *entry) +{ + unsigned long irqstate; + + irqstate = aal_mc_spinlock_lock(&waitq->lock); + waitq_remove_entry_locked(waitq, entry); + aal_mc_spinlock_unlock(&waitq->lock, irqstate); +} + + +void +waitq_remove_entry_locked(waitq_t *waitq, waitq_entry_t *entry) +{ + //BUG_ON(list_empty(&entry->link)); + list_del_init(&entry->link); +} + + +void +waitq_prepare_to_wait(waitq_t *waitq, waitq_entry_t *entry, int state) +{ + unsigned long irqstate; + + irqstate = aal_mc_spinlock_lock(&waitq->lock); + if (list_empty(&entry->link)) + list_add(&entry->link, &waitq->waitq); + cpu_local_var(current)->status = state; + aal_mc_spinlock_unlock(&waitq->lock, irqstate); +} + +void +waitq_finish_wait(waitq_t *waitq, waitq_entry_t *entry) +{ + cpu_local_var(current)->status = PS_RUNNING; + waitq_remove_entry(waitq, entry); +} + +void +waitq_wakeup(waitq_t *waitq) +{ + unsigned long irqstate; + struct list_head *tmp; + waitq_entry_t *entry; + + irqstate = aal_mc_spinlock_lock(&waitq->lock); + list_for_each(tmp, &waitq->waitq) { + entry = list_entry(tmp, waitq_entry_t, link); + entry->func(entry, 0, 0, NULL); + } + aal_mc_spinlock_unlock(&waitq->lock, irqstate); +} + + +int +waitq_wake_nr(waitq_t * waitq, int nr) +{ + unsigned long irqstate; + + irqstate = aal_mc_spinlock_lock(&waitq->lock); + int count = waitq_wake_nr_locked(waitq, nr); + aal_mc_spinlock_unlock(&waitq->lock, irqstate); + + if (count > 0) + schedule(); + + return count; +} + + +int +waitq_wake_nr_locked( waitq_t * waitq, int nr ) +{ + int count = 0; + waitq_entry_t *entry; + + list_for_each_entry(entry, &waitq->waitq, link) { + if (++count > nr) + break; + + entry->func(entry, 0, 0, NULL); + } + + return count - 1; +} +