aboutsummaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/core/idt.c15
-rw-r--r--src/kernel/core/thread.c110
-rw-r--r--src/kernel/core/worker.c9
-rw-r--r--src/kernel/dev/pciide.c33
-rw-r--r--src/kernel/include/process.h18
-rw-r--r--src/kernel/include/thread.h11
-rw-r--r--src/kernel/user/process.c105
-rw-r--r--src/kernel/user/syscall.c2
8 files changed, 219 insertions, 84 deletions
diff --git a/src/kernel/core/idt.c b/src/kernel/core/idt.c
index 5c77502..fcf8074 100644
--- a/src/kernel/core/idt.c
+++ b/src/kernel/core/idt.c
@@ -103,6 +103,11 @@ void idt_ex_handler(registers_t *regs) {
current_thread->user_ex_handler(regs);
}
}
+
+ // maybe exit
+ if (current_thread != 0 && regs->eip < K_HIGHHALF_ADDR && current_thread->must_exit) {
+ exit();
+ }
}
/* Called in interrupt.s when an IRQ fires (interrupt 32 to 47) */
@@ -120,11 +125,21 @@ void idt_irq_handler(registers_t *regs) {
}
exit_critical(st);
+
+ // maybe exit
+ if (current_thread != 0 && regs->eip < K_HIGHHALF_ADDR && current_thread->must_exit) {
+ exit();
+ }
}
/* Caled in interrupt.s when a syscall is called */
void idt_syscall_handler(registers_t *regs) {
syscall_handler(regs);
+
+ // maybe exit
+ if (current_thread != 0 && regs->eip < K_HIGHHALF_ADDR && current_thread->must_exit) {
+ exit();
+ }
}
/* For internal use only. Sets up an entry of the IDT with given parameters. */
diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c
index 63523af..144a905 100644
--- a/src/kernel/core/thread.c
+++ b/src/kernel/core/thread.c
@@ -4,6 +4,8 @@
#include <idt.h>
#include <gdt.h>
+#include <hashtbl.h>
+
#include <frame.h>
#include <paging.h>
#include <worker.h>
@@ -15,6 +17,9 @@ void resume_context(saved_context_t *ctx);
thread_t *current_thread = 0;
+static hashtbl_t *waiters = 0; // threads waiting on a ressource
+STATIC_MUTEX(waiters_mutex);
+
// ====================== //
// THE PROGRAMMABLE TIMER //
// ====================== //
@@ -180,9 +185,12 @@ thread_t *new_thread(entry_t entry, void* data) {
*(--t->ctx.esp) = 0; // push invalid return address (the run_thread function never returns)
t->ctx.eip = (void(*)())run_thread;
- t->state = T_STATE_PAUSED;
+ t->state = T_STATE_LOADING;
t->last_ran = 0;
+ t->waiting_on = 0;
+ t->must_exit = false;
+
t->current_pd_d = get_kernel_pagedir();
t->critical_level = CL_USER;
@@ -195,6 +203,8 @@ thread_t *new_thread(entry_t entry, void* data) {
}
static void delete_thread(thread_t *t) {
+ dbg_printf("Deleting thread 0x%p\n", t);
+
if (t->proc != 0)
process_thread_deleted(t);
@@ -213,13 +223,17 @@ static void irq0_handler(registers_t *regs) {
}
}
void threading_setup(entry_t cont, void* arg) {
+ waiters = create_hashtbl(id_key_eq_fun, id_hash_fun, 0);
+ ASSERT(waiters != 0);
+
set_pit_frequency(TASK_SWITCH_FREQUENCY);
idt_set_irq_handler(IRQ0, irq0_handler);
thread_t *t = new_thread(cont, arg);
ASSERT(t != 0);
- resume_thread(t);
+ start_thread(t);
+
exit_critical(CL_USER);
run_scheduler(); // never returns
@@ -230,27 +244,65 @@ void threading_setup(entry_t cont, void* arg) {
// TASK STATE MANIPULATION //
// ======================= //
+void start_thread(thread_t *t) {
+ ASSERT(t->state == T_STATE_LOADING);
+
+ t->state = T_STATE_RUNNING;
+
+ { int st = enter_critical(CL_NOINT);
+
+ enqueue_thread(t, false);
+
+ exit_critical(st); }
+}
+
void yield() {
ASSERT(current_thread != 0 && current_thread->critical_level != CL_EXCL);
save_context_and_enter_scheduler(&current_thread->ctx);
}
-void pause() {
+bool wait_on(void* x) {
ASSERT(current_thread != 0 && current_thread->critical_level != CL_EXCL);
+ mutex_lock(&waiters_mutex);
+
+ void* prev_th = hashtbl_find(waiters, x);
+ if (prev_th == 0) {
+ bool add_ok = hashtbl_add(waiters, x, (void*)1);
+ if (!add_ok) return false; // should not happen to often, I hope
+ } else if (prev_th != (void*)1) {
+ mutex_unlock(&waiters_mutex);
+ return false;
+ }
+
+ int st = enter_critical(CL_NOSWITCH);
+
+ if (current_thread->must_exit) return false;
+
+ current_thread->waiting_on = x;
+
+ ASSERT(hashtbl_change(waiters, x, current_thread));
+ mutex_unlock(&waiters_mutex);
+
current_thread->state = T_STATE_PAUSED;
save_context_and_enter_scheduler(&current_thread->ctx);
+
+ exit_critical(st);
+
+ if (current_thread->must_exit) return false;
+ return true;
}
void usleep(int usecs) {
- void sleeper_resume(void* t) {
- thread_t *thread = (thread_t*)t;
- resume_thread(thread);
- }
if (current_thread == 0) return;
- bool ok = worker_push_in(usecs, sleeper_resume, current_thread);
- if (ok) pause();
+
+ void resume_on_v(void* x) {
+ resume_on(x);
+ }
+ bool ok = worker_push_in(usecs, resume_on_v, current_thread);
+
+ if (ok) wait_on(current_thread);
}
void exit() {
@@ -263,6 +315,8 @@ void exit() {
// (it may switch before adding the delete_thread task), but once the task is added
// no other switch may happen, therefore this thread will not get re-enqueued
+ dbg_printf("Thread 0x%p exiting.\n", current_thread);
+
worker_push(delete_thread_v, current_thread);
current_thread->state = T_STATE_FINISHED;
@@ -272,20 +326,29 @@ void exit() {
ASSERT(false);
}
-bool resume_thread(thread_t *thread) {
- bool ret = false;
+bool resume_on(void* x) {
+ thread_t *thread;
+
+ { mutex_lock(&waiters_mutex);
- int st = enter_critical(CL_NOINT);
+ thread = hashtbl_find(waiters, x);
+ hashtbl_change(waiters, x, (void*)1);
- if (thread->state == T_STATE_PAUSED) {
- ret = true;
+ mutex_unlock(&waiters_mutex); }
+
+ if (thread == 0 || thread == (void*)1) return false;
+
+ { int st = enter_critical(CL_NOINT);
+
+ ASSERT(thread->state == T_STATE_PAUSED);
thread->state = T_STATE_RUNNING;
+ thread->waiting_on = 0;
+
enqueue_thread(thread, false);
- }
- exit_critical(st);
+ exit_critical(st); }
- return ret;
+ return true;
}
void kill_thread(thread_t *thread) {
@@ -293,9 +356,16 @@ void kill_thread(thread_t *thread) {
int st = enter_critical(CL_NOSWITCH);
- thread->state = T_STATE_FINISHED;
- remove_thread_from_queue(thread);
- delete_thread(thread);
+ thread->must_exit = true;
+
+ int i = 0;
+ while (thread->state != T_STATE_FINISHED) {
+ if (thread->state == T_STATE_PAUSED) {
+ resume_on(thread->waiting_on);
+ }
+ yield();
+ if (i++ > 100) dbg_printf("Thread 0x%p must be killed but will not exit.\n", thread);
+ }
exit_critical(st);
}
diff --git a/src/kernel/core/worker.c b/src/kernel/core/worker.c
index e50085a..c3fa04d 100644
--- a/src/kernel/core/worker.c
+++ b/src/kernel/core/worker.c
@@ -36,7 +36,10 @@ void start_workers(int n) {
nworkers = n;
for (int i = 0; i < n; i++) {
workers[i] = new_thread(worker_thread, 0);
- dbg_printf("New worker thread: 0x%p\n", workers[i]);
+ if (workers[i] != 0) {
+ dbg_printf("New worker thread: 0x%p\n", workers[i]);
+ start_thread(workers[i]);
+ }
}
}
@@ -59,7 +62,7 @@ void worker_thread(void* x) {
t->fun(t->data);
free(t);
} else {
- pause();
+ ASSERT(wait_on(current_thread));
}
}
}
@@ -89,7 +92,7 @@ void worker_notify_time(int usecs) {
time += usecs;
if (next_task_time <= time) {
for (int i = 0; i < nworkers; i++) {
- if (resume_thread(workers[i])) break;
+ if (resume_on(workers[i])) break;
}
}
}
diff --git a/src/kernel/dev/pciide.c b/src/kernel/dev/pciide.c
index 05c0b28..c357190 100644
--- a/src/kernel/dev/pciide.c
+++ b/src/kernel/dev/pciide.c
@@ -144,6 +144,8 @@ static uint8_t ide_print_error(ide_controller_t *c, int drive, uint8_t err) {
} else if (err == 4) {
dbg_printf("- Write Protected\n ");
err = 8;
+ } else if (err == 5) {
+ dbg_printf("- Interrupted\n ");
}
dbg_printf("- [%s %s] %s\n",
(const char *[]){"Primary", "Secondary"}[c->devices[drive].channel],
@@ -167,7 +169,7 @@ void irq14_handler(registers_t *regs) {
if (wait_irq14) {
thread_t *t = wait_irq14;
wait_irq14 = 0;
- resume_thread(t);
+ resume_on(t);
}
}
@@ -175,7 +177,7 @@ void irq15_handler(registers_t *regs) {
if (wait_irq15) {
thread_t *t = wait_irq15;
wait_irq15 = 0;
- resume_thread(t);
+ resume_on(t);
}
}
@@ -183,7 +185,7 @@ void pciirq_handler(int pci_id) {
if (wait_pciirq) {
thread_t *t = wait_pciirq;
wait_pciirq = 0;
- resume_thread(t);
+ resume_on(t);
}
}
@@ -201,22 +203,26 @@ static void ide_prewait_irq(ide_controller_t *c, int channel) {
}
}
-static void ide_wait_irq(ide_controller_t *c, int channel) {
+static bool ide_wait_irq(ide_controller_t *c, int channel) {
+ bool ret = true;
+
int st = enter_critical(CL_NOINT);
int irq = c->channels[channel].irq;
if (irq == 14) {
- if (wait_irq14) pause();
+ if (wait_irq14) ret = wait_on(current_thread);
mutex_unlock(&on_irq14);
} else if (irq == 15) {
- if (wait_irq15) pause();
+ if (wait_irq15) ret = wait_on(current_thread);
mutex_unlock(&on_irq15);
} else {
- if (wait_pciirq) pause();
+ if (wait_pciirq) ret = wait_on(current_thread);
mutex_unlock(&on_pciirq);
}
exit_critical(st);
+
+ return ret;
}
// ===================================== //
@@ -352,7 +358,7 @@ static uint8_t ide_ata_access(ide_controller_t *c, int direction,
}
}
- return 0; // Easy, isn't it?
+ return 0;
}
@@ -413,7 +419,7 @@ static uint8_t ide_atapi_read(ide_controller_t *c, uint8_t drive, uint32_t lba,
// (IX): Receiving Data:
for (int i = 0; i < numsects; i++) {
- ide_wait_irq(c, channel); // Wait for an IRQ.
+ if (!ide_wait_irq(c, channel)) goto must_terminate; // Wait for an IRQ.
ide_prewait_irq(c, channel);
err = ide_polling(c, channel, 1);
if (err) return err; // Polling and return if error.
@@ -423,12 +429,17 @@ static uint8_t ide_atapi_read(ide_controller_t *c, uint8_t drive, uint32_t lba,
}
// (X): Waiting for an IRQ:
- ide_wait_irq(c, channel);
+ if (!ide_wait_irq(c, channel)) goto must_terminate;
// (XI): Waiting for BSY & DRQ to clear:
while (ide_read(c, channel, ATA_REG_STATUS) & (ATA_SR_BSY | ATA_SR_DRQ));
- return 0; // Easy, ... Isn't it?
+ return 0;
+
+must_terminate:
+ // TODO : end communication with device...
+ dbg_printf("TODO (pciide may be stuck)\n");
+ return 5;
}
static uint8_t ide_read_sectors(ide_controller_t *c, uint8_t drive,
diff --git a/src/kernel/include/process.h b/src/kernel/include/process.h
index 1b4012f..def3397 100644
--- a/src/kernel/include/process.h
+++ b/src/kernel/include/process.h
@@ -56,23 +56,29 @@ typedef struct process {
struct process *parent;
struct process *next_child;
struct process *children;
+
+ mutex_t lock;
} process_t;
typedef void* proc_entry_t;
// ---- Process creation, deletion, waiting, etc.
-// Simple semantics : when a process exits, all its ressources are freed immediately
-// except for the process_t that remains attached to the parent process until it does
-// a wait() and acknowleges the process' ending
-// When a process exits, all the children are orphaned and nobody can wait on them anymore,
-// which is a bad thing : a user process must always wait for all its children !
+// Simple semantics :
+// - When a process exits, all its ressources are freed immediately
+// except for the process_t that remains attached to the parent process until it does
+// a wait() and acknowleges the process' ending
+// - When a process exits, if it has living children then they are terminated too. (a process
+// is seen as a "virtual machine", and spawning sub-process is seen as "sharing some ressources
+// it was allocated to create a new VM", therefore removing the ressource allocated to the
+// parent implies removing the ressource from its children too)
process_t *current_process();
process_t *new_process(process_t *parent);
bool start_process(process_t *p, proc_entry_t entry); // maps a region for user stack
-void process_exit(process_t *p, int status, int exit_code); // exit current process
+void process_exit(process_t *p, int status, int exit_code); // exit specified process (must not be current process)
+void current_process_exit(int status, int exit_code);
bool process_new_thread(process_t *p, proc_entry_t entry, void* sp);
void process_thread_deleted(thread_t *t); // called by threading code when a thread exits
diff --git a/src/kernel/include/thread.h b/src/kernel/include/thread.h
index 163045d..2d385ea 100644
--- a/src/kernel/include/thread.h
+++ b/src/kernel/include/thread.h
@@ -5,6 +5,7 @@
#include <region.h>
#include <idt.h>
+#define T_STATE_LOADING 0
#define T_STATE_RUNNING 1
#define T_STATE_PAUSED 2
#define T_STATE_FINISHED 3
@@ -34,21 +35,25 @@ typedef struct thread {
struct thread *next_in_queue;
struct thread *next_in_proc;
+
+ void* waiting_on;
+ bool must_exit;
} thread_t;
typedef void (*entry_t)(void*);
void threading_setup(entry_t cont, void* data); // never returns
-thread_t *new_thread(entry_t entry, void* data); // thread is PAUSED, and must be resume_thread'ed
+thread_t *new_thread(entry_t entry, void* data); // thread is PAUSED, and must be started with start_thread
+void start_thread(thread_t *t);
extern thread_t *current_thread;
void yield();
-void pause();
void exit();
void usleep(int usecs);
+bool wait_on(void* x); // true : resumed normally, false : resumed because thread was killed, or someone else already waiting
-bool resume_thread(thread_t *thread);
+bool resume_on(void* x);
void kill_thread(thread_t *thread); // cannot be called for current thread
// Kernel critical sections
diff --git a/src/kernel/user/process.c b/src/kernel/user/process.c
index a003143..4514218 100644
--- a/src/kernel/user/process.c
+++ b/src/kernel/user/process.c
@@ -50,10 +50,26 @@ process_t *new_process(process_t *parent) {
proc->last_ran = 0;
proc->regions = 0;
proc->threads = 0;
- proc->pid = (next_pid++);
+
proc->parent = parent;
+ proc->children = 0;
+ proc->next_child = 0;
+
proc->next_fd = 1;
+ proc->pid = (next_pid++);
proc->status = PS_LOADING;
+ proc->exit_code = 0;
+
+ proc->lock = MUTEX_UNLOCKED;
+
+ if (parent != 0) {
+ mutex_lock(&parent->lock);
+
+ proc->next_child = parent->children;
+ parent->children = proc;
+
+ mutex_unlock(&parent->lock);
+ }
return proc;
@@ -111,14 +127,14 @@ bool process_new_thread(process_t *p, proc_entry_t entry, void* sp) {
th->proc = p;
th->user_ex_handler = proc_user_exception;
- { int st = enter_critical(CL_NOSWITCH); // it's a bit complicated to use mutexes on process_t
+ { mutex_lock(&p->lock);
th->next_in_proc = p->threads;
p->threads = th;
- exit_critical(st); }
+ mutex_unlock(&p->lock); }
- resume_thread(th);
+ start_thread(th);
return true;
}
@@ -138,41 +154,59 @@ bool start_process(process_t *p, void* entry) {
return true;
}
-void process_exit(process_t *p, int status, int exit_code) {
- // --- Make sure we are not running in a thread we are about to kill
-
+void current_process_exit(int status, int exit_code) {
void process_exit_v(void* args) {
exit_data_t *d = (exit_data_t*)args;
process_exit(d->proc, d->status, d->exit_code);
free(d);
}
- if (current_process() == p) {
- exit_data_t *d = (exit_data_t*)malloc(sizeof(exit_data_t));
+ exit_data_t *d = (exit_data_t*)malloc(sizeof(exit_data_t));
+
+ d->proc = current_process();;
+ d->status = status;
+ d->exit_code = exit_code;
- d->proc = p;
- d->status = status;
- d->exit_code = exit_code;
+ worker_push(process_exit_v, d);
+ exit();
+}
+
+void process_exit(process_t *p, int status, int exit_code) {
+ // --- Make sure we are not running in a thread we are about to kill
+ ASSERT(current_process() != p);
- worker_push(process_exit_v, d);
- pause();
+ // ---- Check we are not killing init process
+ if (p->parent == 0) {
+ PANIC("Attempted to exit init process!");
}
// ---- Now we can do the actual cleanup
- int st = enter_critical(CL_NOSWITCH);
+ mutex_lock(&p->lock);
+ ASSERT(p->status == PS_RUNNING || p->status == PS_LOADING);
p->status = status;
p->exit_code = exit_code;
+ // neutralize the process
while (p->threads != 0) {
thread_t *t = p->threads;
p->threads = p->threads->next_in_proc;
- t->proc = 0;
+ t->proc = 0; // we don't want process_thread_deleted to be called
kill_thread(t);
}
+ // terminate all the children as well and free associated process_t structures
+ while (p->children != 0) {
+ process_t *ch = p->children;
+ p->children = ch->next_child;
+
+ if (ch->status == PS_RUNNING || ch->status == PS_LOADING)
+ process_exit(ch, PS_KILLED, 0);
+ free(ch);
+ }
+
// release file descriptors
void release_fd(void* a, void* fd) {
unref_file((fs_handle_t*)fd);
@@ -202,29 +236,20 @@ void process_exit(process_t *p, int status, int exit_code) {
delete_pagedir(p->pd);
p->pd = 0;
- // orphan children
- while (p->children != 0) {
- process_t *c = p->children;
- p->children = c->next_child;
-
- c->parent = 0;
- c->next_child = 0;
- // TODO : if c was terminated, free it or what ?
- }
-
- if (p->parent == 0) {
- free(p);
- } else {
- // TODO : notify parent
+ // notify parent
+ process_t *par = p->parent;
+ if (par->status == PS_RUNNING) {
+ // TODO: notify that child is exited
}
- exit_critical(st);
+ mutex_unlock(&p->lock);
}
void process_thread_deleted(thread_t *t) {
- int st = enter_critical(CL_NOSWITCH);
-
process_t *p = t->proc;
+
+ mutex_lock(&p->lock);
+
if (p->threads == t) {
p->threads = t->next_in_proc;
} else {
@@ -236,10 +261,10 @@ void process_thread_deleted(thread_t *t) {
}
}
+ mutex_unlock(&p->lock);
+
if (p->threads == 0 && p->status == PS_RUNNING)
process_exit(p, PS_FINISHED, 0);
-
- exit_critical(st);
}
@@ -457,7 +482,7 @@ bool munmap(process_t *proc, void* addr) {
static void proc_user_exception(registers_t *regs) {
dbg_printf("Usermode exception in user process : exiting.\n");
dbg_dump_registers(regs);
- process_exit(current_process(), PS_FAILURE, FAIL_EXCEPTION);
+ current_process_exit(PS_FAILURE, FAIL_EXCEPTION);
}
static void proc_usermem_pf(void* p, registers_t *regs, void* addr) {
process_t *proc = (process_t*)p;
@@ -466,13 +491,13 @@ static void proc_usermem_pf(void* p, registers_t *regs, void* addr) {
if (r == 0) {
dbg_printf("Segmentation fault in process %d (0x%p : not mapped) : exiting.\n", proc->pid, addr);
dbg_dump_registers(regs);
- process_exit(current_process(), PS_FAILURE, (addr < (void*)PAGE_SIZE ? FAIL_ZEROPTR : FAIL_SEGFAULT));
+ current_process_exit(PS_FAILURE, (addr < (void*)PAGE_SIZE ? FAIL_ZEROPTR : FAIL_SEGFAULT));
}
bool wr = ((regs->err_code & PF_WRITE_BIT) != 0);
if (wr && !(r->mode & MM_WRITE)) {
dbg_printf("Segmentation fault in process %d (0x%p : not allowed to write) : exiting.\n", proc->pid, addr);
- process_exit(current_process(), PS_FAILURE, (addr < (void*)PAGE_SIZE ? FAIL_ZEROPTR : FAIL_SEGFAULT));
+ current_process_exit(PS_FAILURE, (addr < (void*)PAGE_SIZE ? FAIL_ZEROPTR : FAIL_SEGFAULT));
}
bool pr = ((regs->err_code & PF_PRESENT_BIT) != 0);
@@ -506,7 +531,7 @@ void probe_for_read(const void* addr, size_t len) {
if (r == 0 || addr + len > r->addr + r->size || !(r->mode & MM_READ)) {
dbg_printf("Access violation on read at 0x%p len 0x%p in process %d : exiting.\n",
addr, len, proc->pid);
- process_exit(current_process(), PS_FAILURE, FAIL_SC_SEGFAULT);
+ current_process_exit(PS_FAILURE, FAIL_SC_SEGFAULT);
}
}
@@ -516,7 +541,7 @@ void probe_for_write(const void* addr, size_t len) {
if (r == 0 || addr + len > r->addr + r->size || !(r->mode & MM_WRITE)) {
dbg_printf("Access violation on write at 0x%p len 0x%p in process %d : exiting.\n",
addr, len, proc->pid);
- process_exit(current_process(), PS_FAILURE, FAIL_SC_SEGFAULT);
+ current_process_exit(PS_FAILURE, FAIL_SC_SEGFAULT);
}
}
diff --git a/src/kernel/user/syscall.c b/src/kernel/user/syscall.c
index d9131b2..73b3341 100644
--- a/src/kernel/user/syscall.c
+++ b/src/kernel/user/syscall.c
@@ -34,7 +34,7 @@ static char* sc_copy_string(uint32_t s, uint32_t slen) {
static uint32_t exit_sc(sc_args_t args) {
dbg_printf("Proc %d exit with code %d\n", current_process()->pid, args.a);
- process_exit(current_process(), PS_FINISHED, args.a);
+ current_process_exit(PS_FINISHED, args.a);
ASSERT(false);
return 0;
}