kern_clocksource.c revision 210298
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/kern/kern_clocksource.c 210298 2010-07-20 15:48: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_kdtrace.h"
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/bus.h>
42#include <sys/lock.h>
43#include <sys/kdb.h>
44#include <sys/mutex.h>
45#include <sys/proc.h>
46#include <sys/kernel.h>
47#include <sys/sched.h>
48#include <sys/smp.h>
49#include <sys/sysctl.h>
50#include <sys/timeet.h>
51
52#include <machine/atomic.h>
53#include <machine/clock.h>
54#include <machine/cpu.h>
55#include <machine/smp.h>
56
57#ifdef KDTRACE_HOOKS
58#include <sys/dtrace_bsd.h>
59cyclic_clock_func_t	cyclic_clock_func[MAXCPU];
60#endif
61
62static void		cpu_restartclocks(void);
63static void		timercheck(void);
64inline static int	doconfigtimer(int i);
65static void		configtimer(int i);
66
67static struct eventtimer *timer[2] = { NULL, NULL };
68static int		timertest = 0;
69static int		timerticks[2] = { 0, 0 };
70static int		profiling_on = 0;
71static struct bintime	timerperiod[2];
72
73static char		timername[2][32];
74TUNABLE_STR("kern.eventtimer.timer1", timername[0], sizeof(*timername));
75TUNABLE_STR("kern.eventtimer.timer2", timername[1], sizeof(*timername));
76
77static u_int		singlemul = 0;
78TUNABLE_INT("kern.eventtimer.singlemul", &singlemul);
79SYSCTL_INT(_kern_eventtimer, OID_AUTO, singlemul, CTLFLAG_RW, &singlemul,
80    0, "Multiplier, used in single timer mode");
81
82typedef u_int tc[2];
83static DPCPU_DEFINE(tc, configtimer);
84
85#define FREQ2BT(freq, bt)						\
86{									\
87	(bt)->sec = 0;							\
88	(bt)->frac = ((uint64_t)0x8000000000000000  / (freq)) << 1;	\
89}
90#define BT2FREQ(bt)							\
91	(((uint64_t)0x8000000000000000 + ((bt)->frac >> 2)) /		\
92	    ((bt)->frac >> 1))
93
94/* Per-CPU timer1 handler. */
95static int
96hardclockhandler(struct trapframe *frame)
97{
98
99#ifdef KDTRACE_HOOKS
100	/*
101	 * If the DTrace hooks are configured and a callback function
102	 * has been registered, then call it to process the high speed
103	 * timers.
104	 */
105	int cpu = curcpu;
106	if (cyclic_clock_func[cpu] != NULL)
107		(*cyclic_clock_func[cpu])(frame);
108#endif
109
110	timer1clock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
111	return (FILTER_HANDLED);
112}
113
114/* Per-CPU timer2 handler. */
115static int
116statclockhandler(struct trapframe *frame)
117{
118
119	timer2clock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
120	return (FILTER_HANDLED);
121}
122
123/* timer1 broadcast IPI handler. */
124int
125hardclockintr(struct trapframe *frame)
126{
127
128	if (doconfigtimer(0))
129		return (FILTER_HANDLED);
130	return (hardclockhandler(frame));
131}
132
133/* timer2 broadcast IPI handler. */
134int
135statclockintr(struct trapframe *frame)
136{
137
138	if (doconfigtimer(1))
139		return (FILTER_HANDLED);
140	return (statclockhandler(frame));
141}
142
143/* timer1 callback. */
144static void
145timer1cb(struct eventtimer *et, void *arg)
146{
147
148#ifdef SMP
149	/* Broadcast interrupt to other CPUs for non-per-CPU timers */
150	if (smp_started && (et->et_flags & ET_FLAGS_PERCPU) == 0)
151		ipi_all_but_self(IPI_HARDCLOCK);
152#endif
153	if (timertest) {
154		if ((et->et_flags & ET_FLAGS_PERCPU) == 0 || curcpu == 0) {
155			timerticks[0]++;
156			if (timerticks[0] >= timer1hz) {
157				ET_LOCK();
158				timercheck();
159				ET_UNLOCK();
160			}
161		}
162	}
163	hardclockhandler(curthread->td_intr_frame);
164}
165
166/* timer2 callback. */
167static void
168timer2cb(struct eventtimer *et, void *arg)
169{
170
171#ifdef SMP
172	/* Broadcast interrupt to other CPUs for non-per-CPU timers */
173	if (smp_started && (et->et_flags & ET_FLAGS_PERCPU) == 0)
174		ipi_all_but_self(IPI_STATCLOCK);
175#endif
176	if (timertest) {
177		if ((et->et_flags & ET_FLAGS_PERCPU) == 0 || curcpu == 0) {
178			timerticks[1]++;
179			if (timerticks[1] >= timer2hz * 2) {
180				ET_LOCK();
181				timercheck();
182				ET_UNLOCK();
183			}
184		}
185	}
186	statclockhandler(curthread->td_intr_frame);
187}
188
189/*
190 * Check that both timers are running with at least 1/4 of configured rate.
191 * If not - replace the broken one.
192 */
193static void
194timercheck(void)
195{
196
197	if (!timertest)
198		return;
199	timertest = 0;
200	if (timerticks[0] * 4 < timer1hz) {
201		printf("Event timer \"%s\" is dead.\n", timer[0]->et_name);
202		timer1hz = 0;
203		configtimer(0);
204		et_ban(timer[0]);
205		et_free(timer[0]);
206		timer[0] = et_find(NULL, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
207		if (timer[0] == NULL) {
208			timer2hz = 0;
209			configtimer(1);
210			et_free(timer[1]);
211			timer[1] = NULL;
212			timer[0] = timer[1];
213		}
214		et_init(timer[0], timer1cb, NULL, NULL);
215		cpu_restartclocks();
216		return;
217	}
218	if (timerticks[1] * 4 < timer2hz) {
219		printf("Event timer \"%s\" is dead.\n", timer[1]->et_name);
220		timer2hz = 0;
221		configtimer(1);
222		et_ban(timer[1]);
223		et_free(timer[1]);
224		timer[1] = et_find(NULL, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
225		if (timer[1] != NULL)
226			et_init(timer[1], timer2cb, NULL, NULL);
227		cpu_restartclocks();
228		return;
229	}
230}
231
232/*
233 * Reconfigure specified per-CPU timer on other CPU. Called from IPI handler.
234 */
235inline static int
236doconfigtimer(int i)
237{
238	tc *conf;
239
240	conf = DPCPU_PTR(configtimer);
241	if (atomic_load_acq_int(*conf + i)) {
242		if (i == 0 ? timer1hz : timer2hz)
243			et_start(timer[i], NULL, &timerperiod[i]);
244		else
245			et_stop(timer[i]);
246		atomic_store_rel_int(*conf + i, 0);
247		return (1);
248	}
249	return (0);
250}
251
252/*
253 * Reconfigure specified timer.
254 * For per-CPU timers use IPI to make other CPUs to reconfigure.
255 */
256static void
257configtimer(int i)
258{
259#ifdef SMP
260	tc *conf;
261	int cpu;
262
263	critical_enter();
264#endif
265	/* Start/stop global timer or per-CPU timer of this CPU. */
266	if (i == 0 ? timer1hz : timer2hz)
267		et_start(timer[i], NULL, &timerperiod[i]);
268	else
269		et_stop(timer[i]);
270#ifdef SMP
271	if ((timer[i]->et_flags & ET_FLAGS_PERCPU) == 0 || !smp_started) {
272		critical_exit();
273		return;
274	}
275	/* Set reconfigure flags for other CPUs. */
276	CPU_FOREACH(cpu) {
277		conf = DPCPU_ID_PTR(cpu, configtimer);
278		atomic_store_rel_int(*conf + i, (cpu == curcpu) ? 0 : 1);
279	}
280	/* Send reconfigure IPI. */
281	ipi_all_but_self(i == 0 ? IPI_HARDCLOCK : IPI_STATCLOCK);
282	/* Wait for reconfiguration completed. */
283restart:
284	cpu_spinwait();
285	CPU_FOREACH(cpu) {
286		if (cpu == curcpu)
287			continue;
288		conf = DPCPU_ID_PTR(cpu, configtimer);
289		if (atomic_load_acq_int(*conf + i))
290			goto restart;
291	}
292	critical_exit();
293#endif
294}
295
296static int
297round_freq(struct eventtimer *et, int freq)
298{
299	uint64_t div;
300
301	if (et->et_frequency != 0) {
302		div = lmax((et->et_frequency + freq / 2) / freq, 1);
303		if (et->et_flags & ET_FLAGS_POW2DIV)
304			div = 1 << (flsl(div + div / 2) - 1);
305		freq = (et->et_frequency + div / 2) / div;
306	}
307	if (et->et_min_period.sec > 0)
308		freq = 0;
309	else if (et->et_min_period.frac != 0)
310		freq = min(freq, BT2FREQ(&et->et_min_period));
311	if (et->et_max_period.sec == 0 && et->et_max_period.frac != 0)
312		freq = max(freq, BT2FREQ(&et->et_max_period));
313	return (freq);
314}
315
316/*
317 * Configure and start event timers.
318 */
319void
320cpu_initclocks_bsp(void)
321{
322	int base, div;
323
324	timer[0] = et_find(timername[0], ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
325	if (timer[0] == NULL)
326		timer[0] = et_find(NULL, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
327	if (timer[0] == NULL)
328		panic("No usable event timer found!");
329	et_init(timer[0], timer1cb, NULL, NULL);
330	timer[1] = et_find(timername[1][0] ? timername[1] : NULL,
331	    ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
332	if (timer[1])
333		et_init(timer[1], timer2cb, NULL, NULL);
334	/*
335	 * We honor the requested 'hz' value.
336	 * We want to run stathz in the neighborhood of 128hz.
337	 * We would like profhz to run as often as possible.
338	 */
339	if (singlemul == 0) {
340		if (hz >= 1500 || (hz % 128) == 0)
341			singlemul = 1;
342		else if (hz >= 750)
343			singlemul = 2;
344		else
345			singlemul = 4;
346	}
347	if (timer[1] == NULL) {
348		base = round_freq(timer[0], hz * singlemul);
349		singlemul = max((base + hz / 2) / hz, 1);
350		hz = (base + singlemul / 2) / singlemul;
351		if (base <= 128)
352			stathz = base;
353		else {
354			div = base / 128;
355			if (div >= singlemul && (div % singlemul) == 0)
356				div++;
357			stathz = base / div;
358		}
359		profhz = stathz;
360		while ((profhz + stathz) <= 128 * 64)
361			profhz += stathz;
362		profhz = round_freq(timer[0], profhz);
363	} else {
364		hz = round_freq(timer[0], hz);
365		stathz = round_freq(timer[1], 127);
366		profhz = round_freq(timer[1], stathz * 64);
367	}
368	tick = 1000000 / hz;
369	ET_LOCK();
370	cpu_restartclocks();
371	ET_UNLOCK();
372}
373
374/* Start per-CPU event timers on APs. */
375void
376cpu_initclocks_ap(void)
377{
378
379	ET_LOCK();
380	if (timer[0]->et_flags & ET_FLAGS_PERCPU)
381		et_start(timer[0], NULL, &timerperiod[0]);
382	if (timer[1] && timer[1]->et_flags & ET_FLAGS_PERCPU)
383		et_start(timer[1], NULL, &timerperiod[1]);
384	ET_UNLOCK();
385}
386
387/* Reconfigure and restart event timers after configuration changes. */
388static void
389cpu_restartclocks(void)
390{
391
392	/* Stop all event timers. */
393	timertest = 0;
394	if (timer1hz) {
395		timer1hz = 0;
396		configtimer(0);
397	}
398	if (timer[1] && timer2hz) {
399		timer2hz = 0;
400		configtimer(1);
401	}
402	/* Calculate new event timers parameters. */
403	if (timer[1] == NULL) {
404		timer1hz = hz * singlemul;
405		while (timer1hz < (profiling_on ? profhz : stathz))
406			timer1hz += hz;
407		timer2hz = 0;
408	} else {
409		timer1hz = hz;
410		timer2hz = profiling_on ? profhz : stathz;
411		timer2hz = round_freq(timer[1], timer2hz);
412	}
413	timer1hz = round_freq(timer[0], timer1hz);
414	printf("Starting kernel event timers: %s @ %dHz, %s @ %dHz\n",
415	    timer[0]->et_name, timer1hz,
416	    timer[1] ? timer[1]->et_name : "NONE", timer2hz);
417	/* Restart event timers. */
418	FREQ2BT(timer1hz, &timerperiod[0]);
419	configtimer(0);
420	if (timer[1]) {
421		timerticks[0] = 0;
422		timerticks[1] = 0;
423		FREQ2BT(timer2hz, &timerperiod[1]);
424		configtimer(1);
425		timertest = 1;
426	}
427}
428
429/* Switch to profiling clock rates. */
430void
431cpu_startprofclock(void)
432{
433
434	ET_LOCK();
435	profiling_on = 1;
436	cpu_restartclocks();
437	ET_UNLOCK();
438}
439
440/* Switch to regular clock rates. */
441void
442cpu_stopprofclock(void)
443{
444
445	ET_LOCK();
446	profiling_on = 0;
447	cpu_restartclocks();
448	ET_UNLOCK();
449}
450
451/* Report or change the active event timers hardware. */
452static int
453sysctl_kern_eventtimer_timer1(SYSCTL_HANDLER_ARGS)
454{
455	char buf[32];
456	struct eventtimer *et;
457	int error;
458
459	ET_LOCK();
460	et = timer[0];
461	snprintf(buf, sizeof(buf), "%s", et->et_name);
462	ET_UNLOCK();
463	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
464	ET_LOCK();
465	et = timer[0];
466	if (error != 0 || req->newptr == NULL ||
467	    strcmp(buf, et->et_name) == 0) {
468		ET_UNLOCK();
469		return (error);
470	}
471	et = et_find(buf, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
472	if (et == NULL) {
473		ET_UNLOCK();
474		return (ENOENT);
475	}
476	timer1hz = 0;
477	configtimer(0);
478	et_free(timer[0]);
479	timer[0] = et;
480	et_init(timer[0], timer1cb, NULL, NULL);
481	cpu_restartclocks();
482	ET_UNLOCK();
483	return (error);
484}
485SYSCTL_PROC(_kern_eventtimer, OID_AUTO, timer1,
486    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
487    0, 0, sysctl_kern_eventtimer_timer1, "A", "Primary event timer");
488
489static int
490sysctl_kern_eventtimer_timer2(SYSCTL_HANDLER_ARGS)
491{
492	char buf[32];
493	struct eventtimer *et;
494	int error;
495
496	ET_LOCK();
497	et = timer[1];
498	if (et == NULL)
499		snprintf(buf, sizeof(buf), "NONE");
500	else
501		snprintf(buf, sizeof(buf), "%s", et->et_name);
502	ET_UNLOCK();
503	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
504	ET_LOCK();
505	et = timer[1];
506	if (error != 0 || req->newptr == NULL ||
507	    strcmp(buf, et ? et->et_name : "NONE") == 0) {
508		ET_UNLOCK();
509		return (error);
510	}
511	et = et_find(buf, ET_FLAGS_PERIODIC, ET_FLAGS_PERIODIC);
512	if (et == NULL && strcasecmp(buf, "NONE") != 0) {
513		ET_UNLOCK();
514		return (ENOENT);
515	}
516	if (timer[1] != NULL) {
517		timer2hz = 0;
518		configtimer(1);
519		et_free(timer[1]);
520	}
521	timer[1] = et;
522	if (timer[1] != NULL)
523		et_init(timer[1], timer2cb, NULL, NULL);
524	cpu_restartclocks();
525	ET_UNLOCK();
526	return (error);
527}
528SYSCTL_PROC(_kern_eventtimer, OID_AUTO, timer2,
529    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
530    0, 0, sysctl_kern_eventtimer_timer2, "A", "Secondary event timer");
531
532#endif
533
534