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 | |
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@
-rw-r--r-- | sys/kern/kern_time.c | 41 | ||||
-rw-r--r-- | sys/sys/proc.h | 6 |
2 files changed, 35 insertions, 12 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); } /* diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 257175addec..d4ec6450a7a 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.300 2020/09/16 08:01:15 mpi Exp $ */ +/* $OpenBSD: proc.h,v 1.301 2020/11/10 17:26:54 cheloha Exp $ */ /* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */ /*- @@ -219,7 +219,7 @@ struct process { struct rusage *ps_ru; /* sum of stats for dead threads. */ struct tusage ps_tu; /* accumulated times. */ struct rusage ps_cru; /* sum of stats for reaped children */ - struct itimerspec ps_timer[3]; /* [K] ITIMER_REAL timer */ + struct itimerspec ps_timer[3]; /* [m] ITIMER_REAL timer */ /* [T] ITIMER_{VIRTUAL,PROF} timers */ struct timeout ps_rucheck_to; /* [] resource limit check timer */ time_t ps_nextxcpu; /* when to send next SIGXCPU, */ @@ -273,7 +273,7 @@ struct process { int ps_refcnt; /* Number of references. */ struct timespec ps_start; /* starting uptime. */ - struct timeout ps_realit_to; /* [K] ITIMER_REAL timeout */ + struct timeout ps_realit_to; /* [m] ITIMER_REAL timeout */ }; #define ps_session ps_pgrp->pg_session |