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