git: 943421bdf764 - stable/13 - signal: Add SIG_FOREACH and refactor issignal()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 01 Nov 2021 13:23:07 UTC
The branch stable/13 has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=943421bdf7649af10806823bf3f30d7bf9b1cfaf
commit 943421bdf7649af10806823bf3f30d7bf9b1cfaf
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2021-10-16 13:44:40 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2021-11-01 13:20:11 +0000
signal: Add SIG_FOREACH and refactor issignal()
Add a SIG_FOREACH macro that can be used to iterate over a signal set.
This is a bit cleaner and more efficient than calling sig_ffs() in a
loop. The implementation is based on BIT_FOREACH_ISSET(), except
that the bitset limbs are always 32 bits wide, and signal sets are
1-indexed rather than 0-indexed like bitset(9) sets.
issignal() cannot really be modified to use SIG_FOREACH() directly.
Take this opportunity to split the function into two explicit loops.
I've always found this function hard to read and think that this change
is an improvement.
Remove sig_ffs(), nothing uses it now.
Reviewed by: kib
Sponsored by: The FreeBSD Foundation
(cherry picked from commit 81f2e9063d64cc976b47e7ee1e9c35692cda7cb4)
---
sys/kern/kern_sig.c | 390 +++++++++++++++++++++++++++++-----------------------
sys/sys/signalvar.h | 1 -
2 files changed, 218 insertions(+), 173 deletions(-)
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index f73c9e85442f..0c5e9f41f153 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -249,6 +249,29 @@ static int sigproptbl[NSIG] = {
[SIGUSR2] = SIGPROP_KILL,
};
+#define _SIG_FOREACH_ADVANCE(i, set) ({ \
+ int __found; \
+ for (;;) { \
+ if (__bits != 0) { \
+ int __sig = ffs(__bits); \
+ __bits &= ~(1u << (__sig - 1)); \
+ sig = __i * sizeof((set)->__bits[0]) * NBBY + __sig; \
+ __found = 1; \
+ break; \
+ } \
+ if (++__i == _SIG_WORDS) { \
+ __found = 0; \
+ break; \
+ } \
+ __bits = (set)->__bits[__i]; \
+ } \
+ __found != 0; \
+})
+
+#define SIG_FOREACH(i, set) \
+ for (int32_t __i = -1, __bits = 0; \
+ _SIG_FOREACH_ADVANCE(i, set); ) \
+
sigset_t fastblock_mask;
static void
@@ -660,17 +683,6 @@ sigprop(int sig)
return (0);
}
-int
-sig_ffs(sigset_t *set)
-{
- int i;
-
- for (i = 0; i < _SIG_WORDS; i++)
- if (set->__bits[i])
- return (ffs(set->__bits[i]) + (i * 32));
- return (0);
-}
-
static bool
sigact_flag_test(const struct sigaction *act, int flag)
{
@@ -1009,9 +1021,7 @@ execsigs(struct proc *p)
*/
if (SV_PROC_ABI(p) == SV_ABI_CLOUDABI) {
osigignore = ps->ps_sigignore;
- while (SIGNOTEMPTY(osigignore)) {
- sig = sig_ffs(&osigignore);
- SIGDELSET(osigignore, sig);
+ SIG_FOREACH(sig, &osigignore) {
if (sig != SIGPIPE)
sigdflt(ps, sig);
}
@@ -2780,8 +2790,7 @@ reschedule_signals(struct proc *p, sigset_t block, int flags)
return;
SIGSETAND(block, p->p_siglist);
fastblk = (flags & SIGPROCMASK_FASTBLK) != 0;
- while ((sig = sig_ffs(&block)) != 0) {
- SIGDELSET(block, sig);
+ SIG_FOREACH(sig, &block) {
td = sigtd(p, sig, fastblk);
/*
@@ -2913,13 +2922,188 @@ sigallowstop_impl(int prev)
}
}
+enum sigstatus {
+ SIGSTATUS_HANDLE,
+ SIGSTATUS_HANDLED,
+ SIGSTATUS_IGNORE,
+ SIGSTATUS_SBDRY_STOP,
+};
+
+/*
+ * The thread has signal "sig" pending. Figure out what to do with it:
+ *
+ * _HANDLE -> the caller should handle the signal
+ * _HANDLED -> handled internally, reload pending signal set
+ * _IGNORE -> ignored, remove from the set of pending signals and try the
+ * next pending signal
+ * _SBDRY_STOP -> the signal should stop the thread but this is not
+ * permitted in the current context
+ */
+static enum sigstatus
+sigprocess(struct thread *td, int sig)
+{
+ struct proc *p;
+ struct sigacts *ps;
+ struct sigqueue *queue;
+ ksiginfo_t ksi;
+ int prop;
+
+ KASSERT(_SIG_VALID(sig), ("%s: invalid signal %d", __func__, sig));
+
+ p = td->td_proc;
+ ps = p->p_sigacts;
+ mtx_assert(&ps->ps_mtx, MA_OWNED);
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+
+ /*
+ * We should allow pending but ignored signals below
+ * only if there is sigwait() active, or P_TRACED was
+ * on when they were posted.
+ */
+ if (SIGISMEMBER(ps->ps_sigignore, sig) &&
+ (p->p_flag & P_TRACED) == 0 &&
+ (td->td_flags & TDF_SIGWAIT) == 0) {
+ return (SIGSTATUS_IGNORE);
+ }
+
+ if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED) {
+ /*
+ * If traced, always stop.
+ * Remove old signal from queue before the stop.
+ * XXX shrug off debugger, it causes siginfo to
+ * be thrown away.
+ */
+ queue = &td->td_sigqueue;
+ ksiginfo_init(&ksi);
+ if (sigqueue_get(queue, sig, &ksi) == 0) {
+ queue = &p->p_sigqueue;
+ sigqueue_get(queue, sig, &ksi);
+ }
+ td->td_si = ksi.ksi_info;
+
+ mtx_unlock(&ps->ps_mtx);
+ sig = ptracestop(td, sig, &ksi);
+ mtx_lock(&ps->ps_mtx);
+
+ td->td_si.si_signo = 0;
+
+ /*
+ * Keep looking if the debugger discarded or
+ * replaced the signal.
+ */
+ if (sig == 0)
+ return (SIGSTATUS_HANDLED);
+
+ /*
+ * If the signal became masked, re-queue it.
+ */
+ if (SIGISMEMBER(td->td_sigmask, sig)) {
+ ksi.ksi_flags |= KSI_HEAD;
+ sigqueue_add(&p->p_sigqueue, sig, &ksi);
+ return (SIGSTATUS_HANDLED);
+ }
+
+ /*
+ * If the traced bit got turned off, requeue the signal and
+ * reload the set of pending signals. This ensures that p_sig*
+ * and p_sigact are consistent.
+ */
+ if ((p->p_flag & P_TRACED) == 0) {
+ ksi.ksi_flags |= KSI_HEAD;
+ sigqueue_add(queue, sig, &ksi);
+ return (SIGSTATUS_HANDLED);
+ }
+ }
+
+ /*
+ * Decide whether the signal should be returned.
+ * Return the signal's number, or fall through
+ * to clear it from the pending mask.
+ */
+ switch ((intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) {
+ case (intptr_t)SIG_DFL:
+ /*
+ * Don't take default actions on system processes.
+ */
+ if (p->p_pid <= 1) {
+#ifdef DIAGNOSTIC
+ /*
+ * Are you sure you want to ignore SIGSEGV
+ * in init? XXX
+ */
+ printf("Process (pid %lu) got signal %d\n",
+ (u_long)p->p_pid, sig);
+#endif
+ return (SIGSTATUS_IGNORE);
+ }
+
+ /*
+ * If there is a pending stop signal to process with
+ * default action, stop here, then clear the signal.
+ * Traced or exiting processes should ignore stops.
+ * Additionally, a member of an orphaned process group
+ * should ignore tty stops.
+ */
+ prop = sigprop(sig);
+ if (prop & SIGPROP_STOP) {
+ mtx_unlock(&ps->ps_mtx);
+ if ((p->p_flag & (P_TRACED | P_WEXIT |
+ P_SINGLE_EXIT)) != 0 || ((p->p_pgrp->
+ pg_flags & PGRP_ORPHANED) != 0 &&
+ (prop & SIGPROP_TTYSTOP) != 0)) {
+ mtx_lock(&ps->ps_mtx);
+ return (SIGSTATUS_IGNORE);
+ }
+ if (TD_SBDRY_INTR(td)) {
+ KASSERT((td->td_flags & TDF_SBDRY) != 0,
+ ("lost TDF_SBDRY"));
+ mtx_lock(&ps->ps_mtx);
+ return (SIGSTATUS_SBDRY_STOP);
+ }
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK,
+ &p->p_mtx.lock_object, "Catching SIGSTOP");
+ sigqueue_delete(&td->td_sigqueue, sig);
+ sigqueue_delete(&p->p_sigqueue, sig);
+ p->p_flag |= P_STOPPED_SIG;
+ p->p_xsig = sig;
+ PROC_SLOCK(p);
+ sig_suspend_threads(td, p, 0);
+ thread_suspend_switch(td, p);
+ PROC_SUNLOCK(p);
+ mtx_lock(&ps->ps_mtx);
+ return (SIGSTATUS_HANDLED);
+ } else if ((prop & SIGPROP_IGNORE) != 0 &&
+ (td->td_flags & TDF_SIGWAIT) == 0) {
+ /*
+ * Default action is to ignore; drop it if
+ * not in kern_sigtimedwait().
+ */
+ return (SIGSTATUS_IGNORE);
+ } else {
+ return (SIGSTATUS_HANDLE);
+ }
+
+ case (intptr_t)SIG_IGN:
+ if ((td->td_flags & TDF_SIGWAIT) == 0)
+ return (SIGSTATUS_IGNORE);
+ else
+ return (SIGSTATUS_HANDLE);
+
+ default:
+ /*
+ * This signal has an action, let postsig() process it.
+ */
+ return (SIGSTATUS_HANDLE);
+ }
+}
+
/*
* If the current process has received a signal (should be caught or cause
* termination, should interrupt current syscall), return the signal number.
* Stop signals with default action are processed immediately, then cleared;
* they aren't returned. This is checked after each entry to the system for
- * a syscall or trap (though this can usually be done without calling issignal
- * by checking the pending signal masks in cursig.) The normal call
+ * a syscall or trap (though this can usually be done without calling
+ * issignal by checking the pending signal masks in cursig.) The normal call
* sequence is
*
* while (sig = cursig(curthread))
@@ -2929,16 +3113,12 @@ static int
issignal(struct thread *td)
{
struct proc *p;
- struct sigacts *ps;
- struct sigqueue *queue;
sigset_t sigpending;
- ksiginfo_t ksi;
- int prop, sig;
+ int sig;
p = td->td_proc;
- ps = p->p_sigacts;
- mtx_assert(&ps->ps_mtx, MA_OWNED);
PROC_LOCK_ASSERT(p, MA_OWNED);
+
for (;;) {
sigpending = td->td_sigqueue.sq_signals;
SIGSETOR(sigpending, p->p_sigqueue.sq_signals);
@@ -2976,160 +3156,27 @@ issignal(struct thread *td)
* execute the debugger attach ritual in
* order.
*/
- sig = SIGSTOP;
td->td_dbgflags |= TDB_FSTP;
- } else {
- sig = sig_ffs(&sigpending);
+ SIGEMPTYSET(sigpending);
+ SIGADDSET(sigpending, SIGSTOP);
}
- /*
- * We should allow pending but ignored signals below
- * only if there is sigwait() active, or P_TRACED was
- * on when they were posted.
- */
- if (SIGISMEMBER(ps->ps_sigignore, sig) &&
- (p->p_flag & P_TRACED) == 0 &&
- (td->td_flags & TDF_SIGWAIT) == 0) {
- sigqueue_delete(&td->td_sigqueue, sig);
- sigqueue_delete(&p->p_sigqueue, sig);
- continue;
- }
- if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED) {
- /*
- * If traced, always stop.
- * Remove old signal from queue before the stop.
- * XXX shrug off debugger, it causes siginfo to
- * be thrown away.
- */
- queue = &td->td_sigqueue;
- ksiginfo_init(&ksi);
- if (sigqueue_get(queue, sig, &ksi) == 0) {
- queue = &p->p_sigqueue;
- sigqueue_get(queue, sig, &ksi);
- }
- td->td_si = ksi.ksi_info;
-
- mtx_unlock(&ps->ps_mtx);
- sig = ptracestop(td, sig, &ksi);
- mtx_lock(&ps->ps_mtx);
-
- td->td_si.si_signo = 0;
-
- /*
- * Keep looking if the debugger discarded or
- * replaced the signal.
- */
- if (sig == 0)
- continue;
-
- /*
- * If the signal became masked, re-queue it.
- */
- if (SIGISMEMBER(td->td_sigmask, sig)) {
- ksi.ksi_flags |= KSI_HEAD;
- sigqueue_add(&p->p_sigqueue, sig, &ksi);
- continue;
- }
-
- /*
- * If the traced bit got turned off, requeue
- * the signal and go back up to the top to
- * rescan signals. This ensures that p_sig*
- * and p_sigact are consistent.
- */
- if ((p->p_flag & P_TRACED) == 0) {
- ksi.ksi_flags |= KSI_HEAD;
- sigqueue_add(queue, sig, &ksi);
- continue;
- }
- }
-
- prop = sigprop(sig);
-
- /*
- * Decide whether the signal should be returned.
- * Return the signal's number, or fall through
- * to clear it from the pending mask.
- */
- switch ((intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) {
- case (intptr_t)SIG_DFL:
- /*
- * Don't take default actions on system processes.
- */
- if (p->p_pid <= 1) {
-#ifdef DIAGNOSTIC
- /*
- * Are you sure you want to ignore SIGSEGV
- * in init? XXX
- */
- printf("Process (pid %lu) got signal %d\n",
- (u_long)p->p_pid, sig);
-#endif
- break; /* == ignore */
- }
- /*
- * If there is a pending stop signal to process with
- * default action, stop here, then clear the signal.
- * Traced or exiting processes should ignore stops.
- * Additionally, a member of an orphaned process group
- * should ignore tty stops.
- */
- if (prop & SIGPROP_STOP) {
- mtx_unlock(&ps->ps_mtx);
- if ((p->p_flag & (P_TRACED | P_WEXIT |
- P_SINGLE_EXIT)) != 0 || ((p->p_pgrp->
- pg_flags & PGRP_ORPHANED) != 0 &&
- (prop & SIGPROP_TTYSTOP) != 0)) {
- mtx_lock(&ps->ps_mtx);
- break; /* == ignore */
- }
- if (TD_SBDRY_INTR(td)) {
- KASSERT((td->td_flags & TDF_SBDRY) != 0,
- ("lost TDF_SBDRY"));
- mtx_lock(&ps->ps_mtx);
- return (-1);
- }
- WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK,
- &p->p_mtx.lock_object, "Catching SIGSTOP");
+ SIG_FOREACH(sig, &sigpending) {
+ switch (sigprocess(td, sig)) {
+ case SIGSTATUS_HANDLE:
+ return (sig);
+ case SIGSTATUS_HANDLED:
+ goto next;
+ case SIGSTATUS_IGNORE:
sigqueue_delete(&td->td_sigqueue, sig);
sigqueue_delete(&p->p_sigqueue, sig);
- p->p_flag |= P_STOPPED_SIG;
- p->p_xsig = sig;
- PROC_SLOCK(p);
- sig_suspend_threads(td, p, 0);
- thread_suspend_switch(td, p);
- PROC_SUNLOCK(p);
- mtx_lock(&ps->ps_mtx);
- goto next;
- } else if ((prop & SIGPROP_IGNORE) != 0 &&
- (td->td_flags & TDF_SIGWAIT) == 0) {
- /*
- * Default action is to ignore; drop it if
- * not in kern_sigtimedwait().
- */
- break; /* == ignore */
- } else
- return (sig);
- /*NOTREACHED*/
-
- case (intptr_t)SIG_IGN:
- if ((td->td_flags & TDF_SIGWAIT) == 0)
- break; /* == ignore */
- else
- return (sig);
-
- default:
- /*
- * This signal has an action, let
- * postsig() process it.
- */
- return (sig);
+ break;
+ case SIGSTATUS_SBDRY_STOP:
+ return (-1);
+ }
}
- sigqueue_delete(&td->td_sigqueue, sig); /* take the signal! */
- sigqueue_delete(&p->p_sigqueue, sig);
next:;
}
- /* NOTREACHED */
}
void
@@ -4138,8 +4185,7 @@ sig_drop_caught(struct proc *p)
ps = p->p_sigacts;
PROC_LOCK_ASSERT(p, MA_OWNED);
mtx_assert(&ps->ps_mtx, MA_OWNED);
- while (SIGNOTEMPTY(ps->ps_sigcatch)) {
- sig = sig_ffs(&ps->ps_sigcatch);
+ SIG_FOREACH(sig, &ps->ps_sigcatch) {
sigdflt(ps, sig);
if ((sigprop(sig) & SIGPROP_IGNORE) != 0)
sigqueue_delete_proc(p, sig);
diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h
index df761a1e1a5d..d43dd4a44190 100644
--- a/sys/sys/signalvar.h
+++ b/sys/sys/signalvar.h
@@ -404,7 +404,6 @@ int sig_ast_needsigchk(struct thread *td);
void sig_drop_caught(struct proc *p);
void sigexit(struct thread *td, int sig) __dead2;
int sigev_findtd(struct proc *p, struct sigevent *sigev, struct thread **);
-int sig_ffs(sigset_t *set);
void sigfastblock_clear(struct thread *td);
void sigfastblock_fetch(struct thread *td);
void sigfastblock_setpend(struct thread *td, bool resched);