Author: badger
Date: Thu Mar 16 01:41:36 2017
New Revision: 315345
URL: https://svnweb.freebsd.org/changeset/base/315345

Log:
  MFC r313733:
  
  sleepq_catch_signals: do thread suspension before signal check
  
  Since locks are dropped when a thread suspends, it's possible for another
  thread to deliver a signal to the suspended thread. If the thread awakens from
  suspension without checking for signals, it may go to sleep despite having
  a pending signal that should wake it up. Therefore the suspension check is
  done first, so any signals sent while suspended will be caught in the
  subsequent signal check.
  
  Sponsored by: Dell EMC

Modified:
  stable/10/sys/kern/subr_sleepqueue.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/kern/subr_sleepqueue.c
==============================================================================
--- stable/10/sys/kern/subr_sleepqueue.c        Thu Mar 16 01:38:07 2017        
(r315344)
+++ stable/10/sys/kern/subr_sleepqueue.c        Thu Mar 16 01:41:36 2017        
(r315345)
@@ -411,6 +411,7 @@ sleepq_catch_signals(void *wchan, int pr
        struct sigacts *ps;
        int sig, ret;
 
+       ret = 0;
        td = curthread;
        p = curproc;
        sc = SC_LOOKUP(wchan);
@@ -424,44 +425,51 @@ sleepq_catch_signals(void *wchan, int pr
        }
 
        /*
-        * See if there are any pending signals for this thread.  If not
-        * we can switch immediately.  Otherwise do the signal processing
-        * directly.
+        * See if there are any pending signals or suspension requests for this
+        * thread.  If not, we can switch immediately.
         */
        thread_lock(td);
-       if ((td->td_flags & (TDF_NEEDSIGCHK | TDF_NEEDSUSPCHK)) == 0) {
-               sleepq_switch(wchan, pri);
-               return (0);
-       }
-       thread_unlock(td);
-       mtx_unlock_spin(&sc->sc_lock);
-       CTR3(KTR_PROC, "sleepq catching signals: thread %p (pid %ld, %s)",
-               (void *)td, (long)p->p_pid, td->td_name);
-       PROC_LOCK(p);
-       ps = p->p_sigacts;
-       mtx_lock(&ps->ps_mtx);
-       sig = cursig(td);
-       if (sig == 0) {
-               mtx_unlock(&ps->ps_mtx);
-               ret = thread_suspend_check(1);
-               MPASS(ret == 0 || ret == EINTR || ret == ERESTART);
-       } else {
-               if (SIGISMEMBER(ps->ps_sigintr, sig))
-                       ret = EINTR;
-               else
-                       ret = ERESTART;
-               mtx_unlock(&ps->ps_mtx);
+       if ((td->td_flags & (TDF_NEEDSIGCHK | TDF_NEEDSUSPCHK)) != 0) {
+               thread_unlock(td);
+               mtx_unlock_spin(&sc->sc_lock);
+               CTR3(KTR_PROC, "sleepq catching signals: thread %p (pid %ld, 
%s)",
+                       (void *)td, (long)p->p_pid, td->td_name);
+               PROC_LOCK(p);
+               /*
+                * Check for suspension first. Checking for signals and then
+                * suspending could result in a missed signal, since a signal
+                * can be delivered while this thread is suspended.
+                */
+               if ((td->td_flags & TDF_NEEDSUSPCHK) != 0) {
+                       ret = thread_suspend_check(1);
+                       MPASS(ret == 0 || ret == EINTR || ret == ERESTART);
+                       if (ret != 0) {
+                               PROC_UNLOCK(p);
+                               mtx_lock_spin(&sc->sc_lock);
+                               thread_lock(td);
+                               goto out;
+                       }
+               }
+               if ((td->td_flags & TDF_NEEDSIGCHK) != 0) {
+                       ps = p->p_sigacts;
+                       mtx_lock(&ps->ps_mtx);
+                       sig = cursig(td);
+                       if (sig != 0)
+                               ret = SIGISMEMBER(ps->ps_sigintr, sig) ?
+                                   EINTR : ERESTART;
+                       mtx_unlock(&ps->ps_mtx);
+               }
+               /*
+                * Lock the per-process spinlock prior to dropping the PROC_LOCK
+                * to avoid a signal delivery race.  PROC_LOCK, PROC_SLOCK, and
+                * thread_lock() are currently held in tdsendsignal().
+                */
+               PROC_SLOCK(p);
+               mtx_lock_spin(&sc->sc_lock);
+               PROC_UNLOCK(p);
+               thread_lock(td);
+               PROC_SUNLOCK(p);
        }
-       /*
-        * Lock the per-process spinlock prior to dropping the PROC_LOCK
-        * to avoid a signal delivery race.  PROC_LOCK, PROC_SLOCK, and
-        * thread_lock() are currently held in tdsendsignal().
-        */
-       PROC_SLOCK(p);
-       mtx_lock_spin(&sc->sc_lock);
-       PROC_UNLOCK(p);
-       thread_lock(td);
-       PROC_SUNLOCK(p);
        if (ret == 0) {
                sleepq_switch(wchan, pri);
                return (0);
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to