From 9177166817bf9d07aa427a5b8577faf9d1d3a6e3 Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Mon, 19 Oct 2009 00:33:10 +0300 Subject: [PATCH] Managed to self-destruct pager. Issues: - A page-faulting thread suspends if receives -1 from pager page fault ipc. This is fine if pager is about to delete the thread, but it is not if it is a buggy pager. - Need to find a way to completely get rid of suspended pager. - A method of deleting suspended tasks could remedy both cases above. --- conts/posix/mm0/main.c | 7 ++++ include/l4/generic/tcb.h | 1 + include/l4/generic/thread.h | 3 ++ include/l4/macros.h | 2 + src/api/ipc.c | 5 ++- src/api/space.c | 2 +- src/api/thread.c | 83 +++++++++++++++++++++++++++++++++---- src/arch/arm/exception.c | 51 +++++++++++++++-------- src/generic/container.c | 8 ++-- src/generic/scheduler.c | 36 +++++++++++++--- 10 files changed, 162 insertions(+), 36 deletions(-) diff --git a/conts/posix/mm0/main.c b/conts/posix/mm0/main.c index cb6f0c6..828c275 100644 --- a/conts/posix/mm0/main.c +++ b/conts/posix/mm0/main.c @@ -187,10 +187,17 @@ void handle_requests(void) void main(void) { + struct task_ids ids; + + l4_getid(&ids); + printf("\n%s: Started with thread id %d\n", __TASKNAME__, self_tid()); init(); + printf("\n%s: Destroying self (%d), along with any tasks.\n", __TASKNAME__, self_tid()); + l4_thread_control(THREAD_DESTROY, &ids); + printf("%s: Memory/Process manager initialized. Listening requests.\n", __TASKNAME__); while (1) { handle_requests(); diff --git a/include/l4/generic/tcb.h b/include/l4/generic/tcb.h index 2ef077a..0fc504c 100644 --- a/include/l4/generic/tcb.h +++ b/include/l4/generic/tcb.h @@ -28,6 +28,7 @@ #define TASK_INTERRUPTED (1 << 0) #define TASK_SUSPENDING (1 << 1) #define TASK_RESUMING (1 << 2) +#define TASK_EXITING (1 << 3) /* Task states */ enum task_state { diff --git a/include/l4/generic/thread.h b/include/l4/generic/thread.h index eab4809..5389c13 100644 --- a/include/l4/generic/thread.h +++ b/include/l4/generic/thread.h @@ -4,11 +4,14 @@ #ifndef __THREAD_H__ #define __THREAD_H__ +#include /* Thread id creation and deleting */ void thread_id_pool_init(void); int thread_id_new(void); int thread_id_del(int tid); +void thread_destroy_self(void); +int thread_destroy(struct task_ids *ids); #endif /* __THREAD_H__ */ diff --git a/include/l4/macros.h b/include/l4/macros.h index cb21ed9..e8ef5f5 100644 --- a/include/l4/macros.h +++ b/include/l4/macros.h @@ -91,6 +91,8 @@ static inline int is_err(int x) #define BUG_ON(x) {if (x) BUG();} +#define WARN_ON(x) {if (x) printk("%s, %s, %s: Warning something is off here.\n", __FILE__, __FUNCTION__, __LINE__); } + #define BUG_ON_MSG(msg, x) do { \ printk(msg); \ BUG_ON(x) \ diff --git a/src/api/ipc.c b/src/api/ipc.c index 4139cd1..5bf6a28 100644 --- a/src/api/ipc.c +++ b/src/api/ipc.c @@ -215,10 +215,13 @@ int ipc_handle_errors(void) /* Interruptible ipc */ int ipc_send(l4id_t recv_tid, unsigned int flags) { - struct ktcb *receiver = tcb_find(recv_tid); + struct ktcb *receiver; struct waitqueue_head *wqhs, *wqhr; int ret = 0; + if (!(receiver = tcb_find(recv_tid))) + return -ESRCH; + wqhs = &receiver->wqh_send; wqhr = &receiver->wqh_recv; diff --git a/src/api/space.c b/src/api/space.c index a860c26..8c19775 100644 --- a/src/api/space.c +++ b/src/api/space.c @@ -25,7 +25,7 @@ int sys_map(unsigned long phys, unsigned long virt, unsigned long npages, return -EINVAL; found: - printk("%s (%d) Mapping from 0x%lx to 0x%lxp, %lu pages\n", __FUNCTION__, tid, phys, virt, npages); +// printk("%s (%d) Mapping from 0x%lx to 0x%lxp, %lu pages\n", __FUNCTION__, tid, phys, virt, npages); add_mapping_pgd(phys, virt, npages << PAGE_BITS, flags, TASK_PGD(target)); return 0; diff --git a/src/api/thread.c b/src/api/thread.c index a41a929..72680af 100644 --- a/src/api/thread.c +++ b/src/api/thread.c @@ -4,6 +4,7 @@ * Copyright (C) 2007 Bahadir Balban */ #include +#include #include #include #include @@ -25,12 +26,12 @@ int sys_thread_switch(void) * This suspends a thread which is in either suspended, * sleeping or runnable state. */ -int thread_suspend(struct task_ids *ids) +int thread_suspend(l4id_t tid) { struct ktcb *task; int ret = 0; - if (!(task = tcb_find(ids->tid))) + if (!(task = tcb_find(tid))) return -ESRCH; if (task->state == TASK_INACTIVE) @@ -90,7 +91,7 @@ int thread_recycle(struct task_ids *ids) if (!(task = tcb_find(ids->tid))) return -ESRCH; - if ((ret = thread_suspend(ids)) < 0) + if ((ret = thread_suspend(ids->tid)) < 0) return ret; /* @@ -112,15 +113,27 @@ int thread_recycle(struct task_ids *ids) return 0; } -int thread_destroy(struct task_ids *ids) +void thread_destroy_self(); + +int thread_destroy(l4id_t tid) { struct ktcb *task; int ret; - if (!(task = tcb_find(ids->tid))) + printk("%s: Destroying (%d)\n", __FUNCTION__, tid); + + /* + * Pager destroying itself + */ + if (tid == current->tid) { + thread_destroy_self(); + BUG(); + } + + if (!(task = tcb_find(tid))) return -ESRCH; - if ((ret = thread_suspend(ids)) < 0) + if ((ret = thread_suspend(tid)) < 0) return ret; /* Remove tcb from global list so any callers will get -ESRCH */ @@ -139,6 +152,60 @@ int thread_destroy(struct task_ids *ids) return 0; } +/* + * Pagers destroy themselves either by accessing an illegal + * address or voluntarily. All threads managed also get + * destroyed. + */ +void thread_destroy_self(void) +{ + struct ktcb *task, *n; + + /* Destroy all threads under control of this pager */ + spin_lock(&curcont->ktcb_list.list_lock); + list_foreach_removable_struct(task, n, + &curcont->ktcb_list.list, + task_list) { + if (task->tid == current->tid) + continue; + spin_unlock(&curcont->ktcb_list.list_lock); + thread_destroy(task->tid); + spin_lock(&curcont->ktcb_list.list_lock); + } + spin_unlock(&curcont->ktcb_list.list_lock); + + /* + * Indicate intention to destroy to any + * destroyer code we will add later on + */ + current->flags |= TASK_EXITING; + + tcb_remove(current); + + /* + * If there are any sleepers on any of the task's + * waitqueues, we need to wake those tasks up. + * + * These could be tasks that have called us from + * other containers. + */ + wake_up_all(¤t->wqh_send, 0); + wake_up_all(¤t->wqh_recv, 0); + + printk("%s: Suspending self (%d)\n", __FUNCTION__, current->tid); + + /* Remain as a zombie for now */ + sched_suspend_sync(); + + /* NOTE: + * If we deleted ourself here, probably that would be + * the end of what we need to do and could get away + * with that because ktcb's are cached and always mapped. + * It wouldn't hurt to delete ourself inside a + * non-preemptable point and schedule never to return. + */ +} + int thread_resume(struct task_ids *ids) { struct ktcb *task; @@ -365,13 +432,13 @@ int sys_thread_control(unsigned int flags, struct task_ids *ids) ret = thread_start(ids); break; case THREAD_SUSPEND: - ret = thread_suspend(ids); + ret = thread_suspend(ids->tid); break; case THREAD_RESUME: ret = thread_resume(ids); break; case THREAD_DESTROY: - ret = thread_destroy(ids); + ret = thread_destroy(ids->tid); break; case THREAD_RECYCLE: ret = thread_recycle(ids); diff --git a/src/arch/arm/exception.c b/src/arch/arm/exception.c index 29686df..3282191 100644 --- a/src/arch/arm/exception.c +++ b/src/arch/arm/exception.c @@ -4,6 +4,8 @@ * Copyright (C) 2007, 2008 Bahadir Balban */ #include +#include +#include #include #include #include @@ -62,9 +64,14 @@ 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 err; + /* mr[0] has the fault tag. The rest is the fault structure */ - u32 mr[MR_TOTAL] = { [MR_TAG] = L4_IPC_TAG_PFAULT, - [MR_SENDER] = current->tid }; + u32 mr[MR_TOTAL] = { + [MR_TAG] = L4_IPC_TAG_PFAULT, + [MR_SENDER] = current->tid + }; + fault_kdata_t *fault = (fault_kdata_t *)&mr[MR_UNUSED_START]; /* Fill in fault information to pass over during ipc */ @@ -94,16 +101,22 @@ void fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far) /* Set current flags to short ipc */ tcb_set_ipc_flags(current, IPC_FLAGS_SHORT); - /* Send ipc to the task's pager */ - ipc_sendrecv(current->pagerid, current->pagerid, 0); + /* Detect if a pager is self-faulting */ + if (current->tid == current->pagerid) { + printk("Pager (%d) self-faulting. Exiting.\n", + current->tid); + thread_destroy_self(); + } - /* - * FIXME: CHECK TASK KILL REPLY !!! - * Here, pager has handled the request and sent us back a message. - * It is natural that a pager might want to kill the task due to - * illegal access. Here we ought to check this and kill it rather - * than return back to it. - */ + /* 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). Suspending.\n", + // current->tid, err); + BUG_ON(current->nlocks); + sched_suspend_sync(); + } } /* @@ -226,11 +239,6 @@ void data_abort_handler(u32 faulted_pc, u32 fsr, u32 far) { set_abort_type(fsr, ARM_DABT); - /* - * FIXME: Find why if we use a clause like if tid == PAGER_TID - * this prints just the faulted_pc but not the text "Data abort @ PC:" - * Strange. - */ dbg_abort("Data abort @ PC: ", faulted_pc); /* Check for more details */ @@ -244,6 +252,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: @@ -256,7 +269,6 @@ error: while (1) ; } - void prefetch_abort_handler(u32 faulted_pc, u32 fsr, u32 far, u32 lr) { set_abort_type(fsr, ARM_PABT); @@ -268,6 +280,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/container.c b/src/generic/container.c index 25bcf90..9d88c1b 100644 --- a/src/generic/container.c +++ b/src/generic/container.c @@ -162,9 +162,10 @@ int init_first_pager(struct pager *pager, space->pgd = current_pgd; address_space_attach(task, space); - /* Initialize container relationships */ + /* Initialize container/pager relationships */ pager->tcb = task; task->pager = pager; + task->pagerid = task->tid; task->container = cont; task->cap_list_ptr = &pager->cap_list; @@ -220,12 +221,13 @@ int init_pager(struct pager *pager, struct container *cont) task->space = address_space_create(0); - /* Initialize container relationships */ + /* Initialize container/pager relationships */ pager->tcb = task; task->pager = pager; task->container = cont; - task->cap_list_ptr = &pager->cap_list; + task->pagerid = task->tid; + task->cap_list_ptr = &pager->cap_list; add_mapping_pgd(pager->start_lma, pager->start_vma, page_align_up(pager->memsize), MAP_USR_DEFAULT_FLAGS, TASK_PGD(task)); diff --git a/src/generic/scheduler.c b/src/generic/scheduler.c index 37af6ef..cd4d8d9 100644 --- a/src/generic/scheduler.c +++ b/src/generic/scheduler.c @@ -236,11 +236,23 @@ void sched_suspend_sync(void) current->state = TASK_INACTIVE; current->flags &= ~TASK_SUSPENDING; scheduler.prio_total -= current->priority; - BUG_ON(scheduler.prio_total <= 0); + BUG_ON(scheduler.prio_total < 0); preempt_enable(); - /* Async wake up any waiters */ - wake_up_task(tcb_find(current->pagerid), 0); + /* + * Async wake up any waiting pagers + * + * If we're not a pager, then a pager must have + * signalled us to suspend, and it must have been + * waiting for us to wake it up when we suspend. + * We do it here. + * + * If though, we _are_ a pager that is suspending, + * we silently do so. Noone is waiting us. + */ + if (current->pagerid != current->tid) + wake_up_task(tcb_find(current->pagerid), 0); + schedule(); } @@ -251,13 +263,25 @@ void sched_suspend_async(void) current->state = TASK_INACTIVE; current->flags &= ~TASK_SUSPENDING; scheduler.prio_total -= current->priority; - BUG_ON(scheduler.prio_total <= 0); + BUG_ON(scheduler.prio_total < 0); /* This will make sure we yield soon */ preempt_enable(); - /* Async wake up any waiters */ - wake_up_task(tcb_find(current->pagerid), 0); + /* + * Async wake up any waiting pagers + * + * If we're not a pager, then a pager must have + * signalled us to suspend, and it must have been + * waiting for us to wake it up when we suspend. + * We do it here. + * + * If though, we _are_ a pager that is suspending, + * we silently do so. Noone is waiting us. + */ + if (current->pagerid != current->tid) + wake_up_task(tcb_find(current->pagerid), 0); + need_resched = 1; }