mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-16 15:11:52 +00:00
Keep track of threads waiting in kse_release() to avoid a race
condition where kse_wakeup() doesn't yet see them in (interruptible) sleep queues. Also add an upcall check to sleepqueue_catch_signals() suggested by jhb. This commit should fix recent mysql hangs. Reviewed by: jhb, davidxu Mysql'd by: Robin P. Blanchard <robin.blanchard at gactr uga edu>
This commit is contained in:
parent
d7837b06fe
commit
4fc21c0947
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=128721
@ -621,9 +621,12 @@ kse_release(struct thread *td, struct kse_release_args *uap)
|
||||
PROC_LOCK(p);
|
||||
if (ku->ku_mflags & KMF_WAITSIGEVENT) {
|
||||
/* UTS wants to wait for signal event */
|
||||
if (!(p->p_flag & P_SIGEVENT) && !(ku->ku_flags & KUF_DOUPCALL))
|
||||
if (!(p->p_flag & P_SIGEVENT) && !(ku->ku_flags & KUF_DOUPCALL)) {
|
||||
td->td_kflags |= TDK_KSERELSIG;
|
||||
error = msleep(&p->p_siglist, &p->p_mtx, PPAUSE|PCATCH,
|
||||
"ksesigwait", (uap->timeout ? tvtohz(&tv) : 0));
|
||||
td->td_kflags &= ~(TDK_KSERELSIG | TDK_WAKEUP);
|
||||
}
|
||||
p->p_flag &= ~P_SIGEVENT;
|
||||
sigset = p->p_siglist;
|
||||
PROC_UNLOCK(p);
|
||||
@ -632,9 +635,11 @@ kse_release(struct thread *td, struct kse_release_args *uap)
|
||||
} else {
|
||||
if (! kg->kg_completed && !(ku->ku_flags & KUF_DOUPCALL)) {
|
||||
kg->kg_upsleeps++;
|
||||
td->td_kflags |= TDK_KSEREL;
|
||||
error = msleep(&kg->kg_completed, &p->p_mtx,
|
||||
PPAUSE|PCATCH, "kserel",
|
||||
(uap->timeout ? tvtohz(&tv) : 0));
|
||||
td->td_kflags &= ~(TDK_KSEREL | TDK_WAKEUP);
|
||||
kg->kg_upsleeps--;
|
||||
}
|
||||
PROC_UNLOCK(p);
|
||||
@ -678,31 +683,36 @@ kse_wakeup(struct thread *td, struct kse_wakeup_args *uap)
|
||||
} else {
|
||||
kg = td->td_ksegrp;
|
||||
if (kg->kg_upsleeps) {
|
||||
wakeup_one(&kg->kg_completed);
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
wakeup_one(&kg->kg_completed);
|
||||
PROC_UNLOCK(p);
|
||||
return (0);
|
||||
}
|
||||
ku = TAILQ_FIRST(&kg->kg_upcalls);
|
||||
}
|
||||
if (ku) {
|
||||
if ((td2 = ku->ku_owner) == NULL) {
|
||||
panic("%s: no owner", __func__);
|
||||
} else if (TD_ON_SLEEPQ(td2) && (td2->td_flags & TDF_SINTR) &&
|
||||
((td2->td_wchan == &kg->kg_completed) ||
|
||||
(td2->td_wchan == &p->p_siglist &&
|
||||
(ku->ku_mflags & KMF_WAITSIGEVENT)))) {
|
||||
sleepq_abort(td2);
|
||||
} else {
|
||||
ku->ku_flags |= KUF_DOUPCALL;
|
||||
}
|
||||
if (ku == NULL) {
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
PROC_UNLOCK(p);
|
||||
return (0);
|
||||
return (ESRCH);
|
||||
}
|
||||
if ((td2 = ku->ku_owner) == NULL) {
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
panic("%s: no owner", __func__);
|
||||
} else if (td2->td_kflags & (TDK_KSEREL | TDK_KSERELSIG)) {
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
if (!(td2->td_kflags & TDK_WAKEUP)) {
|
||||
td2->td_kflags |= TDK_WAKEUP;
|
||||
if (td2->td_kflags & TDK_KSEREL)
|
||||
sleepq_remove(td2, &kg->kg_completed);
|
||||
else
|
||||
sleepq_remove(td2, &p->p_siglist);
|
||||
}
|
||||
} else {
|
||||
ku->ku_flags |= KUF_DOUPCALL;
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
}
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
PROC_UNLOCK(p);
|
||||
return (ESRCH);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1464,6 +1474,7 @@ thread_link(struct thread *td, struct ksegrp *kg)
|
||||
td->td_ksegrp = kg;
|
||||
td->td_last_kse = NULL;
|
||||
td->td_flags = 0;
|
||||
td->td_kflags = 0;
|
||||
td->td_kse = NULL;
|
||||
|
||||
LIST_INIT(&td->td_contested);
|
||||
@ -2222,3 +2233,13 @@ thread_single_end(void)
|
||||
}
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
}
|
||||
|
||||
int
|
||||
thread_upcall_check(struct thread *td)
|
||||
{
|
||||
PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
|
||||
if (td->td_kflags & TDK_WAKEUP)
|
||||
return (1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
@ -621,9 +621,12 @@ kse_release(struct thread *td, struct kse_release_args *uap)
|
||||
PROC_LOCK(p);
|
||||
if (ku->ku_mflags & KMF_WAITSIGEVENT) {
|
||||
/* UTS wants to wait for signal event */
|
||||
if (!(p->p_flag & P_SIGEVENT) && !(ku->ku_flags & KUF_DOUPCALL))
|
||||
if (!(p->p_flag & P_SIGEVENT) && !(ku->ku_flags & KUF_DOUPCALL)) {
|
||||
td->td_kflags |= TDK_KSERELSIG;
|
||||
error = msleep(&p->p_siglist, &p->p_mtx, PPAUSE|PCATCH,
|
||||
"ksesigwait", (uap->timeout ? tvtohz(&tv) : 0));
|
||||
td->td_kflags &= ~(TDK_KSERELSIG | TDK_WAKEUP);
|
||||
}
|
||||
p->p_flag &= ~P_SIGEVENT;
|
||||
sigset = p->p_siglist;
|
||||
PROC_UNLOCK(p);
|
||||
@ -632,9 +635,11 @@ kse_release(struct thread *td, struct kse_release_args *uap)
|
||||
} else {
|
||||
if (! kg->kg_completed && !(ku->ku_flags & KUF_DOUPCALL)) {
|
||||
kg->kg_upsleeps++;
|
||||
td->td_kflags |= TDK_KSEREL;
|
||||
error = msleep(&kg->kg_completed, &p->p_mtx,
|
||||
PPAUSE|PCATCH, "kserel",
|
||||
(uap->timeout ? tvtohz(&tv) : 0));
|
||||
td->td_kflags &= ~(TDK_KSEREL | TDK_WAKEUP);
|
||||
kg->kg_upsleeps--;
|
||||
}
|
||||
PROC_UNLOCK(p);
|
||||
@ -678,31 +683,36 @@ kse_wakeup(struct thread *td, struct kse_wakeup_args *uap)
|
||||
} else {
|
||||
kg = td->td_ksegrp;
|
||||
if (kg->kg_upsleeps) {
|
||||
wakeup_one(&kg->kg_completed);
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
wakeup_one(&kg->kg_completed);
|
||||
PROC_UNLOCK(p);
|
||||
return (0);
|
||||
}
|
||||
ku = TAILQ_FIRST(&kg->kg_upcalls);
|
||||
}
|
||||
if (ku) {
|
||||
if ((td2 = ku->ku_owner) == NULL) {
|
||||
panic("%s: no owner", __func__);
|
||||
} else if (TD_ON_SLEEPQ(td2) && (td2->td_flags & TDF_SINTR) &&
|
||||
((td2->td_wchan == &kg->kg_completed) ||
|
||||
(td2->td_wchan == &p->p_siglist &&
|
||||
(ku->ku_mflags & KMF_WAITSIGEVENT)))) {
|
||||
sleepq_abort(td2);
|
||||
} else {
|
||||
ku->ku_flags |= KUF_DOUPCALL;
|
||||
}
|
||||
if (ku == NULL) {
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
PROC_UNLOCK(p);
|
||||
return (0);
|
||||
return (ESRCH);
|
||||
}
|
||||
if ((td2 = ku->ku_owner) == NULL) {
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
panic("%s: no owner", __func__);
|
||||
} else if (td2->td_kflags & (TDK_KSEREL | TDK_KSERELSIG)) {
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
if (!(td2->td_kflags & TDK_WAKEUP)) {
|
||||
td2->td_kflags |= TDK_WAKEUP;
|
||||
if (td2->td_kflags & TDK_KSEREL)
|
||||
sleepq_remove(td2, &kg->kg_completed);
|
||||
else
|
||||
sleepq_remove(td2, &p->p_siglist);
|
||||
}
|
||||
} else {
|
||||
ku->ku_flags |= KUF_DOUPCALL;
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
}
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
PROC_UNLOCK(p);
|
||||
return (ESRCH);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1464,6 +1474,7 @@ thread_link(struct thread *td, struct ksegrp *kg)
|
||||
td->td_ksegrp = kg;
|
||||
td->td_last_kse = NULL;
|
||||
td->td_flags = 0;
|
||||
td->td_kflags = 0;
|
||||
td->td_kse = NULL;
|
||||
|
||||
LIST_INIT(&td->td_contested);
|
||||
@ -2222,3 +2233,13 @@ thread_single_end(void)
|
||||
}
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
}
|
||||
|
||||
int
|
||||
thread_upcall_check(struct thread *td)
|
||||
{
|
||||
PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
|
||||
if (td->td_kflags & TDK_WAKEUP)
|
||||
return (1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
@ -293,8 +293,10 @@ sleepq_catch_signals(void *wchan)
|
||||
struct sleepqueue *sq;
|
||||
struct thread *td;
|
||||
struct proc *p;
|
||||
int do_upcall;
|
||||
int sig;
|
||||
|
||||
do_upcall = 0;
|
||||
td = curthread;
|
||||
p = td->td_proc;
|
||||
sc = SC_LOOKUP(wchan);
|
||||
@ -318,6 +320,8 @@ sleepq_catch_signals(void *wchan)
|
||||
mtx_unlock(&p->p_sigacts->ps_mtx);
|
||||
if (sig == 0 && thread_suspend_check(1))
|
||||
sig = SIGSTOP;
|
||||
else
|
||||
do_upcall = thread_upcall_check(td);
|
||||
PROC_UNLOCK(p);
|
||||
|
||||
/*
|
||||
@ -326,7 +330,7 @@ sleepq_catch_signals(void *wchan)
|
||||
*/
|
||||
sq = sleepq_lookup(wchan);
|
||||
mtx_lock_spin(&sched_lock);
|
||||
if (TD_ON_SLEEPQ(td) && sig != 0) {
|
||||
if (TD_ON_SLEEPQ(td) && (sig != 0 || do_upcall != 0)) {
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
sleepq_wakeup_thread(sq, td, -1);
|
||||
} else
|
||||
|
@ -302,6 +302,7 @@ struct thread {
|
||||
TAILQ_ENTRY(thread) td_umtx; /* (c?) Link for when we're blocked. */
|
||||
volatile u_int td_generation; /* (k) Enable detection of preemption */
|
||||
stack_t td_sigstk; /* (k) Stack ptr and on-stack flag. */
|
||||
int td_kflags; /* (c) Flags for KSE threading. */
|
||||
|
||||
#define td_endzero td_base_pri
|
||||
|
||||
@ -371,6 +372,10 @@ struct thread {
|
||||
#define TDI_LOCK 0x0008 /* Stopped on a lock. */
|
||||
#define TDI_IWAIT 0x0010 /* Awaiting interrupt. */
|
||||
|
||||
#define TDK_KSEREL 0x0001 /* Blocked in msleep on kg->kg_completed. */
|
||||
#define TDK_KSERELSIG 0x0002 /* Blocked in msleep on p->p_siglist. */
|
||||
#define TDK_WAKEUP 0x0004 /* Thread has been woken by kse_wakeup. */
|
||||
|
||||
#define TD_CAN_UNBIND(td) \
|
||||
(((td)->td_flags & TDF_CAN_UNBIND) == TDF_CAN_UNBIND && \
|
||||
((td)->td_upcall != NULL))
|
||||
@ -913,6 +918,7 @@ void thread_unlink(struct thread *td);
|
||||
void thread_unsuspend(struct proc *p);
|
||||
void thread_unsuspend_one(struct thread *td);
|
||||
int thread_userret(struct thread *td, struct trapframe *frame);
|
||||
int thread_upcall_check(struct thread *td);
|
||||
void thread_user_enter(struct proc *p, struct thread *td);
|
||||
void thread_wait(struct proc *p);
|
||||
int thread_statclock(int user);
|
||||
|
Loading…
Reference in New Issue
Block a user