diff options
author | kib <kib@FreeBSD.org> | 2009-10-27 10:42:24 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2009-10-27 10:42:24 +0000 |
commit | eb4c68098b66d84de3abc7be00acfdc6d2f8f980 (patch) | |
tree | 5f94dc1615964e0cca1ac079c4b76e9157688e6a | |
parent | feb999713be0c5c7cf9239074dd5d49d5dff1fa0 (diff) | |
download | FreeBSD-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
-rw-r--r-- | sys/compat/freebsd32/freebsd32_misc.c | 13 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 51 | ||||
-rw-r--r-- | sys/sys/signalvar.h | 7 | ||||
-rw-r--r-- | sys/sys/syscallsubr.h | 2 |
4 files changed, 36 insertions, 37 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 71b22aa..9f6b16d 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -2579,21 +2579,10 @@ int ofreebsd32_sigsuspend(struct thread *td, struct ofreebsd32_sigsuspend_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)); } struct sigstack32 { 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 */ diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h index b39f2bf..b9a54f0 100644 --- a/sys/sys/signalvar.h +++ b/sys/sys/signalvar.h @@ -316,6 +316,10 @@ extern int kern_logsigexit; /* Sysctl variable kern.logsigexit */ #define SIG_STOP_ALLOWED 100 #define SIG_STOP_NOT_ALLOWED 101 +/* flags for kern_sigprocmask */ +#define SIGPROCMASK_OLD 0x0001 +#define SIGPROCMASK_PROC_LOCKED 0x0002 + /* * Machine-independent functions: */ @@ -359,7 +363,8 @@ void sigqueue_delete_stopmask_proc(struct proc *); void sigqueue_take(ksiginfo_t *ksi); int kern_sigtimedwait(struct thread *, sigset_t, ksiginfo_t *, struct timespec *); - +int kern_sigprocmask(struct thread *td, int how, + sigset_t *set, sigset_t *oset, int flags); /* * Machine-dependent functions: */ diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index e1c83cc..c389873 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -190,8 +190,6 @@ int kern_shmctl(struct thread *td, int shmid, int cmd, void *buf, int kern_sigaction(struct thread *td, int sig, struct sigaction *act, struct sigaction *oact, int flags); int kern_sigaltstack(struct thread *td, stack_t *ss, stack_t *oss); -int kern_sigprocmask(struct thread *td, int how, - sigset_t *set, sigset_t *oset, int old); int kern_sigsuspend(struct thread *td, sigset_t mask); int kern_stat(struct thread *td, char *path, enum uio_seg pathseg, struct stat *sbp); |