From 5ed93b65630b550aaed81042366da3d64c99982c Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Sat, 31 Oct 2009 22:46:29 +0200 Subject: [PATCH] Thread exiting Pagers kill all children but suspend themselves. Currently not straightforward for a pager to delete its own tcb and quit. It should take all allocator locks without sleeping, remove itself from scheduler queue and then delete itself and quit. This is not so easy now as some allocation locks are mutexes. (Address space lock, ktcb/space allocators etc.) An easier approach would be to have a kernel thread or a superior thread that would delete the pager --- src/api/thread.c | 98 ++++++++++++++++------------------------ src/arch/arm/exception.c | 40 ++++++++++++---- src/generic/scheduler.c | 62 ------------------------- src/glue/arm/systable.c | 3 -- 4 files changed, 69 insertions(+), 134 deletions(-) diff --git a/src/api/thread.c b/src/api/thread.c index bca194e..f39aa39 100644 --- a/src/api/thread.c +++ b/src/api/thread.c @@ -60,72 +60,15 @@ static inline int TASK_IS_CHILD(struct ktcb *task) ((task)->pagerid == current->tid)); } -int thread_delete_children(void) +int thread_destroy_child(struct ktcb *task) { - struct ktcb *task, *n; - - spin_lock(&curcont->ktcb_list.list_lock); - list_foreach_removable_struct(task, n, - &curcont->ktcb_list.list, - task_list) { - if (TASK_IS_CHILD(task)) { - spin_unlock(&curcont->ktcb_list.list_lock); - tcb_remove(task); - wake_up_all(¤t->wqh_send, 0); - wake_up_all(¤t->wqh_recv, 0); - BUG_ON(task->wqh_pager.sleepers > 0); - BUG_ON(task->state != TASK_INACTIVE); - tcb_delete(task); - spin_lock(&curcont->ktcb_list.list_lock); - } - } - spin_unlock(&curcont->ktcb_list.list_lock); - return 0; -} - -int thread_suspend_children(void) -{ - struct ktcb *task, *n; - - spin_lock(&curcont->ktcb_list.list_lock); - list_foreach_removable_struct(task, n, - &curcont->ktcb_list.list, - task_list) { - if (TASK_IS_CHILD(task)) { - spin_unlock(&curcont->ktcb_list.list_lock); - thread_suspend(task); - spin_lock(&curcont->ktcb_list.list_lock); - } - } - spin_unlock(&curcont->ktcb_list.list_lock); - return 0; - -} - -/* - * Put them in TASK_DEAD so that a suspended exiting thread - * does not run again if issued THREAD_RUN - */ -int thread_destroy(struct ktcb *task) -{ - if (task == current) { - if (current->tid == current->pagerid) { - /* Suspend children */ - thread_suspend_children(); - thread_delete_children(); - sched_exit_pager(); - } else { - sched_suspend_sync(); - } - return 0; - } thread_suspend(task); tcb_remove(task); /* Wake up waiters */ - wake_up_all(&task->wqh_send, 0); - wake_up_all(&task->wqh_recv, 0); + wake_up_all(&task->wqh_send, WAKEUP_INTERRUPT); + wake_up_all(&task->wqh_recv, WAKEUP_INTERRUPT); BUG_ON(task->wqh_pager.sleepers > 0); BUG_ON(task->state != TASK_INACTIVE); @@ -134,6 +77,41 @@ int thread_destroy(struct ktcb *task) return 0; } +int thread_destroy_children(void) +{ + struct ktcb *task, *n; + + spin_lock(&curcont->ktcb_list.list_lock); + list_foreach_removable_struct(task, n, + &curcont->ktcb_list.list, + task_list) { + if (TASK_IS_CHILD(task)) { + spin_unlock(&curcont->ktcb_list.list_lock); + thread_destroy_child(task); + spin_lock(&curcont->ktcb_list.list_lock); + } + } + spin_unlock(&curcont->ktcb_list.list_lock); + return 0; + +} + +void thread_destroy_self() +{ + thread_destroy_children(); + + sched_suspend_sync(); +} + +int thread_destroy(struct ktcb *task) +{ + if (TASK_IS_CHILD(task)) + return thread_destroy_child(task); + else if (task == current) + thread_destroy_self(); + return 0; +} + int arch_clear_thread(struct ktcb *tcb) { /* Remove from the global list */ diff --git a/src/arch/arm/exception.c b/src/arch/arm/exception.c index fa88618..03e95b2 100644 --- a/src/arch/arm/exception.c +++ b/src/arch/arm/exception.c @@ -62,7 +62,7 @@ void ipc_restore_state(struct ipc_state *state) } /* Send data fault ipc to the faulty task's pager */ -void fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far) +int fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far) { int err; @@ -119,15 +119,24 @@ void fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far) /* Send ipc to the task's pager */ if ((err = ipc_sendrecv(current->pagerid, current->pagerid, 0)) < 0) { - printk("Thread (%d) faulted in kernel and its pager " - "returned error (%d). Suspend and exiting thread.\n", - current->tid, err); - BUG_ON(current->nlocks); + BUG_ON(current->nlocks); - /* Exit as if signalled */ - current->flags |= TASK_EXITING; - sched_exit_sync(); + /* Return on interrupt */ + if (err == -EINTR) { + printk("Thread (%d) page-faulted " + "and got interrupted by its pager.\n", + current->tid); + return err; + } else { /* Suspend on any other error */ + printk("Thread (%d) faulted in kernel " + "and an error occured during " + "page-fault ipc. err=%d. Suspending task.\n", + current->tid, err); + current->flags |= TASK_SUSPENDING; + sched_suspend_sync(); + } } + return 0; } /* @@ -141,6 +150,7 @@ void fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far) int pager_pagein_request(unsigned long addr, unsigned long size, unsigned int flags) { + int err; u32 abort = 0; unsigned long npages = __pfn(align_up(size, PAGE_SIZE)); struct ipc_state ipc_state; @@ -154,7 +164,9 @@ int pager_pagein_request(unsigned long addr, unsigned long size, /* For every page to be used by the kernel send a page-in request */ for (int i = 0; i < npages; i++) - fault_ipc_to_pager(0, abort, addr + (i * PAGE_SIZE)); + if ((err = fault_ipc_to_pager(0, abort, + addr + (i * PAGE_SIZE))) < 0) + return err; /* Restore ipc state */ ipc_restore_state(&ipc_state); @@ -266,6 +278,11 @@ void data_abort_handler(u32 faulted_pc, u32 fsr, u32 far) /* This notifies the pager */ fault_ipc_to_pager(faulted_pc, fsr, far); + + if (current->flags & TASK_SUSPENDING) { + BUG_ON(current->nlocks); + sched_suspend_sync(); + } return; error: @@ -290,6 +307,11 @@ void prefetch_abort_handler(u32 faulted_pc, u32 fsr, u32 far, u32 lr) if (KERN_ADDR(lr)) goto error; fault_ipc_to_pager(faulted_pc, fsr, far); + + if (current->flags & TASK_SUSPENDING) { + BUG_ON(current->nlocks); + sched_suspend_sync(); + } return; error: diff --git a/src/generic/scheduler.c b/src/generic/scheduler.c index 2999e46..0997a3a 100644 --- a/src/generic/scheduler.c +++ b/src/generic/scheduler.c @@ -225,68 +225,6 @@ void sched_resume_async(struct ktcb *task) RQ_ADD_FRONT); } -/* - * A self-paging thread deletes itself, - * schedules and disappears from the system. - */ -void sched_exit_pager(void) -{ - // printk("Pager (%d) Exiting...\n", current->tid); - /* Remove from its list, callers get -ESRCH */ - tcb_remove(current); - - /* - * If there are any sleepers on any of the task's - * waitqueues, we need to wake those tasks up. - */ - wake_up_all(¤t->wqh_send, 0); - wake_up_all(¤t->wqh_recv, 0); - - /* - * We're a self-paging thread. We're gonna - * delete ourself and disappear from the - * system as soon as we schedule - */ - preempt_disable(); - - /* - * FIXME: TOO LONG! - */ - tcb_delete(current); - - sched_rq_remove_task(current); - current->state = TASK_INACTIVE; - scheduler.prio_total -= current->priority; - BUG_ON(scheduler.prio_total < 0); - - /* As soon as we schedule, we're gone */ - preempt_enable(); - schedule(); - BUG(); -} - -void sched_exit_sync(void) -{ - struct ktcb *pager = tcb_find(current->pagerid); - - /* Go to exit list */ - ktcb_list_add(current, &pager->child_exit_list); - - preempt_disable(); - - /* Hint pager we're ready */ - wake_up(¤t->wqh_pager, 0); - - sched_rq_remove_task(current); - current->state = TASK_DEAD; - current->flags &= ~TASK_EXITING; - preempt_enable(); - - /* Quit */ - schedule(); - BUG(); -} - /* * NOTE: Could do these as sched_prepare_suspend() * + schedule() or need_resched = 1 diff --git a/src/glue/arm/systable.c b/src/glue/arm/systable.c index 542251d..83a61ce 100644 --- a/src/glue/arm/systable.c +++ b/src/glue/arm/systable.c @@ -170,9 +170,6 @@ int syscall(syscall_context_t *regs, unsigned long swi_addr) if (current->flags & TASK_SUSPENDING) { BUG_ON(current->nlocks); sched_suspend_sync(); - } else if (current->flags & TASK_EXITING) { - BUG_ON(current->nlocks); - sched_exit_sync(); } return ret;