summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_sig.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2009-10-27 10:42:24 +0000
committerkib <kib@FreeBSD.org>2009-10-27 10:42:24 +0000
commiteb4c68098b66d84de3abc7be00acfdc6d2f8f980 (patch)
tree5f94dc1615964e0cca1ac079c4b76e9157688e6a /sys/kern/kern_sig.c
parentfeb999713be0c5c7cf9239074dd5d49d5dff1fa0 (diff)
downloadFreeBSD-src-eb4c68098b66d84de3abc7be00acfdc6d2f8f980.zip
FreeBSD-src-eb4c68098b66d84de3abc7be00acfdc6d2f8f980.tar.gz
In kern_sigsuspend(), better manipulate thread signal mask using
kern_sigprocmask() to properly notify other possible candidate threads for signal delivery. Since sigsuspend() shall only return to usermode after a signal was delivered, do cursig/postsig loop immediately after waiting for signal, repeating the wait if wakeup was spurious due to race with other thread fetching signal from the process queue before us. Add thread_suspend_check() call to allow the thread to be stopped or killed while in loop. Modify last argument of kern_sigprocmask() from boolean to flags, allowing the function to be called with locked proc. Convertion of the callers that supplied 1 to the old argument will be done in the next commit, and due to SIGPROCMASK_OLD value equial to 1, code is formally correct in between. Reviewed by: davidxu Tested by: pho MFC after: 1 month
Diffstat (limited to 'sys/kern/kern_sig.c')
-rw-r--r--sys/kern/kern_sig.c51
1 files changed, 29 insertions, 22 deletions
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index bc09686..a453b55 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -970,14 +970,15 @@ execsigs(struct proc *p)
*/
int
kern_sigprocmask(struct thread *td, int how, sigset_t *set, sigset_t *oset,
- int old)
+ int flags)
{
sigset_t new_block, oset1;
struct proc *p;
int error;
p = td->td_proc;
- PROC_LOCK(p);
+ if (!(flags & SIGPROCMASK_PROC_LOCKED))
+ PROC_LOCK(p);
if (oset != NULL)
*oset = td->td_sigmask;
@@ -999,7 +1000,7 @@ kern_sigprocmask(struct thread *td, int how, sigset_t *set, sigset_t *oset,
case SIG_SETMASK:
SIG_CANTMASK(*set);
oset1 = td->td_sigmask;
- if (old)
+ if (flags & SIGPROCMASK_OLD)
SIGSETLO(td->td_sigmask, *set);
else
td->td_sigmask = *set;
@@ -1025,7 +1026,8 @@ kern_sigprocmask(struct thread *td, int how, sigset_t *set, sigset_t *oset,
if (p->p_numthreads != 1)
reschedule_signals(p, new_block);
- PROC_UNLOCK(p);
+ if (!(flags & SIGPROCMASK_PROC_LOCKED))
+ PROC_UNLOCK(p);
return (error);
}
@@ -1458,6 +1460,7 @@ int
kern_sigsuspend(struct thread *td, sigset_t mask)
{
struct proc *p = td->td_proc;
+ int has_sig, sig;
/*
* When returning from sigsuspend, we want
@@ -1467,13 +1470,28 @@ kern_sigsuspend(struct thread *td, sigset_t mask)
* to indicate this.
*/
PROC_LOCK(p);
- td->td_oldsigmask = td->td_sigmask;
+ kern_sigprocmask(td, SIG_SETMASK, &mask, &td->td_oldsigmask,
+ SIGPROCMASK_PROC_LOCKED);
td->td_pflags |= TDP_OLDMASK;
- SIG_CANTMASK(mask);
- td->td_sigmask = mask;
- signotify(td);
- while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "pause", 0) == 0)
- /* void */;
+
+ /*
+ * Process signals now. Otherwise, we can get spurious wakeup
+ * due to signal entered process queue, but delivered to other
+ * thread. But sigsuspend should return only on signal
+ * delivery.
+ */
+ for (has_sig = 0; !has_sig;) {
+ while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "pause",
+ 0) == 0)
+ /* void */;
+ thread_suspend_check(0);
+ mtx_lock(&p->p_sigacts->ps_mtx);
+ while ((sig = cursig(td, SIG_STOP_ALLOWED)) != 0) {
+ postsig(sig);
+ has_sig = 1;
+ }
+ mtx_unlock(&p->p_sigacts->ps_mtx);
+ }
PROC_UNLOCK(p);
/* always return EINTR rather than ERESTART... */
return (EINTR);
@@ -1495,21 +1513,10 @@ osigsuspend(td, uap)
struct thread *td;
struct osigsuspend_args *uap;
{
- struct proc *p = td->td_proc;
sigset_t mask;
- PROC_LOCK(p);
- td->td_oldsigmask = td->td_sigmask;
- td->td_pflags |= TDP_OLDMASK;
OSIG2SIG(uap->mask, mask);
- SIG_CANTMASK(mask);
- SIGSETLO(td->td_sigmask, mask);
- signotify(td);
- while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "opause", 0) == 0)
- /* void */;
- PROC_UNLOCK(p);
- /* always return EINTR rather than ERESTART... */
- return (EINTR);
+ return (kern_sigsuspend(td, mask));
}
#endif /* COMPAT_43 */
OpenPOWER on IntegriCloud