SIGCHLD and wait4(), i.e. wait()/waitpid() implementation

This commit is contained in:
Balazs Gerofi bgerofi@riken.jp
2014-04-23 13:33:18 +09:00
parent 7103fed1dc
commit 0e348e6295
6 changed files with 315 additions and 14 deletions

View File

@@ -47,6 +47,7 @@ SYSCALL_HANDLED(56, clone)
SYSCALL_DELEGATED(57, fork)
SYSCALL_HANDLED(59, execve)
SYSCALL_HANDLED(60, exit)
SYSCALL_HANDLED(61, wait4)
SYSCALL_HANDLED(62, kill)
SYSCALL_DELEGATED(63, uname)
SYSCALL_DELEGATED(72, fcntl)

View File

@@ -1156,6 +1156,19 @@ int main_loop(int fd, int cpu, pthread_mutex_t *lock)
break;
}
case __NR_wait4: {
int status;
int ret;
pid_t pid = w.sr.args[0];
if ((ret = waitpid(pid, &status, 0)) != pid) {
fprintf(stderr, "ERROR: waiting for %lu\n", w.sr.args[0]);
}
do_syscall_return(fd, cpu, ret, 0, 0, 0, 0);
break;
}
default:
ret = do_generic_syscall(&w);
do_syscall_return(fd, cpu, ret, 0, 0, 0, 0);

View File

@@ -103,6 +103,7 @@ static int process_msg_prepare_process(unsigned long rphys)
}
proc->pid = pn->pid;
proc->tid = pn->pid;
proc->ftn->pid = pn->pid;
proc->vm->region.user_start = pn->user_start;
proc->vm->region.user_end = pn->user_end;
proc->vm->region.map_start = (USER_END / 3) & LARGE_PAGE_MASK;

View File

@@ -52,6 +52,32 @@
#define PS_NORMAL (PS_INTERRUPTIBLE | PS_UNINTERRUPTIBLE)
/* Waitpid options */
#define WNOHANG 0x00000001
#define WUNTRACED 0x00000002
#define WSTOPPED WUNTRACED
#define WEXITED 0x00000004
#define WCONTINUED 0x00000008
#define WNOWAIT 0x01000000 /* Don't reap, just poll status. */
/* If WIFEXITED(STATUS), the low-order 8 bits of the status. */
#define __WEXITSTATUS(status) (((status) & 0xff00) >> 8)
/* If WIFSIGNALED(STATUS), the terminating signal. */
#define __WTERMSIG(status) ((status) & 0x7f)
/* If WIFSTOPPED(STATUS), the signal that stopped the child. */
#define __WSTOPSIG(status) __WEXITSTATUS(status)
/* Nonzero if STATUS indicates normal termination. */
#define __WIFEXITED(status) (__WTERMSIG(status) == 0)
/* Nonzero if STATUS indicates termination by a signal. */
#define __WIFSIGNALED(status) \
(((signed char) (((status) & 0x7f) + 1) >> 1) > 0)
/* Nonzero if STATUS indicates the child is stopped. */
#define __WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
#ifdef ATTACHED_MIC
//#define USE_LARGE_PAGES
#endif
@@ -88,7 +114,7 @@ struct sig_handler {
struct sig_pending {
struct list_head list;
sigset_t sigmask;
// TODO: siginfo
siginfo_t info;
};
struct sig_shared {
@@ -99,6 +125,29 @@ struct sig_shared {
typedef void pgio_func_t(void *arg);
/* Represents a node in the process fork tree, it may exist even after the
* corresponding process exited due to references from the parent and/or
* children and is used for implementing wait/waitpid without having a
* special "init" process */
struct fork_tree_node {
ihk_spinlock_t lock;
ihk_atomic_t refcount;
int exit_status;
int status;
struct process *owner;
int pid;
struct fork_tree_node *parent;
struct list_head children;
struct list_head siblings_list;
struct waitq waitpid_q;
};
void hold_fork_tree_node(struct fork_tree_node *ftn);
void release_fork_tree_node(struct fork_tree_node *ftn);
struct process {
int pid;
int status;
@@ -136,6 +185,8 @@ struct process {
struct rlimit rlimit_stack;
pgio_func_t *pgio_fp;
void *pgio_arg;
struct fork_tree_node *ftn;
};
struct process_vm {

View File

@@ -44,13 +44,63 @@
#define USER_STACK_NR_PAGES 8192
#define KERNEL_STACK_NR_PAGES 24
#define KERNEL_STACK_NR_PAGES 25
extern long do_arch_prctl(unsigned long code, unsigned long address);
static void insert_vm_range_list(struct process_vm *vm,
struct vm_range *newrange);
static int copy_user_ranges(struct process *proc, struct process *org);
enum ihk_mc_pt_attribute vrflag_to_ptattr(unsigned long flag);
void hold_fork_tree_node(struct fork_tree_node *ftn)
{
ihk_atomic_inc(&ftn->refcount);
dkprintf("hold ftn(%d): %d\n",
ftn->pid, ihk_atomic_read(&ftn->refcount));
}
void release_fork_tree_node(struct fork_tree_node *ftn)
{
dkprintf("release ftn(%d): %d\n",
ftn->pid, ihk_atomic_read(&ftn->refcount));
if (!ihk_atomic_dec_and_test(&ftn->refcount)) {
return;
}
dkprintf("dealloc ftn(%d): %d\n",
ftn->pid, ihk_atomic_read(&ftn->refcount));
/* Dealloc */
kfree(ftn);
}
void init_fork_tree_node(struct fork_tree_node *ftn,
struct fork_tree_node *parent, struct process *owner)
{
ihk_mc_spinlock_init(&ftn->lock);
/* Only the process/thread holds a reference at this point */
ihk_atomic_set(&ftn->refcount, 1);
ftn->owner = owner;
/* These will be filled out when changing status */
ftn->pid = -1;
ftn->exit_status = -1;
ftn->status = PS_RUNNING;
ftn->parent = NULL;
if (parent) {
ftn->parent = parent;
}
INIT_LIST_HEAD(&ftn->children);
INIT_LIST_HEAD(&ftn->siblings_list);
waitq_init(&ftn->waitpid_q);
}
static int init_process_vm(struct process *owner, struct process_vm *vm)
{
@@ -80,7 +130,7 @@ struct process *create_process(unsigned long user_pc)
return NULL;
memset(proc, 0, sizeof(struct process));
ihk_atomic_set(&proc->refcount, 2); /* one for exit, another for wait */
ihk_atomic_set(&proc->refcount, 1);
proc->sighandler = kmalloc(sizeof(struct sig_handler), IHK_MC_AP_NOWAIT);
if(!proc->sighandler){
@@ -105,6 +155,13 @@ struct process *create_process(unsigned long user_pc)
proc->vm = (struct process_vm *)(proc + 1);
proc->ftn = kmalloc(sizeof(struct fork_tree_node), IHK_MC_AP_NOWAIT);
if (!proc->ftn) {
goto err_free_sigshared;
}
init_fork_tree_node(proc->ftn, NULL, proc);
if(init_process_vm(proc, proc->vm) != 0){
goto err_free_sigshared;
}
@@ -137,7 +194,7 @@ struct process *clone_process(struct process *org, unsigned long pc,
}
memset(proc, 0, sizeof(struct process));
ihk_atomic_set(&proc->refcount, 2); /* one for exit, another for wait */
ihk_atomic_set(&proc->refcount, 1);
/* NOTE: sp is the user mode stack! */
ihk_mc_init_user_process(&proc->ctx, &proc->uctx,
@@ -149,6 +206,14 @@ struct process *clone_process(struct process *org, unsigned long pc,
ihk_mc_modify_user_context(proc->uctx, IHK_UCR_PROGRAM_COUNTER, pc);
proc->rlimit_stack = org->rlimit_stack;
proc->ftn = kmalloc(sizeof(struct fork_tree_node), IHK_MC_AP_NOWAIT);
if (!proc->ftn) {
goto err_free_sigshared;
}
init_fork_tree_node(proc->ftn, (clone_flags & CLONE_VM) ? NULL : org->ftn,
proc);
/* clone() */
if (clone_flags & CLONE_VM) {
@@ -207,6 +272,17 @@ struct process *clone_process(struct process *org, unsigned long pc,
}
dkprintf("fork(): copy_user_ranges() OK\n");
/* Add proc's fork_tree_node to parent's children list */
ihk_mc_spinlock_lock_noirq(&org->ftn->lock);
list_add_tail(&proc->ftn->siblings_list, &org->ftn->children);
ihk_mc_spinlock_unlock_noirq(&org->ftn->lock);
/* We hold a reference to parent */
hold_fork_tree_node(proc->ftn->parent);
/* Parent holds a reference to us */
hold_fork_tree_node(proc->ftn);
}
ihk_mc_spinlock_init(&proc->spin_sleep_lock);
@@ -1610,6 +1686,9 @@ void destroy_process(struct process *proc)
{
struct sig_pending *pending;
struct sig_pending *next;
free_process_memory(proc);
if(ihk_atomic_dec_and_test(&proc->sighandler->use)){
kfree(proc->sighandler);
}

View File

@@ -222,11 +222,138 @@ long syscall_generic_forwarding(int n, ihk_mc_user_context_t *ctx)
SYSCALL_FOOTER;
}
void sigchld_parent(struct process *parent, int status)
{
struct process *proc = cpu_local_var(current);
int irqstate;
struct sig_pending *pending;
struct list_head *head;
__sigset_t mask;
mask = __sigmask(SIGCHLD);
head = &parent->sigpending;
irqstate = ihk_mc_spinlock_lock(&parent->sigpendinglock);
list_for_each_entry(pending, head, list) {
if (pending->sigmask.__val[0] == mask)
break;
}
if (&pending->list == head) {
pending = kmalloc(sizeof(struct sig_pending), IHK_MC_AP_NOWAIT);
if (!pending) {
/* TODO: what to do here?? */
panic("ERROR: not enough memory for signaling parent process!");
}
pending->sigmask.__val[0] = mask;
pending->info.si_signo = SIGCHLD;
pending->info._sifields._sigchld.si_pid = proc->pid;
pending->info._sifields._sigchld.si_status = status;
list_add_tail(&pending->list, head);
proc->sigevent = 1;
}
/* TODO: There was a SIGCHLD pending */
else {
}
ihk_mc_spinlock_unlock(&parent->sigpendinglock, irqstate);
}
/*
* From glibc: INLINE_SYSCALL (wait4, 4, pid, stat_loc, options, NULL);
*/
SYSCALL_DECLARE(wait4)
{
struct process *proc = cpu_local_var(current);
struct fork_tree_node *child, *child_iter;
int pid = (int)ihk_mc_syscall_arg0(ctx);
int *status = (int *)ihk_mc_syscall_arg1(ctx);
int options = (int)ihk_mc_syscall_arg2(ctx);
int ret;
struct waitq_entry waitpid_wqe;
rescan:
child = NULL;
pid = (int)ihk_mc_syscall_arg0(ctx);
ihk_mc_spinlock_lock_noirq(&proc->ftn->lock);
list_for_each_entry(child_iter, &proc->ftn->children, siblings_list) {
ihk_mc_spinlock_lock_noirq(&child_iter->lock);
if (child_iter->status == PS_ZOMBIE
&& (pid == -1 || pid == child_iter->pid)) {
child = child_iter;
ihk_mc_spinlock_unlock_noirq(&child_iter->lock);
break;
}
ihk_mc_spinlock_unlock_noirq(&child_iter->lock);
}
if (child) {
struct syscall_request request IHK_DMA_ALIGN;
dkprintf("wait: found PS_ZOMBIE process: %d\n", child->pid);
list_del(&child->siblings_list);
ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock);
*status = child->exit_status;
pid = child->pid;
release_fork_tree_node(child);
/* Ask host to clean up exited child */
request.number = __NR_wait4;
request.args[0] = pid;
request.args[1] = 0;
ret = do_syscall(&request, ctx, ihk_mc_get_processor_id(), 0);
if (ret != pid)
kprintf("WARNING: host waitpid failed?\n");
goto exit;
}
/* Don't sleep if WNOHANG requested */
if (options & WNOHANG) {
ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock);
*status = 0;
pid = 0;
goto exit;
}
/* Sleep */
waitq_init_entry(&waitpid_wqe, proc);
waitq_prepare_to_wait(&proc->ftn->waitpid_q, &waitpid_wqe, PS_INTERRUPTIBLE);
ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock);
schedule();
dkprintf("wait4(): woken up\n");
waitq_finish_wait(&proc->ftn->waitpid_q, &waitpid_wqe);
goto rescan;
exit:
return pid;
}
void
terminate(int rc, int sig, ihk_mc_user_context_t *ctx)
{
struct syscall_request request IHK_DMA_ALIGN;
struct process *proc = cpu_local_var(current);
struct fork_tree_node *ftn = proc->ftn;
struct fork_tree_node *child, *next;
request.number = __NR_exit_group;
request.args[0] = ((rc & 0x00ff) << 8) | (sig & 0x7f);
@@ -241,13 +368,43 @@ terminate(int rc, int sig, ihk_mc_user_context_t *ctx)
do_syscall(&request, ctx, ihk_mc_get_processor_id(), 0);
#define IS_DETACHED_PROCESS(proc) (1) /* should be implemented in the future */
proc->status = PS_ZOMBIE;
if (IS_DETACHED_PROCESS(proc)) {
/* release a reference for wait(2) */
proc->status = PS_EXITED;
free_process(proc);
/* Do a "wait" on all children and detach owner process */
ihk_mc_spinlock_lock_noirq(&ftn->lock);
list_for_each_entry_safe(child, next, &ftn->children, siblings_list) {
list_del(&child->siblings_list);
release_fork_tree_node(child);
}
ftn->owner = NULL;
ihk_mc_spinlock_unlock_noirq(&ftn->lock);
/* Send SIGCHILD to parent */
if (ftn->parent) {
ihk_mc_spinlock_lock_noirq(&ftn->lock);
ftn->pid = proc->pid;
ftn->exit_status = 0; // WIFEXITED gives true
ftn->status = PS_ZOMBIE;
ihk_mc_spinlock_unlock_noirq(&ftn->lock);
/* Signal parent if still attached */
ihk_mc_spinlock_lock_noirq(&ftn->parent->lock);
if (ftn->parent->owner) {
sigchld_parent(ftn->parent->owner, 0);
}
ihk_mc_spinlock_unlock_noirq(&ftn->parent->lock);
/* Wake parent (if sleeping in wait4()) */
waitq_wakeup(&ftn->parent->waitpid_q);
release_fork_tree_node(ftn->parent);
}
release_fork_tree_node(ftn);
release_process(proc);
schedule();
}
@@ -1017,6 +1174,8 @@ SYSCALL_DECLARE(clone)
kprintf("ERROR: clearing PTEs in host process\n");
}
}
new->ftn->pid = new->pid;
if (clone_flags & CLONE_PARENT_SETTID) {
dkprintf("clone_flags & CLONE_PARENT_SETTID: 0x%lX\n",
@@ -1583,11 +1742,8 @@ SYSCALL_DECLARE(exit)
}
proc->status = PS_ZOMBIE;
if (IS_DETACHED_PROCESS(proc)) {
/* release a reference for wait(2) */
proc->status = PS_EXITED;
free_process(proc);
}
release_process(proc);
schedule();