summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_sig.c
diff options
context:
space:
mode:
authordavidxu <davidxu@FreeBSD.org>2006-02-15 23:52:01 +0000
committerdavidxu <davidxu@FreeBSD.org>2006-02-15 23:52:01 +0000
commitf1ce5c86603b5f73e31ea323d24ea1433850f704 (patch)
tree03af904c96a6fe12d7ee078d5642aca586f9f9f0 /sys/kern/kern_sig.c
parentfc705888e9a61ca2e02d7b2a13ad04491eb844e2 (diff)
downloadFreeBSD-src-f1ce5c86603b5f73e31ea323d24ea1433850f704.zip
FreeBSD-src-f1ce5c86603b5f73e31ea323d24ea1433850f704.tar.gz
Fix a long standing race between sleep queue and thread
suspension code. When a thread A is going to sleep, it calls sleepq_catch_signals() to detect any pending signals or thread suspension request, if nothing happens, it returns without holding process lock or scheduler lock, this opens a race window which allows thread B to come in and do process suspension work, however since A is still at running state, thread B can do nothing to A, thread A continues, and puts itself into actually sleeping state, but B has never seen it, and it sits there forever until B is woken up by other threads sometimes later(this can be very long delay or never happen). Fix this bug by forcing sleepq_catch_signals to return with scheduler lock held. Fix sleepq_abort() by passing it an interrupted code, previously, it worked as wakeup_one(), and the interruption can not be identified correctly by sleep queue code when the sleeping thread is resumed. Let thread_suspend_check() returns EINTR or ERESTART, so sleep queue no longer has to use SIGSTOP as a hack to build a return value. Reviewed by: jhb MFC after: 1 week
Diffstat (limited to 'sys/kern/kern_sig.c')
-rw-r--r--sys/kern/kern_sig.c17
1 files changed, 11 insertions, 6 deletions
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 41bb6ff..1a49a0c 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -92,7 +92,7 @@ static char *expand_name(const char *, uid_t, pid_t);
static int killpg1(struct thread *td, int sig, int pgid, int all);
static int issignal(struct thread *p);
static int sigprop(int sig);
-static void tdsigwakeup(struct thread *, int, sig_t);
+static void tdsigwakeup(struct thread *, int, sig_t, int);
static void sig_suspend_threads(struct thread *, struct proc *, int);
static int filt_sigattach(struct knote *kn);
static void filt_sigdetach(struct knote *kn);
@@ -2056,6 +2056,7 @@ do_tdsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
sigqueue_t *sigqueue;
int prop;
struct sigacts *ps;
+ int intrval;
int ret = 0;
PROC_LOCK_ASSERT(p, MA_OWNED);
@@ -2116,6 +2117,10 @@ do_tdsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
action = SIG_CATCH;
else
action = SIG_DFL;
+ if (SIGISMEMBER(ps->ps_sigintr, sig))
+ intrval = EINTR;
+ else
+ intrval = ERESTART;
mtx_unlock(&ps->ps_mtx);
if (prop & SA_CONT)
@@ -2260,7 +2265,7 @@ do_tdsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
*/
mtx_lock_spin(&sched_lock);
if (TD_ON_SLEEPQ(td) && (td->td_flags & TDF_SINTR))
- sleepq_abort(td);
+ sleepq_abort(td, intrval);
mtx_unlock_spin(&sched_lock);
goto out;
/*
@@ -2270,7 +2275,7 @@ do_tdsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
} else if (p->p_state == PRS_NORMAL) {
if (p->p_flag & P_TRACED || action == SIG_CATCH) {
mtx_lock_spin(&sched_lock);
- tdsigwakeup(td, sig, action);
+ tdsigwakeup(td, sig, action, intrval);
mtx_unlock_spin(&sched_lock);
goto out;
}
@@ -2315,7 +2320,7 @@ do_tdsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
runfast:
mtx_lock_spin(&sched_lock);
- tdsigwakeup(td, sig, action);
+ tdsigwakeup(td, sig, action, intrval);
thread_unsuspend(p);
mtx_unlock_spin(&sched_lock);
out:
@@ -2330,7 +2335,7 @@ out:
* out of any sleep it may be in etc.
*/
static void
-tdsigwakeup(struct thread *td, int sig, sig_t action)
+tdsigwakeup(struct thread *td, int sig, sig_t action, int intrval)
{
struct proc *p = td->td_proc;
register int prop;
@@ -2382,7 +2387,7 @@ tdsigwakeup(struct thread *td, int sig, sig_t action)
if (td->td_priority > PUSER)
sched_prio(td, PUSER);
- sleepq_abort(td);
+ sleepq_abort(td, intrval);
} else {
/*
* Other states do nothing with the signal immediately,
OpenPOWER on IntegriCloud