aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2016-06-26 20:07:24 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2016-06-26 20:07:24 +0000
commit3a1e5dd8e63cbee02f14030ae0a1538a0b1907b9 (patch)
treebf68775a98ecb1f2103f9d9861cc640243eb614c /sys
parentd929c32b7fa99762b489f8ddb3d404a9dfeaca01 (diff)
downloadsrc-3a1e5dd8e63cbee02f14030ae0a1538a0b1907b9.tar.gz
src-3a1e5dd8e63cbee02f14030ae0a1538a0b1907b9.zip
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)
Notes
Notes: svn path=/head/; revision=302215
Diffstat (limited to 'sys')
-rw-r--r--sys/fs/fifofs/fifo_vnops.c10
-rw-r--r--sys/kern/kern_sig.c80
-rw-r--r--sys/kern/kern_thread.c18
-rw-r--r--sys/kern/subr_trap.c2
-rw-r--r--sys/sys/mount.h10
-rw-r--r--sys/sys/proc.h4
-rw-r--r--sys/sys/signalvar.h16
7 files changed, 102 insertions, 38 deletions
diff --git a/sys/fs/fifofs/fifo_vnops.c b/sys/fs/fifofs/fifo_vnops.c
index 716faa302bf5..fb3eb904a75e 100644
--- a/sys/fs/fifofs/fifo_vnops.c
+++ b/sys/fs/fifofs/fifo_vnops.c
@@ -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--;
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index f8bb87ef87cd..c740848abd74 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -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);
+ }
}
/*
diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c
index 70491c562f6e..5a2d3920862b 100644
--- a/sys/kern/kern_thread.c
+++ b/sys/kern/kern_thread.c
@@ -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);
}
/*
diff --git a/sys/kern/subr_trap.c b/sys/kern/subr_trap.c
index 6d1ac709febb..eb44087a490e 100644
--- a/sys/kern/subr_trap.c
+++ b/sys/kern/subr_trap.c
@@ -160,7 +160,7 @@ userret(struct thread *td, struct trapframe *frame)
("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"));
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index f11f8f5389f0..5438140dc365 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -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) ({ \
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 6162a162d6a1..7ae4a87ea40c 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -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 */
diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h
index e574ec31e67b..176bb2a9d3c5 100644
--- a/sys/sys/signalvar.h
+++ b/sys/sys/signalvar.h
@@ -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);