Changeset View
Standalone View
sys/kern/kern_sig.c
Show First 20 Lines • Show All 1,272 Lines • ▼ Show 20 Lines | kern_sigtimedwait(struct thread *td, sigset_t waitset, ksiginfo_t *ksi, | ||||
ksiginfo_init(ksi); | ksiginfo_init(ksi); | ||||
/* Some signals can not be waited for. */ | /* Some signals can not be waited for. */ | ||||
SIG_CANTMASK(waitset); | SIG_CANTMASK(waitset); | ||||
ps = p->p_sigacts; | ps = p->p_sigacts; | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
saved_mask = td->td_sigmask; | saved_mask = td->td_sigmask; | ||||
SIGSETNAND(td->td_sigmask, waitset); | SIGSETNAND(td->td_sigmask, waitset); | ||||
if ((p->p_sysent->sv_flags & SV_SIG_DISCIGN) != 0 || | if ((p->p_sysent->sv_flags & SV_SIG_DISCIGN) != 0 || | ||||
!kern_sig_discard_ign) | !kern_sig_discard_ign) { | ||||
td->td_pflags2 |= TDP2_SIGWAIT; | thread_lock(td); | ||||
td->td_flags |= TDF_SIGWAIT; | |||||
thread_unlock(td); | |||||
} | |||||
for (;;) { | for (;;) { | ||||
mtx_lock(&ps->ps_mtx); | mtx_lock(&ps->ps_mtx); | ||||
sig = cursig(td); | sig = cursig(td); | ||||
mtx_unlock(&ps->ps_mtx); | mtx_unlock(&ps->ps_mtx); | ||||
KASSERT(sig >= 0, ("sig %d", sig)); | KASSERT(sig >= 0, ("sig %d", sig)); | ||||
if (sig != 0 && SIGISMEMBER(waitset, sig)) { | if (sig != 0 && SIGISMEMBER(waitset, sig)) { | ||||
if (sigqueue_get(&td->td_sigqueue, sig, ksi) != 0 || | if (sigqueue_get(&td->td_sigqueue, sig, ksi) != 0 || | ||||
sigqueue_get(&p->p_sigqueue, sig, ksi) != 0) { | sigqueue_get(&p->p_sigqueue, sig, ksi) != 0) { | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | for (;;) { | ||||
* userspace entered the syscall, return spurious | * userspace entered the syscall, return spurious | ||||
* EINTR after wait was done. Only do this as last | * EINTR after wait was done. Only do this as last | ||||
* resort after rechecking for possible queued signals | * resort after rechecking for possible queued signals | ||||
* and expired timeouts. | * and expired timeouts. | ||||
*/ | */ | ||||
if (error == 0 && (p->p_ptevents & PTRACE_SYSCALL) != 0) | if (error == 0 && (p->p_ptevents & PTRACE_SYSCALL) != 0) | ||||
traced = true; | traced = true; | ||||
} | } | ||||
td->td_pflags2 &= ~TDP2_SIGWAIT; | thread_lock(td); | ||||
td->td_flags &= ~TDF_SIGWAIT; | |||||
thread_unlock(td); | |||||
new_block = saved_mask; | new_block = saved_mask; | ||||
SIGSETNAND(new_block, td->td_sigmask); | SIGSETNAND(new_block, td->td_sigmask); | ||||
td->td_sigmask = saved_mask; | td->td_sigmask = saved_mask; | ||||
/* | /* | ||||
* Fewer signals can be delivered to us, reschedule signal | * Fewer signals can be delivered to us, reschedule signal | ||||
* notification. | * notification. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 791 Lines • ▼ Show 20 Lines | |||||
void | void | ||||
tdksignal(struct thread *td, int sig, ksiginfo_t *ksi) | tdksignal(struct thread *td, int sig, ksiginfo_t *ksi) | ||||
{ | { | ||||
(void) tdsendsignal(td->td_proc, td, sig, ksi); | (void) tdsendsignal(td->td_proc, td, sig, ksi); | ||||
} | } | ||||
static int | |||||
sig_sleepq_abort(struct thread *td, int intrval) | |||||
{ | |||||
THREAD_LOCK_ASSERT(td, MA_OWNED); | |||||
if (intrval == 0 && (td->td_flags & TDF_SIGWAIT) == 0) | |||||
return (0); | |||||
return (sleepq_abort(td, intrval)); | |||||
} | |||||
int | int | ||||
tdsendsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi) | tdsendsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi) | ||||
{ | { | ||||
sig_t action; | sig_t action; | ||||
sigqueue_t *sigqueue; | sigqueue_t *sigqueue; | ||||
int prop; | int prop; | ||||
struct sigacts *ps; | struct sigacts *ps; | ||||
int intrval; | int intrval; | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | if (kern_sig_discard_ign && | ||||
SDT_PROBE3(proc, , , signal__discard, td, p, sig); | SDT_PROBE3(proc, , , signal__discard, td, p, sig); | ||||
mtx_unlock(&ps->ps_mtx); | mtx_unlock(&ps->ps_mtx); | ||||
if (ksi && (ksi->ksi_flags & KSI_INS)) | if (ksi && (ksi->ksi_flags & KSI_INS)) | ||||
ksiginfo_tryfree(ksi); | ksiginfo_tryfree(ksi); | ||||
return (ret); | return (ret); | ||||
} else { | } else { | ||||
action = SIG_CATCH; | action = SIG_CATCH; | ||||
intrval = 0; | |||||
markj: Why isn't it ERESTART? How can a sleepq_wait_sig() caller distinguish between a proper wakeup… | |||||
Done Inline ActionsI do not believe ERESTART would work. First, several interesting syscalls (WRT to this change) convert ERESTART to EINTR. Second, sleepq_wait_sig() caller does not need to distinguish these cases. For caller, this looks like a spurious wakeup which we allow anyway. For instance, msleep() would return 0 (AKA spurious wakeup), sx retries because the lock cannot be taken still, cv gets spurious wakeup etc. There, we are waken up anyway, so we need to only prevent a damage to the state. I was worried that this change breaks sigwait() back to the state before sig_discard was added, but both code reading and trasz' testing show that it is not. kib: I do not believe ERESTART would work. First, several interesting syscalls (WRT to this change)… | |||||
Not Done Inline ActionsI think we have discussed this before but I can't remember any details: how can spurious wakeups occur? I do not believe it is possible with the existing sleepqueue design, so long as the wchan is unique. I believe it is harmless for many consumers, especially when an interlock is used (as in msleep() etc.), but I'm sure spurious wakeups would be problematic in general. Consider the implementation of pause() for instance. markj: I think we have discussed this before but I can't remember any details: how can spurious… | |||||
Done Inline ActionsFor instance, spurious wakeup could happen due to race, where the address is reused for different kind of object, and old object is woken up by code assuming its previous use (and waking up without interlock). Another example, in case of wait(2), if more than one thread does the syscall, only one thread would find the zombie that signaled them. kib: For instance, spurious wakeup could happen due to race, where the address is reused for… | |||||
Not Done Inline ActionsThese scenarios are possible only when the consumer allows them to happen. Arbitrary sleepq interface consumers might not handle spurious wakeups. Consider the cv_wait_signal() call in vn_sendfile(). It does not handle spurious wakeups. Even if it did, when a signal is received, the system call should return, but with this change vn_sendfile() cannot determine whether to sleep again or not. markj: These scenarios are possible only when the consumer allows them to happen. Arbitrary sleepq… | |||||
Done Inline ActionsWell, address collisions cannot be controlled by the caller. Looking at vn_sendfile(), I think it is at least weird, to say it mildly. Intent of SF_SYNC is to provide the caller of sendfile() a notification that the operation finished, e.g. to allow further modifications of the file/shmfd. Now, a signal send does not stop the operation, it only stops the wait for the finish. Since signals are external events that might be created by actors not controlled by the code requested sendfile(), getting EINTR (ERESTART is translated) means that they cannot correctly continue at all. And of course, secondary problem is that they are not prepared for spurious wakeups that can happen. kib: Well, address collisions cannot be controlled by the caller.
Looking at vn_sendfile(), I think… | |||||
Done Inline Actions
kib: * And there is no way to continue interrupted wait for already initiated operation. | |||||
} | } | ||||
} else if (SIGISMEMBER(td->td_sigmask, sig)) | } else { | ||||
if (SIGISMEMBER(td->td_sigmask, sig)) | |||||
action = SIG_HOLD; | action = SIG_HOLD; | ||||
else if (SIGISMEMBER(ps->ps_sigcatch, sig)) | else if (SIGISMEMBER(ps->ps_sigcatch, sig)) | ||||
action = SIG_CATCH; | action = SIG_CATCH; | ||||
else | else | ||||
action = SIG_DFL; | action = SIG_DFL; | ||||
if (SIGISMEMBER(ps->ps_sigintr, sig)) | if (SIGISMEMBER(ps->ps_sigintr, sig)) | ||||
intrval = EINTR; | intrval = EINTR; | ||||
else | else | ||||
intrval = ERESTART; | intrval = ERESTART; | ||||
} | |||||
mtx_unlock(&ps->ps_mtx); | mtx_unlock(&ps->ps_mtx); | ||||
if (prop & SIGPROP_CONT) | if (prop & SIGPROP_CONT) | ||||
sigqueue_delete_stopmask_proc(p); | sigqueue_delete_stopmask_proc(p); | ||||
else if (prop & SIGPROP_STOP) { | else if (prop & SIGPROP_STOP) { | ||||
/* | /* | ||||
* If sending a tty stop signal to a member of an orphaned | * If sending a tty stop signal to a member of an orphaned | ||||
* process group, discard the signal here if the action | * process group, discard the signal here if the action | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | if (P_SHOULDSTOP(p)) { | ||||
* wakeup so that when it is continued it will be made | * wakeup so that when it is continued it will be made | ||||
* runnable and can look at the signal. However, don't make | * runnable and can look at the signal. However, don't make | ||||
* the PROCESS runnable, leave it stopped. | * the PROCESS runnable, leave it stopped. | ||||
* It may run a bit until it hits a thread_suspend_check(). | * It may run a bit until it hits a thread_suspend_check(). | ||||
*/ | */ | ||||
PROC_SLOCK(p); | PROC_SLOCK(p); | ||||
thread_lock(td); | thread_lock(td); | ||||
if (TD_CAN_ABORT(td)) | if (TD_CAN_ABORT(td)) | ||||
wakeup_swapper = sleepq_abort(td, intrval); | wakeup_swapper = sig_sleepq_abort(td, intrval); | ||||
else | else | ||||
thread_unlock(td); | thread_unlock(td); | ||||
PROC_SUNLOCK(p); | PROC_SUNLOCK(p); | ||||
goto out; | goto out; | ||||
/* | /* | ||||
* Mutexes are short lived. Threads waiting on them will | * Mutexes are short lived. Threads waiting on them will | ||||
* hit thread_suspend_check() soon. | * hit thread_suspend_check() soon. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | if ((prop & SIGPROP_STOP) != 0 && (td->td_flags & (TDF_SBDRY | | ||||
goto out; | goto out; | ||||
/* | /* | ||||
* Give low priority threads a better chance to run. | * Give low priority threads a better chance to run. | ||||
*/ | */ | ||||
if (td->td_priority > PUSER && !TD_IS_IDLETHREAD(td)) | if (td->td_priority > PUSER && !TD_IS_IDLETHREAD(td)) | ||||
sched_prio(td, PUSER); | sched_prio(td, PUSER); | ||||
wakeup_swapper = sleepq_abort(td, intrval); | wakeup_swapper = sig_sleepq_abort(td, intrval); | ||||
PROC_SUNLOCK(p); | PROC_SUNLOCK(p); | ||||
if (wakeup_swapper) | if (wakeup_swapper) | ||||
kick_proc0(); | kick_proc0(); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
* Other states do nothing with the signal immediately, | * Other states do nothing with the signal immediately, | ||||
▲ Show 20 Lines • Show All 437 Lines • ▼ Show 20 Lines | for (;;) { | ||||
/* | /* | ||||
* We should allow pending but ignored signals below | * We should allow pending but ignored signals below | ||||
* only if there is sigwait() active, or P_TRACED was | * only if there is sigwait() active, or P_TRACED was | ||||
* on when they were posted. | * on when they were posted. | ||||
*/ | */ | ||||
if (SIGISMEMBER(ps->ps_sigignore, sig) && | if (SIGISMEMBER(ps->ps_sigignore, sig) && | ||||
(p->p_flag & P_TRACED) == 0 && | (p->p_flag & P_TRACED) == 0 && | ||||
(td->td_pflags2 & TDP2_SIGWAIT) == 0) { | (td->td_flags & TDF_SIGWAIT) == 0) { | ||||
sigqueue_delete(&td->td_sigqueue, sig); | sigqueue_delete(&td->td_sigqueue, sig); | ||||
sigqueue_delete(&p->p_sigqueue, sig); | sigqueue_delete(&p->p_sigqueue, sig); | ||||
continue; | continue; | ||||
} | } | ||||
if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED) { | if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED) { | ||||
/* | /* | ||||
* If traced, always stop. | * If traced, always stop. | ||||
* Remove old signal from queue before the stop. | * Remove old signal from queue before the stop. | ||||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | #endif | ||||
p->p_xsig = sig; | p->p_xsig = sig; | ||||
PROC_SLOCK(p); | PROC_SLOCK(p); | ||||
sig_suspend_threads(td, p, 0); | sig_suspend_threads(td, p, 0); | ||||
thread_suspend_switch(td, p); | thread_suspend_switch(td, p); | ||||
PROC_SUNLOCK(p); | PROC_SUNLOCK(p); | ||||
mtx_lock(&ps->ps_mtx); | mtx_lock(&ps->ps_mtx); | ||||
goto next; | goto next; | ||||
} else if ((prop & SIGPROP_IGNORE) != 0 && | } else if ((prop & SIGPROP_IGNORE) != 0 && | ||||
(td->td_pflags2 & TDP2_SIGWAIT) == 0) { | (td->td_flags & TDF_SIGWAIT) == 0) { | ||||
/* | /* | ||||
* Default action is to ignore; drop it if | * Default action is to ignore; drop it if | ||||
* not in kern_sigtimedwait(). | * not in kern_sigtimedwait(). | ||||
*/ | */ | ||||
break; /* == ignore */ | break; /* == ignore */ | ||||
} else | } else | ||||
return (sig); | return (sig); | ||||
/*NOTREACHED*/ | /*NOTREACHED*/ | ||||
case (intptr_t)SIG_IGN: | case (intptr_t)SIG_IGN: | ||||
if ((td->td_pflags2 & TDP2_SIGWAIT) == 0) | if ((td->td_flags & TDF_SIGWAIT) == 0) | ||||
break; /* == ignore */ | break; /* == ignore */ | ||||
else | else | ||||
return (sig); | return (sig); | ||||
default: | default: | ||||
/* | /* | ||||
* This signal has an action, let | * This signal has an action, let | ||||
* postsig() process it. | * postsig() process it. | ||||
▲ Show 20 Lines • Show All 1,216 Lines • Show Last 20 Lines |
Why isn't it ERESTART? How can a sleepq_wait_sig() caller distinguish between a proper wakeup and a wakeup due to an ignored signal?