svn commit: r302215 - in head/sys: fs/fifofs kern sys

Konstantin Belousov kib at FreeBSD.org
Sun Jun 26 20:07:26 UTC 2016


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);


More information about the svn-src-head mailing list