Simplify vn_lock(), VOP_LOCK(), and VOP_UNLOCK() by removing the thread_t
[dragonfly.git] / sys / kern / kern_sig.c
index b8f2df9..1ea0029 100644 (file)
@@ -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.35 2005/02/21 01:36:05 davidxu Exp $
+ * $DragonFly: src/sys/kern/kern_sig.c,v 1.45 2006/05/05 21:15:08 dillon Exp $
  */
 
 #include "opt_ktrace.h"
@@ -65,6 +65,7 @@
 #include <sys/malloc.h>
 #include <sys/unistd.h>
 #include <sys/kern_syscall.h>
+#include <sys/thread2.h>
 
 
 #include <machine/ipl.h>
@@ -76,7 +77,6 @@ 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
@@ -260,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)]);
@@ -331,7 +331,7 @@ kern_sigaction(int sig, struct sigaction *act, struct sigaction *oact)
                                SIGADDSET(p->p_sigcatch, sig);
                }
 
-               spl0();
+               crit_exit();
        }
        return (0);
 }
@@ -413,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)
@@ -629,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);
@@ -760,16 +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
- */
-
 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) {
@@ -777,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);
 
@@ -813,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
@@ -836,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
@@ -911,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
@@ -1008,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);
        }
@@ -1051,6 +1080,7 @@ kern_sigtimedwait(sigset_t waitset, siginfo_t *info, struct timespec *timeout)
                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
@@ -1113,6 +1143,9 @@ kern_sigtimedwait(sigset_t waitset, siginfo_t *info, struct timespec *timeout)
                bzero(info, sizeof(*info));
                info->si_signo = sig;
                SIGDELSET(p->p_siglist, sig);   /* take the signal! */
+
+               if (sig == SIGKILL)
+                       sigexit(p, sig);
        }
        return (error);
 }
@@ -1201,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);
  */
@@ -1210,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);
 
@@ -1217,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);
@@ -1231,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,
@@ -1278,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.
@@ -1316,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) {
                                /*
@@ -1328,6 +1377,7 @@ issignal(struct proc *p)
                                 */
                                break;          /* == ignore */
                        } else {
+                               rel_mplock();
                                return (sig);
                        }
 
@@ -1349,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! */
@@ -1356,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.
@@ -1415,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;
@@ -1437,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;
@@ -1630,7 +1668,7 @@ coredump(struct proc *p)
        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;
@@ -1647,12 +1685,11 @@ 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;