diff options
author | cheloha <cheloha@cvs.openbsd.org> | 2020-11-10 17:26:55 +0000 |
---|---|---|
committer | cheloha <cheloha@cvs.openbsd.org> | 2020-11-10 17:26:55 +0000 |
commit | 99f633ef4b00f6af362062735dca8e757495c52e (patch) | |
tree | 5d0c8a70f90106d57861804c55c6285020396616 /sys/kern/kern_time.c | |
parent | 70280662f81f86d4ac17c0003062560e46a90527 (diff) |
setitimer(2): ITIMER_REAL: protect state with per-process mutex ps_mtx
To unlock getitimer(2) and setitimer(2) we need to protect the
per-process ITIMER_REAL state with something other than the kernel
lock. As the ITIMER_REAL timeout callback realitexpire() runs at
IPL_SOFTCLOCK the per-process mutex ps_mtx is appropriate.
In setitimer() we need to use ps_mtx instead of the global itimer_mtx
if the given timer is ITIMER_REAL. Easy.
The ITIMER_REAL timeout callback routine realitexpire() is trickier.
When we enter ps_mtx during the callback we need to check if the timer
was cancelled or rescheduled. A thread from the process can call
setitimer(2) at the exact moment the callback is about to run from
timeout_run() (see kern_timeout.c).
Update the locking annotation in sys/proc.h accordingly.
ok anton@
Diffstat (limited to 'sys/kern/kern_time.c')
-rw-r--r-- | sys/kern/kern_time.c | 41 |
1 files changed, 32 insertions, 9 deletions
diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c index 76953229a0e..d34329ee642 100644 --- a/sys/kern/kern_time.c +++ b/sys/kern/kern_time.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_time.c,v 1.149 2020/10/25 01:55:18 cheloha Exp $ */ +/* $OpenBSD: kern_time.c,v 1.150 2020/11/10 17:26:54 cheloha Exp $ */ /* $NetBSD: kern_time.c,v 1.20 1996/02/18 11:57:06 fvdl Exp $ */ /* @@ -535,10 +535,11 @@ setitimer(int which, const struct itimerval *itv, struct itimerval *olditv) TIMEVAL_TO_TIMESPEC(&itv->it_interval, &its.it_interval); } - if (which != ITIMER_REAL) - mtx_enter(&itimer_mtx); - else + if (which == ITIMER_REAL) { + mtx_enter(&pr->ps_mtx); nanouptime(&now); + } else + mtx_enter(&itimer_mtx); if (olditv != NULL) oldits = *itimer; @@ -553,7 +554,9 @@ setitimer(int which, const struct itimerval *itv, struct itimerval *olditv) *itimer = its; } - if (which != ITIMER_REAL) + if (which == ITIMER_REAL) + mtx_leave(&pr->ps_mtx); + else mtx_leave(&itimer_mtx); if (olditv != NULL) { @@ -660,21 +663,41 @@ realitexpire(void *arg) struct timespec cts; struct process *pr = arg; struct itimerspec *tp = &pr->ps_timer[ITIMER_REAL]; + int need_signal = 0; + + mtx_enter(&pr->ps_mtx); - prsignal(pr, SIGALRM); + /* + * Do nothing if the timer was cancelled or rescheduled while we + * were entering the mutex. + */ + if (!timespecisset(&tp->it_value) || timeout_pending(&pr->ps_realit_to)) + goto out; - /* If it was a one-shot timer we're done. */ + /* The timer expired. We need to send the signal. */ + need_signal = 1; + + /* One-shot timers are not reloaded. */ if (!timespecisset(&tp->it_interval)) { timespecclear(&tp->it_value); - return; + goto out; } - /* Find the nearest future expiration point and restart the timeout. */ + /* + * Find the nearest future expiration point and restart + * the timeout. + */ nanouptime(&cts); while (timespeccmp(&tp->it_value, &cts, <=)) timespecadd(&tp->it_value, &tp->it_interval, &tp->it_value); if ((pr->ps_flags & PS_EXITING) == 0) timeout_at_ts(&pr->ps_realit_to, &tp->it_value); + +out: + mtx_leave(&pr->ps_mtx); + + if (need_signal) + prsignal(pr, SIGALRM); } /* |