- Move the setitimer(2) code responsible for updating the ITIMER_VIRTUAL
and ITIMER_PROF timers from hardclock(9) into a new clock interrupt
routine, itimer_update(). itimer_update() is periodic and runs at the
same frequency as the hardclock.
+ Revise itimerdecr() to run within itimer_mtx instead of entering
and leaving it.
- Each schedstate_percpu has its own itimer_update() handle, spc_itimer.
A new scheduler flag, SPCF_ITIMER, indicates whether spc_itimer was
started during the last mi_switch() and needs to be stopped during the
next mi_switch() or sched_exit().
- A new per-process flag, PS_ITIMER, indicates whether ITIMER_VIRTUAL
and/or ITIMER_PROF are running. Checking the flag is easier than
entering itimer_mtx to check process.ps_timer[]. The flag is set
and cleared in a new helper function, process_reset_itimer_flag().
- In setitimer(), call need_resched() when the state of ITIMER_VIRTUAL
or ITIMER_PROF is changed to force an mi_switch() and update
spc_itimer.
claudio@ notes that ITIMER_PROF could be implemented as a high-res
timer using the thread's execution time as a guide for when to
interrupt the process and assert SIGPROF. This would probably work
really well in single-threaded processes. ITIMER_VIRTUAL would be
more difficult to make high-res, though, as you need to exclude time
spent in the kernel.
Tested on powerpc64 by gkoehler@. With input from claudio@.
Thread: https://marc.info/?l=openbsd-tech&m=
169038818517101&w=2
ok claudio@
-/* $OpenBSD: kern_clock.c,v 1.110 2023/08/01 07:57:55 claudio Exp $ */
+/* $OpenBSD: kern_clock.c,v 1.111 2023/08/05 20:07:55 cheloha Exp $ */
/* $NetBSD: kern_clock.c,v 1.34 1996/06/09 04:51:03 briggs Exp $ */
/*-
inittimecounter();
}
-/*
- * 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, to use an idea from
- * FreeBSD, we set a flag on the thread and when it goes to return to
- * userspace it signals itself.
- */
-
/*
* The real-time timer, interrupting hz times per second.
*/
void
hardclock(struct clockframe *frame)
{
- struct proc *p;
struct cpu_info *ci = curcpu();
- p = curproc;
- if (p && ((p->p_flag & (P_SYSTEM | P_WEXIT)) == 0)) {
- struct process *pr = p->p_p;
-
- /*
- * Run current process's virtual and profile time, as needed.
- */
- if (CLKF_USERMODE(frame) &&
- timespecisset(&pr->ps_timer[ITIMER_VIRTUAL].it_value) &&
- itimerdecr(&pr->ps_timer[ITIMER_VIRTUAL], tick_nsec) == 0) {
- atomic_setbits_int(&p->p_flag, P_ALRMPEND);
- need_proftick(p);
- }
- if (timespecisset(&pr->ps_timer[ITIMER_PROF].it_value) &&
- itimerdecr(&pr->ps_timer[ITIMER_PROF], tick_nsec) == 0) {
- atomic_setbits_int(&p->p_flag, P_PROFPEND);
- need_proftick(p);
- }
- }
-
if (--ci->ci_schedstate.spc_rrticks <= 0)
roundrobin(ci);
-/* $OpenBSD: kern_clockintr.c,v 1.29 2023/07/27 17:52:53 cheloha Exp $ */
+/* $OpenBSD: kern_clockintr.c,v 1.30 2023/08/05 20:07:55 cheloha Exp $ */
/*
* Copyright (c) 2003 Dale Rahn <drahn@openbsd.org>
* Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
* XXX Need to find a better place to do this. We can't do it in
* sched_init_cpu() because initclocks() runs after it.
*/
+ if (spc->spc_itimer->cl_expiration == 0) {
+ clockintr_stagger(spc->spc_itimer, hardclock_period,
+ multiplier, MAXCPUS);
+ }
if (spc->spc_profclock->cl_expiration == 0) {
clockintr_stagger(spc->spc_profclock, profclock_period,
multiplier, MAXCPUS);
-/* $OpenBSD: kern_sched.c,v 1.83 2023/08/05 12:41:04 claudio Exp $ */
+/* $OpenBSD: kern_sched.c,v 1.84 2023/08/05 20:07:55 cheloha Exp $ */
/*
* Copyright (c) 2007, 2008 Artur Grabowski <art@openbsd.org>
*
#include <sys/clockintr.h>
#include <sys/resourcevar.h>
#include <sys/task.h>
+#include <sys/time.h>
#include <sys/smr.h>
#include <sys/tracepoint.h>
spc->spc_idleproc = NULL;
+ if (spc->spc_itimer == NULL) {
+ spc->spc_itimer = clockintr_establish(&ci->ci_queue,
+ itimer_update);
+ if (spc->spc_itimer == NULL) {
+ panic("%s: clockintr_establish itimer_update",
+ __func__);
+ }
+ }
if (spc->spc_profclock == NULL) {
spc->spc_profclock = clockintr_establish(&ci->ci_queue,
profclock);
timespecsub(&ts, &spc->spc_runtime, &ts);
timespecadd(&p->p_rtime, &ts, &p->p_rtime);
+ if (ISSET(spc->spc_schedflags, SPCF_ITIMER)) {
+ atomic_clearbits_int(&spc->spc_schedflags, SPCF_ITIMER);
+ clockintr_cancel(spc->spc_itimer);
+ }
if (ISSET(spc->spc_schedflags, SPCF_PROFCLOCK)) {
atomic_clearbits_int(&spc->spc_schedflags, SPCF_PROFCLOCK);
clockintr_cancel(spc->spc_profclock);
-/* $OpenBSD: kern_time.c,v 1.163 2023/02/15 10:07:50 claudio Exp $ */
+/* $OpenBSD: kern_time.c,v 1.164 2023/08/05 20:07:55 cheloha Exp $ */
/* $NetBSD: kern_time.c,v 1.20 1996/02/18 11:57:06 fvdl Exp $ */
/*
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/clockintr.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/proc.h>
#include <sys/stdint.h>
#include <sys/pledge.h>
#include <sys/task.h>
+#include <sys/time.h>
#include <sys/timeout.h>
#include <sys/timetc.h>
#include <dev/clock_subr.h>
int itimerfix(struct itimerval *);
+void process_reset_itimer_flag(struct process *);
/*
* Time of day and interval timer support.
timeout_del(&pr->ps_realit_to);
}
*itimer = its;
+ if (which == ITIMER_VIRTUAL || which == ITIMER_PROF) {
+ process_reset_itimer_flag(pr);
+ need_resched(curcpu());
+ }
}
if (which == ITIMER_REAL)
}
/*
- * Decrement an interval timer by the given number of nanoseconds.
+ * Decrement an interval timer by the given duration.
* If the timer expires and it is periodic then reload it. When reloading
* the timer we subtract any overrun from the next period so that the timer
* does not drift.
*/
int
-itimerdecr(struct itimerspec *itp, long nsec)
+itimerdecr(struct itimerspec *itp, const struct timespec *decrement)
{
- struct timespec decrement;
-
- NSEC_TO_TIMESPEC(nsec, &decrement);
-
- mtx_enter(&itimer_mtx);
-
- /*
- * Double-check that the timer is enabled. A different thread
- * in setitimer(2) may have disabled it while we were entering
- * the mutex.
- */
- if (!timespecisset(&itp->it_value)) {
- mtx_leave(&itimer_mtx);
+ timespecsub(&itp->it_value, decrement, &itp->it_value);
+ if (itp->it_value.tv_sec >= 0 && timespecisset(&itp->it_value))
return (1);
- }
-
- /*
- * The timer is enabled. Update and reload it as needed.
- */
- timespecsub(&itp->it_value, &decrement, &itp->it_value);
- if (itp->it_value.tv_sec >= 0 && timespecisset(&itp->it_value)) {
- mtx_leave(&itimer_mtx);
- return (1);
- }
if (!timespecisset(&itp->it_interval)) {
timespecclear(&itp->it_value);
- mtx_leave(&itimer_mtx);
return (0);
}
while (itp->it_value.tv_sec < 0 || !timespecisset(&itp->it_value))
timespecadd(&itp->it_value, &itp->it_interval, &itp->it_value);
- mtx_leave(&itimer_mtx);
return (0);
}
+void
+itimer_update(struct clockintr *cl, void *cf)
+{
+ struct timespec elapsed;
+ uint64_t nsecs;
+ struct clockframe *frame = cf;
+ struct proc *p = curproc;
+ struct process *pr;
+
+ if (p == NULL || ISSET(p->p_flag, P_SYSTEM | P_WEXIT))
+ return;
+
+ pr = p->p_p;
+ if (!ISSET(pr->ps_flags, PS_ITIMER))
+ return;
+
+ nsecs = clockintr_advance(cl, hardclock_period) * hardclock_period;
+ NSEC_TO_TIMESPEC(nsecs, &elapsed);
+
+ mtx_enter(&itimer_mtx);
+ if (CLKF_USERMODE(frame) &&
+ timespecisset(&pr->ps_timer[ITIMER_VIRTUAL].it_value) &&
+ itimerdecr(&pr->ps_timer[ITIMER_VIRTUAL], &elapsed) == 0) {
+ process_reset_itimer_flag(pr);
+ atomic_setbits_int(&p->p_flag, P_ALRMPEND);
+ need_proftick(p);
+ }
+ if (timespecisset(&pr->ps_timer[ITIMER_PROF].it_value) &&
+ itimerdecr(&pr->ps_timer[ITIMER_PROF], &elapsed) == 0) {
+ process_reset_itimer_flag(pr);
+ atomic_setbits_int(&p->p_flag, P_PROFPEND);
+ need_proftick(p);
+ }
+ mtx_leave(&itimer_mtx);
+}
+
+void
+process_reset_itimer_flag(struct process *ps)
+{
+ if (timespecisset(&ps->ps_timer[ITIMER_VIRTUAL].it_value) ||
+ timespecisset(&ps->ps_timer[ITIMER_PROF].it_value))
+ atomic_setbits_int(&ps->ps_flags, PS_ITIMER);
+ else
+ atomic_clearbits_int(&ps->ps_flags, PS_ITIMER);
+}
+
struct mutex ratecheck_mtx = MUTEX_INITIALIZER(IPL_HIGH);
/*
-/* $OpenBSD: sched_bsd.c,v 1.78 2023/07/25 18:16:19 cheloha Exp $ */
+/* $OpenBSD: sched_bsd.c,v 1.79 2023/08/05 20:07:55 cheloha Exp $ */
/* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */
/*-
/* add the time counts for this thread to the process's total */
tuagg_unlocked(pr, p);
- /* Stop the profclock if it's running. */
+ /* Stop any optional clock interrupts. */
+ if (ISSET(spc->spc_schedflags, SPCF_ITIMER)) {
+ atomic_clearbits_int(&spc->spc_schedflags, SPCF_ITIMER);
+ clockintr_cancel(spc->spc_itimer);
+ }
if (ISSET(spc->spc_schedflags, SPCF_PROFCLOCK)) {
atomic_clearbits_int(&spc->spc_schedflags, SPCF_PROFCLOCK);
clockintr_cancel(spc->spc_profclock);
*/
KASSERT(p->p_cpu == curcpu());
- /* Start the profclock if profil(2) is enabled. */
+ /* Start any optional clock interrupts needed by the thread. */
+ if (ISSET(p->p_p->ps_flags, PS_ITIMER)) {
+ atomic_setbits_int(&p->p_cpu->ci_schedstate.spc_schedflags,
+ SPCF_ITIMER);
+ clockintr_advance(p->p_cpu->ci_schedstate.spc_itimer,
+ hardclock_period);
+ }
if (ISSET(p->p_p->ps_flags, PS_PROFIL)) {
atomic_setbits_int(&p->p_cpu->ci_schedstate.spc_schedflags,
SPCF_PROFCLOCK);
-/* $OpenBSD: proc.h,v 1.346 2023/07/14 07:07:08 claudio Exp $ */
+/* $OpenBSD: proc.h,v 1.347 2023/08/05 20:07:56 cheloha Exp $ */
/* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */
/*-
#define PS_ORPHAN 0x00800000 /* Process is on an orphan list */
#define PS_CHROOT 0x01000000 /* Process is chrooted */
#define PS_NOBTCFI 0x02000000 /* No Branch Target CFI */
+#define PS_ITIMER 0x04000000 /* Virtual interval timers running */
#define PS_BITS \
("\20" "\01CONTROLT" "\02EXEC" "\03INEXEC" "\04EXITING" "\05SUGID" \
"\013WAITED" "\014COREDUMP" "\015SINGLEEXIT" "\016SINGLEUNWIND" \
"\017NOZOMBIE" "\020STOPPED" "\021SYSTEM" "\022EMBRYO" "\023ZOMBIE" \
"\024NOBROADCASTKILL" "\025PLEDGE" "\026WXNEEDED" "\027EXECPLEDGE" \
- "\030ORPHAN" "\031CHROOT" "\032NOBTCFI")
+ "\030ORPHAN" "\031CHROOT" "\032NOBTCFI" "\033ITIMER")
struct kcov_dev;
-/* $OpenBSD: sched.h,v 1.59 2023/08/03 16:12:08 claudio Exp $ */
+/* $OpenBSD: sched.h,v 1.60 2023/08/05 20:07:56 cheloha Exp $ */
/* $NetBSD: sched.h,v 1.2 1999/02/28 18:14:58 ross Exp $ */
/*-
u_char spc_curpriority; /* usrpri of curproc */
int spc_rrticks; /* ticks until roundrobin() */
+ struct clockintr *spc_itimer; /* [o] itimer_update handle */
struct clockintr *spc_profclock; /* [o] profclock handle */
u_int spc_nrun; /* procs on the run queues */
#define SPCF_SHOULDHALT 0x0004 /* CPU should be vacated */
#define SPCF_HALTED 0x0008 /* CPU has been halted */
#define SPCF_PROFCLOCK 0x0010 /* profclock() was started */
+#define SPCF_ITIMER 0x0020 /* itimer_update() was started */
#define SCHED_PPQ (128 / SCHED_NQS) /* priorities per queue */
#define NICE_WEIGHT 2 /* priorities per nice level */
-/* $OpenBSD: systm.h,v 1.163 2023/07/14 07:07:08 claudio Exp $ */
+/* $OpenBSD: systm.h,v 1.164 2023/08/05 20:07:56 cheloha Exp $ */
/* $NetBSD: systm.h,v 1.50 1996/06/09 04:55:09 briggs Exp $ */
/*-
int tstohz(const struct timespec *);
void realitexpire(void *);
+extern uint32_t hardclock_period;
+
struct clockframe;
void hardclock(struct clockframe *);
void statclock(struct clockframe *);
-/* $OpenBSD: time.h,v 1.63 2022/12/13 17:30:36 cheloha Exp $ */
+/* $OpenBSD: time.h,v 1.64 2023/08/05 20:07:56 cheloha Exp $ */
/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */
/*
struct proc;
int clock_gettime(struct proc *, clockid_t, struct timespec *);
+struct clockintr;
+void itimer_update(struct clockintr *, void *);
+
void cancel_all_itimers(void);
-int itimerdecr(struct itimerspec *, long);
int settime(const struct timespec *);
int ratecheck(struct timeval *, const struct timeval *);
int ppsratecheck(struct timeval *, int *, int);