summaryrefslogtreecommitdiff
path: root/sys/kern/kern_time.c
diff options
context:
space:
mode:
authorcheloha <cheloha@cvs.openbsd.org>2020-11-10 17:26:55 +0000
committercheloha <cheloha@cvs.openbsd.org>2020-11-10 17:26:55 +0000
commit99f633ef4b00f6af362062735dca8e757495c52e (patch)
tree5d0c8a70f90106d57861804c55c6285020396616 /sys/kern/kern_time.c
parent70280662f81f86d4ac17c0003062560e46a90527 (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.c41
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);
}
/*