]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/sun4v/sun4v/tick.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / sun4v / sun4v / tick.c
1 /*-
2  * Copyright (c) 2001 Jake Burkholder.
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  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/interrupt.h>
35 #include <sys/pcpu.h>
36 #include <sys/sysctl.h>
37 #include <sys/timetc.h>
38
39 #include <machine/clock.h>
40 #include <machine/cpu.h>
41 #include <machine/frame.h>
42 #include <machine/intr_machdep.h>
43 #include <machine/tick.h>
44
45 #ifdef DEBUG
46 #include <sys/proc.h>
47 #endif
48
49 #define TICK_GRACE      10000
50
51 SYSCTL_NODE(_machdep, OID_AUTO, tick, CTLFLAG_RD, 0, "tick statistics");
52
53 static int adjust_edges = 0;
54 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_edges, CTLFLAG_RD, &adjust_edges,
55     0, "total number of times tick interrupts got more than 12.5% behind");
56
57 static int adjust_excess = 0;
58 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_excess, CTLFLAG_RD, &adjust_excess,
59     0, "total number of ignored tick interrupts");
60
61 static int adjust_missed = 0;
62 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_missed, CTLFLAG_RD, &adjust_missed,
63     0, "total number of missed tick interrupts");
64
65 static int adjust_ticks = 0;
66 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_ticks, CTLFLAG_RD, &adjust_ticks,
67     0, "total number of tick interrupts with adjustment");
68
69 static void tick_hardclock(struct trapframe *);
70
71 static uint64_t
72 tick_cputicks(void)
73 {
74
75         return (rd(tick));
76 }
77
78 void
79 cpu_initclocks(void)
80 {
81
82         stathz = hz;
83         tick_start();
84 }
85
86 static __inline void
87 tick_process(struct trapframe *tf)
88 {
89
90         if (curcpu == 0)
91                 hardclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
92         else
93                 hardclock_cpu(TRAPF_USERMODE(tf));
94         if (profprocs != 0)
95                 profclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
96
97         statclock(TRAPF_USERMODE(tf));
98 }
99
100 static void
101 tick_hardclock(struct trapframe *tf)
102 {
103         u_long adj, s, tick, ref;
104         long delta;
105         int count;
106
107 #ifdef DEBUG    
108         if (curthread->td_critnest > 2 || curthread->td_critnest < 1)
109                 panic("nested hardclock %d\n", curthread->td_critnest);
110 #endif
111         /*
112          * The sequence of reading the TICK register, calculating the value
113          * of the next tick and writing it to the TICK_CMPR register must not
114          * be interrupted, not even by an IPI, otherwise a value that is in
115          * the past could be written in the worst case, causing hardclock to
116          * stop.
117          */
118         adj = PCPU_GET(tickadj);
119         s = intr_disable_all();
120         tick = rd(tick);
121         wrtickcmpr(tick + tick_increment - adj, 0);
122         intr_restore_all(s);
123
124         ref = PCPU_GET(tickref);
125         delta = tick - ref;
126         count = 0;
127         while (delta >= tick_increment) {
128                 tick_process(tf);
129                 delta -= tick_increment;
130                 ref += tick_increment;
131                 if (adj != 0)
132                         adjust_ticks++;
133                 count++;
134         }
135         if (count > 0) {
136                 adjust_missed += count - 1;
137                 if (delta > (tick_increment >> 3)) {
138                         if (adj == 0)
139                                 adjust_edges++;
140                         adj = tick_increment >> 4;
141                 } else
142                         adj = 0;
143         } else {
144                 adj = 0;
145                 adjust_excess++;
146         }
147         PCPU_SET(tickref, ref);
148         PCPU_SET(tickadj, adj);
149
150 }
151
152 void
153 tick_init(u_long clock)
154 {
155
156         tick_freq = clock;
157         tick_MHz = clock / 1000000;
158         tick_increment = clock / hz;
159         /*
160          * Avoid stopping of hardclock in terms of a lost tick interrupt
161          * by ensuring that the tick period is at least TICK_GRACE ticks.
162          */
163         printf("tick_freq=%ld hz=%d tick_increment=%ld\n",
164                tick_freq, hz, tick_increment);
165
166 #ifndef SIMULATOR
167         if (tick_increment < TICK_GRACE)
168                 panic("%s: HZ too high, decrease to at least %ld", __func__,
169                     clock / TICK_GRACE);
170 #endif
171         set_cputicker(tick_cputicks, tick_freq, 0);
172 }
173
174 void
175 tick_start(void)
176 {
177         u_long base, s;
178
179         if (curcpu == 0)
180                 intr_setup(PIL_TICK, tick_hardclock, -1, NULL, NULL);
181
182         /*
183          * Try to make the tick interrupts as synchronously as possible on
184          * all CPUs to avoid inaccuracies for migrating processes. Leave out
185          * one tick to make sure that it is not missed.
186          */
187         PCPU_SET(tickadj, 0);
188         s = intr_disable_all();
189         base = rd(tick);
190         base = roundup(base, tick_increment);
191         PCPU_SET(tickref, base);
192         wrtickcmpr(base + tick_increment, 0);
193         intr_restore_all(s);
194 }
195