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)
This commit is contained in:
Konstantin Belousov 2016-06-26 20:07:24 +00:00
parent d929c32b7f
commit 3a1e5dd8e6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=302215
7 changed files with 106 additions and 42 deletions

View File

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

View File

@ -2596,41 +2596,81 @@ tdsigcleanup(struct thread *td)
}
/*
* Defer the delivery of SIGSTOP for the current thread. Returns true
* if stops were deferred and false if they were already deferred.
*/
int
sigdeferstop(void)
static int
sigdeferstop_curr_flags(int cflags)
{
struct thread *td;
td = curthread;
if (td->td_flags & TDF_SBDRY)
return (0);
thread_lock(td);
td->td_flags |= TDF_SBDRY;
thread_unlock(td);
return (1);
MPASS((cflags & (TDF_SEINTR | TDF_SERESTART)) == 0 ||
(cflags & TDF_SBDRY) != 0);
return (cflags & (TDF_SBDRY | TDF_SEINTR | TDF_SERESTART));
}
/*
* 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.
* 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
sigallowstop(void)
sigdeferstop(int mode)
{
struct thread *td;
int prev;
int cflags, nflags;
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);
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);
}
/*
* 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.
*/
void
sigallowstop(int prev)
{
struct thread *td;
int cflags;
KASSERT((prev & ~(TDF_SBDRY | TDF_SEINTR | TDF_SERESTART)) == 0,
("sigallowstop: incorrect previous mode %x", prev));
td = curthread;
cflags = sigdeferstop_curr_flags(td->td_flags);
if (cflags != prev) {
thread_lock(td);
td->td_flags = (td->td_flags & ~cflags) | prev;
thread_unlock(td);
}
}
/*

View File

@ -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);
}
/*

View File

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

View File

@ -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) ({ \

View File

@ -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 */

View File

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