Add kern.eventtimer.activetick tunable/sysctl, specifying whether each
hardclock() tick should be run on every active CPU, or on only one. On my tests, avoiding extra interrupts because of this on 8-CPU Core i7 system with HZ=10000 saves about 2% of performance. At this moment option implemented only for global timers, as reprogramming per-CPU timers is too expensive now to be compensated by this benefit, especially since we still have to regularly run hardclock() on at least one active CPU to update system uptime. For global timer it is quite trivial: timer runs always, but we just skip IPIs to other CPUs when possible. Option is enabled by default now, keeping previous behavior, as periodic hardclock() calls are still used at least to implement setitimer(2) with ITIMER_VIRTUAL and ITIMER_PROF arguments. But since default schedulers don't depend on it since r232917, we are much more free to experiment with it. MFC after: 1 month
This commit is contained in:
parent
11753bd018
commit
fd053fae73
@ -24,7 +24,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd September 15, 2010
|
.Dd March 13, 2012
|
||||||
.Dt EVENTTIMERS 4
|
.Dt EVENTTIMERS 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -143,6 +143,12 @@ By default this options is disabled.
|
|||||||
If chosen timer is per-CPU
|
If chosen timer is per-CPU
|
||||||
and runs in periodic mode, this option has no effect - all interrupts are
|
and runs in periodic mode, this option has no effect - all interrupts are
|
||||||
always generating.
|
always generating.
|
||||||
|
.It Va kern.eventtimer.activetick
|
||||||
|
makes each CPU to receive all kinds of timer interrupts when they are busy.
|
||||||
|
Disabling it allows to skip some hardclock() calls in some cases.
|
||||||
|
By default this options is enabled.
|
||||||
|
If chosen timer is per-CPU, this option has no effect - all interrupts are
|
||||||
|
always generating, as timer reprogramming is too expensive for that case.
|
||||||
.El
|
.El
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr apic 4 ,
|
.Xr apic 4 ,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*-
|
/*-
|
||||||
* Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
|
* Copyright (c) 2010-2012 Alexander Motin <mav@FreeBSD.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -99,6 +99,7 @@ static struct bintime hardperiod; /* hardclock() events period. */
|
|||||||
static struct bintime statperiod; /* statclock() events period. */
|
static struct bintime statperiod; /* statclock() events period. */
|
||||||
static struct bintime profperiod; /* profclock() events period. */
|
static struct bintime profperiod; /* profclock() events period. */
|
||||||
static struct bintime nexttick; /* Next global timer tick time. */
|
static struct bintime nexttick; /* Next global timer tick time. */
|
||||||
|
static struct bintime nexthard; /* Next global hardlock() event. */
|
||||||
static u_int busy = 0; /* Reconfiguration is in progress. */
|
static u_int busy = 0; /* Reconfiguration is in progress. */
|
||||||
static int profiling = 0; /* Profiling events enabled. */
|
static int profiling = 0; /* Profiling events enabled. */
|
||||||
|
|
||||||
@ -110,11 +111,16 @@ TUNABLE_INT("kern.eventtimer.singlemul", &singlemul);
|
|||||||
SYSCTL_INT(_kern_eventtimer, OID_AUTO, singlemul, CTLFLAG_RW, &singlemul,
|
SYSCTL_INT(_kern_eventtimer, OID_AUTO, singlemul, CTLFLAG_RW, &singlemul,
|
||||||
0, "Multiplier for periodic mode");
|
0, "Multiplier for periodic mode");
|
||||||
|
|
||||||
static u_int idletick = 0; /* Idle mode allowed. */
|
static u_int idletick = 0; /* Run periodic events when idle. */
|
||||||
TUNABLE_INT("kern.eventtimer.idletick", &idletick);
|
TUNABLE_INT("kern.eventtimer.idletick", &idletick);
|
||||||
SYSCTL_UINT(_kern_eventtimer, OID_AUTO, idletick, CTLFLAG_RW, &idletick,
|
SYSCTL_UINT(_kern_eventtimer, OID_AUTO, idletick, CTLFLAG_RW, &idletick,
|
||||||
0, "Run periodic events when idle");
|
0, "Run periodic events when idle");
|
||||||
|
|
||||||
|
static u_int activetick = 1; /* Run all periodic events when active. */
|
||||||
|
TUNABLE_INT("kern.eventtimer.activetick", &activetick);
|
||||||
|
SYSCTL_UINT(_kern_eventtimer, OID_AUTO, activetick, CTLFLAG_RW, &activetick,
|
||||||
|
0, "Run all periodic events when active");
|
||||||
|
|
||||||
static int periodic = 0; /* Periodic or one-shot mode. */
|
static int periodic = 0; /* Periodic or one-shot mode. */
|
||||||
static int want_periodic = 0; /* What mode to prefer. */
|
static int want_periodic = 0; /* What mode to prefer. */
|
||||||
TUNABLE_INT("kern.eventtimer.periodic", &want_periodic);
|
TUNABLE_INT("kern.eventtimer.periodic", &want_periodic);
|
||||||
@ -202,6 +208,9 @@ handleevents(struct bintime *now, int fake)
|
|||||||
bintime_add(&state->nexthard, &hardperiod);
|
bintime_add(&state->nexthard, &hardperiod);
|
||||||
runs++;
|
runs++;
|
||||||
}
|
}
|
||||||
|
if ((timer->et_flags & ET_FLAGS_PERCPU) == 0 &&
|
||||||
|
bintime_cmp(&state->nexthard, &nexthard, >))
|
||||||
|
nexthard = state->nexthard;
|
||||||
if (runs && fake < 2) {
|
if (runs && fake < 2) {
|
||||||
hardclock_cnt(runs, usermode);
|
hardclock_cnt(runs, usermode);
|
||||||
done = 1;
|
done = 1;
|
||||||
@ -263,9 +272,11 @@ getnextcpuevent(struct bintime *event, int idle)
|
|||||||
int skip;
|
int skip;
|
||||||
|
|
||||||
state = DPCPU_PTR(timerstate);
|
state = DPCPU_PTR(timerstate);
|
||||||
|
/* Handle hardclock() events. */
|
||||||
*event = state->nexthard;
|
*event = state->nexthard;
|
||||||
if (idle) { /* If CPU is idle - ask callouts for how long. */
|
if (idle || (!activetick && !profiling &&
|
||||||
skip = 4;
|
(timer->et_flags & ET_FLAGS_PERCPU) == 0)) {
|
||||||
|
skip = idle ? 4 : (stathz / 2);
|
||||||
if (curcpu == CPU_FIRST() && tc_min_ticktock_freq > skip)
|
if (curcpu == CPU_FIRST() && tc_min_ticktock_freq > skip)
|
||||||
skip = tc_min_ticktock_freq;
|
skip = tc_min_ticktock_freq;
|
||||||
skip = callout_tickstofirst(hz / skip) - 1;
|
skip = callout_tickstofirst(hz / skip) - 1;
|
||||||
@ -273,7 +284,8 @@ getnextcpuevent(struct bintime *event, int idle)
|
|||||||
tmp = hardperiod;
|
tmp = hardperiod;
|
||||||
bintime_mul(&tmp, skip);
|
bintime_mul(&tmp, skip);
|
||||||
bintime_add(event, &tmp);
|
bintime_add(event, &tmp);
|
||||||
} else { /* If CPU is active - handle all types of events. */
|
}
|
||||||
|
if (!idle) { /* If CPU is active - handle other types of events. */
|
||||||
if (bintime_cmp(event, &state->nextstat, >))
|
if (bintime_cmp(event, &state->nextstat, >))
|
||||||
*event = state->nextstat;
|
*event = state->nextstat;
|
||||||
if (profiling && bintime_cmp(event, &state->nextprof, >))
|
if (profiling && bintime_cmp(event, &state->nextprof, >))
|
||||||
@ -295,24 +307,28 @@ getnextevent(struct bintime *event)
|
|||||||
#ifdef SMP
|
#ifdef SMP
|
||||||
int cpu;
|
int cpu;
|
||||||
#endif
|
#endif
|
||||||
int c;
|
int c, nonidle;
|
||||||
|
|
||||||
state = DPCPU_PTR(timerstate);
|
state = DPCPU_PTR(timerstate);
|
||||||
*event = state->nextevent;
|
*event = state->nextevent;
|
||||||
c = curcpu;
|
c = curcpu;
|
||||||
#ifdef SMP
|
nonidle = !state->idle;
|
||||||
if ((timer->et_flags & ET_FLAGS_PERCPU) == 0) {
|
if ((timer->et_flags & ET_FLAGS_PERCPU) == 0) {
|
||||||
|
#ifdef SMP
|
||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
if (curcpu == cpu)
|
if (curcpu == cpu)
|
||||||
continue;
|
continue;
|
||||||
state = DPCPU_ID_PTR(cpu, timerstate);
|
state = DPCPU_ID_PTR(cpu, timerstate);
|
||||||
|
nonidle += !state->idle;
|
||||||
if (bintime_cmp(event, &state->nextevent, >)) {
|
if (bintime_cmp(event, &state->nextevent, >)) {
|
||||||
*event = state->nextevent;
|
*event = state->nextevent;
|
||||||
c = cpu;
|
c = cpu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
if (nonidle != 0 && bintime_cmp(event, &nexthard, >))
|
||||||
|
*event = nexthard;
|
||||||
|
}
|
||||||
CTR5(KTR_SPARE2, "next at %d: next %d.%08x%08x by %d",
|
CTR5(KTR_SPARE2, "next at %d: next %d.%08x%08x by %d",
|
||||||
curcpu, event->sec, (unsigned int)(event->frac >> 32),
|
curcpu, event->sec, (unsigned int)(event->frac >> 32),
|
||||||
(unsigned int)(event->frac & 0xffffffff), c);
|
(unsigned int)(event->frac & 0xffffffff), c);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user