mirror of
https://github.com/drasko/codezero.git
synced 2026-03-03 02:53:15 +01:00
Added support for faulty pagers and their threads to become zombies
Added support for pagers that fault to suspend and become zombies along with all the threads that they manage. Zombie killing is to be done at a later time, from this special zombie queue. The implementation works same as a suspension, with the added action that the thread is moved to a queue in kernel container.
This commit is contained in:
@@ -8,8 +8,6 @@
|
|||||||
#define __RESOURCES_H__
|
#define __RESOURCES_H__
|
||||||
|
|
||||||
/* Number of containers defined at compile-time */
|
/* Number of containers defined at compile-time */
|
||||||
#include <l4/config.h>
|
|
||||||
|
|
||||||
#include <l4/generic/capability.h>
|
#include <l4/generic/capability.h>
|
||||||
#include <l4/lib/idpool.h>
|
#include <l4/lib/idpool.h>
|
||||||
#include INC_SUBARCH(mm.h)
|
#include INC_SUBARCH(mm.h)
|
||||||
@@ -41,6 +39,13 @@ container_head_init(struct container_head *chead)
|
|||||||
link_init(&chead->list);
|
link_init(&chead->list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hash table for all existing tasks */
|
||||||
|
struct ktcb_list {
|
||||||
|
struct link list;
|
||||||
|
struct spinlock list_lock;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Everything on the platform is described and stored
|
* Everything on the platform is described and stored
|
||||||
* in the structure below.
|
* in the structure below.
|
||||||
@@ -81,6 +86,9 @@ struct kernel_container {
|
|||||||
struct mem_cache *mutex_cache;
|
struct mem_cache *mutex_cache;
|
||||||
struct mem_cache *cap_cache;
|
struct mem_cache *cap_cache;
|
||||||
struct mem_cache *cont_cache;
|
struct mem_cache *cont_cache;
|
||||||
|
|
||||||
|
/* Zombie thread list */
|
||||||
|
struct ktcb_list zombie_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct kernel_container kernel_container;
|
extern struct kernel_container kernel_container;
|
||||||
|
|||||||
@@ -133,13 +133,6 @@ union ktcb_union {
|
|||||||
char kstack[PAGE_SIZE];
|
char kstack[PAGE_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Hash table for all existing tasks */
|
|
||||||
struct ktcb_list {
|
|
||||||
struct link list;
|
|
||||||
struct spinlock list_lock;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each task is allocated a unique global id. A thread group can only belong to
|
* Each task is allocated a unique global id. A thread group can only belong to
|
||||||
* a single leader, and every thread can only belong to a single thread group.
|
* a single leader, and every thread can only belong to a single thread group.
|
||||||
@@ -161,6 +154,8 @@ void tcb_init(struct ktcb *tcb);
|
|||||||
struct ktcb *tcb_alloc_init(void);
|
struct ktcb *tcb_alloc_init(void);
|
||||||
void tcb_delete(struct ktcb *tcb);
|
void tcb_delete(struct ktcb *tcb);
|
||||||
|
|
||||||
|
|
||||||
|
void ktcb_list_add(struct ktcb *new, struct ktcb_list *ktcb_list);
|
||||||
void init_ktcb_list(struct ktcb_list *ktcb_list);
|
void init_ktcb_list(struct ktcb_list *ktcb_list);
|
||||||
void task_update_utcb(struct ktcb *task);
|
void task_update_utcb(struct ktcb *task);
|
||||||
int tcb_check_and_lazy_map_utcb(struct ktcb *task);
|
int tcb_check_and_lazy_map_utcb(struct ktcb *task);
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ void thread_id_pool_init(void);
|
|||||||
int thread_id_new(void);
|
int thread_id_new(void);
|
||||||
int thread_id_del(int tid);
|
int thread_id_del(int tid);
|
||||||
|
|
||||||
void thread_destroy_self(void);
|
void thread_destroy_current(void);
|
||||||
int thread_destroy(struct task_ids *ids);
|
int thread_destroy(struct task_ids *ids);
|
||||||
|
void thread_make_zombie(struct ktcb *task);
|
||||||
|
|
||||||
#endif /* __THREAD_H__ */
|
#endif /* __THREAD_H__ */
|
||||||
|
|||||||
@@ -24,9 +24,24 @@ int sys_thread_switch(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* This suspends a thread which is in either suspended,
|
* This suspends a thread which is in either suspended,
|
||||||
* sleeping or runnable state.
|
* sleeping or runnable state. `flags' field specifies an additional
|
||||||
|
* status for the thread, that implies an additional action as well
|
||||||
|
* as suspending. For example, a TASK_EXITING flag ensures the task
|
||||||
|
* is moved to a zombie queue during suspension.
|
||||||
|
*
|
||||||
|
* Why no race?
|
||||||
|
*
|
||||||
|
* There is no race between the pager setting TASK_SUSPENDING,
|
||||||
|
* and waiting for TASK_INACTIVE non-atomically because the target
|
||||||
|
* task starts suspending only when it sees TASK_SUSPENDING set and
|
||||||
|
* it only wakes up the pager after it has switched state to
|
||||||
|
* TASK_INACTIVE.
|
||||||
|
*
|
||||||
|
* If the pager hasn't come to wait_event() and the wake up call is
|
||||||
|
* already gone, the state is already TASK_INACTIVE so the pager
|
||||||
|
* won't sleep at all.
|
||||||
*/
|
*/
|
||||||
int thread_suspend(l4id_t tid)
|
int thread_suspend(l4id_t tid, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct ktcb *task;
|
struct ktcb *task;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@@ -38,7 +53,7 @@ int thread_suspend(l4id_t tid)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Signify we want to suspend the thread */
|
/* Signify we want to suspend the thread */
|
||||||
task->flags |= TASK_SUSPENDING;
|
task->flags |= TASK_SUSPENDING | flags;
|
||||||
|
|
||||||
/* Wake it up if it's sleeping */
|
/* Wake it up if it's sleeping */
|
||||||
wake_up_task(task, WAKEUP_INTERRUPT | WAKEUP_SYNC);
|
wake_up_task(task, WAKEUP_INTERRUPT | WAKEUP_SYNC);
|
||||||
@@ -91,7 +106,7 @@ int thread_recycle(struct task_ids *ids)
|
|||||||
if (!(task = tcb_find(ids->tid)))
|
if (!(task = tcb_find(ids->tid)))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
if ((ret = thread_suspend(ids->tid)) < 0)
|
if ((ret = thread_suspend(ids->tid, 0)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -113,7 +128,7 @@ int thread_recycle(struct task_ids *ids)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_destroy_self();
|
void thread_destroy_current();
|
||||||
|
|
||||||
int thread_destroy(l4id_t tid)
|
int thread_destroy(l4id_t tid)
|
||||||
{
|
{
|
||||||
@@ -126,14 +141,14 @@ int thread_destroy(l4id_t tid)
|
|||||||
* Pager destroying itself
|
* Pager destroying itself
|
||||||
*/
|
*/
|
||||||
if (tid == current->tid) {
|
if (tid == current->tid) {
|
||||||
thread_destroy_self();
|
thread_destroy_current();
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(task = tcb_find(tid)))
|
if (!(task = tcb_find(tid)))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
if ((ret = thread_suspend(tid)) < 0)
|
if ((ret = thread_suspend(tid, 0)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Remove tcb from global list so any callers will get -ESRCH */
|
/* Remove tcb from global list so any callers will get -ESRCH */
|
||||||
@@ -152,16 +167,36 @@ int thread_destroy(l4id_t tid)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void thread_make_zombie(struct ktcb *task)
|
||||||
|
{
|
||||||
|
/* Remove from its list, callers get -ESRCH */
|
||||||
|
tcb_remove(task);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are any sleepers on any of the task's
|
||||||
|
* waitqueues, we need to wake those tasks up.
|
||||||
|
*/
|
||||||
|
wake_up_all(&task->wqh_send, 0);
|
||||||
|
wake_up_all(&task->wqh_recv, 0);
|
||||||
|
|
||||||
|
printk("Thread (%d) becoming zombie. (Current: (%d))\n", task->tid, current->tid);
|
||||||
|
|
||||||
|
BUG_ON(!(task->flags & TASK_EXITING));
|
||||||
|
|
||||||
|
/* Add to zombie list, to be destroyed later */
|
||||||
|
ktcb_list_add(task, &kernel_container.zombie_list);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pagers destroy themselves either by accessing an illegal
|
* Pagers destroy themselves either by accessing an illegal
|
||||||
* address or voluntarily. All threads managed also get
|
* address or voluntarily. All threads managed also get
|
||||||
* destroyed.
|
* destroyed.
|
||||||
*/
|
*/
|
||||||
void thread_destroy_self(void)
|
void thread_destroy_current(void)
|
||||||
{
|
{
|
||||||
struct ktcb *task, *n;
|
struct ktcb *task, *n;
|
||||||
|
|
||||||
/* Destroy all threads under control of this pager */
|
/* Suspend all threads under control of this pager */
|
||||||
spin_lock(&curcont->ktcb_list.list_lock);
|
spin_lock(&curcont->ktcb_list.list_lock);
|
||||||
list_foreach_removable_struct(task, n,
|
list_foreach_removable_struct(task, n,
|
||||||
&curcont->ktcb_list.list,
|
&curcont->ktcb_list.list,
|
||||||
@@ -169,41 +204,15 @@ void thread_destroy_self(void)
|
|||||||
if (task->tid == current->tid)
|
if (task->tid == current->tid)
|
||||||
continue;
|
continue;
|
||||||
spin_unlock(&curcont->ktcb_list.list_lock);
|
spin_unlock(&curcont->ktcb_list.list_lock);
|
||||||
thread_destroy(task->tid);
|
thread_suspend(task->tid, TASK_EXITING);
|
||||||
spin_lock(&curcont->ktcb_list.list_lock);
|
spin_lock(&curcont->ktcb_list.list_lock);
|
||||||
}
|
}
|
||||||
spin_unlock(&curcont->ktcb_list.list_lock);
|
spin_unlock(&curcont->ktcb_list.list_lock);
|
||||||
|
|
||||||
/*
|
/* Indicate we want to become zombie on suspend */
|
||||||
* Indicate intention to destroy to any
|
|
||||||
* destroyer code we will add later on
|
|
||||||
*/
|
|
||||||
current->flags |= TASK_EXITING;
|
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();
|
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)
|
int thread_resume(struct task_ids *ids)
|
||||||
@@ -432,7 +441,7 @@ int sys_thread_control(unsigned int flags, struct task_ids *ids)
|
|||||||
ret = thread_start(ids);
|
ret = thread_start(ids);
|
||||||
break;
|
break;
|
||||||
case THREAD_SUSPEND:
|
case THREAD_SUSPEND:
|
||||||
ret = thread_suspend(ids->tid);
|
ret = thread_suspend(ids->tid, 0);
|
||||||
break;
|
break;
|
||||||
case THREAD_RESUME:
|
case THREAD_RESUME:
|
||||||
ret = thread_resume(ids);
|
ret = thread_resume(ids);
|
||||||
|
|||||||
@@ -79,21 +79,29 @@ void fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far)
|
|||||||
fault->fsr = fsr;
|
fault->fsr = fsr;
|
||||||
fault->far = far;
|
fault->far = far;
|
||||||
|
|
||||||
/* Write pte of the abort address, which is different on pabt/dabt */
|
/*
|
||||||
|
* Write pte of the abort address,
|
||||||
|
* which is different on pabt/dabt
|
||||||
|
*/
|
||||||
if (is_prefetch_abort(fsr))
|
if (is_prefetch_abort(fsr))
|
||||||
fault->pte = virt_to_pte(faulty_pc);
|
fault->pte = virt_to_pte(faulty_pc);
|
||||||
else
|
else
|
||||||
fault->pte = virt_to_pte(far);
|
fault->pte = virt_to_pte(far);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* System calls save arguments (and message registers) on the kernel
|
* System calls save arguments (and message registers)
|
||||||
* stack. They are then referenced from the caller's ktcb. Here, we
|
* on the kernel stack. They are then referenced from
|
||||||
* forge a fault structure as if an ipc syscall has occured. Then
|
* the caller's ktcb. Here, we forge a fault structure
|
||||||
* the reference to the fault structure is set in the ktcb such that
|
* as if an ipc syscall has occured. Then the reference
|
||||||
* it lies on the mr0 offset when referred as the syscall context.
|
* to the fault structure is set in the ktcb such that
|
||||||
|
* it lies on the mr0 offset when referred as the syscall
|
||||||
|
* context.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Assign fault such that it overlaps as the MR0 reference in ktcb. */
|
/*
|
||||||
|
* Assign fault such that it overlaps
|
||||||
|
* as the MR0 reference in ktcb.
|
||||||
|
*/
|
||||||
current->syscall_regs = (syscall_context_t *)
|
current->syscall_regs = (syscall_context_t *)
|
||||||
((unsigned long)&mr[0] -
|
((unsigned long)&mr[0] -
|
||||||
offsetof(syscall_context_t, r3));
|
offsetof(syscall_context_t, r3));
|
||||||
@@ -105,25 +113,28 @@ void fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far)
|
|||||||
if (current->tid == current->pagerid) {
|
if (current->tid == current->pagerid) {
|
||||||
printk("Pager (%d) self-faulting. Exiting.\n",
|
printk("Pager (%d) self-faulting. Exiting.\n",
|
||||||
current->tid);
|
current->tid);
|
||||||
thread_destroy_self();
|
thread_destroy_current();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send ipc to the task's pager */
|
/* Send ipc to the task's pager */
|
||||||
if ((err = ipc_sendrecv(current->pagerid,
|
if ((err = ipc_sendrecv(current->pagerid,
|
||||||
current->pagerid, 0)) < 0) {
|
current->pagerid, 0)) < 0) {
|
||||||
//printk("Thread (%d) faulted in kernel and its pager "
|
printk("Thread (%d) faulted in kernel and its pager "
|
||||||
// "returned error (%d). Suspending.\n",
|
"returned error (%d). Suspend and exiting thread.\n",
|
||||||
// current->tid, err);
|
current->tid, err);
|
||||||
BUG_ON(current->nlocks);
|
BUG_ON(current->nlocks);
|
||||||
|
current->flags |= TASK_EXITING;
|
||||||
sched_suspend_sync();
|
sched_suspend_sync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When a task calls the kernel and the supplied user buffer is not mapped, the kernel
|
* When a task calls the kernel and the supplied user buffer is
|
||||||
* generates a page fault to the task's pager so that the pager can make the decision
|
* not mapped, the kernel generates a page fault to the task's
|
||||||
* on mapping the buffer. Remember that if a task maps its own user buffer to itself
|
* pager so that the pager can make the decision on mapping the
|
||||||
* this way, the kernel can access it, since it shares that task's page table.
|
* buffer. Remember that if a task maps its own user buffer to
|
||||||
|
* itself this way, the kernel can access it, since it shares
|
||||||
|
* that task's page table.
|
||||||
*/
|
*/
|
||||||
int pager_pagein_request(unsigned long addr, unsigned long size,
|
int pager_pagein_request(unsigned long addr, unsigned long size,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
@@ -251,12 +262,6 @@ void data_abort_handler(u32 faulted_pc, u32 fsr, u32 far)
|
|||||||
|
|
||||||
/* This notifies the pager */
|
/* This notifies the pager */
|
||||||
fault_ipc_to_pager(faulted_pc, fsr, far);
|
fault_ipc_to_pager(faulted_pc, fsr, far);
|
||||||
|
|
||||||
if (current->flags & TASK_SUSPENDING) {
|
|
||||||
BUG_ON(current->nlocks);
|
|
||||||
sched_suspend_sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@@ -269,6 +274,7 @@ error:
|
|||||||
while (1)
|
while (1)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
void prefetch_abort_handler(u32 faulted_pc, u32 fsr, u32 far, u32 lr)
|
void prefetch_abort_handler(u32 faulted_pc, u32 fsr, u32 far, u32 lr)
|
||||||
{
|
{
|
||||||
set_abort_type(fsr, ARM_PABT);
|
set_abort_type(fsr, ARM_PABT);
|
||||||
@@ -280,11 +286,6 @@ void prefetch_abort_handler(u32 faulted_pc, u32 fsr, u32 far, u32 lr)
|
|||||||
if (KERN_ADDR(lr))
|
if (KERN_ADDR(lr))
|
||||||
goto error;
|
goto error;
|
||||||
fault_ipc_to_pager(faulted_pc, fsr, far);
|
fault_ipc_to_pager(faulted_pc, fsr, far);
|
||||||
|
|
||||||
if (current->flags & TASK_SUSPENDING) {
|
|
||||||
BUG_ON(current->nlocks);
|
|
||||||
sched_suspend_sync();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@@ -306,14 +307,17 @@ void dump_undef_abort(u32 undef_addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern int current_irq_nest_count;
|
extern int current_irq_nest_count;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is called right where the nest count is increased in case the nesting
|
* This is called right where the nest count is increased
|
||||||
* is beyond the predefined max limit. It is another matter whether this
|
* in case the nesting is beyond the predefined max limit.
|
||||||
* limit is enough to guarantee the kernel stack is not overflown.
|
* It is another matter whether this limit is enough to
|
||||||
|
* guarantee the kernel stack is not overflown.
|
||||||
*/
|
*/
|
||||||
void irq_overnest_error(void)
|
void irq_overnest_error(void)
|
||||||
{
|
{
|
||||||
dprintk("Irqs nested beyond limit. Current count: ", current_irq_nest_count);
|
dprintk("Irqs nested beyond limit. Current count: ",
|
||||||
|
current_irq_nest_count);
|
||||||
printascii("Halting system...\n");
|
printascii("Halting system...\n");
|
||||||
while(1)
|
while(1)
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -399,6 +399,8 @@ void init_kernel_container(struct kernel_container *kcont)
|
|||||||
memcap_unmap(&kcont->physmem_free, kernel_area->start,
|
memcap_unmap(&kcont->physmem_free, kernel_area->start,
|
||||||
kernel_area->end);
|
kernel_area->end);
|
||||||
|
|
||||||
|
init_ktcb_list(&kcont->zombie_list);
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
* Add all virtual memory areas used by the kernel
|
* Add all virtual memory areas used by the kernel
|
||||||
* e.g. kernel virtual area, syscall page, kip page,
|
* e.g. kernel virtual area, syscall page, kip page,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <l4/generic/resource.h>
|
#include <l4/generic/resource.h>
|
||||||
#include <l4/generic/container.h>
|
#include <l4/generic/container.h>
|
||||||
#include <l4/generic/preempt.h>
|
#include <l4/generic/preempt.h>
|
||||||
|
#include <l4/generic/thread.h>
|
||||||
#include <l4/generic/irq.h>
|
#include <l4/generic/irq.h>
|
||||||
#include <l4/generic/tcb.h>
|
#include <l4/generic/tcb.h>
|
||||||
#include <l4/api/errno.h>
|
#include <l4/api/errno.h>
|
||||||
@@ -239,6 +240,9 @@ void sched_suspend_sync(void)
|
|||||||
BUG_ON(scheduler.prio_total < 0);
|
BUG_ON(scheduler.prio_total < 0);
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
|
|
||||||
|
if (current->flags & TASK_EXITING)
|
||||||
|
thread_make_zombie(current);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Async wake up any waiting pagers
|
* Async wake up any waiting pagers
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -127,6 +127,15 @@ struct ktcb *tcb_find(l4id_t tid)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ktcb_list_add(struct ktcb *new, struct ktcb_list *ktcb_list)
|
||||||
|
{
|
||||||
|
spin_lock(&ktcb_list->list_lock);
|
||||||
|
BUG_ON(!list_empty(&new->task_list));
|
||||||
|
BUG_ON(!++ktcb_list->count);
|
||||||
|
list_insert(&new->task_list, &ktcb_list->list);
|
||||||
|
spin_unlock(&ktcb_list->list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
void tcb_add(struct ktcb *new)
|
void tcb_add(struct ktcb *new)
|
||||||
{
|
{
|
||||||
struct container *c = new->container;
|
struct container *c = new->container;
|
||||||
|
|||||||
Reference in New Issue
Block a user