Author: kib
Date: Sun Jun 26 20:07:24 2016
New Revision: 302215
URL: https://svnweb.freebsd.org/changeset/base/302215

Log:
  Rewrite sigdeferstop(9) and sigallowstop(9) into more flexible
  framework allowing to set the suspension policy for the dynamic block.
  Extend the currently possible policies of stopping on interruptible
  sleeps and ignoring such sleeps by two more: do not suspend at
  interruptible sleeps, but interrupt them with either EINTR or ERESTART.
  
  Reviewed by:  jilles
  Tested by:    pho
  Sponsored by: The FreeBSD Foundation
  MFC after:    2 weeks
  Approved by:  re (gjb)

Modified:
  head/sys/fs/fifofs/fifo_vnops.c
  head/sys/kern/kern_sig.c
  head/sys/kern/kern_thread.c
  head/sys/kern/subr_trap.c
  head/sys/sys/mount.h
  head/sys/sys/proc.h
  head/sys/sys/signalvar.h

Modified: head/sys/fs/fifofs/fifo_vnops.c
==============================================================================
--- head/sys/fs/fifofs/fifo_vnops.c     Sun Jun 26 18:43:42 2016        
(r302214)
+++ head/sys/fs/fifofs/fifo_vnops.c     Sun Jun 26 20:07:24 2016        
(r302215)
@@ -194,11 +194,10 @@ fifo_open(ap)
                if ((ap->a_mode & FREAD) && fip->fi_writers == 0) {
                        gen = fip->fi_wgen;
                        VOP_UNLOCK(vp, 0);
-                       stops_deferred = sigallowstop();
+                       stops_deferred = sigdeferstop(SIGDEFERSTOP_OFF);
                        error = msleep(&fip->fi_readers, PIPE_MTX(fpipe),
                            PDROP | PCATCH | PSOCK, "fifoor", 0);
-                       if (stops_deferred)
-                               sigdeferstop();
+                       sigallowstop(stops_deferred);
                        vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
                        if (error != 0 && gen == fip->fi_wgen) {
                                fip->fi_readers--;
@@ -222,11 +221,10 @@ fifo_open(ap)
                if ((ap->a_mode & FWRITE) && fip->fi_readers == 0) {
                        gen = fip->fi_rgen;
                        VOP_UNLOCK(vp, 0);
-                       stops_deferred = sigallowstop();
+                       stops_deferred = sigdeferstop(SIGDEFERSTOP_OFF);
                        error = msleep(&fip->fi_writers, PIPE_MTX(fpipe),
                            PDROP | PCATCH | PSOCK, "fifoow", 0);
-                       if (stops_deferred)
-                               sigdeferstop();
+                       sigallowstop(stops_deferred);
                        vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
                        if (error != 0 && gen == fip->fi_rgen) {
                                fip->fi_writers--;

Modified: head/sys/kern/kern_sig.c
==============================================================================
--- head/sys/kern/kern_sig.c    Sun Jun 26 18:43:42 2016        (r302214)
+++ head/sys/kern/kern_sig.c    Sun Jun 26 20:07:24 2016        (r302215)
@@ -2596,41 +2596,81 @@ tdsigcleanup(struct thread *td)
 
 }
 
+static int
+sigdeferstop_curr_flags(int cflags)
+{
+
+       MPASS((cflags & (TDF_SEINTR | TDF_SERESTART)) == 0 ||
+           (cflags & TDF_SBDRY) != 0);
+       return (cflags & (TDF_SBDRY | TDF_SEINTR | TDF_SERESTART));
+}
+
 /*
- * Defer the delivery of SIGSTOP for the current thread.  Returns true
- * if stops were deferred and false if they were already deferred.
+ * Defer the delivery of SIGSTOP for the current thread, according to
+ * the requested mode.  Returns previous flags, which must be restored
+ * by sigallowstop().
+ *
+ * TDF_SBDRY, TDF_SEINTR, and TDF_SERESTART flags are only set and
+ * cleared by the current thread, which allow the lock-less read-only
+ * accesses below.
  */
 int
-sigdeferstop(void)
+sigdeferstop(int mode)
 {
        struct thread *td;
+       int cflags, nflags;
 
        td = curthread;
-       if (td->td_flags & TDF_SBDRY)
-               return (0);
-       thread_lock(td);
-       td->td_flags |= TDF_SBDRY;
-       thread_unlock(td);
-       return (1);
+       cflags = sigdeferstop_curr_flags(td->td_flags);
+       switch (mode) {
+       case SIGDEFERSTOP_NOP:
+               nflags = cflags;
+               break;
+       case SIGDEFERSTOP_OFF:
+               nflags = 0;
+               break;
+       case SIGDEFERSTOP_SILENT:
+               nflags = (cflags | TDF_SBDRY) & ~(TDF_SEINTR | TDF_SERESTART);
+               break;
+       case SIGDEFERSTOP_EINTR:
+               nflags = (cflags | TDF_SBDRY | TDF_SEINTR) & ~TDF_SERESTART;
+               break;
+       case SIGDEFERSTOP_ERESTART:
+               nflags = (cflags | TDF_SBDRY | TDF_SERESTART) & ~TDF_SEINTR;
+               break;
+       default:
+               panic("sigdeferstop: invalid mode %x", mode);
+               break;
+       }
+       if (cflags != nflags) {
+               thread_lock(td);
+               td->td_flags = (td->td_flags & ~cflags) | nflags;
+               thread_unlock(td);
+       }
+       return (cflags);
 }
 
 /*
- * Permit the delivery of SIGSTOP for the current thread.  This does
- * not immediately suspend if a stop was posted.  Instead, the thread
- * will suspend either via ast() or a subsequent interruptible sleep.
+ * Restores the STOP handling mode, typically permitting the delivery
+ * of SIGSTOP for the current thread.  This does not immediately
+ * suspend if a stop was posted.  Instead, the thread will suspend
+ * either via ast() or a subsequent interruptible sleep.
  */
-int
-sigallowstop(void)
+void
+sigallowstop(int prev)
 {
        struct thread *td;
-       int prev;
+       int cflags;
 
+       KASSERT((prev & ~(TDF_SBDRY | TDF_SEINTR | TDF_SERESTART)) == 0,
+           ("sigallowstop: incorrect previous mode %x", prev));
        td = curthread;
-       thread_lock(td);
-       prev = (td->td_flags & TDF_SBDRY) != 0;
-       td->td_flags &= ~TDF_SBDRY;
-       thread_unlock(td);
-       return (prev);
+       cflags = sigdeferstop_curr_flags(td->td_flags);
+       if (cflags != prev) {
+               thread_lock(td);
+               td->td_flags = (td->td_flags & ~cflags) | prev;
+               thread_unlock(td);
+       }
 }
 
 /*

Modified: head/sys/kern/kern_thread.c
==============================================================================
--- head/sys/kern/kern_thread.c Sun Jun 26 18:43:42 2016        (r302214)
+++ head/sys/kern/kern_thread.c Sun Jun 26 20:07:24 2016        (r302215)
@@ -894,7 +894,7 @@ thread_suspend_check(int return_instead)
 {
        struct thread *td;
        struct proc *p;
-       int wakeup_swapper;
+       int wakeup_swapper, r;
 
        td = curthread;
        p = td->td_proc;
@@ -927,7 +927,21 @@ thread_suspend_check(int return_instead)
                if ((td->td_flags & TDF_SBDRY) != 0) {
                        KASSERT(return_instead,
                            ("TDF_SBDRY set for unsafe thread_suspend_check"));
-                       return (0);
+                       switch (td->td_flags & (TDF_SEINTR | TDF_SERESTART)) {
+                       case 0:
+                               r = 0;
+                               break;
+                       case TDF_SEINTR:
+                               r = EINTR;
+                               break;
+                       case TDF_SERESTART:
+                               r = ERESTART;
+                               break;
+                       default:
+                               panic("both TDF_SEINTR and TDF_SERESTART");
+                               break;
+                       }
+                       return (r);
                }
 
                /*

Modified: head/sys/kern/subr_trap.c
==============================================================================
--- head/sys/kern/subr_trap.c   Sun Jun 26 18:43:42 2016        (r302214)
+++ head/sys/kern/subr_trap.c   Sun Jun 26 20:07:24 2016        (r302215)
@@ -160,7 +160,7 @@ userret(struct thread *td, struct trapfr
            ("userret: Returning with with pinned thread"));
        KASSERT(td->td_vp_reserv == 0,
            ("userret: Returning while holding vnode reservation"));
-       KASSERT((td->td_flags & TDF_SBDRY) == 0,
+       KASSERT((td->td_flags & (TDF_SBDRY | TDF_SEINTR | TDF_SERESTART)) == 0,
            ("userret: Returning with stop signals deferred"));
        KASSERT(td->td_su == NULL,
            ("userret: Returning with SU cleanup request not handled"));

Modified: head/sys/sys/mount.h
==============================================================================
--- head/sys/sys/mount.h        Sun Jun 26 18:43:42 2016        (r302214)
+++ head/sys/sys/mount.h        Sun Jun 26 20:07:24 2016        (r302215)
@@ -653,15 +653,15 @@ vfs_statfs_t      __vfs_statfs;
 
 #define        VFS_PROLOGUE(MP)        do {                                    
\
        struct mount *mp__;                                             \
-       int _enable_stops;                                              \
+       int _prev_stops;                                                \
                                                                        \
        mp__ = (MP);                                                    \
-       _enable_stops = (mp__ != NULL &&                                \
-           (mp__->mnt_vfc->vfc_flags & VFCF_SBDRY) && sigdeferstop())
+       _prev_stops = sigdeferstop((mp__ != NULL &&                     \
+           (mp__->mnt_vfc->vfc_flags & VFCF_SBDRY) != 0) ?             \
+           SIGDEFERSTOP_SILENT : SIGDEFERSTOP_NOP);
 
 #define        VFS_EPILOGUE(MP)                                                
\
-       if (_enable_stops)                                              \
-               sigallowstop();                                         \
+       sigallowstop(_prev_stops);                                      \
 } while (0)
 
 #define        VFS_MOUNT(MP) ({                                                
\

Modified: head/sys/sys/proc.h
==============================================================================
--- head/sys/sys/proc.h Sun Jun 26 18:43:42 2016        (r302214)
+++ head/sys/sys/proc.h Sun Jun 26 20:07:24 2016        (r302215)
@@ -395,9 +395,9 @@ do {                                                        
                \
 #define        TDF_NEEDRESCHED 0x00010000 /* Thread needs to yield. */
 #define        TDF_NEEDSIGCHK  0x00020000 /* Thread may need signal delivery. 
*/
 #define        TDF_NOLOAD      0x00040000 /* Ignore during load avg 
calculations. */
-#define        TDF_UNUSED19    0x00080000 /* --available-- */
+#define        TDF_SERESTART   0x00080000 /* ERESTART on stop attempts. */
 #define        TDF_THRWAKEUP   0x00100000 /* Libthr thread must not suspend 
itself. */
-#define        TDF_UNUSED21    0x00200000 /* --available-- */
+#define        TDF_SEINTR      0x00200000 /* EINTR on stop attempts. */
 #define        TDF_SWAPINREQ   0x00400000 /* Swapin request due to wakeup. */
 #define        TDF_UNUSED23    0x00800000 /* --available-- */
 #define        TDF_SCHED0      0x01000000 /* Reserved for scheduler private 
use */

Modified: head/sys/sys/signalvar.h
==============================================================================
--- head/sys/sys/signalvar.h    Sun Jun 26 18:43:42 2016        (r302214)
+++ head/sys/sys/signalvar.h    Sun Jun 26 20:07:24 2016        (r302215)
@@ -325,9 +325,21 @@ extern struct mtx  sigio_lock;
 #define        SIGPROCMASK_PROC_LOCKED 0x0002
 #define        SIGPROCMASK_PS_LOCKED   0x0004
 
+/*
+ * Modes for sigdeferstop().  Manages behaviour of
+ * thread_suspend_check() in the region delimited by
+ * sigdeferstop()/sigallowstop().  Must be restored to
+ * SIGDEFERSTOP_OFF before returning to userspace.
+ */
+#define        SIGDEFERSTOP_NOP        0 /* continue doing whatever is done 
now */
+#define        SIGDEFERSTOP_OFF        1 /* stop ignoring STOPs */
+#define        SIGDEFERSTOP_SILENT     2 /* silently ignore STOPs */
+#define        SIGDEFERSTOP_EINTR      3 /* ignore STOPs, return EINTR */
+#define        SIGDEFERSTOP_ERESTART   4 /* ignore STOPs, return ERESTART */
+
 int    cursig(struct thread *td);
-int    sigdeferstop(void);
-int    sigallowstop(void);
+int    sigdeferstop(int mode);
+void   sigallowstop(int prev);
 void   execsigs(struct proc *p);
 void   gsignal(int pgid, int sig, ksiginfo_t *ksi);
 void   killproc(struct proc *p, char *why);
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to