summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArtur Grabowski <art@cvs.openbsd.org>2004-08-04 21:49:20 +0000
committerArtur Grabowski <art@cvs.openbsd.org>2004-08-04 21:49:20 +0000
commitccf71ee5bcc792c009b8dbf0c51f6c02bca9a059 (patch)
tree0391d54fe59e6c1c51369cf7cd71a5a800461e82
parentef68052c976c827bdfedb0eefb04cce7a37af7a7 (diff)
hardclock detects if ITIMER_VIRTUAL and ITIMER_PROF have expired and
sends SIGVTALRM and SIGPROF to the process if they had. There is a big problem with calling psignal from hardclock on MULTIPROCESSOR machines though. It means we need to protect all signal state in the process with a lock because hardclock doesn't obtain KERNEL_LOCK. Trying to track down all the tentacles of this quickly becomes very messy. What saves us at the moment is that SCHED_LOCK (which is used to protect parts of the signal state, but not all) happens to be recursive and forgives small and big errors. That's about to change. So instead of trying to hunt down all the locking problems here, just make hardclock not send signals. Instead hardclock schedules a timeout that will send the signal later. There are many reasons why this works just as good as the previous code, all explained in a comment written in big, friendly letters in kern_clock. miod@ ok noone else dared to ok this, but noone screamed in agony either.
-rw-r--r--sys/kern/kern_clock.c47
-rw-r--r--sys/kern/kern_exit.c4
-rw-r--r--sys/kern/kern_fork.c5
-rw-r--r--sys/sys/resourcevar.h9
4 files changed, 59 insertions, 6 deletions
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
index b7fcfa717c5..524688b58cb 100644
--- a/sys/kern/kern_clock.c
+++ b/sys/kern/kern_clock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_clock.c,v 1.48 2004/08/04 16:29:32 art Exp $ */
+/* $OpenBSD: kern_clock.c,v 1.49 2004/08/04 21:49:19 art Exp $ */
/* $NetBSD: kern_clock.c,v 1.34 1996/06/09 04:51:03 briggs Exp $ */
/*-
@@ -171,6 +171,47 @@ initclocks()
}
/*
+ * hardclock does the accounting needed for ITIMER_PROF and ITIMER_VIRTUAL.
+ * We don't want to send signals with psignal from hardclock because it makes
+ * MULTIPROCESSOR locking very complicated. Instead we use a small trick
+ * to send the signals safely and without blocking too many interrupts
+ * while doing that (signal handling can be heavy).
+ *
+ * hardclock detects that the itimer has expired, and schedules a timeout
+ * to deliver the signal. This works becuse of the following reasons:
+ * - The tiemout structures can be in struct pstats because the timers
+ * can be only activated on curproc (never swapped). Swapout can
+ * only happen from a kernel thread and softclock runs before threads
+ * are scheduled.
+ * - The timeout can be scheduled with a 1 tick time because we're
+ * doing it before the timeout processing in hardclock. So it will
+ * be scheduled to run as soon as possible.
+ * - The timeout will be run in softclock which will run before we
+ * return to userland and process pending signals.
+ * - If the system is so busy that several VIRTUAL/PROF ticks are
+ * sent before softclock processing, we'll send only one signal.
+ * But if we'd send the signal from hardclock only one signal would
+ * be delivered to the user process. So userland will only see one
+ * signal anyway.
+ */
+
+void
+virttimer_trampoline(void *v)
+{
+ struct proc *p = v;
+
+ psignal(p, SIGVTALRM);
+}
+
+void
+proftimer_trampoline(void *v)
+{
+ struct proc *p = v;
+
+ psignal(p, SIGPROF);
+}
+
+/*
* The real-time timer, interrupting hz times per second.
*/
void
@@ -197,10 +238,10 @@ hardclock(struct clockframe *frame)
if (CLKF_USERMODE(frame) &&
timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) &&
itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0)
- psignal(p, SIGVTALRM);
+ timeout_add(&pstats->p_virt_to, 1);
if (timerisset(&pstats->p_timer[ITIMER_PROF].it_value) &&
itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0)
- psignal(p, SIGPROF);
+ timeout_add(&pstats->p_prof_to, 1);
}
/*
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 53bb4326ad8..a57f4c3db60 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_exit.c,v 1.52 2004/07/22 15:42:11 art Exp $ */
+/* $OpenBSD: kern_exit.c,v 1.53 2004/08/04 21:49:19 art Exp $ */
/* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */
/*
@@ -128,6 +128,8 @@ exit1(p, rv)
p->p_sigignore = ~0;
p->p_siglist = 0;
timeout_del(&p->p_realit_to);
+ timeout_del(&p->p_stats->p_virt_to);
+ timeout_del(&p->p_stats->p_prof_to);
/*
* Close open files and release open-file table.
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index ebb7fa5b31d..9d217e76f37 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_fork.c,v 1.70 2004/06/24 19:35:24 tholo Exp $ */
+/* $OpenBSD: kern_fork.c,v 1.71 2004/08/04 21:49:19 art Exp $ */
/* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */
/*
@@ -346,6 +346,9 @@ fork1(struct proc *p1, int exitsig, int flags, void *stack, size_t stacksize,
setrunqueue(p2);
SCHED_UNLOCK(s);
+ timeout_set(&p2->p_stats->p_virt_to, virttimer_trampoline, p2);
+ timeout_set(&p2->p_stats->p_prof_to, proftimer_trampoline, p2);
+
/*
* Now can be swapped.
*/
diff --git a/sys/sys/resourcevar.h b/sys/sys/resourcevar.h
index 7a8251ee2fd..8f870eb65a4 100644
--- a/sys/sys/resourcevar.h
+++ b/sys/sys/resourcevar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: resourcevar.h,v 1.7 2003/06/02 23:28:21 millert Exp $ */
+/* $OpenBSD: resourcevar.h,v 1.8 2004/08/04 21:49:18 art Exp $ */
/* $NetBSD: resourcevar.h,v 1.12 1995/11/22 23:01:53 cgd Exp $ */
/*
@@ -35,6 +35,8 @@
#ifndef _SYS_RESOURCEVAR_H_
#define _SYS_RESOURCEVAR_H_
+#include <sys/timeout.h>
+
/*
* Kernel per-process accounting / statistics
* (not necessarily resident except when running).
@@ -57,6 +59,8 @@ struct pstats {
} p_prof;
#define pstat_endcopy p_start
struct timeval p_start; /* starting time */
+ struct timeout p_virt_to; /* virtual itimer trampoline. */
+ struct timeout p_prof_to; /* prof itimer trampoline. */
};
/*
@@ -86,5 +90,8 @@ struct plimit *limcopy(struct plimit *lim);
void limfree(struct plimit *);
void ruadd(struct rusage *ru, struct rusage *ru2);
+
+void virttimer_trampoline(void *);
+void proftimer_trampoline(void *);
#endif
#endif /* !_SYS_RESOURCEVAR_H_ */