From d9cf1d49b1baf4f2e549756118c6eaea900dda63 Mon Sep 17 00:00:00 2001 From: Tomoki Shirasawa Date: Sun, 22 Feb 2015 20:05:30 +0900 Subject: [PATCH] support waitid send SIGCHLD to parent when SIGSTOP or SIGCONT received refs #425 refs #283 --- arch/x86/kernel/include/syscall_list.h | 1 + arch/x86/kernel/syscall.c | 107 ++++++++++++++++-------- kernel/include/process.h | 5 ++ kernel/syscall.c | 110 +++++++++++++++++++++---- 4 files changed, 173 insertions(+), 50 deletions(-) diff --git a/arch/x86/kernel/include/syscall_list.h b/arch/x86/kernel/include/syscall_list.h index 4e0a7337..505cd03b 100644 --- a/arch/x86/kernel/include/syscall_list.h +++ b/arch/x86/kernel/include/syscall_list.h @@ -95,6 +95,7 @@ SYSCALL_HANDLED(234, tgkill) SYSCALL_HANDLED(237, mbind) SYSCALL_HANDLED(238, set_mempolicy) SYSCALL_HANDLED(239, get_mempolicy) +SYSCALL_HANDLED(247, waitid) SYSCALL_HANDLED(256, migrate_pages) SYSCALL_HANDLED(273, set_robust_list) SYSCALL_HANDLED(279, move_pages) diff --git a/arch/x86/kernel/syscall.c b/arch/x86/kernel/syscall.c index 63b3e1b7..14e79418 100644 --- a/arch/x86/kernel/syscall.c +++ b/arch/x86/kernel/syscall.c @@ -525,6 +525,8 @@ do_signal(unsigned long rc, void *regs0, struct process *proc, struct sig_pendin } else { int coredumped = 0; + siginfo_t info; + kfree(pending); ihk_mc_spinlock_unlock(&proc->sighandler->lock, irqstate); switch (sig) { @@ -532,6 +534,12 @@ do_signal(unsigned long rc, void *regs0, struct process *proc, struct sig_pendin case SIGTSTP: case SIGTTIN: case SIGTTOU: + memset(&info, '\0', sizeof info); + info.si_signo = SIGCHLD; + info.si_code = CLD_STOPPED; + info._sifields._sigchld.si_pid = proc->ftn->pid; + info._sifields._sigchld.si_status = (sig << 8) | 0x7f; + do_kill(proc->ftn->parent->pid, -1, SIGCHLD, &info, 0); if(ptraceflag){ ptrace_report_signal(proc, orgsig); } @@ -580,6 +588,13 @@ do_signal(unsigned long rc, void *regs0, struct process *proc, struct sig_pendin dkprintf("SIGTRAP(): woken up\n"); break; case SIGCONT: + memset(&info, '\0', sizeof info); + info.si_signo = SIGCHLD; + info.si_code = CLD_CONTINUED; + info._sifields._sigchld.si_pid = proc->ftn->pid; + info._sifields._sigchld.si_status = 0x0000ffff; + do_kill(proc->ftn->parent->pid, -1, SIGCHLD, &info, 0); + ftn->signal_flags = SIGNAL_STOP_CONTINUED; dkprintf("do_signal,SIGCONT,do nothing\n"); break; case SIGQUIT: @@ -606,58 +621,82 @@ do_signal(unsigned long rc, void *regs0, struct process *proc, struct sig_pendin } } +static struct sig_pending * +getsigpending(struct process *proc, int delflag){ + struct list_head *head; + ihk_spinlock_t *lock; + struct sig_pending *next; + struct sig_pending *pending; + __sigset_t w; + int irqstate; + + w = proc->sigmask.__val[0]; + + lock = &proc->sigshared->lock; + head = &proc->sigshared->sigpending; + for(;;){ + irqstate = ihk_mc_spinlock_lock(lock); + list_for_each_entry_safe(pending, next, head, list){ + if(!(pending->sigmask.__val[0] & w)){ + if(delflag) + list_del(&pending->list); + ihk_mc_spinlock_unlock(lock, irqstate); + return pending; + } + } + ihk_mc_spinlock_unlock(lock, irqstate); + + if(lock == &proc->sigpendinglock) + return NULL; + lock = &proc->sigpendinglock; + head = &proc->sigpending; + } + + return NULL; +} + +struct sig_pending * +hassigpending(struct process *proc) +{ + return getsigpending(proc, 0); +} + void check_signal(unsigned long rc, void *regs0) { struct x86_regs *regs = regs0; struct process *proc; struct sig_pending *pending; - struct sig_pending *next; - struct list_head *head; - ihk_spinlock_t *lock; int irqstate; - __sigset_t w; if(clv == NULL) return; proc = cpu_local_var(current); - if(proc == NULL || proc->ftn->pid == 0) + if(proc == NULL || proc->ftn->pid == 0){ + struct process *p; + + irqstate = ihk_mc_spinlock_lock(&(cpu_local_var(runq_lock))); + list_for_each_entry(p, &(cpu_local_var(runq)), sched_list){ + if(p->ftn->pid <= 0) + continue; + if(p->ftn->status == PS_INTERRUPTIBLE && + hassigpending(p)){ + p->ftn->status = PS_RUNNING; + ihk_mc_spinlock_unlock(&(cpu_local_var(runq_lock)), irqstate); + // schedule(); + return; + } + } + ihk_mc_spinlock_unlock(&(cpu_local_var(runq_lock)), irqstate); return; + } if(regs != NULL && (regs->rsp & 0x8000000000000000)) { return; } for(;;){ - w = proc->sigmask.__val[0]; - lock = &proc->sigshared->lock; - head = &proc->sigshared->sigpending; - pending = NULL; - irqstate = ihk_mc_spinlock_lock(lock); - list_for_each_entry_safe(pending, next, head, list){ - if(!(pending->sigmask.__val[0] & w)){ - list_del(&pending->list); - break; - } - } - if(&pending->list == head) - pending = NULL; - ihk_mc_spinlock_unlock(lock, irqstate); - - if(!pending){ - lock = &proc->sigpendinglock; - head = &proc->sigpending; - irqstate = ihk_mc_spinlock_lock(lock); - list_for_each_entry_safe(pending, next, head, list){ - if(!(pending->sigmask.__val[0] & w)){ - list_del(&pending->list); - break; - } - } - if(&pending->list == head) - pending = NULL; - ihk_mc_spinlock_unlock(lock, irqstate); - } + pending = getsigpending(proc, 1); if(!pending) { dkprintf("check_signal,queue is empty\n"); return; diff --git a/kernel/include/process.h b/kernel/include/process.h index c3890260..c21cd006 100644 --- a/kernel/include/process.h +++ b/kernel/include/process.h @@ -122,6 +122,11 @@ #define WNOWAIT 0x01000000 /* Don't reap, just poll status. */ #define __WCLONE 0x80000000 +/* idtype */ +#define P_ALL 0 +#define P_PID 1 +#define P_PGID 2 + /* If WIFEXITED(STATUS), the low-order 8 bits of the status. */ #define __WEXITSTATUS(status) (((status) & 0xff00) >> 8) diff --git a/kernel/syscall.c b/kernel/syscall.c index ed0b31b1..707207af 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -97,6 +97,7 @@ static char *syscall_name[] MCKERNEL_UNUSED = { void check_signal(unsigned long rc, void *regs); void do_signal(long rc, void *regs, struct process *proc, struct sig_pending *pending); extern unsigned long do_kill(int pid, int tid, int sig, struct siginfo *info, int ptracecont); +extern struct sigpending *hassigpending(struct process *proc); int copy_from_user(struct process *, void *, const void *, size_t); int copy_to_user(struct process *, void *, const void *, size_t); void do_setpgid(int, int); @@ -383,26 +384,20 @@ static int wait_continued(struct process *proc, struct fork_tree_node *child, in /* * From glibc: INLINE_SYSCALL (wait4, 4, pid, stat_loc, options, NULL); */ -SYSCALL_DECLARE(wait4) +static int +do_wait(int pid, int *status, int options, void *rusage, ihk_mc_user_context_t *ctx) { struct process *proc = cpu_local_var(current); struct fork_tree_node *child_iter, *next; - int pid = (int)ihk_mc_syscall_arg0(ctx); int pgid = proc->ftn->pgid; - int *status = (int *)ihk_mc_syscall_arg1(ctx); - int options = (int)ihk_mc_syscall_arg2(ctx); int ret; struct waitq_entry waitpid_wqe; int empty = 1; + int orgpid = pid; dkprintf("wait4,proc->pid=%d,pid=%d\n", proc->ftn->pid, pid); - if (options & ~(WNOHANG | WUNTRACED | WCONTINUED | __WCLONE)) { - dkprintf("wait4: unexpected options(%x).\n", options); - ret = -EINVAL; - goto exit; - } rescan: - pid = (int)ihk_mc_syscall_arg0(ctx); + pid = orgpid; ihk_mc_spinlock_lock_noirq(&proc->ftn->lock); list_for_each_entry_safe(child_iter, next, &proc->ftn->children, siblings_list) { @@ -420,11 +415,14 @@ SYSCALL_DECLARE(wait4) empty = 0; - if(child_iter->status == PS_ZOMBIE) { + if((options & WEXITED) && + child_iter->status == PS_ZOMBIE) { ret = wait_zombie(proc, child_iter, status, ctx); if(ret) { - list_del(&child_iter->siblings_list); - release_fork_tree_node(child_iter); + if(!(options & WNOWAIT)){ + list_del(&child_iter->siblings_list); + release_fork_tree_node(child_iter); + } goto out_found; } } @@ -434,6 +432,9 @@ SYSCALL_DECLARE(wait4) /* Not ptraced and in stopped state and WUNTRACED is specified */ ret = wait_stopped(proc, child_iter, status, options); if(ret) { + if(!(options & WNOWAIT)){ + child_iter->signal_flags &= ~SIGNAL_STOP_STOPPED; + } goto out_found; } } @@ -442,6 +443,9 @@ SYSCALL_DECLARE(wait4) (options & WCONTINUED)) { ret = wait_continued(proc, child_iter, status, options); if(ret) { + if(!(options & WNOWAIT)){ + child_iter->signal_flags &= ~SIGNAL_STOP_CONTINUED; + } goto out_found; } } @@ -464,11 +468,14 @@ SYSCALL_DECLARE(wait4) empty = 0; - if(child_iter->status == PS_ZOMBIE) { + if((options & WEXITED) && + child_iter->status == PS_ZOMBIE) { ret = wait_zombie(proc, child_iter, status, ctx); if(ret) { - list_del(&child_iter->ptrace_siblings_list); - release_fork_tree_node(child_iter); + if(!(options & WNOWAIT)){ + list_del(&child_iter->ptrace_siblings_list); + release_fork_tree_node(child_iter); + } goto out_found; } } @@ -477,6 +484,9 @@ SYSCALL_DECLARE(wait4) /* ptraced and in stopped or trace-stopped state */ ret = wait_stopped(proc, child_iter, status, options); if(ret) { + if(!(options & WNOWAIT)){ + child_iter->signal_flags &= ~SIGNAL_STOP_STOPPED; + } goto out_found; } } else { @@ -487,6 +497,9 @@ SYSCALL_DECLARE(wait4) (options & WCONTINUED)) { ret = wait_continued(proc, child_iter, status, options); if(ret) { + if(!(options & WNOWAIT)){ + child_iter->signal_flags &= ~SIGNAL_STOP_CONTINUED; + } goto out_found; } } @@ -513,6 +526,11 @@ SYSCALL_DECLARE(wait4) waitq_prepare_to_wait(&proc->ftn->waitpid_q, &waitpid_wqe, PS_INTERRUPTIBLE); ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); + if(hassigpending(proc)){ + waitq_finish_wait(&proc->ftn->waitpid_q, &waitpid_wqe); + return -EINTR; + } + schedule(); dkprintf("wait4(): woken up\n"); @@ -532,6 +550,66 @@ SYSCALL_DECLARE(wait4) goto exit; } +SYSCALL_DECLARE(wait4) +{ + int pid = (int)ihk_mc_syscall_arg0(ctx); + int *status = (int *)ihk_mc_syscall_arg1(ctx); + int options = (int)ihk_mc_syscall_arg2(ctx); + void *rusage = (void *)ihk_mc_syscall_arg3(ctx); + + if(options & ~(WNOHANG | WUNTRACED | WCONTINUED | __WCLONE)){ + dkprintf("wait4: unexpected options(%x).\n", options); + return -EINVAL; + } + return do_wait(pid, status, WEXITED | options, rusage, ctx); +} + +SYSCALL_DECLARE(waitid) +{ + int idtype = (int)ihk_mc_syscall_arg0(ctx); + int id = (int)ihk_mc_syscall_arg1(ctx); + siginfo_t *info = (siginfo_t *)ihk_mc_syscall_arg2(ctx); + int options = (int)ihk_mc_syscall_arg3(ctx); + int pid; + int status; + int rc; + + if(idtype == P_PID) + pid = id; + else if(idtype == P_PGID) + pid = -id; + else if(idtype == P_ALL) + pid = -1; + else + return -EINVAL; + if(options & ~(WEXITED | WSTOPPED | WCONTINUED | WNOHANG | WNOWAIT | __WCLONE)){ + dkprintf("waitid: unexpected options(%x).\n", options); + return -EINVAL; + } + if(!(options & (WEXITED | WSTOPPED | WCONTINUED))){ + dkprintf("waitid: no waiting status(%x).\n", options); + return -EINVAL; + } + rc = do_wait(pid, &status, options, NULL, ctx); + if(rc < 0) + return rc; + if(rc && info){ + memset(info, '\0', sizeof(siginfo_t)); + info->si_signo = SIGCHLD; + info->_sifields._sigchld.si_pid = rc; + info->_sifields._sigchld.si_status = status; + if((status & 0x000000ff) == 0x0000007f) + info->si_code = CLD_STOPPED; + else if((status & 0x0000ffff) == 0x0000ffff) + info->si_code = CLD_CONTINUED; + else if(status & 0x000000ff) + info->si_code = CLD_KILLED; + else + info->si_code = CLD_EXITED; + } + return 0; +} + static int ptrace_terminate_tracer(struct process *proc, struct fork_tree_node *tracer); void