diff options
author | davidxu <davidxu@FreeBSD.org> | 2006-02-15 23:52:01 +0000 |
---|---|---|
committer | davidxu <davidxu@FreeBSD.org> | 2006-02-15 23:52:01 +0000 |
commit | f1ce5c86603b5f73e31ea323d24ea1433850f704 (patch) | |
tree | 03af904c96a6fe12d7ee078d5642aca586f9f9f0 /sys/kern/kern_sig.c | |
parent | fc705888e9a61ca2e02d7b2a13ad04491eb844e2 (diff) | |
download | FreeBSD-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.c | 17 |
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, |