From 0e348e62957b12d76c31c1bf6150ee0f7cdd260d Mon Sep 17 00:00:00 2001 From: "Balazs Gerofi bgerofi@riken.jp" Date: Wed, 23 Apr 2014 13:33:18 +0900 Subject: [PATCH] SIGCHLD and wait4(), i.e. wait()/waitpid() implementation --- arch/x86/kernel/include/syscall_list.h | 1 + executer/user/mcexec.c | 13 ++ kernel/host.c | 1 + kernel/include/process.h | 53 +++++++- kernel/process.c | 85 +++++++++++- kernel/syscall.c | 176 +++++++++++++++++++++++-- 6 files changed, 315 insertions(+), 14 deletions(-) diff --git a/arch/x86/kernel/include/syscall_list.h b/arch/x86/kernel/include/syscall_list.h index b171bb59..4bc9b2f0 100644 --- a/arch/x86/kernel/include/syscall_list.h +++ b/arch/x86/kernel/include/syscall_list.h @@ -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) diff --git a/executer/user/mcexec.c b/executer/user/mcexec.c index c9d64a3a..cf783335 100644 --- a/executer/user/mcexec.c +++ b/executer/user/mcexec.c @@ -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); diff --git a/kernel/host.c b/kernel/host.c index c357cb3d..5aea10bd 100644 --- a/kernel/host.c +++ b/kernel/host.c @@ -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; diff --git a/kernel/include/process.h b/kernel/include/process.h index f5fedc9b..cfc0c3e5 100644 --- a/kernel/include/process.h +++ b/kernel/include/process.h @@ -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 { diff --git a/kernel/process.c b/kernel/process.c index fa2f871a..d883b59f 100644 --- a/kernel/process.c +++ b/kernel/process.c @@ -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); } diff --git a/kernel/syscall.c b/kernel/syscall.c index 530894b7..a2c32e17 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -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();