X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/ec4aa998d8662f4289e327da3dda794a6fd27d9d..ca466baeb91097acf0817cd63a0c33886ca69d31:/sys/kern/kern_sig.c diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index ea0a838570..1ea00291ca 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -37,7 +37,7 @@ * * @(#)kern_sig.c 8.7 (Berkeley) 4/18/94 * $FreeBSD: src/sys/kern/kern_sig.c,v 1.72.2.17 2003/05/16 16:34:34 obrien Exp $ - * $DragonFly: src/sys/kern/kern_sig.c,v 1.31 2004/10/12 19:20:46 dillon Exp $ + * $DragonFly: src/sys/kern/kern_sig.c,v 1.45 2006/05/05 21:15:08 dillon Exp $ */ #include "opt_ktrace.h" @@ -51,7 +51,7 @@ #include #include #include -#include +#include #include #include #include @@ -65,6 +65,7 @@ #include #include #include +#include #include @@ -76,10 +77,11 @@ static char *expand_name(const char *, uid_t, pid_t); static int killpg(int sig, int pgid, int all); static int sig_ffs(sigset_t *set); static int sigprop(int sig); -static void stop(struct proc *); #ifdef SMP static void signotify_remote(void *arg); #endif +static int kern_sigtimedwait(sigset_t set, siginfo_t *info, + struct timespec *timeout); static int filt_sigattach(struct knote *kn); static void filt_sigdetach(struct knote *kn); @@ -258,7 +260,7 @@ kern_sigaction(int sig, struct sigaction *act, struct sigaction *oact) /* * Change setting atomically. */ - splhigh(); + crit_enter(); ps->ps_catchmask[_SIG_IDX(sig)] = act->sa_mask; SIG_CANTMASK(ps->ps_catchmask[_SIG_IDX(sig)]); @@ -329,7 +331,7 @@ kern_sigaction(int sig, struct sigaction *act, struct sigaction *oact) SIGADDSET(p->p_sigcatch, sig); } - spl0(); + crit_exit(); } return (0); } @@ -411,8 +413,7 @@ execsigs(struct proc *p) * kern_sigprocmask() - MP SAFE ONLY IF p == curproc * * Manipulate signal mask. This routine is MP SAFE *ONLY* if - * p == curproc. Also remember that in order to remain MP SAFE - * no spl*() calls may be made. + * p == curproc. */ int kern_sigprocmask(int how, sigset_t *set, sigset_t *oset) @@ -627,10 +628,11 @@ killpg(int sig, int pgid, int all) return (ESRCH); } LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { - if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || - p->p_stat == SZOMB || - !CANSIGNAL(p, sig)) + if (p->p_pid <= 1 || + (p->p_flag & (P_SYSTEM | P_ZOMBIE)) || + !CANSIGNAL(p, sig)) { continue; + } nfound++; if (sig) psignal(p, sig); @@ -758,28 +760,11 @@ trapsignal(struct proc *p, int sig, u_long code) * * Other ignored signals are discarded immediately. */ - -/* - * temporary hack to allow checkpoint code to continue to - * be in a module for the moment - */ - -static proc_func_t ckpt_func; - -proc_func_t -register_ckpt_func(proc_func_t func) -{ - proc_func_t old_func; - - old_func = ckpt_func; - ckpt_func = func; - return (old_func); -} - void psignal(struct proc *p, int sig) { - int s, prop; + struct lwp *lp = &p->p_lwp; + int prop; sig_t action; if (sig > _SIG_MAXSIG || sig <= 0) { @@ -787,9 +772,9 @@ psignal(struct proc *p, int sig) panic("psignal signal number"); } - s = splhigh(); + crit_enter(); KNOTE(&p->p_klist, NOTE_SIGNAL | sig); - splx(s); + crit_exit(); prop = sigprop(sig); @@ -823,10 +808,12 @@ psignal(struct proc *p, int sig) p->p_nice = NZERO; } + /* + * If continuing, clear any pending STOP signals. + */ if (prop & SA_CONT) SIG_STOPSIGMASK(p->p_siglist); - if (prop & SA_STOP) { /* * If sending a tty stop signal to a member of an orphaned @@ -846,74 +833,111 @@ psignal(struct proc *p, int sig) * Defer further processing for signals which are held, * except that stopped processes must be continued by SIGCONT. */ - if (action == SIG_HOLD && (!(prop & SA_CONT) || p->p_stat != SSTOP)) - return; - s = splhigh(); - switch (p->p_stat) { - case SSLEEP: + if (action == SIG_HOLD) { + if ((prop & SA_CONT) == 0 || (p->p_flag & P_STOPPED) == 0) + return; + } + + crit_enter(); + + /* + * Process is in tsleep and not stopped + */ + if (p->p_stat == SSLEEP && (p->p_flag & P_STOPPED) == 0) { /* - * If process is sleeping uninterruptibly + * If the process is sleeping uninterruptibly * we can't interrupt the sleep... the signal will * be noticed when the process returns through * trap() or syscall(). */ if ((p->p_flag & P_SINTR) == 0) goto out; + /* - * Process is sleeping and traced... make it runnable + * If the process is sleeping and traced, make it runnable * so it can discover the signal in issignal() and stop * for the parent. + * + * If the process is stopped and traced, no further action + * is necessary. */ if (p->p_flag & P_TRACED) goto run; + /* - * If SIGCONT is default (or ignored) and process is - * asleep, we are finished; the process should not - * be awakened. + * If the process is sleeping and SA_CONT, and the signal + * mode is SIG_DFL, then make the process runnable. + * + * However, do *NOT* set P_BREAKTSLEEP. We do not want + * a SIGCONT to terminate an interruptable tsleep early + * and generate a spurious EINTR. */ if ((prop & SA_CONT) && action == SIG_DFL) { SIGDELSET(p->p_siglist, sig); - goto out; + goto run_no_break; } + /* - * When a sleeping process receives a stop - * signal, process immediately if possible. - * All other (caught or default) signals - * cause the process to run. + * If the process is sleeping and receives a STOP signal, + * process immediately if possible. All other (caught or + * default) signals cause the process to run. */ if (prop & SA_STOP) { if (action != SIG_DFL) goto run; + /* - * If a child holding parent blocked, - * stopping could cause deadlock. + * If a child holding parent blocked, stopping + * could cause deadlock. Take no action at this + * time. */ if (p->p_flag & P_PPWAIT) goto out; + + /* + * Do not actually try to manipulate the process + * while it is sleeping, simply set P_STOPPED to + * indicate that it should stop as soon as it safely + * can. + */ SIGDELSET(p->p_siglist, sig); + p->p_flag |= P_STOPPED; + p->p_flag &= ~P_WAITED; p->p_xstat = sig; + wakeup(p->p_pptr); if ((p->p_pptr->p_procsig->ps_flag & PS_NOCLDSTOP) == 0) psignal(p->p_pptr, SIGCHLD); - stop(p); goto out; - } else { - goto run; } - /*NOTREACHED*/ - case SSTOP: + + /* + * Otherwise the signal can interrupt the sleep. + */ + goto run; + } + + /* + * Process is in tsleep and is stopped + */ + if (p->p_stat == SSLEEP && (p->p_flag & P_STOPPED)) { /* - * If traced process is already stopped, - * then no further action is necessary. + * If the process is stopped and is being traced, then no + * further action is necessary. */ if (p->p_flag & P_TRACED) goto out; /* - * Kill signal always sets processes running. + * If the process is stopped and receives a KILL signal, + * make the process runnable. */ if (sig == SIGKILL) goto run; + /* + * If the process is stopped and receives a CONT signal, + * then try to make the process runnable again. + */ if (prop & SA_CONT) { /* * If SIGCONT is default (or ignored), we continue the @@ -921,91 +945,86 @@ psignal(struct proc *p, int sig) * it has no further action. If SIGCONT is held, we * continue the process and leave the signal in * p_siglist. If the process catches SIGCONT, let it - * handle the signal itself. If it isn't waiting on - * an event, then it goes back to run state. - * Otherwise, process goes back to sleep state. + * handle the signal itself. */ if (action == SIG_DFL) SIGDELSET(p->p_siglist, sig); if (action == SIG_CATCH) goto run; - if (p->p_wchan == 0) - goto run; - clrrunnable(p, SSLEEP); - goto out; - } - if (prop & SA_STOP) { /* - * Already stopped, don't need to stop again. - * (If we did the shell could get confused.) + * Make runnable but do not break a tsleep unless + * some other signal was pending. */ + goto run_no_break; + } + + /* + * If the process is stopped and receives another STOP + * signal, we do not need to stop it again. If we did + * the shell could get confused. + */ + if (prop & SA_STOP) { SIGDELSET(p->p_siglist, sig); goto out; } /* - * If process is sleeping interruptibly, then simulate a - * wakeup so that when it is continued, it will be made - * runnable and can look at the signal. But don't make - * the process runnable, leave it stopped. + * Otherwise the process is sleeping interruptably but + * is stopped, just set the P_BREAKTSLEEP flag and take + * no further action. The next runnable action will wake + * the process up. */ - if (p->p_wchan && (p->p_flag & P_SINTR)) - unsleep(p->p_thread); + p->p_flag |= P_BREAKTSLEEP; goto out; - default: - /* - * SRUN, SIDL, SZOMB do nothing with the signal, - * other than kicking ourselves if we are running. - * It will either never be noticed, or noticed very soon. - * - * Note that p_thread may be NULL or may not be completely - * initialized if the process is in the SIDL or SZOMB state. - * - * For SMP we may have to forward the request to another cpu. - * YYY the MP lock prevents the target process from moving - * to another cpu, see kern/kern_switch.c - * - * If the target thread is waiting on its message port, - * wakeup the target thread so it can check (or ignore) - * the new signal. YYY needs cleanup. - */ + } + + /* + * Otherwise the process is running + * + * SRUN, SIDL, SZOMB do nothing with the signal, + * other than kicking ourselves if we are running. + * It will either never be noticed, or noticed very soon. + * + * Note that p_thread may be NULL or may not be completely + * initialized if the process is in the SIDL or SZOMB state. + * + * For SMP we may have to forward the request to another cpu. + * YYY the MP lock prevents the target process from moving + * to another cpu, see kern/kern_switch.c + * + * If the target thread is waiting on its message port, + * wakeup the target thread so it can check (or ignore) + * the new signal. YYY needs cleanup. + */ + if (lp == lwkt_preempted_proc()) { + signotify(); + } else if (p->p_stat == SRUN) { + struct thread *td = p->p_thread; + + KASSERT(td != NULL, + ("pid %d NULL p_thread stat %d flags %08x", + p->p_pid, p->p_stat, p->p_flag)); + #ifdef SMP - if (p == lwkt_preempted_proc()) { - signotify(); - } else if (p->p_stat == SRUN) { - struct thread *td = p->p_thread; - - KASSERT(td != NULL, - ("pid %d NULL p_thread stat %d flags %08x", - p->p_pid, p->p_stat, p->p_flag)); - - if (td->td_gd != mycpu) - lwkt_send_ipiq(td->td_gd, signotify_remote, p); - else if (td->td_msgport.mp_flags & MSGPORTF_WAITING) - lwkt_schedule(td); - } -#else - if (p == lwkt_preempted_proc()) { - signotify(); - } else if (p->p_stat == SRUN) { - struct thread *td = p->p_thread; - - KASSERT(td != NULL, - ("pid %d NULL p_thread stat %d flags %08x", - p->p_pid, p->p_stat, p->p_flag)); - - if (td->td_msgport.mp_flags & MSGPORTF_WAITING) - lwkt_schedule(td); - } + if (td->td_gd != mycpu) + lwkt_send_ipiq(td->td_gd, signotify_remote, lp); + else #endif - goto out; + if (td->td_msgport.mp_flags & MSGPORTF_WAITING) + lwkt_schedule(td); } + goto out; /*NOTREACHED*/ run: + /* + * Make runnable and break out of any tsleep as well. + */ + p->p_flag |= P_BREAKTSLEEP; +run_no_break: setrunnable(p); out: - splx(s); + crit_exit(); } #ifdef SMP @@ -1018,12 +1037,12 @@ out: static void signotify_remote(void *arg) { - struct proc *p = arg; + struct lwp *lp = arg; - if (p == lwkt_preempted_proc()) { + if (lp == lwkt_preempted_proc()) { signotify(); } else { - struct thread *td = p->p_thread; + struct thread *td = lp->lwp_thread; if (td->td_msgport.mp_flags & MSGPORTF_WAITING) lwkt_schedule(td); } @@ -1031,6 +1050,162 @@ signotify_remote(void *arg) #endif +static int +kern_sigtimedwait(sigset_t waitset, siginfo_t *info, struct timespec *timeout) +{ + sigset_t savedmask, set; + struct proc *p = curproc; + int error, sig, hz, timevalid = 0; + struct timespec rts, ets, ts; + struct timeval tv; + + error = 0; + sig = 0; + SIG_CANTMASK(waitset); + savedmask = p->p_sigmask; + + if (timeout) { + if (timeout->tv_sec >= 0 && timeout->tv_nsec >= 0 && + timeout->tv_nsec < 1000000000) { + timevalid = 1; + getnanouptime(&rts); + ets = rts; + timespecadd(&ets, timeout); + } + } + + for (;;) { + set = p->p_siglist; + SIGSETAND(set, waitset); + if ((sig = sig_ffs(&set)) != 0) { + SIGFILLSET(p->p_sigmask); + SIGDELSET(p->p_sigmask, sig); + SIG_CANTMASK(p->p_sigmask); + sig = issignal(p); + /* + * It may be a STOP signal, in the case, issignal + * returns 0, because we may stop there, and new + * signal can come in, we should restart if we got + * nothing. + */ + if (sig == 0) + continue; + else + break; + } + + /* + * Previous checking got nothing, and we retried but still + * got nothing, we should return the error status. + */ + if (error) + break; + + /* + * POSIX says this must be checked after looking for pending + * signals. + */ + if (timeout) { + if (!timevalid) { + error = EINVAL; + break; + } + getnanouptime(&rts); + if (timespeccmp(&rts, &ets, >=)) { + error = EAGAIN; + break; + } + ts = ets; + timespecsub(&ts, &rts); + TIMESPEC_TO_TIMEVAL(&tv, &ts); + hz = tvtohz_high(&tv); + } else + hz = 0; + + p->p_sigmask = savedmask; + SIGSETNAND(p->p_sigmask, waitset); + error = tsleep(&p->p_sigacts, PCATCH, "sigwt", hz); + if (timeout) { + if (error == ERESTART) { + /* can not restart a timeout wait. */ + error = EINTR; + } else if (error == EAGAIN) { + /* will calculate timeout by ourself. */ + error = 0; + } + } + /* Retry ... */ + } + + p->p_sigmask = savedmask; + if (sig) { + error = 0; + bzero(info, sizeof(*info)); + info->si_signo = sig; + SIGDELSET(p->p_siglist, sig); /* take the signal! */ + + if (sig == SIGKILL) + sigexit(p, sig); + } + return (error); +} + +int +sigtimedwait(struct sigtimedwait_args *uap) +{ + struct timespec ts; + struct timespec *timeout; + sigset_t set; + siginfo_t info; + int error; + + if (uap->timeout) { + error = copyin(uap->timeout, &ts, sizeof(ts)); + if (error) + return (error); + timeout = &ts; + } else { + timeout = NULL; + } + error = copyin(uap->set, &set, sizeof(set)); + if (error) + return (error); + error = kern_sigtimedwait(set, &info, timeout); + if (error) + return (error); + if (uap->info) + error = copyout(&info, uap->info, sizeof(info)); + /* Repost if we got an error. */ + if (error) + psignal(curproc, info.si_signo); + else + uap->sysmsg_result = info.si_signo; + return (error); +} + +int +sigwaitinfo(struct sigwaitinfo_args *uap) +{ + siginfo_t info; + sigset_t set; + int error; + + error = copyin(uap->set, &set, sizeof(set)); + if (error) + return (error); + error = kern_sigtimedwait(set, &info, NULL); + if (error) + return (error); + if (uap->info) + error = copyout(&info, uap->info, sizeof(info)); + /* Repost if we got an error. */ + if (error) + psignal(curproc, info.si_signo); + else + uap->sysmsg_result = info.si_signo; + return (error); +} + /* * If the current process has received a signal that would interrupt a * system call, return EINTR or ERESTART as appropriate. @@ -1059,6 +1234,9 @@ iscaught(struct proc *p) * by checking the pending signal masks in the CURSIG macro.) The normal call * sequence is * + * This routine is called via CURSIG/__cursig and the MP lock might not be + * held. Obtain the MP lock for the duration of the operation. + * * while (sig = CURSIG(curproc)) * postsig(sig); */ @@ -1068,6 +1246,7 @@ issignal(struct proc *p) sigset_t mask; int sig, prop; + get_mplock(); for (;;) { int traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG); @@ -1075,8 +1254,10 @@ issignal(struct proc *p) SIGSETNAND(mask, p->p_sigmask); if (p->p_flag & P_PPWAIT) SIG_STOPSIGMASK(mask); - if (!SIGNOTEMPTY(mask)) /* no signal to send */ + if (!SIGNOTEMPTY(mask)) { /* no signal to send */ + rel_mplock(); return (0); + } sig = sig_ffs(&mask); STOPEVENT(p, S_SIG, sig); @@ -1089,17 +1270,24 @@ issignal(struct proc *p) SIGDELSET(p->p_siglist, sig); continue; } - if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { + if ((p->p_flag & P_TRACED) && (p->p_flag & P_PPWAIT) == 0) { /* - * If traced, always stop, and stay - * stopped until released by the parent. + * If traced, always stop, and stay stopped until + * released by the parent. + * + * NOTE: P_STOPPED may get cleared during the loop, + * but we do not re-notify the parent if we have + * to loop several times waiting for the parent + * to let us continue. */ p->p_xstat = sig; + p->p_flag |= P_STOPPED; + p->p_flag &= ~P_WAITED; psignal(p->p_pptr, SIGCHLD); do { - stop(p); - mi_switch(p); - } while (!trace_req(p) && p->p_flag & P_TRACED); + tstop(p); + } while (!trace_req(p) && (p->p_flag & P_TRACED)); + p->p_flag &= ~P_STOPPED; /* * If parent wants us to take the signal, @@ -1136,7 +1324,6 @@ issignal(struct proc *p) * to clear it from the pending mask. */ switch ((int)(intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) { - case (int)SIG_DFL: /* * Don't take default actions on system processes. @@ -1157,8 +1344,7 @@ issignal(struct proc *p) * Handle the in-kernel checkpoint action */ if (prop & SA_CKPT) { - if (ckpt_func) - ckpt_func(p); + checkpoint_signal_handler(p); break; } @@ -1175,10 +1361,14 @@ issignal(struct proc *p) prop & SA_TTYSTOP)) break; /* == ignore */ p->p_xstat = sig; - stop(p); + p->p_flag |= P_STOPPED; + p->p_flag &= ~P_WAITED; + if ((p->p_pptr->p_procsig->ps_flag & PS_NOCLDSTOP) == 0) psignal(p->p_pptr, SIGCHLD); - mi_switch(p); + while (p->p_flag & P_STOPPED) { + tstop(p); + } break; } else if (prop & SA_IGNORE) { /* @@ -1187,6 +1377,7 @@ issignal(struct proc *p) */ break; /* == ignore */ } else { + rel_mplock(); return (sig); } @@ -1208,6 +1399,7 @@ issignal(struct proc *p) * This signal has an action, let * postsig() process it. */ + rel_mplock(); return (sig); } SIGDELSET(p->p_siglist, sig); /* take the signal! */ @@ -1215,19 +1407,6 @@ issignal(struct proc *p) /* NOTREACHED */ } -/* - * Put the argument process into the stopped state and notify the parent - * via wakeup. Signals are handled elsewhere. The process must not be - * on the run queue. - */ -void -stop(struct proc *p) -{ - p->p_stat = SSTOP; - p->p_flag &= ~P_WAITED; - wakeup((caddr_t)p->p_pptr); -} - /* * Take the action for the specified signal * from the current set of pending signals. @@ -1274,7 +1453,7 @@ postsig(int sig) * mask from before the sigsuspend is what we want * restored after the signal processing is completed. */ - splhigh(); + crit_enter(); if (p->p_flag & P_OLDMASK) { returnmask = p->p_oldsigmask; p->p_flag &= ~P_OLDMASK; @@ -1296,7 +1475,7 @@ postsig(int sig) SIGADDSET(p->p_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } - spl0(); + crit_exit(); p->p_stats->p_ru.ru_nsignals++; if (p->p_sig != sig) { code = 0; @@ -1451,7 +1630,7 @@ coredump(struct proc *p) struct ucred *cred = p->p_ucred; struct thread *td = p->p_thread; struct flock lf; - struct nameidata nd; + struct nlookupdata nd; struct vattr vattr; int error, error1; char *name; /* name of corefile */ @@ -1477,15 +1656,19 @@ coredump(struct proc *p) name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid); if (name == NULL) return (EINVAL); - NDINIT(&nd, NAMEI_LOOKUP, 0, UIO_SYSSPACE, name, td); - error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR); + error = nlookup_init(&nd, name, UIO_SYSSPACE, NLC_LOCKVP); + if (error == 0) + error = vn_open(&nd, NULL, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR); free(name, M_TEMP); - if (error) + if (error) { + nlookup_done(&nd); return (error); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; + } + vp = nd.nl_open_vp; + nd.nl_open_vp = NULL; + nlookup_done(&nd); - VOP_UNLOCK(vp, 0, td); + VOP_UNLOCK(vp, 0); lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; @@ -1502,16 +1685,14 @@ coredump(struct proc *p) } VATTR_NULL(&vattr); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vattr.va_size = 0; - VOP_LEASE(vp, td, cred, LEASE_WRITE); VOP_SETATTR(vp, &vattr, cred, td); p->p_acflag |= ACORE; - VOP_UNLOCK(vp, 0, td); + VOP_UNLOCK(vp, 0); error = p->p_sysent->sv_coredump ? - p->p_sysent->sv_coredump(p, vp, limit) : - ENOSYS; + p->p_sysent->sv_coredump(p, vp, limit) : ENOSYS; out1: lf.l_type = F_UNLCK;