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