kern_cctr.c revision 1.6 1 /* $NetBSD: kern_cctr.c,v 1.6 2008/04/27 22:43:08 ad Exp $ */
2
3 /*-
4 * Copyright (c) 2006, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * re-implementation of TSC for MP systems merging cc_microtime and
8 * TSC for timecounters by Frank Kardel
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /* basic calibration ideas are (kern_microtime.c): */
40 /******************************************************************************
41 * *
42 * Copyright (c) David L. Mills 1993, 1994 *
43 * *
44 * Permission to use, copy, modify, and distribute this software and its *
45 * documentation for any purpose and without fee is hereby granted, provided *
46 * that the above copyright notice appears in all copies and that both the *
47 * copyright notice and this permission notice appear in supporting *
48 * documentation, and that the name University of Delaware not be used in *
49 * advertising or publicity pertaining to distribution of the software *
50 * without specific, written prior permission. The University of Delaware *
51 * makes no representations about the suitability this software for any *
52 * purpose. It is provided "as is" without express or implied warranty. *
53 * *
54 ******************************************************************************/
55
56 /* reminiscents from older version of this file are: */
57 /*-
58 * Copyright (c) 1998-2003 Poul-Henning Kamp
59 * All rights reserved.
60 *
61 * Redistribution and use in source and binary forms, with or without
62 * modification, are permitted provided that the following conditions
63 * are met:
64 * 1. Redistributions of source code must retain the above copyright
65 * notice, this list of conditions and the following disclaimer.
66 * 2. Redistributions in binary form must reproduce the above copyright
67 * notice, this list of conditions and the following disclaimer in the
68 * documentation and/or other materials provided with the distribution.
69 *
70 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
71 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
74 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
75 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
76 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
77 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
78 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
79 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
80 * SUCH DAMAGE.
81 */
82
83 #include <sys/cdefs.h>
84 /* __FBSDID("$FreeBSD: src/sys/i386/i386/tsc.c,v 1.204 2003/10/21 18:28:34 silby Exp $"); */
85 __KERNEL_RCSID(0, "$NetBSD: kern_cctr.c,v 1.6 2008/04/27 22:43:08 ad Exp $");
86
87 #include "opt_multiprocessor.h"
88
89 #include <sys/param.h>
90 #include <sys/systm.h>
91 #include <sys/sysctl.h>
92 #include <sys/time.h>
93 #include <sys/timetc.h>
94 #include <sys/kernel.h>
95 #include <sys/power.h>
96 #include <sys/cpu.h>
97 #include <machine/cpu_counter.h>
98
99 /* XXX make cc_timecounter.tc_frequency settable by sysctl() */
100
101 static timecounter_pps_t cc_calibrate;
102
103 void cc_calibrate_cpu(struct cpu_info *);
104
105 static int64_t cc_cal_val; /* last calibrate time stamp */
106
107 static struct timecounter cc_timecounter = {
108 .tc_get_timecount = cc_get_timecount,
109 .tc_poll_pps = cc_calibrate,
110 .tc_counter_mask = ~0u,
111 .tc_frequency = 0,
112 .tc_name = "unkown cycle counter",
113 /*
114 * don't pick cycle counter automatically
115 * if frequency changes might affect cycle counter
116 */
117 .tc_quality = -100000,
118
119 .tc_priv = NULL,
120 .tc_next = NULL
121 };
122
123 /*
124 * initialize cycle counter based timecounter
125 */
126 struct timecounter *
127 cc_init(timecounter_get_t getcc, uint64_t freq, const char *name, int quality)
128 {
129
130 if (getcc != NULL)
131 cc_timecounter.tc_get_timecount = getcc;
132
133 cc_timecounter.tc_frequency = freq;
134 cc_timecounter.tc_name = name;
135 cc_timecounter.tc_quality = quality;
136 tc_init(&cc_timecounter);
137
138 return &cc_timecounter;
139 }
140
141 /*
142 * pick up tick count scaled to reference tick count
143 */
144 u_int
145 cc_get_timecount(struct timecounter *tc)
146 {
147 struct cpu_info *ci;
148 int64_t rcc, cc, ncsw;
149 u_int gen;
150
151 retry:
152 ncsw = curlwp->l_ncsw;
153 __insn_barrier();
154 ci = curcpu();
155 if (ci->ci_cc.cc_denom == 0) {
156 /*
157 * This is our first time here on this CPU. Just
158 * start with reasonable initial values.
159 */
160 ci->ci_cc.cc_cc = cpu_counter32();
161 ci->ci_cc.cc_val = 0;
162 if (ci->ci_cc.cc_gen == 0)
163 ci->ci_cc.cc_gen++;
164
165 ci->ci_cc.cc_denom = cpu_frequency(ci);
166 if (ci->ci_cc.cc_denom == 0)
167 ci->ci_cc.cc_denom = cc_timecounter.tc_frequency;
168 ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
169 }
170
171 /*
172 * read counter and re-read when the re-calibration
173 * strikes inbetween
174 */
175 do {
176 /* pick up current generation number */
177 gen = ci->ci_cc.cc_gen;
178
179 /* determine local delta ticks */
180 cc = cpu_counter32() - ci->ci_cc.cc_cc;
181 if (cc < 0)
182 cc += 0x100000000LL;
183
184 /* scale to primary */
185 rcc = (cc * ci->ci_cc.cc_delta) / ci->ci_cc.cc_denom
186 + ci->ci_cc.cc_val;
187 } while (gen == 0 || gen != ci->ci_cc.cc_gen);
188 __insn_barrier();
189 if (ncsw != curlwp->l_ncsw) {
190 /* Was preempted */
191 goto retry;
192 }
193
194 return rcc;
195 }
196
197 /*
198 * called once per clock tick via the pps callback
199 * for the calibration of the TSC counters.
200 * it is called only for the PRIMARY cpu. all
201 * other cpus are called via a broadcast IPI
202 * calibration interval is 1 second - we call
203 * the calibration code only every hz calls
204 */
205 static void
206 cc_calibrate(struct timecounter *tc)
207 {
208 static int calls;
209 struct cpu_info *ci;
210
211 KASSERT(kpreempt_disabled());
212
213 /*
214 * XXX: for high interrupt frequency
215 * support: ++calls < hz / tc_tick
216 */
217 if (++calls < hz)
218 return;
219
220 calls = 0;
221 ci = curcpu();
222 /* pick up reference ticks */
223 cc_cal_val = cpu_counter32();
224
225 #if defined(MULTIPROCESSOR)
226 cc_calibrate_mp(ci);
227 #endif
228 cc_calibrate_cpu(ci);
229 }
230
231 /*
232 * This routine is called about once per second directly by the master
233 * processor and via an interprocessor interrupt for other processors.
234 * It determines the CC frequency of each processor relative to the
235 * master clock and the time this determination is made. These values
236 * are used by cc_get_timecount() to interpolate the ticks between
237 * timer interrupts. Note that we assume the kernel variables have
238 * been zeroed early in life.
239 */
240 void
241 cc_calibrate_cpu(struct cpu_info *ci)
242 {
243 u_int gen;
244 int64_t val;
245 int64_t delta, denom;
246 int s;
247 #ifdef TIMECOUNTER_DEBUG
248 int64_t factor, old_factor;
249 #endif
250 val = cc_cal_val;
251
252 s = splhigh();
253 /* create next generation number */
254 gen = ci->ci_cc.cc_gen;
255 gen++;
256 if (gen == 0)
257 gen++;
258
259 /* update in progress */
260 ci->ci_cc.cc_gen = 0;
261
262 denom = ci->ci_cc.cc_cc;
263 ci->ci_cc.cc_cc = cpu_counter32();
264
265 if (ci->ci_cc.cc_denom == 0) {
266 /*
267 * This is our first time here on this CPU. Just
268 * start with reasonable initial values.
269 */
270 ci->ci_cc.cc_val = val;
271 ci->ci_cc.cc_denom = cpu_frequency(ci);
272 if (ci->ci_cc.cc_denom == 0)
273 ci->ci_cc.cc_denom = cc_timecounter.tc_frequency;;
274 ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
275 ci->ci_cc.cc_gen = gen;
276 splx(s);
277 return;
278 }
279
280 #ifdef TIMECOUNTER_DEBUG
281 old_factor = (ci->ci_cc.cc_delta * 1000 ) / ci->ci_cc.cc_denom;
282 #endif
283
284 /* local ticks per period */
285 denom = ci->ci_cc.cc_cc - denom;
286 if (denom < 0)
287 denom += 0x100000000LL;
288
289 ci->ci_cc.cc_denom = denom;
290
291 /* reference ticks per period */
292 delta = val - ci->ci_cc.cc_val;
293 if (delta < 0)
294 delta += 0x100000000LL;
295
296 ci->ci_cc.cc_val = val;
297 ci->ci_cc.cc_delta = delta;
298
299 /* publish new generation number */
300 ci->ci_cc.cc_gen = gen;
301 splx(s);
302
303 #ifdef TIMECOUNTER_DEBUG
304 factor = (delta * 1000) / denom - old_factor;
305 if (factor < 0)
306 factor = -factor;
307
308 if (factor > old_factor / 10)
309 printf("cc_calibrate_cpu[%u]: 10%% exceeded - delta %"
310 PRId64 ", denom %" PRId64 ", factor %" PRId64
311 ", old factor %" PRId64"\n", ci->ci_index,
312 delta, denom, (delta * 1000) / denom, old_factor);
313 #endif /* TIMECOUNTER_DEBUG */
314 }
315