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