kern_clocksource.c revision 209371
1/*-
2 * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/x86/x86/timeevents.c 209371 2010-06-20 21:33:29Z mav $");
29
30/*
31 * Common routines to manage event timers hardware.
32 */
33
34/* XEN has own timer routines now. */
35#ifndef XEN
36
37#include "opt_clock.h"
38#include "opt_kdtrace.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/bus.h>
43#include <sys/lock.h>
44#include <sys/kdb.h>
45#include <sys/mutex.h>
46#include <sys/proc.h>
47#include <sys/kernel.h>
48#include <sys/sched.h>
49#include <sys/smp.h>
50#include <sys/sysctl.h>
51#include <sys/timeet.h>
52
53#include <machine/atomic.h>
54#include <machine/clock.h>
55#include <machine/cpu.h>
56#include <machine/intr_machdep.h>
57#include <machine/md_var.h>
58#include <machine/smp.h>
59
60#ifdef KDTRACE_HOOKS
61#include <sys/dtrace_bsd.h>
62cyclic_clock_func_t	cyclic_clock_func[MAXCPU];
63#endif
64
65static void		cpu_restartclocks(void);
66static void		timercheck(void);
67inline static int	doconfigtimer(int i);
68static void		configtimer(int i);
69
70static struct eventtimer *timer[2] = { NULL, NULL };
71static int		timertest = 0;
72static int		timerticks[2] = { 0, 0 };
73static int		profiling_on = 0;
74static struct bintime	timerperiod[2];
75
76static char		timername[2][32];
77TUNABLE_STR("kern.eventtimer.timer1", timername[0], sizeof(*timername));
78TUNABLE_STR("kern.eventtimer.timer2", timername[1], sizeof(*timername));
79
80static u_int		singlemul = 0;
81TUNABLE_INT("kern.eventtimer.singlemul", &singlemul);
82SYSCTL_INT(_kern_eventtimer, OID_AUTO, singlemul, CTLFLAG_RW, &singlemul,
83    0, "Multiplier, used in single timer mode");
84
85typedef u_int tc[2];
86static DPCPU_DEFINE(tc, configtimer);
87
88#define FREQ2BT(freq, bt)						\
89{									\
90	(bt)->sec = 0;							\
91	(bt)->frac = ((uint64_t)0x8000000000000000  / (freq)) << 1;	\
92}
93#define BT2FREQ(bt, freq)						\
94{									\
95	*(freq) = ((uint64_t)0x8000000000000000 + ((bt)->frac >> 2)) /	\
96		    ((bt)->frac >> 1);					\
97}
98
99/* Per-CPU timer1 handler. */
100static int
101hardclockhandler(struct trapframe *frame)
102{
103
104#ifdef KDTRACE_HOOKS
105	/*
106	 * If the DTrace hooks are configured and a callback function
107	 * has been registered, then call it to process the high speed
108	 * timers.
109	 */
110	int cpu = curcpu;
111	if (cyclic_clock_func[cpu] != NULL)
112		(*cyclic_clock_func[cpu])(frame);
113#endif
114
115	timer1clock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
116	return (FILTER_HANDLED);
117}
118
119/* Per-CPU timer2 handler. */
120static int
121statclockhandler(struct trapframe *frame)
122{
123
124	timer2clock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
125	return (FILTER_HANDLED);
126}
127
128/* timer1 broadcast IPI handler. */
129int
130hardclockintr(struct trapframe *frame)
131{
132
133	if (doconfigtimer(0))
134		return (FILTER_HANDLED);
135	return (hardclockhandler(frame));
136}
137
138/* timer2 broadcast IPI handler. */
139int
140statclockintr(struct trapframe *frame)
141{
142
143	if (doconfigtimer(1))
144		return (FILTER_HANDLED);
145	return (statclockhandler(frame));
146}
147
148/* timer1 callback. */
149static void
150timer1cb(struct eventtimer *et, void *arg)
151{
152
153#ifdef SMP
154	/* Broadcast interrupt to other CPUs for non-per-CPU timers */
155	if (smp_started && (et->et_flags & ET_FLAGS_PERCPU) == 0)
156		ipi_all_but_self(IPI_HARDCLOCK);
157#endif
158	if (timertest) {
159		if ((et->et_flags & ET_FLAGS_PERCPU) == 0 || curcpu == 0) {
160			timerticks[0]++;
161			if (timerticks[0] >= timer1hz) {
162				ET_LOCK();
163				timercheck();
164				ET_UNLOCK();
165			}
166		}
167	}
168	hardclockhandler((struct trapframe *)arg);
169}
170
171/* timer2 callback. */
172static void
173timer2cb(struct eventtimer *et, void *arg)
174{
175
176#ifdef SMP
177	/* Broadcast interrupt to other CPUs for non-per-CPU timers */
178	if (smp_started && (et->et_flags & ET_FLAGS_PERCPU) == 0)
179		ipi_all_but_self(IPI_STATCLOCK);
180#endif
181	if (timertest) {
182		if ((et->et_flags & ET_FLAGS_PERCPU) == 0 || curcpu == 0) {
183			timerticks[1]++;
184			if (timerticks[1] >= timer2hz * 2) {
185				ET_LOCK();
186				timercheck();
187				ET_UNLOCK();
188			}
189		}
190	}
191	statclockhandler((struct trapframe *)arg);
192}
193
194/*
195 * Check that both timers are running with at least 1/4 of configured rate.
196 * If not - replace the broken one.
197 */
198static void
199timercheck(void)
200{
201
202	if (!timertest)
203		return;
204	timertest = 0;
205	if (timerticks[0] * 4 < timer1hz) {
206		printf("Event timer \"%s\" is dead.\n", timer[0]->et_name);
207		timer1hz = 0;
208		configtimer(0);
209		et_ban(timer[0]);
210		et_free(timer[0]);
211		timer[0] = et_find(NULL, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
212		if (timer[0] == NULL) {
213			timer2hz = 0;
214			configtimer(1);
215			et_free(timer[1]);
216			timer[1] = NULL;
217			timer[0] = timer[1];
218		}
219		et_init(timer[0], timer1cb, NULL, NULL);
220		cpu_restartclocks();
221		return;
222	}
223	if (timerticks[1] * 4 < timer2hz) {
224		printf("Event timer \"%s\" is dead.\n", timer[1]->et_name);
225		timer2hz = 0;
226		configtimer(1);
227		et_ban(timer[1]);
228		et_free(timer[1]);
229		timer[1] = et_find(NULL, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
230		if (timer[1] != NULL)
231			et_init(timer[1], timer2cb, NULL, NULL);
232		cpu_restartclocks();
233		return;
234	}
235}
236
237/*
238 * Reconfigure specified per-CPU timer on other CPU. Called from IPI handler.
239 */
240inline static int
241doconfigtimer(int i)
242{
243	tc *conf;
244
245	conf = DPCPU_PTR(configtimer);
246	if (atomic_load_acq_int(*conf + i)) {
247		if (i == 0 ? timer1hz : timer2hz)
248			et_start(timer[i], NULL, &timerperiod[i]);
249		else
250			et_stop(timer[i]);
251		atomic_store_rel_int(*conf + i, 0);
252		return (1);
253	}
254	return (0);
255}
256
257/*
258 * Reconfigure specified timer.
259 * For per-CPU timers use IPI to make other CPUs to reconfigure.
260 */
261static void
262configtimer(int i)
263{
264#ifdef SMP
265	tc *conf;
266	int cpu;
267
268	critical_enter();
269#endif
270	/* Start/stop global timer or per-CPU timer of this CPU. */
271	if (i == 0 ? timer1hz : timer2hz)
272		et_start(timer[i], NULL, &timerperiod[i]);
273	else
274		et_stop(timer[i]);
275#ifdef SMP
276	if ((timer[i]->et_flags & ET_FLAGS_PERCPU) == 0 || !smp_started) {
277		critical_exit();
278		return;
279	}
280	/* Set reconfigure flags for other CPUs. */
281	CPU_FOREACH(cpu) {
282		conf = DPCPU_ID_PTR(cpu, configtimer);
283		atomic_store_rel_int(*conf + i, (cpu == curcpu) ? 0 : 1);
284	}
285	/* Send reconfigure IPI. */
286	ipi_all_but_self(i == 0 ? IPI_HARDCLOCK : IPI_STATCLOCK);
287	/* Wait for reconfiguration completed. */
288restart:
289	cpu_spinwait();
290	CPU_FOREACH(cpu) {
291		if (cpu == curcpu)
292			continue;
293		conf = DPCPU_ID_PTR(cpu, configtimer);
294		if (atomic_load_acq_int(*conf + i))
295			goto restart;
296	}
297	critical_exit();
298#endif
299}
300
301/*
302 * Configure and start event timers.
303 */
304void
305cpu_initclocks_bsp(void)
306{
307	int base, div;
308
309	timer[0] = et_find(timername[0], ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
310	if (timer[0] == NULL)
311		timer[0] = et_find(NULL, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
312	et_init(timer[0], timer1cb, NULL, NULL);
313	timer[1] = et_find(timername[1][0] ? timername[1] : NULL,
314	    ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
315	if (timer[1])
316		et_init(timer[1], timer2cb, NULL, NULL);
317	/*
318	 * We honor the requested 'hz' value.
319	 * We want to run stathz in the neighborhood of 128hz.
320	 * We would like profhz to run as often as possible.
321	 */
322	if (singlemul == 0) {
323		if (hz >= 1500 || (hz % 128) == 0)
324			singlemul = 1;
325		else if (hz >= 750)
326			singlemul = 2;
327		else
328			singlemul = 4;
329	}
330	if (timer[1] == NULL) {
331		base = hz * singlemul;
332		if (base < 128)
333			stathz = base;
334		else {
335			div = base / 128;
336			if (div % 2 == 0)
337				div++;
338			stathz = base / div;
339		}
340		profhz = stathz;
341		while ((profhz + stathz) <= 8192)
342			profhz += stathz;
343	} else {
344		stathz = 128;
345		profhz = stathz * 64;
346	}
347	ET_LOCK();
348	cpu_restartclocks();
349	ET_UNLOCK();
350}
351
352/* Start per-CPU event timers on APs. */
353void
354cpu_initclocks_ap(void)
355{
356
357	ET_LOCK();
358	if (timer[0]->et_flags & ET_FLAGS_PERCPU)
359		et_start(timer[0], NULL, &timerperiod[0]);
360	if (timer[1] && timer[1]->et_flags & ET_FLAGS_PERCPU)
361		et_start(timer[1], NULL, &timerperiod[1]);
362	ET_UNLOCK();
363}
364
365/* Reconfigure and restart event timers after configuration changes. */
366static void
367cpu_restartclocks(void)
368{
369
370	/* Stop all event timers. */
371	timertest = 0;
372	if (timer1hz) {
373		timer1hz = 0;
374		configtimer(0);
375	}
376	if (timer[1] && timer2hz) {
377		timer2hz = 0;
378		configtimer(1);
379	}
380	/* Calculate new event timers parameters. */
381	if (timer[1] == NULL) {
382		timer1hz = hz * singlemul;
383		while (timer1hz < (profiling_on ? profhz : stathz))
384			timer1hz += hz;
385		timer2hz = 0;
386	} else {
387		timer1hz = hz;
388		timer2hz = profiling_on ? profhz : stathz;
389	}
390	printf("Starting kernel event timers: %s @ %dHz, %s @ %dHz\n",
391	    timer[0]->et_name, timer1hz,
392	    timer[1] ? timer[1]->et_name : "NONE", timer2hz);
393	/* Restart event timers. */
394	FREQ2BT(timer1hz, &timerperiod[0]);
395	configtimer(0);
396	if (timer[1]) {
397		timerticks[0] = 0;
398		timerticks[1] = 0;
399		FREQ2BT(timer2hz, &timerperiod[1]);
400		configtimer(1);
401		timertest = 1;
402	}
403}
404
405/* Switch to profiling clock rates. */
406void
407cpu_startprofclock(void)
408{
409
410	ET_LOCK();
411	profiling_on = 1;
412	cpu_restartclocks();
413	ET_UNLOCK();
414}
415
416/* Switch to regular clock rates. */
417void
418cpu_stopprofclock(void)
419{
420
421	ET_LOCK();
422	profiling_on = 0;
423	cpu_restartclocks();
424	ET_UNLOCK();
425}
426
427/* Report or change the active event timers hardware. */
428static int
429sysctl_kern_eventtimer_timer1(SYSCTL_HANDLER_ARGS)
430{
431	char buf[32];
432	struct eventtimer *et;
433	int error;
434
435	ET_LOCK();
436	et = timer[0];
437	snprintf(buf, sizeof(buf), "%s", et->et_name);
438	ET_UNLOCK();
439	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
440	ET_LOCK();
441	et = timer[0];
442	if (error != 0 || req->newptr == NULL ||
443	    strcmp(buf, et->et_name) == 0) {
444		ET_UNLOCK();
445		return (error);
446	}
447	et = et_find(buf, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
448	if (et == NULL) {
449		ET_UNLOCK();
450		return (ENOENT);
451	}
452	timer1hz = 0;
453	configtimer(0);
454	et_free(timer[0]);
455	timer[0] = et;
456	et_init(timer[0], timer1cb, NULL, NULL);
457	cpu_restartclocks();
458	ET_UNLOCK();
459	return (error);
460}
461SYSCTL_PROC(_kern_eventtimer, OID_AUTO, timer1,
462    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
463    0, 0, sysctl_kern_eventtimer_timer1, "A", "Primary event timer");
464
465static int
466sysctl_kern_eventtimer_timer2(SYSCTL_HANDLER_ARGS)
467{
468	char buf[32];
469	struct eventtimer *et;
470	int error;
471
472	ET_LOCK();
473	et = timer[1];
474	if (et == NULL)
475		snprintf(buf, sizeof(buf), "NONE");
476	else
477		snprintf(buf, sizeof(buf), "%s", et->et_name);
478	ET_UNLOCK();
479	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
480	ET_LOCK();
481	et = timer[1];
482	if (error != 0 || req->newptr == NULL ||
483	    strcmp(buf, et ? et->et_name : "NONE") == 0) {
484		ET_UNLOCK();
485		return (error);
486	}
487	et = et_find(buf, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
488	if (et == NULL && strcasecmp(buf, "NONE") != 0) {
489		ET_UNLOCK();
490		return (ENOENT);
491	}
492	if (timer[1] != NULL) {
493		timer2hz = 0;
494		configtimer(1);
495		et_free(timer[1]);
496	}
497	timer[1] = et;
498	if (timer[1] != NULL)
499		et_init(timer[1], timer2cb, NULL, NULL);
500	cpu_restartclocks();
501	ET_UNLOCK();
502	return (error);
503}
504SYSCTL_PROC(_kern_eventtimer, OID_AUTO, timer2,
505    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
506    0, 0, sysctl_kern_eventtimer_timer2, "A", "Secondary event timer");
507
508#endif
509
510