From dbecaa2fc866019e4739ec0ea959ed6a82e8be18 Mon Sep 17 00:00:00 2001 From: Masamichi Takagi Date: Thu, 18 Sep 2014 19:05:58 +0900 Subject: [PATCH] Add ptrace functions of job-control and signal Note that a forked process automatically becomes ptraced state in this commit. --- arch/x86/kernel/cpu.c | 2 + arch/x86/kernel/include/signal.h | 75 +++++- arch/x86/kernel/syscall.c | 254 ++++++++++++------- executer/user/mcexec.c | 2 +- kernel/host.c | 1 + kernel/include/process.h | 32 +++ kernel/process.c | 32 ++- kernel/syscall.c | 404 +++++++++++++++++++++++++------ 8 files changed, 632 insertions(+), 170 deletions(-) diff --git a/arch/x86/kernel/cpu.c b/arch/x86/kernel/cpu.c index 7d4ba5de..38261579 100644 --- a/arch/x86/kernel/cpu.c +++ b/arch/x86/kernel/cpu.c @@ -448,6 +448,8 @@ void handle_interrupt(int vector, struct x86_regs *regs) check_signal(0, regs); check_need_resched(); + //kprintf("handle_interrupt,exit\n"); + //schedule(); } void gpe_handler(struct x86_regs *regs) diff --git a/arch/x86/kernel/include/signal.h b/arch/x86/kernel/include/signal.h index 2d1a8717..9e3989cd 100644 --- a/arch/x86/kernel/include/signal.h +++ b/arch/x86/kernel/include/signal.h @@ -36,6 +36,12 @@ struct sigaction { sigset_t sa_mask; }; +typedef void __sig_fn_t(int); +typedef __sig_fn_t *__sig_handler_t; +#define SIG_DFL (__sig_handler_t)0 +#define SIG_IGN (__sig_handler_t)1 +#define SIG_ERR (__sig_handler_t)-1 + #define SA_NOCLDSTOP 0x00000001U #define SA_NOCLDWAIT 0x00000002U #define SA_NODEFER 0x40000000U @@ -71,16 +77,69 @@ typedef struct siginfo { int si_errno; /* If non-zero, an errno value associated with this signal, as defined in . */ int si_code; /* Signal code. */ -#define SI_USER 0 /* sent by kill, sigsend, raise */ -#define SI_KERNEL 0x80 /* sent by the kernel from somewhere */ -#define SI_QUEUE -1 /* sent by sigqueue */ +#define SI_USER 0 /* sent by kill, sigsend, raise */ +#define SI_KERNEL 0x80 /* sent by the kernel from somewhere */ +#define SI_QUEUE -1 /* sent by sigqueue */ #define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */ #define SI_MESGQ __SI_CODE(__SI_MESGQ,-3) /* sent by real time mesq state change */ -#define SI_ASYNCIO -4 /* sent by AIO completion */ -#define SI_SIGIO -5 /* sent by queued SIGIO */ -#define SI_TKILL -6 /* sent by tkill system call */ -#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */ +#define SI_ASYNCIO -4 /* sent by AIO completion */ +#define SI_SIGIO -5 /* sent by queued SIGIO */ +#define SI_TKILL -6 /* sent by tkill system call */ +#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */ + +#define ILL_ILLOPC 1 /* illegal opcode */ +#define ILL_ILLOPN 2 /* illegal operand */ +#define ILL_ILLADR 3 /* illegal addressing mode */ +#define ILL_ILLTRP 4 /* illegal trap */ +#define ILL_PRVOPC 5 /* privileged opcode */ +#define ILL_PRVREG 6 /* privileged register */ +#define ILL_COPROC 7 /* coprocessor error */ +#define ILL_BADSTK 8 /* internal stack error */ + +#define FPE_INTDIV 1 /* integer divide by zero */ +#define FPE_INTOVF 2 /* integer overflow */ +#define FPE_FLTDIV 3 /* floating point divide by zero */ +#define FPE_FLTOVF 4 /* floating point overflow */ +#define FPE_FLTUND 5 /* floating point underflow */ +#define FPE_FLTRES 6 /* floating point inexact result */ +#define FPE_FLTINV 7 /* floating point invalid operation */ +#define FPE_FLTSUB 8 /* subscript out of range */ + +#define SEGV_MAPERR 1 /* address not mapped to object */ +#define SEGV_ACCERR 2 /* invalid permissions for mapped object */ + +#define BUS_ADRALN 1 /* invalid address alignment */ +#define BUS_ADRERR 2 /* non-existant physical address */ +#define BUS_OBJERR 3 /* object specific hardware error */ +/* hardware memory error consumed on a machine check: action required */ +#define BUS_MCEERR_AR 4 +/* hardware memory error detected in process but not consumed: action optional*/ +#define BUS_MCEERR_AO 5 + +#define TRAP_BRKPT 1 /* process breakpoint */ +#define TRAP_TRACE 2 /* process trace trap */ +#define TRAP_BRANCH 3 /* process taken branch trap */ +#define TRAP_HWBKPT 4 /* hardware breakpoint/watchpoint */ + +#define CLD_EXITED 1 /* child has exited */ +#define CLD_KILLED 2 /* child was killed */ +#define CLD_DUMPED 3 /* child terminated abnormally */ +#define CLD_TRAPPED 4 /* traced child has trapped */ +#define CLD_STOPPED 5 /* child has stopped */ +#define CLD_CONTINUED 6 /* stopped child has continued */ + +#define POLL_IN 1 /* data input available */ +#define POLL_OUT 2 /* output buffers available */ +#define POLL_MSG 3 /* input message available */ +#define POLL_ERR 4 /* i/o error */ +#define POLL_PRI 5 /* high priority input available */ +#define POLL_HUP 6 /* device disconnected */ + +#define SIGEV_SIGNAL 0 /* notify via signal */ +#define SIGEV_NONE 1 /* other notification: meaningless */ +#define SIGEV_THREAD 2 /* deliver via thread creation */ +#define SIGEV_THREAD_ID 4 /* deliver to thread */ union { int _pad[__SI_PAD_SIZE]; @@ -186,4 +245,6 @@ struct signalfd_siginfo { #define SIGUNUSED 31 #define SIGRTMIN 32 +#define PTRACE_EVENT_EXEC 4 + #endif /*__HEADER_X86_COMMON_SIGNAL_H*/ diff --git a/arch/x86/kernel/syscall.c b/arch/x86/kernel/syscall.c index 8573cf33..8f28faca 100644 --- a/arch/x86/kernel/syscall.c +++ b/arch/x86/kernel/syscall.c @@ -216,13 +216,10 @@ do_signal(unsigned long rc, void *regs0, struct process *proc, struct sig_pendin rc = regs->rax; } - if(sig == SIGKILL) - terminate(0, sig, (ihk_mc_user_context_t *)regs->rsp); - irqstate = ihk_mc_spinlock_lock(&proc->sighandler->lock); k = proc->sighandler->action + sig - 1; - if(k->sa.sa_handler == (void *)1){ + if(k->sa.sa_handler == SIG_IGN){ kfree(pending); ihk_mc_spinlock_unlock(&proc->sighandler->lock, irqstate); return; @@ -249,6 +246,7 @@ do_signal(unsigned long rc, void *regs0, struct process *proc, struct sig_pendin copy_to_user(proc, &sigsp->sigrc, &rc, sizeof(long))){ kfree(pending); ihk_mc_spinlock_unlock(&proc->sighandler->lock, irqstate); + kprintf("do_signal,copy_to_user failed\n"); terminate(0, sig, (ihk_mc_user_context_t *)regs->rsp); return; } @@ -272,53 +270,129 @@ do_signal(unsigned long rc, void *regs0, struct process *proc, struct sig_pendin kfree(pending); ihk_mc_spinlock_unlock(&proc->sighandler->lock, irqstate); } - else{ + else { int coredumped = 0; kfree(pending); ihk_mc_spinlock_unlock(&proc->sighandler->lock, irqstate); - switch(sig){ - case SIGCHLD: - case SIGURG: - return; - case SIGSTOP: { - dkprintf("do_signal,SIGSTOP,changing state\n"); - struct process *proc = cpu_local_var(current); - struct fork_tree_node *ftn = proc->ftn; - int exit_code = SIGSTOP; + switch (sig) { + case SIGSTOP: { + dkprintf("do_signal,SIGSTOP,changing state\n"); + struct process *proc = cpu_local_var(current); + struct fork_tree_node *ftn = proc->ftn; - /* Update process state in fork tree */ - ihk_mc_spinlock_lock_noirq(&ftn->lock); - ftn->exit_status = (exit_code << 8) | 0x7f; - ftn->status = PS_STOPPED; - ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); + /* Update process state in fork tree */ + ihk_mc_spinlock_lock_noirq(&ftn->lock); + ftn->group_exit_status = SIGSTOP; - /* Wake up the parent who tried wait4 and sleeping */ - waitq_wakeup(&proc->ftn->parent->waitpid_q); + /* Reap and set new signal_flags */ + ftn->signal_flags = SIGNAL_STOP_STOPPED; - dkprintf("do_signal,SIGSTOP,sleeping\n"); - /* Sleep */ - proc->status = PS_STOPPED; - schedule(); - dkprintf("SIGSTOP(): woken up\n"); - goto out; } - case SIGCONT: - dkprintf("do_signal,SIGCONT,do nothing\n"); - goto out; - case SIGQUIT: - case SIGILL: - case SIGTRAP: - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGUSR1: - case SIGSEGV: - case SIGUSR2: + ftn->status = PS_STOPPED; + ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); + + /* Wake up the parent who tried wait4 and sleeping */ + waitq_wakeup(&proc->ftn->parent->waitpid_q); + + dkprintf("do_signal,SIGSTOP,sleeping\n"); + /* Sleep */ + proc->status = PS_STOPPED; + schedule(); + dkprintf("SIGSTOP(): woken up\n"); + break; } + case SIGTRAP: { + dkprintf("do_signal,SIGTRAP,changing state\n"); + struct process *proc = cpu_local_var(current); + struct fork_tree_node *ftn = proc->ftn; + + /* Update process state in fork tree */ + ihk_mc_spinlock_lock_noirq(&ftn->lock); + ftn->exit_status = SIGTRAP; + ftn->status = PS_TRACED; + ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); + + /* Wake up the parent who tried wait4 and sleeping */ + waitq_wakeup(&proc->ftn->parent->waitpid_q); + + dkprintf("do_signal,SIGTRAP,sleeping\n"); + /* Sleep */ + proc->status = PS_TRACED; + + //struct cpu_local_var *v = get_this_cpu_local_var(); + //v->flags |= CPU_FLAG_NEED_RESCHED; + + schedule(); + dkprintf("SIGTRAP(): woken up\n"); + break; } + case SIGCONT: + dkprintf("do_signal,SIGCONT,do nothing\n"); + break; + case SIGSEGV: + kprintf("do_signal,SIGSEGV received\n"); + case SIGQUIT: + case SIGILL: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGUSR1: + case SIGUSR2: coredump(proc, regs); coredumped = 0x80; + terminate(0, sig | coredumped, (ihk_mc_user_context_t *)regs->rsp); + break; + case SIGKILL: + dkprintf("do_signal,calling terminate\n"); + terminate(0, sig, (ihk_mc_user_context_t *)regs->rsp); + break; + case SIGCHLD: + case SIGURG: + default: + break; } - terminate(0, sig | coredumped, (ihk_mc_user_context_t *)regs->rsp); } - out:; +} + +static int ptrace_report_signal(struct process *proc, struct sig_pending *pending) { + int sig; + __sigset_t w; + long rc; + + /* Save reason why stopped and process state for wait to reap */ + for (w = pending->sigmask.__val[0], sig = 0; w; sig++, w >>= 1); + ihk_mc_spinlock_lock_noirq(&proc->ftn->lock); + proc->ftn->exit_status = sig; + /* Transition process state */ + proc->ftn->status = PS_TRACED; + ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); + + if (proc->ftn->parent) { + /* kill SIGCHLD */ + ihk_mc_spinlock_lock_noirq(&proc->ftn->parent->lock); + if (proc->ftn->parent->owner) { + struct siginfo info; + + memset(&info, '\0', sizeof info); + info.si_signo = SIGCHLD; + info.si_code = CLD_TRAPPED; + info._sifields._sigchld.si_pid = proc->pid; + info._sifields._sigchld.si_status = PS_TRACED; + rc = do_kill(proc->ftn->parent->owner->pid, -1, SIGCHLD, &info); + if (rc < 0) { + kprintf("ptrace_report_signal,do_kill failed\n"); + } + } + ihk_mc_spinlock_unlock_noirq(&proc->ftn->parent->lock); + + /* Wake parent (if sleeping in wait4()) */ + waitq_wakeup(&proc->ftn->parent->waitpid_q); + } + + dkprintf("ptrace_report_signal,sleeping\n"); + /* Sleep */ + proc->status = PS_TRACED; + schedule(); + dkprintf("ptrace_report_signal,wake up\n"); + + return sig; } void @@ -330,8 +404,9 @@ check_signal(unsigned long rc, void *regs0) struct sig_pending *next; struct list_head *head; ihk_spinlock_t *lock; - __sigset_t w; + __sigset_t w, sig_bv; int irqstate; + int sig; if(clv == NULL) return; @@ -339,8 +414,9 @@ check_signal(unsigned long rc, void *regs0) if(proc == NULL || proc->pid == 0) return; - if(regs != NULL && (regs->rsp & 0x8000000000000000)) + if(regs != NULL && (regs->rsp & 0x8000000000000000)) { return; + } for(;;){ w = proc->sigmask.__val[0]; @@ -372,8 +448,16 @@ check_signal(unsigned long rc, void *regs0) pending = NULL; ihk_mc_spinlock_unlock(lock, irqstate); } - if(!pending) + if(!pending) { + dkprintf("check_signal,queue is empty\n"); return; + } + + for(sig_bv = pending->sigmask.__val[0], sig = 0; sig_bv; sig++, sig_bv >>= 1); + if((proc->ftn->ptrace & PT_TRACED) && sig != SIGKILL) { + sig = ptrace_report_signal(proc, pending); + /* TODO: Tracing process could overwrite signal, so handle the case here. */ + } do_signal(rc, regs, proc, pending); } @@ -382,6 +466,7 @@ check_signal(unsigned long rc, void *regs0) unsigned long do_kill(int pid, int tid, int sig, siginfo_t *info) { + dkprintf("do_kill,pid=%d,tid=%d,sig=%d\n", pid, tid, sig); struct cpu_local_var *v; struct process *p; struct process *proc = cpu_local_var(current); @@ -392,7 +477,6 @@ do_kill(int pid, int tid, int sig, siginfo_t *info) struct list_head *head; int rc; unsigned long irqstate = 0; - struct k_sigaction *k; int doint; ihk_spinlock_t *savelock = NULL; int found = 0; @@ -448,7 +532,6 @@ do_kill(int pid, int tid, int sig, siginfo_t *info) kfree(pids); return rc; } - irqstate = cpu_disable_interrupt_save(); mask = __sigmask(sig); if(tid == -1){ @@ -464,8 +547,9 @@ do_kill(int pid, int tid, int sig, siginfo_t *info) if(p->tid == pid || tproc == NULL){ if(!(mask & p->sigmask.__val[0])){ tproc = p; - if(!found && savelock) + if(!found && savelock) { ihk_mc_spinlock_unlock_noirq(savelock); + } found = 1; savelock = &(v->runq_lock); if(savelock0 && savelock0 != savelock){ @@ -486,8 +570,9 @@ do_kill(int pid, int tid, int sig, siginfo_t *info) } } } - if(!found) + if(!found) { ihk_mc_spinlock_unlock_noirq(&(v->runq_lock)); + } } if(tproc == NULL){ tproc = tproc0; @@ -553,31 +638,29 @@ do_kill(int pid, int tid, int sig, siginfo_t *info) } rc = 0; - k = tproc->sighandler->action + sig - 1; - if(k->sa.sa_handler != (void *)1 && - (k->sa.sa_handler != NULL || - (sig != SIGCHLD && sig != SIGURG))){ - pending = NULL; - if(sig < 33){ // SIGRTMIN - SIGRTMAX - list_for_each_entry(pending, head, list){ - if(pending->sigmask.__val[0] == mask) - break; - } - if(&pending->list == head) - pending = NULL; + /* Put signal event even when handler is SIG_IGN or SIG_DFL + because target ptraced process must call ptrace_report_signal + in check_signal */ + pending = NULL; + if (sig < 33) { // SIGRTMIN - SIGRTMAX + list_for_each_entry(pending, head, list){ + if(pending->sigmask.__val[0] == mask) + break; } - if(pending == NULL){ - doint = 1; - pending = kmalloc(sizeof(struct sig_pending), IHK_MC_AP_NOWAIT); - if(!pending){ - rc = -ENOMEM; - } - else{ - pending->sigmask.__val[0] = mask; - memcpy(&pending->info, info, sizeof(siginfo_t)); - list_add_tail(&pending->list, head); - tproc->sigevent = 1; - } + if(&pending->list == head) + pending = NULL; + } + if(pending == NULL){ + doint = 1; + pending = kmalloc(sizeof(struct sig_pending), IHK_MC_AP_NOWAIT); + if(!pending){ + rc = -ENOMEM; + } + else{ + pending->sigmask.__val[0] = mask; + memcpy(&pending->info, info, sizeof(siginfo_t)); + list_add_tail(&pending->list, head); + tproc->sigevent = 1; } } @@ -587,13 +670,13 @@ do_kill(int pid, int tid, int sig, siginfo_t *info) else{ ihk_mc_spinlock_unlock_noirq(&tproc->sigpendinglock); } - dkprintf("do_kill,pid=%d,sig=%d\n", pid, sig); + if(doint && !(mask & tproc->sigmask.__val[0])){ - dkprintf("do_kill,proc=%p,tproc=%p\n", proc, tproc); switch(sig) { case SIGCONT: break; case SIGSTOP: + case SIGKILL: default: if(proc != tproc){ dkprintf("do_kill,ipi,pid=%d,cpu_id=%d\n", @@ -602,25 +685,32 @@ do_kill(int pid, int tid, int sig, siginfo_t *info) } break; } + ihk_mc_spinlock_unlock_noirq(savelock); cpu_restore_interrupt(irqstate); + switch(sig) { - case SIGSTOP: - break; + case SIGKILL: +#if 0 + /* Is this really needed? */ + kprintf("do_kill,sending kill to mcexec,pid=%d,cpuid=%d\n", + tproc->pid, tproc->cpu_id); + interrupt_syscall(tproc->pid, tproc->cpu_id); +#endif + break; case SIGCONT: - dkprintf("do_kill,SIGCONT\n"); /* Wake up the target only when stopped by SIGSTOP */ sched_wakeup_process(tproc, PS_STOPPED); + ihk_mc_spinlock_lock_noirq(&tproc->ftn->lock); if (tproc->ftn->status & PS_STOPPED) { - ihk_mc_spinlock_lock_noirq(&tproc->ftn->lock); xchg4((int *)(&tproc->ftn->status), PS_RUNNING); - ihk_mc_spinlock_unlock_noirq(&tproc->ftn->lock); + /* Reap and set singal_flags */ + tproc->ftn->signal_flags = SIGNAL_STOP_CONTINUED; } + ihk_mc_spinlock_unlock_noirq(&tproc->ftn->lock); break; + case SIGSTOP: default: - dkprintf("do_kill,sending kill to mcexec,pid=%d,cpuid=%d\n", - tproc->pid, tproc->cpu_id); - interrupt_syscall(tproc->pid, tproc->cpu_id); break; } } diff --git a/executer/user/mcexec.c b/executer/user/mcexec.c index 205b3123..ae61ff11 100644 --- a/executer/user/mcexec.c +++ b/executer/user/mcexec.c @@ -447,7 +447,7 @@ retry: /* Check whether the resolved path is a symlink */ if (lstat(path, &sb) == -1) { - fprintf(stderr, "lookup_exec_path(): error stat\n"); + fprintf(stderr, "lookup_exec_path(): %s, error stat\n"); return errno; } diff --git a/kernel/host.c b/kernel/host.c index cc595b3a..15d5f0ba 100644 --- a/kernel/host.c +++ b/kernel/host.c @@ -362,6 +362,7 @@ static int process_msg_prepare_process(unsigned long rphys) return -ENOMEM; } proc->pid = pn->pid; + kprintf("prepare_process,pid=%d,ptrace=%08x\n", proc->pid, proc->ftn->ptrace); proc->pgid = pn->pgid; proc->ftn->pid = pn->pid; proc->vm->region.user_start = pn->user_start; diff --git a/kernel/include/process.h b/kernel/include/process.h index b3f5f7a1..bfc9fb17 100644 --- a/kernel/include/process.h +++ b/kernel/include/process.h @@ -55,9 +55,19 @@ #define PS_ZOMBIE 0x8 #define PS_EXITED 0x10 #define PS_STOPPED 0x20 +#define PS_TRACED 0x40 /* Set to "not running" by a ptrace related event */ #define PS_NORMAL (PS_INTERRUPTIBLE | PS_UNINTERRUPTIBLE) +#define PT_TRACED 0x1 /* The process is ptraced */ +#define PT_TRACE_EXEC 0x2 /* Trace execve(2) */ + +#define PTRACE_CONT 7 +#define PTRACE_KILL 8 + +#define SIGNAL_STOP_STOPPED 0x1 /* The process has been stopped by SIGSTOP */ +#define SIGNAL_STOP_CONTINUED 0x2 /* The process has been resumed by SIGCONT */ + /* Waitpid options */ #define WNOHANG 0x00000001 #define WUNTRACED 0x00000002 @@ -154,7 +164,29 @@ struct fork_tree_node { struct list_head children; struct list_head siblings_list; + /* The ptracing process behave as the parent of the ptraced process + after using PTRACE_ATTACH except getppid. So we save it here. */ + struct fork_tree_node *ppid_parent; + + /* Manage ptraced processes in the separate list to make it easy to + restore the orginal parent child relationship when + performing PTRACE_DETACH */ + struct list_head ptrace_children; + struct list_head ptrace_siblings_list; + struct waitq waitpid_q; + + /* Store exit_status for a group of threads when stopped by SIGSTOP. + exit_status can't be used because values of exit_status of threads + might divert while the threads are exiting by group_exit(). */ + int group_exit_status; + + /* Showing whether or not the process is ptraced */ + int ptrace; + + /* Store event related to signal. For example, + it represents that the proceess has been resumed by SIGCONT. */ + int signal_flags; }; void hold_fork_tree_node(struct fork_tree_node *ftn); diff --git a/kernel/process.c b/kernel/process.c index 012a809e..b41b1d43 100644 --- a/kernel/process.c +++ b/kernel/process.c @@ -84,15 +84,25 @@ void init_fork_tree_node(struct fork_tree_node *ftn, /* These will be filled out when changing status */ ftn->pid = -1; ftn->exit_status = -1; + ftn->group_exit_status = 0; ftn->status = PS_RUNNING; +#if 1 + ftn->ptrace = parent ? PT_TRACED : 0; /*debug*//*takagi*/ +#endif + ftn->signal_flags = 0; ftn->parent = NULL; if (parent) { ftn->parent = parent; } - INIT_LIST_HEAD(&ftn->children); INIT_LIST_HEAD(&ftn->siblings_list); + + if (parent) { + ftn->ppid_parent = parent; /*debug*//*takagi*/ + } + INIT_LIST_HEAD(&ftn->ptrace_children); + INIT_LIST_HEAD(&ftn->ptrace_siblings_list); waitq_init(&ftn->waitpid_q); } @@ -287,6 +297,12 @@ struct process *clone_process(struct process *org, unsigned long pc, list_add_tail(&proc->ftn->siblings_list, &org->ftn->children); ihk_mc_spinlock_unlock_noirq(&org->ftn->lock); + /*takagi*//*debug*/ +#if 1 + ihk_mc_spinlock_lock_noirq(&org->ftn->lock); + list_add_tail(&proc->ftn->ptrace_siblings_list, &org->ftn->ptrace_children); + ihk_mc_spinlock_unlock_noirq(&org->ftn->lock); +#endif /* We hold a reference to parent */ hold_fork_tree_node(proc->ftn->parent); @@ -1927,10 +1943,10 @@ redo: void check_need_resched(void) { struct cpu_local_var *v = get_this_cpu_local_var(); - if (v->flags & CPU_FLAG_NEED_RESCHED) { + if (v->flags & CPU_FLAG_NEED_RESCHED) { v->flags &= ~CPU_FLAG_NEED_RESCHED; schedule(); - } + } } @@ -1940,11 +1956,14 @@ int sched_wakeup_process(struct process *proc, int valid_states) int spin_slept = 0; unsigned long irqstate; struct cpu_local_var *v = get_cpu_local_var(proc->cpu_id); + + dkprintf("sched_wakeup_process,proc->pid=%d,valid_states=%08x,proc->status=%08x,proc->cpu_id=%d,my cpu_id=%d\n", + proc->pid, valid_states, proc->status, proc->cpu_id, ihk_mc_get_processor_id()); irqstate = ihk_mc_spinlock_lock(&(proc->spin_sleep_lock)); if (proc->spin_sleep) { dkprintf("sched_wakeup_process() spin wakeup: cpu_id: %d\n", - proc->cpu_id); + proc->cpu_id); spin_slept = 1; proc->spin_sleep = 0; @@ -1952,8 +1971,9 @@ int sched_wakeup_process(struct process *proc, int valid_states) } ihk_mc_spinlock_unlock(&(proc->spin_sleep_lock), irqstate); - if (spin_slept) + if (spin_slept) { return status; + } irqstate = ihk_mc_spinlock_lock(&(v->runq_lock)); @@ -1968,6 +1988,8 @@ int sched_wakeup_process(struct process *proc, int valid_states) ihk_mc_spinlock_unlock(&(v->runq_lock), irqstate); if (!status && (proc->cpu_id != ihk_mc_get_processor_id())) { + dkprintf("sched_wakeup_process,issuing IPI,proc->cpu_id=%d\n", + proc->cpu_id); ihk_mc_interrupt_cpu(get_x86_cpu_local_variable(proc->cpu_id)->apic_id, 0xd1); } diff --git a/kernel/syscall.c b/kernel/syscall.c index 74d3aafa..df6d1f38 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -297,13 +297,92 @@ void sigchld_parent(struct process *parent, int status) } #endif +static int wait_zombie(struct process *proc, struct fork_tree_node *child, int *status, ihk_mc_user_context_t *ctx) { + int ret; + struct syscall_request request IHK_DMA_ALIGN; + + dkprintf("wait: found PS_ZOMBIE process: %d\n", child->pid); + + list_del(&child->siblings_list); + + if (status) { + *status = child->exit_status; + } + + release_fork_tree_node(child); + + /* Ask host to clean up exited child */ + request.number = __NR_wait4; + request.args[0] = child->pid; + request.args[1] = 0; + ret = do_syscall(&request, ctx, ihk_mc_get_processor_id(), 0); + + if (ret != child->pid) + kprintf("WARNING: host waitpid failed?\n"); + dkprintf("wait_zombie,child->pid=%d,status=%08x\n", + child->pid, status ? *status : -1); + return ret; +} + +static int wait_stopped(struct process *proc, struct fork_tree_node *child, int *status, int options) { + dkprintf("wait_stopped,proc->pid=%d,child->pid=%d,options=%08x\n", + proc->pid, child->pid, options); + int ret; + + /* Copy exit_status created in do_signal */ + int *exit_status = (child->ptrace & PT_TRACED) ? + &child->exit_status : + &child->group_exit_status; + + /* Skip this process because exit_status has been reaped. */ + if (!*exit_status) { + ret = 0; + goto out; + } + + /* TODO: define 0x7f in kernel/include/process.h */ + if (status) { + *status = (*exit_status << 8) | 0x7f; + } + + /* Reap exit_status. signal_flags is reaped on receiving signal + in do_kill(). */ + if(!(options & WNOWAIT)) { + *exit_status = 0; + } + + dkprintf("wait_stopped,child->pid=%d,status=%08x\n", + child->pid, status ? *status : -1); + ret = child->pid; + out: + return ret; +} + +static int wait_continued(struct process *proc, struct fork_tree_node *child, int *status, int options) { + int ret; + + if (status) { + *status = 0xffff; + } + + /* Reap signal_flags */ + if(!(options & WNOWAIT)) { + child->signal_flags &= ~SIGNAL_STOP_CONTINUED; + } + + dkprintf("wait4,SIGNAL_STOP_CONTINUED,pid=%d,status=%08x\n", + child->pid, status ? *status : -1); + ret = child->pid; + return ret; +} + /* * 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; + struct fork_tree_node *child_iter; int pid = (int)ihk_mc_syscall_arg0(ctx); int pgid = proc->pgid; int *status = (int *)ihk_mc_syscall_arg1(ctx); @@ -312,18 +391,17 @@ SYSCALL_DECLARE(wait4) struct waitq_entry waitpid_wqe; int empty = 1; + dkprintf("wait4,proc->pid=%d,pid=%d\n", proc->pid, pid); if (options & ~(WNOHANG | WUNTRACED | WCONTINUED)) { - return -EINVAL; + ret = -EINVAL; + goto exit; } 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) { - - empty = 0; ihk_mc_spinlock_lock_noirq(&child_iter->lock); @@ -331,73 +409,91 @@ rescan: pid == -1 || (pid == 0 && pgid == child_iter->pgid) || (pid > 0 && pid == child_iter->pid)) { - child = child_iter; - break; + + empty = 0; + + if(child_iter->status == PS_ZOMBIE) { + ret = wait_zombie(proc, child_iter, status, ctx); + if(ret) { + goto out_found; + } + } + + if((child_iter->signal_flags & SIGNAL_STOP_STOPPED) && + (options & WUNTRACED)) { + /* Not ptraced and in stopped state and WUNTRACED is specified */ + ret = wait_stopped(proc, child_iter, status, options); + if(ret) { + goto out_found; + } + } + + if((child_iter->signal_flags & SIGNAL_STOP_CONTINUED) && + (options & WCONTINUED)) { + ret = wait_continued(proc, child_iter, status, options); + if(ret) { + goto out_found; + } + } } ihk_mc_spinlock_unlock_noirq(&child_iter->lock); } - - if (empty || (!child && pid != -1)) { - ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); - return -ECHILD; - } - /* If child is valid we are still holding its ftn->lock */ - if (child) { - if (child->status == PS_ZOMBIE) { - struct syscall_request request IHK_DMA_ALIGN; + list_for_each_entry(child_iter, &proc->ftn->ptrace_children, ptrace_siblings_list) { - ihk_mc_spinlock_unlock_noirq(&child->lock); - dkprintf("wait: found PS_ZOMBIE process: %d\n", child->pid); + ihk_mc_spinlock_lock_noirq(&child_iter->lock); + + if ((pid < 0 && -pid == child_iter->pgid) || + pid == -1 || + (pid == 0 && pgid == child_iter->pgid) || + (pid > 0 && pid == child_iter->pid)) { - list_del(&child->siblings_list); - ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); + empty = 0; - if (status) { - *status = child->exit_status; - } - pid = child->pid; + if(child_iter->status == PS_ZOMBIE) { + ret = wait_zombie(proc, child_iter, status, ctx); + if(ret) { + goto out_found; + } + } - release_fork_tree_node(child); + if(child_iter->status & (PS_STOPPED | PS_TRACED)) { + /* ptraced and in stopeed or trace-stopped state */ + ret = wait_stopped(proc, child_iter, status, options); + if(ret) { + goto out_found; + } + } else { + /* ptraced and in running or sleeping state */ + } - /* 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; - } else if(child->status == PS_STOPPED) { - ihk_mc_spinlock_unlock_noirq(&child->lock); - ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); - - /* exit_status is created in do_signal */ - if (status) { - *status = child->exit_status; - } - pid = child->pid; - dkprintf("wait4,PS_STOPPED,pid=%d,status=%08x\n", pid, *status); - - goto exit; + if((child_iter->signal_flags & SIGNAL_STOP_CONTINUED) && + (options & WCONTINUED)) { + ret = wait_continued(proc, child_iter, status, options); + if(ret) { + goto out_found; + } + } } - ihk_mc_spinlock_unlock_noirq(&child->lock); + ihk_mc_spinlock_unlock_noirq(&child_iter->lock); + } + + if (empty) { + ret = -ECHILD; + goto out_notfound; } /* Don't sleep if WNOHANG requested */ if (options & WNOHANG) { - ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); - *status = 0; - pid = 0; - goto exit; + ret = 0; + goto out_notfound; } /* Sleep */ + dkprintf("wait4,sleeping\n"); waitq_init_entry(&waitpid_wqe, proc); waitq_prepare_to_wait(&proc->ftn->waitpid_q, &waitpid_wqe, PS_INTERRUPTIBLE); @@ -409,9 +505,14 @@ rescan: waitq_finish_wait(&proc->ftn->waitpid_q, &waitpid_wqe); goto rescan; - -exit: - return pid; + + exit: + return ret; + out_found: + ihk_mc_spinlock_unlock_noirq(&child_iter->lock); + out_notfound: + ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); + goto exit; } void @@ -421,6 +522,8 @@ terminate(int rc, int sig, ihk_mc_user_context_t *ctx) struct process *proc = cpu_local_var(current); struct fork_tree_node *ftn = proc->ftn; struct fork_tree_node *child, *next; + struct process *parent_owner; + int error; request.number = __NR_exit_group; request.args[0] = ((rc & 0x00ff) << 8) | (sig & 0xff); @@ -439,17 +542,18 @@ terminate(int rc, int sig, ihk_mc_user_context_t *ctx) /* 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) { + int parent_owner_pid; + + dkprintf("terminate,ftn->parent->owner->pid=%d\n", + ftn->parent->owner->pid); ihk_mc_spinlock_lock_noirq(&ftn->lock); ftn->pid = proc->pid; @@ -457,27 +561,38 @@ terminate(int rc, int sig, ihk_mc_user_context_t *ctx) ftn->status = PS_ZOMBIE; ihk_mc_spinlock_unlock_noirq(&ftn->lock); + /* Wake parent (if sleeping in wait4()) */ + dkprintf("terminate,wakeup\n"); + waitq_wakeup(&ftn->parent->waitpid_q); + /* Signal parent if still attached */ ihk_mc_spinlock_lock_noirq(&ftn->parent->lock); - if (ftn->parent->owner) { + parent_owner = ftn->parent->owner; + parent_owner_pid = parent_owner ? ftn->parent->owner->pid : 0; + ihk_mc_spinlock_unlock_noirq(&ftn->parent->lock); + if (parent_owner) { struct siginfo info; memset(&info, '\0', sizeof info); info.si_signo = SIGCHLD; - info.si_code = sig? ((sig & 0x80)? 3: 2): 1; + info.si_code = sig? ((sig & 0x80)? CLD_DUMPED: CLD_KILLED): CLD_EXITED; info._sifields._sigchld.si_pid = proc->pid; info._sifields._sigchld.si_status = ((rc & 0x00ff) << 8) | (sig & 0xff); - do_kill(ftn->parent->owner->pid, -1, SIGCHLD, &info); + dkprintf("terminate,kill SIGCHLD,target pid=%d\n", + parent_owner_pid); + error = do_kill(ftn->parent->owner->pid, -1, SIGCHLD, &info); /* sigchld_parent(ftn->parent->owner, 0); */ + dkprintf("terminate,klll SIGCHILD,error=%d\n", + error); } - 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); - } + } else { + ihk_mc_spinlock_lock_noirq(&ftn->lock); + ftn->status = PS_EXITED; + ihk_mc_spinlock_unlock_noirq(&ftn->lock); + } release_fork_tree_node(ftn); @@ -490,6 +605,7 @@ terminate(int rc, int sig, ihk_mc_user_context_t *ctx) void interrupt_syscall(int pid, int cpuid) { + dkprintf("interrupt_syscall,target pid=%d,target cpuid=%d\n", pid, cpuid); ihk_mc_user_context_t ctx; long lerror; @@ -509,6 +625,7 @@ SYSCALL_DECLARE(exit_group) SYSCALL_HEADER; #endif + dkprintf("sys_exit_group,pid=%d\n", cpu_local_var(current)->pid); terminate((int)ihk_mc_syscall_arg0(ctx), 0, ctx); #if 0 struct process *proc = cpu_local_var(current); @@ -1247,8 +1364,58 @@ SYSCALL_DECLARE(arch_prctl) ihk_mc_syscall_arg1(ctx)); } +static int ptrace_report_exec(struct process *proc) { + int error = 0; + long rc; + struct siginfo info; + + if (!(proc->ftn->ptrace & PT_TRACE_EXEC)) { + goto out; + } + + /* Save reason why stopped and process state for wait to reap */ + ihk_mc_spinlock_lock_noirq(&proc->ftn->lock); + memset(&info, '\0', sizeof info); + info.si_signo = SIGTRAP; + info.si_code = TRAP_TRACE; + proc->ftn->exit_status = (SIGTRAP | (PTRACE_EVENT_EXEC << 8)); + /* Transition process state */ + proc->ftn->status = PS_TRACED; + ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); + + /* Signal myself so that my parent can wait for me */ + rc = do_kill(proc->ftn->pid, -1, SIGTRAP, &info); + if (rc < 0) { + kprintf("ptrace_report_exec,do_kill failed\n"); + } + + if (proc->ftn->parent) { + /* kill SIGCHLD */ + ihk_mc_spinlock_lock_noirq(&proc->ftn->parent->lock); + if (proc->ftn->parent->owner) { + memset(&info, '\0', sizeof info); + info.si_signo = SIGCHLD; + info.si_code = CLD_TRAPPED; + info._sifields._sigchld.si_pid = proc->pid; + info._sifields._sigchld.si_status = PS_TRACED; + rc = do_kill(proc->ftn->parent->owner->pid, -1, SIGCHLD, &info); + if(rc < 0) { + kprintf("ptrace_report_exec,do_kill failed\n"); + } + } + ihk_mc_spinlock_unlock_noirq(&proc->ftn->parent->lock); + + /* Wake parent (if sleeping in wait4()) */ + waitq_wakeup(&proc->ftn->parent->waitpid_q); + } + +out: + return error; +} + SYSCALL_DECLARE(execve) { + int error; long ret; char *empty_envp[1] = {NULL}; const char *filename = (const char *)ihk_mc_syscall_arg0(ctx); @@ -1363,6 +1530,11 @@ SYSCALL_DECLARE(execve) panic(""); } + error = ptrace_report_exec(cpu_local_var(current)); + if(error) { + kprintf("execve(): ERROR: ptrace_report_exec()\n"); + } + /* Switch to new execution context */ dkprintf("execve(): switching to new process\n"); @@ -1414,7 +1586,6 @@ unsigned long do_fork(int clone_flags, unsigned long newsp, else { request1.number = __NR_fork; new->pid = do_syscall(&request1, &ctx1, ihk_mc_get_processor_id(), 0); - if (new->pid == -1) { kprintf("ERROR: forking host process\n"); @@ -1521,13 +1692,17 @@ SYSCALL_DECLARE(kill) int sig = ihk_mc_syscall_arg1(ctx); struct process *proc = cpu_local_var(current); struct siginfo info; + int error; memset(&info, '\0', sizeof info); info.si_signo = sig; info.si_code = SI_USER; info._sifields._kill.si_pid = proc->pid; - return do_kill(pid, -1, sig, &info); + dkprintf("sys_kill,enter,pid=%d,sig=%d\n", pid, sig); + error = do_kill(pid, -1, sig, &info); + dkprintf("sys_kill,returning,pid=%d,sig=%d,error=%d\n", pid, sig, error); + return error; } // see linux-2.6.34.13/kernel/signal.c @@ -2038,6 +2213,7 @@ SYSCALL_DECLARE(futex) SYSCALL_DECLARE(exit) { struct process *proc = cpu_local_var(current); + dkprintf("sys_exit,pid=%d\n", proc->pid); #ifdef DCFA_KMOD do_mod_exit((int)ihk_mc_syscall_arg0(ctx)); @@ -2101,15 +2277,93 @@ SYSCALL_DECLARE(getrlimit) return ret; } +static int ptrace_wakeup_sig(int pid, long request, long data) { + dkprintf("ptrace_wakeup_sig,pid=%d,data=%08x\n", pid, data); + int error; + struct process *child; + ihk_spinlock_t *savelock; + unsigned long irqstate; + struct siginfo info; + + child = findthread_and_lock(pid, -1, &savelock, &irqstate); + if (!child) { + error = -ESRCH; + goto out; + } + ihk_mc_spinlock_unlock(savelock, irqstate); + + error = sched_wakeup_process(child, PS_TRACED); + if (error < 0) { + goto out; + } + + ihk_mc_spinlock_lock_noirq(&child->ftn->lock); + child->ftn->exit_status = data; + if (child->ftn->status & PS_TRACED) { + xchg4((int *)(&child->ftn->status), PS_RUNNING); + } + ihk_mc_spinlock_unlock_noirq(&child->ftn->lock); + + + if (data > 64 || data < 0) { + error = -EINVAL; + goto out; + } + + switch (request) { + case PTRACE_KILL: + memset(&info, '\0', sizeof info); + info.si_signo = SIGKILL; + error = do_kill(pid, -1, SIGKILL, &info); + if (error < 0) { + goto out; + } + break; + case PTRACE_CONT: + if(data != 0) { + struct process *proc; + + /* TODO: Tracing process replace the original + signal with "data" */ + proc = cpu_local_var(current); + memset(&info, '\0', sizeof info); + info.si_signo = data; + info.si_code = SI_USER; + info._sifields._kill.si_pid = proc->pid; + error = do_kill(pid, -1, data, &info); + if (error < 0) { + goto out; + } + } + break; + default: + break; + } + +out: + return error; +} + SYSCALL_DECLARE(ptrace) { - const int request = ihk_mc_syscall_arg0(ctx); - const long pid = ihk_mc_syscall_arg1(ctx); - void * const addr = (void *)ihk_mc_syscall_arg2(ctx); - void * const data = (void *)ihk_mc_syscall_arg3(ctx); + const long request = (long)ihk_mc_syscall_arg0(ctx); + const int pid = (int)ihk_mc_syscall_arg1(ctx); + const long addr = (long)ihk_mc_syscall_arg2(ctx); + const long data = (long)ihk_mc_syscall_arg3(ctx); + int error; - kprintf("ptrace(%d,%ld,%p,%p): ENOSYS\n", request, pid, addr, data); - return -ENOSYS; + switch(request) { + case PTRACE_KILL: + case PTRACE_CONT: + error = ptrace_wakeup_sig(pid, request, data); + break; + default: + error = 0; + break; + } + + dkprintf("ptrace(%d,%ld,%p,%p): returning %d\n", request, pid, addr, data, error); + return error; } #define MIN2(x,y) (x) < (y) ? (x) : (y)