]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/sparc64/sparc64/tick.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / sparc64 / sparc64 / tick.c
1 /*-
2  * Copyright (c) 2001 Jake Burkholder.
3  * Copyright (c) 2005, 2008 Marius Strobl <marius@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
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 AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/pcpu.h>
37 #include <sys/proc.h>
38 #include <sys/sched.h>
39 #include <sys/smp.h>
40 #include <sys/sysctl.h>
41 #include <sys/timetc.h>
42
43 #include <dev/ofw/openfirm.h>
44
45 #include <machine/cpu.h>
46 #include <machine/frame.h>
47 #include <machine/intr_machdep.h>
48 #include <machine/tick.h>
49 #include <machine/ver.h>
50
51 /* 10000 ticks proved okay for 500MHz. */
52 #define TICK_GRACE(clock)       ((clock) / 1000000 * 2 * 10)
53
54 #define TICK_QUALITY_MP 10
55 #define TICK_QUALITY_UP 1000
56
57 SYSCTL_NODE(_machdep, OID_AUTO, tick, CTLFLAG_RD, 0, "tick statistics");
58
59 static int adjust_edges = 0;
60 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_edges, CTLFLAG_RD, &adjust_edges,
61     0, "total number of times tick interrupts got more than 12.5% behind");
62
63 static int adjust_excess = 0;
64 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_excess, CTLFLAG_RD, &adjust_excess,
65     0, "total number of ignored tick interrupts");
66
67 static int adjust_missed = 0;
68 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_missed, CTLFLAG_RD, &adjust_missed,
69     0, "total number of missed tick interrupts");
70
71 static int adjust_ticks = 0;
72 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_ticks, CTLFLAG_RD, &adjust_ticks,
73     0, "total number of tick interrupts with adjustment");
74
75 u_int hardclock_use_stick = 0;
76 SYSCTL_INT(_machdep_tick, OID_AUTO, hardclock_use_stick, CTLFLAG_RD,
77     &hardclock_use_stick, 0, "hardclock uses STICK instead of TICK timer");
78
79 static struct timecounter tick_tc;
80 static u_long tick_increment;
81
82 static uint64_t tick_cputicks(void);
83 static timecounter_get_t tick_get_timecount_up;
84 #ifdef SMP
85 static timecounter_get_t tick_get_timecount_mp;
86 #endif
87 static void tick_hardclock(struct trapframe *tf);
88 static void tick_hardclock_bbwar(struct trapframe *tf);
89 static inline void tick_hardclock_common(struct trapframe *tf, u_long tick,
90     u_long adj);
91 static inline void tick_process(struct trapframe *tf);
92 static void stick_hardclock(struct trapframe *tf);
93
94 static uint64_t
95 tick_cputicks(void)
96 {
97
98         return (rd(tick));
99 }
100
101 void
102 cpu_initclocks(void)
103 {
104         uint32_t clock;
105
106         stathz = hz;
107
108         /*
109          * Given that the STICK timers typically are driven at rather low
110          * frequencies they shouldn't be used except when really necessary.
111          */
112         if (hardclock_use_stick != 0) {
113                 if (OF_getprop(OF_parent(PCPU_GET(node)), "stick-frequency",
114                     &clock, sizeof(clock)) == -1)
115                 panic("%s: could not determine STICK frequency", __func__);
116                 intr_setup(PIL_TICK, stick_hardclock, -1, NULL, NULL);
117                 /*
118                  * We don't provide a CPU ticker as long as the frequency
119                  * supplied isn't actually used per-CPU.
120                  */
121         } else {
122                 clock = PCPU_GET(clock);
123                 intr_setup(PIL_TICK, PCPU_GET(impl) >= CPU_IMPL_ULTRASPARCI &&
124                     PCPU_GET(impl) < CPU_IMPL_ULTRASPARCIII ?
125                     tick_hardclock_bbwar : tick_hardclock, -1, NULL, NULL);
126                 set_cputicker(tick_cputicks, clock, 0);
127         }
128         tick_increment = clock / hz;
129         /*
130          * Avoid stopping of hardclock in terms of a lost (S)TICK interrupt
131          * by ensuring that the (S)TICK period is at least TICK_GRACE ticks.
132          */
133         if (tick_increment < TICK_GRACE(clock))
134                 panic("%s: HZ too high, decrease to at least %d",
135                     __func__, clock / TICK_GRACE(clock));
136         tick_start();
137
138         /*
139          * Initialize the TICK-based timecounter.  This must not happen
140          * before SI_SUB_INTRINSIC for tick_get_timecount_mp() to work.
141          */
142         tick_tc.tc_get_timecount = tick_get_timecount_up;
143         tick_tc.tc_poll_pps = NULL;
144         tick_tc.tc_counter_mask = ~0u;
145         tick_tc.tc_frequency = PCPU_GET(clock);
146         tick_tc.tc_name = "tick";
147         tick_tc.tc_quality = TICK_QUALITY_UP;
148         tick_tc.tc_priv = NULL;
149 #ifdef SMP
150         /*
151          * We (try to) sync the (S)TICK timers of APs with the BSP during
152          * their startup but not afterwards.  The resulting drift can
153          * cause problems when the time is calculated based on (S)TICK
154          * values read on different CPUs.  Thus we bind to the BSP for
155          * reading the register and use a low quality for the otherwise
156          * high quality (S)TICK timers in the MP case.
157          */
158         if (cpu_mp_probe()) {
159                 tick_tc.tc_get_timecount = tick_get_timecount_mp;
160                 tick_tc.tc_quality = TICK_QUALITY_MP;
161         }
162 #endif
163         tc_init(&tick_tc);
164 }
165
166 static inline void
167 tick_process(struct trapframe *tf)
168 {
169
170         if (curcpu == 0)
171                 hardclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
172         else
173                 hardclock_cpu(TRAPF_USERMODE(tf));
174         if (profprocs != 0)
175                 profclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
176         statclock(TRAPF_USERMODE(tf));
177 }
178
179 /*
180  * NB: the sequence of reading the (S)TICK register, calculating the value
181  * of the next tick and writing it to the (S)TICK_COMPARE register must not
182  * be interrupted, not even by an IPI, otherwise a value that is in the past
183  * could be written in the worst case, causing hardclock to stop.
184  */
185
186 static void
187 tick_hardclock(struct trapframe *tf)
188 {
189         u_long adj, tick;
190         register_t s;
191
192         critical_enter();
193         adj = PCPU_GET(tickadj);
194         s = intr_disable();
195         tick = rd(tick);
196         wr(tick_cmpr, tick + tick_increment - adj, 0);
197         intr_restore(s);
198         tick_hardclock_common(tf, tick, adj);
199         critical_exit();
200 }
201
202 static void
203 tick_hardclock_bbwar(struct trapframe *tf)
204 {
205         u_long adj, tick;
206         register_t s;
207
208         critical_enter();
209         adj = PCPU_GET(tickadj);
210         s = intr_disable();
211         tick = rd(tick);
212         wrtickcmpr(tick + tick_increment - adj, 0);
213         intr_restore(s);
214         tick_hardclock_common(tf, tick, adj);
215         critical_exit();
216 }
217
218 static void
219 stick_hardclock(struct trapframe *tf)
220 {
221         u_long adj, stick;
222         register_t s;
223
224         critical_enter();
225         adj = PCPU_GET(tickadj);
226         s = intr_disable();
227         stick = rdstick();
228         wrstickcmpr(stick + tick_increment - adj, 0);
229         intr_restore(s);
230         tick_hardclock_common(tf, stick, adj);
231         critical_exit();
232 }
233
234 static inline void
235 tick_hardclock_common(struct trapframe *tf, u_long tick, u_long adj)
236 {
237         u_long ref;
238         long delta;
239         int count;
240
241         ref = PCPU_GET(tickref);
242         delta = tick - ref;
243         count = 0;
244         while (delta >= tick_increment) {
245                 tick_process(tf);
246                 delta -= tick_increment;
247                 ref += tick_increment;
248                 if (adj != 0)
249                         adjust_ticks++;
250                 count++;
251         }
252         if (count > 0) {
253                 adjust_missed += count - 1;
254                 if (delta > (tick_increment >> 3)) {
255                         if (adj == 0)
256                                 adjust_edges++;
257                         adj = tick_increment >> 4;
258                 } else
259                         adj = 0;
260         } else {
261                 adj = 0;
262                 adjust_excess++;
263         }
264         PCPU_SET(tickref, ref);
265         PCPU_SET(tickadj, adj);
266 }
267
268 static u_int
269 tick_get_timecount_up(struct timecounter *tc)
270 {
271
272         return ((u_int)rd(tick));
273 }
274
275 #ifdef SMP
276 static u_int
277 tick_get_timecount_mp(struct timecounter *tc)
278 {
279         struct thread *td;
280         u_int tick;
281
282         td = curthread;
283         thread_lock(td);
284         sched_bind(td, 0);
285         thread_unlock(td);
286
287         tick = tick_get_timecount_up(tc);
288
289         thread_lock(td);
290         sched_unbind(td);
291         thread_unlock(td);
292
293         return (tick);
294 }
295 #endif
296
297 void
298 tick_start(void)
299 {
300         u_long base;
301         register_t s;
302
303         /*
304          * Try to make the (S)TICK interrupts as synchronously as possible
305          * on all CPUs to avoid inaccuracies for migrating processes.  Leave
306          * out one tick to make sure that it is not missed.
307          */
308         critical_enter();
309         PCPU_SET(tickadj, 0);
310         s = intr_disable();
311         if (hardclock_use_stick != 0)
312                 base = rdstick();
313         else
314                 base = rd(tick);
315         base = roundup(base, tick_increment);
316         PCPU_SET(tickref, base);
317         if (hardclock_use_stick != 0)
318                 wrstickcmpr(base + tick_increment, 0);
319         else
320                 wrtickcmpr(base + tick_increment, 0);
321         intr_restore(s);
322         critical_exit();
323 }
324
325 void
326 tick_clear(u_int cpu_impl)
327 {
328
329         if (cpu_impl == CPU_IMPL_SPARC64V ||
330             cpu_impl >= CPU_IMPL_ULTRASPARCIII)
331                 wrstick(0, 0);
332         wrpr(tick, 0, 0);
333 }
334
335 void
336 tick_stop(u_int cpu_impl)
337 {
338
339         if (cpu_impl == CPU_IMPL_SPARC64V ||
340             cpu_impl >= CPU_IMPL_ULTRASPARCIII)
341                 wrstickcmpr(1L << 63, 0);
342         wrtickcmpr(1L << 63, 0);
343 }