17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 23*d90554ebSdmick * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <sys/types.h> 307c478bd9Sstevel@tonic-gate #include <sys/param.h> 317c478bd9Sstevel@tonic-gate #include <sys/systm.h> 327c478bd9Sstevel@tonic-gate #include <sys/disp.h> 337c478bd9Sstevel@tonic-gate #include <sys/var.h> 347c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 357c478bd9Sstevel@tonic-gate #include <sys/debug.h> 367c478bd9Sstevel@tonic-gate #include <sys/x86_archext.h> 377c478bd9Sstevel@tonic-gate #include <sys/archsystm.h> 387c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 397c478bd9Sstevel@tonic-gate #include <sys/psm_defs.h> 407c478bd9Sstevel@tonic-gate #include <sys/clock.h> 417c478bd9Sstevel@tonic-gate #include <sys/atomic.h> 427c478bd9Sstevel@tonic-gate #include <sys/lockstat.h> 437c478bd9Sstevel@tonic-gate #include <sys/smp_impldefs.h> 447c478bd9Sstevel@tonic-gate #include <sys/dtrace.h> 457c478bd9Sstevel@tonic-gate #include <sys/time.h> 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate /* 487c478bd9Sstevel@tonic-gate * Using the Pentium's TSC register for gethrtime() 497c478bd9Sstevel@tonic-gate * ------------------------------------------------ 507c478bd9Sstevel@tonic-gate * 517c478bd9Sstevel@tonic-gate * The Pentium family, like many chip architectures, has a high-resolution 527c478bd9Sstevel@tonic-gate * timestamp counter ("TSC") which increments once per CPU cycle. The contents 537c478bd9Sstevel@tonic-gate * of the timestamp counter are read with the RDTSC instruction. 547c478bd9Sstevel@tonic-gate * 557c478bd9Sstevel@tonic-gate * As with its UltraSPARC equivalent (the %tick register), TSC's cycle count 567c478bd9Sstevel@tonic-gate * must be translated into nanoseconds in order to implement gethrtime(). 577c478bd9Sstevel@tonic-gate * We avoid inducing floating point operations in this conversion by 587c478bd9Sstevel@tonic-gate * implementing the same nsec_scale algorithm as that found in the sun4u 597c478bd9Sstevel@tonic-gate * platform code. The sun4u NATIVE_TIME_TO_NSEC_SCALE block comment contains 607c478bd9Sstevel@tonic-gate * a detailed description of the algorithm; the comment is not reproduced 617c478bd9Sstevel@tonic-gate * here. This implementation differs only in its value for NSEC_SHIFT: 627c478bd9Sstevel@tonic-gate * we implement an NSEC_SHIFT of 5 (instead of sun4u's 4) to allow for 637c478bd9Sstevel@tonic-gate * 60 MHz Pentiums. 647c478bd9Sstevel@tonic-gate * 657c478bd9Sstevel@tonic-gate * While TSC and %tick are both cycle counting registers, TSC's functionality 667c478bd9Sstevel@tonic-gate * falls short in several critical ways: 677c478bd9Sstevel@tonic-gate * 687c478bd9Sstevel@tonic-gate * (a) TSCs on different CPUs are not guaranteed to be in sync. While in 697c478bd9Sstevel@tonic-gate * practice they often _are_ in sync, this isn't guaranteed by the 707c478bd9Sstevel@tonic-gate * architecture. 717c478bd9Sstevel@tonic-gate * 727c478bd9Sstevel@tonic-gate * (b) The TSC cannot be reliably set to an arbitrary value. The architecture 737c478bd9Sstevel@tonic-gate * only supports writing the low 32-bits of TSC, making it impractical 747c478bd9Sstevel@tonic-gate * to rewrite. 757c478bd9Sstevel@tonic-gate * 767c478bd9Sstevel@tonic-gate * (c) The architecture doesn't have the capacity to interrupt based on 777c478bd9Sstevel@tonic-gate * arbitrary values of TSC; there is no TICK_CMPR equivalent. 787c478bd9Sstevel@tonic-gate * 797c478bd9Sstevel@tonic-gate * Together, (a) and (b) imply that software must track the skew between 807c478bd9Sstevel@tonic-gate * TSCs and account for it (it is assumed that while there may exist skew, 817c478bd9Sstevel@tonic-gate * there does not exist drift). To determine the skew between CPUs, we 827c478bd9Sstevel@tonic-gate * have newly onlined CPUs call tsc_sync_slave(), while the CPU performing 837c478bd9Sstevel@tonic-gate * the online operation calls tsc_sync_master(). Once both CPUs are ready, 847c478bd9Sstevel@tonic-gate * the master sets a shared flag, and each reads its TSC register. To reduce 857c478bd9Sstevel@tonic-gate * bias, we then wait until both CPUs are ready again, but this time the 867c478bd9Sstevel@tonic-gate * slave sets the shared flag, and each reads its TSC register again. The 877c478bd9Sstevel@tonic-gate * master compares the average of the two sample values, and, if observable 887c478bd9Sstevel@tonic-gate * skew is found, changes the gethrtimef function pointer to point to a 897c478bd9Sstevel@tonic-gate * gethrtime() implementation which will take the discovered skew into 907c478bd9Sstevel@tonic-gate * consideration. 917c478bd9Sstevel@tonic-gate * 927c478bd9Sstevel@tonic-gate * In the absence of time-of-day clock adjustments, gethrtime() must stay in 937c478bd9Sstevel@tonic-gate * sync with gettimeofday(). This is problematic; given (c), the software 947c478bd9Sstevel@tonic-gate * cannot drive its time-of-day source from TSC, and yet they must somehow be 957c478bd9Sstevel@tonic-gate * kept in sync. We implement this by having a routine, tsc_tick(), which 967c478bd9Sstevel@tonic-gate * is called once per second from the interrupt which drives time-of-day. 977c478bd9Sstevel@tonic-gate * tsc_tick() recalculates nsec_scale based on the number of the CPU cycles 987c478bd9Sstevel@tonic-gate * since boot versus the number of seconds since boot. This algorithm 997c478bd9Sstevel@tonic-gate * becomes more accurate over time and converges quickly; the error in 1007c478bd9Sstevel@tonic-gate * nsec_scale is typically under 1 ppm less than 10 seconds after boot, and 1017c478bd9Sstevel@tonic-gate * is less than 100 ppb 1 minute after boot. 1027c478bd9Sstevel@tonic-gate * 1037c478bd9Sstevel@tonic-gate * Note that the hrtime base for gethrtime, tsc_hrtime_base, is modified 1047c478bd9Sstevel@tonic-gate * atomically with nsec_scale under CLOCK_LOCK. This assures that time 1057c478bd9Sstevel@tonic-gate * monotonically increases. 1067c478bd9Sstevel@tonic-gate */ 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate #define NSEC_SHIFT 5 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate static uint_t nsec_scale; 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate /* 1137c478bd9Sstevel@tonic-gate * These two variables used to be grouped together inside of a structure that 1147c478bd9Sstevel@tonic-gate * lived on a single cache line. A regression (bug ID 4623398) caused the 1157c478bd9Sstevel@tonic-gate * compiler to emit code that "optimized" away the while-loops below. The 1167c478bd9Sstevel@tonic-gate * result was that no synchronization between the onlining and onlined CPUs 1177c478bd9Sstevel@tonic-gate * took place. 1187c478bd9Sstevel@tonic-gate */ 1197c478bd9Sstevel@tonic-gate static volatile int tsc_ready; 1207c478bd9Sstevel@tonic-gate static volatile int tsc_sync_go; 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate /* 1237c478bd9Sstevel@tonic-gate * Used as indices into the tsc_sync_snaps[] array. 1247c478bd9Sstevel@tonic-gate */ 1257c478bd9Sstevel@tonic-gate #define TSC_MASTER 0 1267c478bd9Sstevel@tonic-gate #define TSC_SLAVE 1 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate /* 1297c478bd9Sstevel@tonic-gate * Used in the tsc_master_sync()/tsc_slave_sync() rendezvous. 1307c478bd9Sstevel@tonic-gate */ 1317c478bd9Sstevel@tonic-gate #define TSC_SYNC_STOP 1 1327c478bd9Sstevel@tonic-gate #define TSC_SYNC_GO 2 1337c478bd9Sstevel@tonic-gate #define TSC_SYNC_AGAIN 3 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate /* 1367c478bd9Sstevel@tonic-gate * XX64 Is the faster way to do this with a 64-bit ABI? 1377c478bd9Sstevel@tonic-gate */ 1387c478bd9Sstevel@tonic-gate #define TSC_CONVERT_AND_ADD(tsc, hrt, scale) { \ 1397c478bd9Sstevel@tonic-gate unsigned int *_l = (unsigned int *)&(tsc); \ 1407c478bd9Sstevel@tonic-gate (hrt) += mul32(_l[1], scale) << NSEC_SHIFT; \ 1417c478bd9Sstevel@tonic-gate (hrt) += mul32(_l[0], scale) >> (32 - NSEC_SHIFT); \ 1427c478bd9Sstevel@tonic-gate } 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate #define TSC_CONVERT(tsc, hrt, scale) { \ 1457c478bd9Sstevel@tonic-gate unsigned int *_l = (unsigned int *)&(tsc); \ 1467c478bd9Sstevel@tonic-gate (hrt) = mul32(_l[1], scale) << NSEC_SHIFT; \ 1477c478bd9Sstevel@tonic-gate (hrt) += mul32(_l[0], scale) >> (32 - NSEC_SHIFT); \ 1487c478bd9Sstevel@tonic-gate } 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate static int tsc_max_delta; 1537c478bd9Sstevel@tonic-gate static hrtime_t tsc_sync_snaps[2]; 1547c478bd9Sstevel@tonic-gate static hrtime_t tsc_sync_delta[NCPU]; 1557c478bd9Sstevel@tonic-gate static hrtime_t tsc_sync_tick_delta[NCPU]; 1567c478bd9Sstevel@tonic-gate static hrtime_t tsc_last = 0; 1577c478bd9Sstevel@tonic-gate static hrtime_t tsc_last_jumped = 0; 1587c478bd9Sstevel@tonic-gate static hrtime_t tsc_hrtime_base = 0; 1597c478bd9Sstevel@tonic-gate static int tsc_jumped = 0; 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate static hrtime_t shadow_tsc_hrtime_base; 1627c478bd9Sstevel@tonic-gate static hrtime_t shadow_tsc_last; 1637c478bd9Sstevel@tonic-gate static uint_t shadow_nsec_scale; 1647c478bd9Sstevel@tonic-gate static uint32_t shadow_hres_lock; 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate /* 1677c478bd9Sstevel@tonic-gate * Called by the master after the sync operation is complete. If the 1687c478bd9Sstevel@tonic-gate * slave is discovered to lag, gethrtimef will be changed to point to 1697c478bd9Sstevel@tonic-gate * tsc_gethrtime_delta(). 1707c478bd9Sstevel@tonic-gate */ 1717c478bd9Sstevel@tonic-gate static void 1727c478bd9Sstevel@tonic-gate tsc_digest(processorid_t target) 1737c478bd9Sstevel@tonic-gate { 1747c478bd9Sstevel@tonic-gate hrtime_t tdelta, hdelta = 0; 1757c478bd9Sstevel@tonic-gate int max = tsc_max_delta; 1767c478bd9Sstevel@tonic-gate processorid_t source = CPU->cpu_id; 1777c478bd9Sstevel@tonic-gate int update; 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate update = tsc_sync_delta[source] != 0 || 1807c478bd9Sstevel@tonic-gate gethrtimef == tsc_gethrtime_delta; 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate /* 1837c478bd9Sstevel@tonic-gate * We divide by 2 since each of the data points is the sum of two TSC 1847c478bd9Sstevel@tonic-gate * reads; this takes the average of the two. 1857c478bd9Sstevel@tonic-gate */ 1867c478bd9Sstevel@tonic-gate tdelta = (tsc_sync_snaps[TSC_SLAVE] - tsc_sync_snaps[TSC_MASTER]) / 2; 1877c478bd9Sstevel@tonic-gate if ((tdelta > max) || ((tdelta >= 0) && update)) { 1887c478bd9Sstevel@tonic-gate TSC_CONVERT_AND_ADD(tdelta, hdelta, nsec_scale); 1897c478bd9Sstevel@tonic-gate tsc_sync_delta[target] = tsc_sync_delta[source] - hdelta; 1907c478bd9Sstevel@tonic-gate tsc_sync_tick_delta[target] = -tdelta; 1917c478bd9Sstevel@tonic-gate gethrtimef = tsc_gethrtime_delta; 1927c478bd9Sstevel@tonic-gate gethrtimeunscaledf = tsc_gethrtimeunscaled_delta; 1937c478bd9Sstevel@tonic-gate return; 1947c478bd9Sstevel@tonic-gate } 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate tdelta = -tdelta; 1977c478bd9Sstevel@tonic-gate if ((tdelta > max) || update) { 1987c478bd9Sstevel@tonic-gate TSC_CONVERT_AND_ADD(tdelta, hdelta, nsec_scale); 1997c478bd9Sstevel@tonic-gate tsc_sync_delta[target] = tsc_sync_delta[source] + hdelta; 2007c478bd9Sstevel@tonic-gate tsc_sync_tick_delta[target] = tdelta; 2017c478bd9Sstevel@tonic-gate gethrtimef = tsc_gethrtime_delta; 2027c478bd9Sstevel@tonic-gate gethrtimeunscaledf = tsc_gethrtimeunscaled_delta; 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate } 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate /* 2087c478bd9Sstevel@tonic-gate * Called by a CPU which has just performed an online operation on another 2097c478bd9Sstevel@tonic-gate * CPU. It is expected that the newly onlined CPU will call tsc_sync_slave(). 2107c478bd9Sstevel@tonic-gate */ 2117c478bd9Sstevel@tonic-gate void 2127c478bd9Sstevel@tonic-gate tsc_sync_master(processorid_t slave) 2137c478bd9Sstevel@tonic-gate { 2147c478bd9Sstevel@tonic-gate int flags; 2157c478bd9Sstevel@tonic-gate hrtime_t hrt; 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate ASSERT(tsc_sync_go != TSC_SYNC_GO); 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate flags = clear_int_flag(); 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate /* 2227c478bd9Sstevel@tonic-gate * Wait for the slave CPU to arrive. 2237c478bd9Sstevel@tonic-gate */ 2247c478bd9Sstevel@tonic-gate while (tsc_ready != TSC_SYNC_GO) 2257c478bd9Sstevel@tonic-gate continue; 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate /* 2287c478bd9Sstevel@tonic-gate * Tell the slave CPU to begin reading its TSC; read our own. 2297c478bd9Sstevel@tonic-gate */ 2307c478bd9Sstevel@tonic-gate tsc_sync_go = TSC_SYNC_GO; 2317c478bd9Sstevel@tonic-gate hrt = tsc_read(); 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate /* 2347c478bd9Sstevel@tonic-gate * Tell the slave that we're ready, and wait for the slave to tell us 2357c478bd9Sstevel@tonic-gate * to read our TSC again. 2367c478bd9Sstevel@tonic-gate */ 2377c478bd9Sstevel@tonic-gate tsc_ready = TSC_SYNC_AGAIN; 2387c478bd9Sstevel@tonic-gate while (tsc_sync_go != TSC_SYNC_AGAIN) 2397c478bd9Sstevel@tonic-gate continue; 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate hrt += tsc_read(); 2427c478bd9Sstevel@tonic-gate tsc_sync_snaps[TSC_MASTER] = hrt; 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate /* 2457c478bd9Sstevel@tonic-gate * Wait for the slave to finish reading its TSC. 2467c478bd9Sstevel@tonic-gate */ 2477c478bd9Sstevel@tonic-gate while (tsc_ready != TSC_SYNC_STOP) 2487c478bd9Sstevel@tonic-gate continue; 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate /* 2517c478bd9Sstevel@tonic-gate * At this point, both CPUs have performed their tsc_read() calls. 2527c478bd9Sstevel@tonic-gate * We'll digest it now before letting the slave CPU return. 2537c478bd9Sstevel@tonic-gate */ 2547c478bd9Sstevel@tonic-gate tsc_digest(slave); 2557c478bd9Sstevel@tonic-gate tsc_sync_go = TSC_SYNC_STOP; 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate restore_int_flag(flags); 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate /* 2617c478bd9Sstevel@tonic-gate * Called by a CPU which has just been onlined. It is expected that the CPU 2627c478bd9Sstevel@tonic-gate * performing the online operation will call tsc_sync_master(). 2637c478bd9Sstevel@tonic-gate */ 2647c478bd9Sstevel@tonic-gate void 2657c478bd9Sstevel@tonic-gate tsc_sync_slave(void) 2667c478bd9Sstevel@tonic-gate { 2677c478bd9Sstevel@tonic-gate int flags; 2687c478bd9Sstevel@tonic-gate hrtime_t hrt; 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate ASSERT(tsc_sync_go != TSC_SYNC_GO); 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate flags = clear_int_flag(); 2737c478bd9Sstevel@tonic-gate 274*d90554ebSdmick /* to test tsc_gethrtime_delta, add wrmsr(REG_TSC, 0) here */ 275*d90554ebSdmick 2767c478bd9Sstevel@tonic-gate /* 2777c478bd9Sstevel@tonic-gate * Tell the master CPU that we're ready, and wait for the master to 2787c478bd9Sstevel@tonic-gate * tell us to begin reading our TSC. 2797c478bd9Sstevel@tonic-gate */ 2807c478bd9Sstevel@tonic-gate tsc_ready = TSC_SYNC_GO; 2817c478bd9Sstevel@tonic-gate while (tsc_sync_go != TSC_SYNC_GO) 2827c478bd9Sstevel@tonic-gate continue; 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate hrt = tsc_read(); 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate /* 2877c478bd9Sstevel@tonic-gate * Wait for the master CPU to be ready to read its TSC again. 2887c478bd9Sstevel@tonic-gate */ 2897c478bd9Sstevel@tonic-gate while (tsc_ready != TSC_SYNC_AGAIN) 2907c478bd9Sstevel@tonic-gate continue; 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate /* 2937c478bd9Sstevel@tonic-gate * Tell the master CPU to read its TSC again; read ours again. 2947c478bd9Sstevel@tonic-gate */ 2957c478bd9Sstevel@tonic-gate tsc_sync_go = TSC_SYNC_AGAIN; 2967c478bd9Sstevel@tonic-gate 2977c478bd9Sstevel@tonic-gate hrt += tsc_read(); 2987c478bd9Sstevel@tonic-gate tsc_sync_snaps[TSC_SLAVE] = hrt; 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate /* 3017c478bd9Sstevel@tonic-gate * Tell the master that we're done, and wait to be dismissed. 3027c478bd9Sstevel@tonic-gate */ 3037c478bd9Sstevel@tonic-gate tsc_ready = TSC_SYNC_STOP; 3047c478bd9Sstevel@tonic-gate while (tsc_sync_go != TSC_SYNC_STOP) 3057c478bd9Sstevel@tonic-gate continue; 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate restore_int_flag(flags); 3087c478bd9Sstevel@tonic-gate } 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate void 3117c478bd9Sstevel@tonic-gate tsc_hrtimeinit(uint64_t cpu_freq_hz) 3127c478bd9Sstevel@tonic-gate { 3137c478bd9Sstevel@tonic-gate longlong_t tsc; 3147c478bd9Sstevel@tonic-gate int flags; 3157c478bd9Sstevel@tonic-gate 3167c478bd9Sstevel@tonic-gate /* 3177c478bd9Sstevel@tonic-gate * cpu_freq_hz is the measured cpu frequency in hertz 3187c478bd9Sstevel@tonic-gate */ 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate /* 3217c478bd9Sstevel@tonic-gate * We can't accommodate CPUs slower than 31.25 MHz. 3227c478bd9Sstevel@tonic-gate */ 3237c478bd9Sstevel@tonic-gate ASSERT(cpu_freq_hz > NANOSEC / (1 << NSEC_SHIFT)); 3247c478bd9Sstevel@tonic-gate nsec_scale = 3257c478bd9Sstevel@tonic-gate (uint_t) 3267c478bd9Sstevel@tonic-gate (((uint64_t)NANOSEC << (32 - NSEC_SHIFT)) / cpu_freq_hz); 3277c478bd9Sstevel@tonic-gate 3287c478bd9Sstevel@tonic-gate flags = clear_int_flag(); 3297c478bd9Sstevel@tonic-gate tsc = tsc_read(); 3307c478bd9Sstevel@tonic-gate (void) tsc_gethrtime(); 3317c478bd9Sstevel@tonic-gate tsc_max_delta = tsc_read() - tsc; 3327c478bd9Sstevel@tonic-gate restore_int_flag(flags); 3337c478bd9Sstevel@tonic-gate } 3347c478bd9Sstevel@tonic-gate 3357c478bd9Sstevel@tonic-gate /* 336*d90554ebSdmick * Called once per second on some CPU from the cyclic subsystem's 337*d90554ebSdmick * CY_HIGH_LEVEL interrupt. (no longer CPU0-only) 3387c478bd9Sstevel@tonic-gate */ 3397c478bd9Sstevel@tonic-gate void 3407c478bd9Sstevel@tonic-gate tsc_tick(void) 3417c478bd9Sstevel@tonic-gate { 3427c478bd9Sstevel@tonic-gate hrtime_t now, delta; 3437c478bd9Sstevel@tonic-gate ushort_t spl; 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate /* 3467c478bd9Sstevel@tonic-gate * Before we set the new variables, we set the shadow values. This 3477c478bd9Sstevel@tonic-gate * allows for lock free operation in dtrace_gethrtime(). 3487c478bd9Sstevel@tonic-gate */ 3497c478bd9Sstevel@tonic-gate lock_set_spl((lock_t *)&shadow_hres_lock + HRES_LOCK_OFFSET, 3507c478bd9Sstevel@tonic-gate ipltospl(CBE_HIGH_PIL), &spl); 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate shadow_tsc_hrtime_base = tsc_hrtime_base; 3537c478bd9Sstevel@tonic-gate shadow_tsc_last = tsc_last; 3547c478bd9Sstevel@tonic-gate shadow_nsec_scale = nsec_scale; 3557c478bd9Sstevel@tonic-gate 3567c478bd9Sstevel@tonic-gate shadow_hres_lock++; 3577c478bd9Sstevel@tonic-gate splx(spl); 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate CLOCK_LOCK(&spl); 3607c478bd9Sstevel@tonic-gate 3617c478bd9Sstevel@tonic-gate now = tsc_read(); 3627c478bd9Sstevel@tonic-gate 363*d90554ebSdmick if (gethrtimef == tsc_gethrtime_delta) 364*d90554ebSdmick now += tsc_sync_tick_delta[CPU->cpu_id]; 365*d90554ebSdmick 3667c478bd9Sstevel@tonic-gate if (now < tsc_last) { 3677c478bd9Sstevel@tonic-gate /* 3687c478bd9Sstevel@tonic-gate * The TSC has just jumped into the past. We assume that 3697c478bd9Sstevel@tonic-gate * this is due to a suspend/resume cycle, and we're going 3707c478bd9Sstevel@tonic-gate * to use the _current_ value of TSC as the delta. This 3717c478bd9Sstevel@tonic-gate * will keep tsc_hrtime_base correct. We're also going to 3727c478bd9Sstevel@tonic-gate * assume that rate of tsc does not change after a suspend 3737c478bd9Sstevel@tonic-gate * resume (i.e nsec_scale remains the same). 3747c478bd9Sstevel@tonic-gate */ 3757c478bd9Sstevel@tonic-gate delta = now; 3767c478bd9Sstevel@tonic-gate tsc_last_jumped += tsc_last; 3777c478bd9Sstevel@tonic-gate tsc_jumped = 1; 3787c478bd9Sstevel@tonic-gate } else { 3797c478bd9Sstevel@tonic-gate /* 3807c478bd9Sstevel@tonic-gate * Determine the number of TSC ticks since the last clock 3817c478bd9Sstevel@tonic-gate * tick, and add that to the hrtime base. 3827c478bd9Sstevel@tonic-gate */ 3837c478bd9Sstevel@tonic-gate delta = now - tsc_last; 3847c478bd9Sstevel@tonic-gate } 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate TSC_CONVERT_AND_ADD(delta, tsc_hrtime_base, nsec_scale); 3877c478bd9Sstevel@tonic-gate tsc_last = now; 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate CLOCK_UNLOCK(spl); 3907c478bd9Sstevel@tonic-gate } 3917c478bd9Sstevel@tonic-gate 3927c478bd9Sstevel@tonic-gate hrtime_t 3937c478bd9Sstevel@tonic-gate tsc_gethrtime(void) 3947c478bd9Sstevel@tonic-gate { 3957c478bd9Sstevel@tonic-gate uint32_t old_hres_lock; 3967c478bd9Sstevel@tonic-gate hrtime_t tsc, hrt; 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate do { 3997c478bd9Sstevel@tonic-gate old_hres_lock = hres_lock; 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate if ((tsc = tsc_read()) >= tsc_last) { 4027c478bd9Sstevel@tonic-gate /* 4037c478bd9Sstevel@tonic-gate * It would seem to be obvious that this is true 4047c478bd9Sstevel@tonic-gate * (that is, the past is less than the present), 4057c478bd9Sstevel@tonic-gate * but it isn't true in the presence of suspend/resume 4067c478bd9Sstevel@tonic-gate * cycles. If we manage to call gethrtime() 4077c478bd9Sstevel@tonic-gate * after a resume, but before the first call to 4087c478bd9Sstevel@tonic-gate * tsc_tick(), we will see the jump. In this case, 4097c478bd9Sstevel@tonic-gate * we will simply use the value in TSC as the delta. 4107c478bd9Sstevel@tonic-gate */ 4117c478bd9Sstevel@tonic-gate tsc -= tsc_last; 4127c478bd9Sstevel@tonic-gate } else if (tsc >= tsc_last - 2*tsc_max_delta) { 4137c478bd9Sstevel@tonic-gate /* 4147c478bd9Sstevel@tonic-gate * There is a chance that tsc_tick() has just run on 4157c478bd9Sstevel@tonic-gate * another CPU, and we have drifted just enough so that 4167c478bd9Sstevel@tonic-gate * we appear behind tsc_last. In this case, force the 4177c478bd9Sstevel@tonic-gate * delta to be zero. 4187c478bd9Sstevel@tonic-gate */ 4197c478bd9Sstevel@tonic-gate tsc = 0; 4207c478bd9Sstevel@tonic-gate } 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate hrt = tsc_hrtime_base; 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate TSC_CONVERT_AND_ADD(tsc, hrt, nsec_scale); 4257c478bd9Sstevel@tonic-gate } while ((old_hres_lock & ~1) != hres_lock); 4267c478bd9Sstevel@tonic-gate 4277c478bd9Sstevel@tonic-gate return (hrt); 4287c478bd9Sstevel@tonic-gate } 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate /* 4317c478bd9Sstevel@tonic-gate * This is similar to the above, but it cannot actually spin on hres_lock. 4327c478bd9Sstevel@tonic-gate * As a result, it caches all of the variables it needs; if the variables 4337c478bd9Sstevel@tonic-gate * don't change, it's done. 4347c478bd9Sstevel@tonic-gate */ 4357c478bd9Sstevel@tonic-gate hrtime_t 4367c478bd9Sstevel@tonic-gate dtrace_gethrtime(void) 4377c478bd9Sstevel@tonic-gate { 4387c478bd9Sstevel@tonic-gate uint32_t old_hres_lock; 4397c478bd9Sstevel@tonic-gate hrtime_t tsc, hrt; 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate do { 4427c478bd9Sstevel@tonic-gate old_hres_lock = hres_lock; 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate /* 4457c478bd9Sstevel@tonic-gate * See the comments in tsc_gethrtime(), above. 4467c478bd9Sstevel@tonic-gate */ 4477c478bd9Sstevel@tonic-gate if ((tsc = tsc_read()) >= tsc_last) 4487c478bd9Sstevel@tonic-gate tsc -= tsc_last; 4497c478bd9Sstevel@tonic-gate else if (tsc >= tsc_last - 2*tsc_max_delta) 4507c478bd9Sstevel@tonic-gate tsc = 0; 4517c478bd9Sstevel@tonic-gate 4527c478bd9Sstevel@tonic-gate hrt = tsc_hrtime_base; 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate TSC_CONVERT_AND_ADD(tsc, hrt, nsec_scale); 4557c478bd9Sstevel@tonic-gate 4567c478bd9Sstevel@tonic-gate if ((old_hres_lock & ~1) == hres_lock) 4577c478bd9Sstevel@tonic-gate break; 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate /* 4607c478bd9Sstevel@tonic-gate * If we're here, the clock lock is locked -- or it has been 4617c478bd9Sstevel@tonic-gate * unlocked and locked since we looked. This may be due to 4627c478bd9Sstevel@tonic-gate * tsc_tick() running on another CPU -- or it may be because 4637c478bd9Sstevel@tonic-gate * some code path has ended up in dtrace_probe() with 4647c478bd9Sstevel@tonic-gate * CLOCK_LOCK held. We'll try to determine that we're in 4657c478bd9Sstevel@tonic-gate * the former case by taking another lap if the lock has 4667c478bd9Sstevel@tonic-gate * changed since when we first looked at it. 4677c478bd9Sstevel@tonic-gate */ 4687c478bd9Sstevel@tonic-gate if (old_hres_lock != hres_lock) 4697c478bd9Sstevel@tonic-gate continue; 4707c478bd9Sstevel@tonic-gate 4717c478bd9Sstevel@tonic-gate /* 4727c478bd9Sstevel@tonic-gate * So the lock was and is locked. We'll use the old data 4737c478bd9Sstevel@tonic-gate * instead. 4747c478bd9Sstevel@tonic-gate */ 4757c478bd9Sstevel@tonic-gate old_hres_lock = shadow_hres_lock; 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate /* 4787c478bd9Sstevel@tonic-gate * See the comments in tsc_gethrtime(), above. 4797c478bd9Sstevel@tonic-gate */ 4807c478bd9Sstevel@tonic-gate if ((tsc = tsc_read()) >= shadow_tsc_last) 4817c478bd9Sstevel@tonic-gate tsc -= shadow_tsc_last; 4827c478bd9Sstevel@tonic-gate else if (tsc >= shadow_tsc_last - 2 * tsc_max_delta) 4837c478bd9Sstevel@tonic-gate tsc = 0; 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate hrt = shadow_tsc_hrtime_base; 4867c478bd9Sstevel@tonic-gate 4877c478bd9Sstevel@tonic-gate TSC_CONVERT_AND_ADD(tsc, hrt, shadow_nsec_scale); 4887c478bd9Sstevel@tonic-gate } while ((old_hres_lock & ~1) != shadow_hres_lock); 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate return (hrt); 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate hrtime_t 4947c478bd9Sstevel@tonic-gate tsc_gethrtime_delta(void) 4957c478bd9Sstevel@tonic-gate { 496*d90554ebSdmick uint32_t old_hres_lock; 497*d90554ebSdmick hrtime_t tsc, hrt; 4987c478bd9Sstevel@tonic-gate int flags; 4997c478bd9Sstevel@tonic-gate 500*d90554ebSdmick do { 501*d90554ebSdmick old_hres_lock = hres_lock; 502*d90554ebSdmick 5037c478bd9Sstevel@tonic-gate /* 504*d90554ebSdmick * We need to disable interrupts here to assure that we 505*d90554ebSdmick * don't migrate between the call to tsc_read() and 506*d90554ebSdmick * adding the CPU's TSC tick delta. Note that disabling 507*d90554ebSdmick * and reenabling preemption is forbidden here because 508*d90554ebSdmick * we may be in the middle of a fast trap. In the amd64 509*d90554ebSdmick * kernel we cannot tolerate preemption during a fast 510*d90554ebSdmick * trap. See _update_sregs(). 5117c478bd9Sstevel@tonic-gate */ 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate flags = clear_int_flag(); 514*d90554ebSdmick tsc = tsc_read() + tsc_sync_tick_delta[CPU->cpu_id]; 5157c478bd9Sstevel@tonic-gate restore_int_flag(flags); 5167c478bd9Sstevel@tonic-gate 517*d90554ebSdmick /* See comments in tsc_gethrtime() above */ 518*d90554ebSdmick 519*d90554ebSdmick if (tsc >= tsc_last) { 520*d90554ebSdmick tsc -= tsc_last; 521*d90554ebSdmick } else if (tsc >= tsc_last - 2 * tsc_max_delta) { 522*d90554ebSdmick tsc = 0; 523*d90554ebSdmick } 524*d90554ebSdmick 525*d90554ebSdmick hrt = tsc_hrtime_base; 526*d90554ebSdmick 527*d90554ebSdmick TSC_CONVERT_AND_ADD(tsc, hrt, nsec_scale); 528*d90554ebSdmick } while ((old_hres_lock & ~1) != hres_lock); 529*d90554ebSdmick 5307c478bd9Sstevel@tonic-gate return (hrt); 5317c478bd9Sstevel@tonic-gate } 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate extern uint64_t cpu_freq_hz; 5347c478bd9Sstevel@tonic-gate extern int tsc_gethrtime_enable; 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate /* 5377c478bd9Sstevel@tonic-gate * The following converts nanoseconds of highres-time to ticks 5387c478bd9Sstevel@tonic-gate */ 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate static uint64_t 5417c478bd9Sstevel@tonic-gate hrtime2tick(hrtime_t ts) 5427c478bd9Sstevel@tonic-gate { 5437c478bd9Sstevel@tonic-gate hrtime_t q = ts / NANOSEC; 5447c478bd9Sstevel@tonic-gate hrtime_t r = ts - (q * NANOSEC); 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate return (q * cpu_freq_hz + ((r * cpu_freq_hz) / NANOSEC)); 5477c478bd9Sstevel@tonic-gate } 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate /* 5507c478bd9Sstevel@tonic-gate * This is used to convert scaled high-res time from nanoseconds to 5517c478bd9Sstevel@tonic-gate * unscaled hardware ticks. (Read from hardware timestamp counter) 5527c478bd9Sstevel@tonic-gate */ 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate uint64_t 5557c478bd9Sstevel@tonic-gate unscalehrtime(hrtime_t ts) 5567c478bd9Sstevel@tonic-gate { 5577c478bd9Sstevel@tonic-gate if (tsc_gethrtime_enable) { 5587c478bd9Sstevel@tonic-gate uint64_t unscale = 0; 5597c478bd9Sstevel@tonic-gate hrtime_t rescale; 5607c478bd9Sstevel@tonic-gate hrtime_t diff = ts; 5617c478bd9Sstevel@tonic-gate 5627c478bd9Sstevel@tonic-gate while (diff > (nsec_per_tick)) { 5637c478bd9Sstevel@tonic-gate unscale += hrtime2tick(diff); 5647c478bd9Sstevel@tonic-gate rescale = unscale; 5657c478bd9Sstevel@tonic-gate scalehrtime(&rescale); 5667c478bd9Sstevel@tonic-gate diff = ts - rescale; 5677c478bd9Sstevel@tonic-gate } 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate return (unscale); 5707c478bd9Sstevel@tonic-gate } 5717c478bd9Sstevel@tonic-gate return (0); 5727c478bd9Sstevel@tonic-gate } 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate 5757c478bd9Sstevel@tonic-gate hrtime_t 5767c478bd9Sstevel@tonic-gate tsc_gethrtimeunscaled(void) 5777c478bd9Sstevel@tonic-gate { 5787c478bd9Sstevel@tonic-gate uint32_t old_hres_lock; 5797c478bd9Sstevel@tonic-gate hrtime_t tsc; 5807c478bd9Sstevel@tonic-gate 5817c478bd9Sstevel@tonic-gate do { 5827c478bd9Sstevel@tonic-gate old_hres_lock = hres_lock; 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate if ((tsc = tsc_read()) < tsc_last) { 5857c478bd9Sstevel@tonic-gate /* 5867c478bd9Sstevel@tonic-gate * see comments in tsc_gethrtime 5877c478bd9Sstevel@tonic-gate */ 5887c478bd9Sstevel@tonic-gate tsc += tsc_last_jumped; 5897c478bd9Sstevel@tonic-gate } 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate } while ((old_hres_lock & ~1) != hres_lock); 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate return (tsc); 5947c478bd9Sstevel@tonic-gate } 5957c478bd9Sstevel@tonic-gate 5967c478bd9Sstevel@tonic-gate 5977c478bd9Sstevel@tonic-gate /* Convert a tsc timestamp to nanoseconds */ 5987c478bd9Sstevel@tonic-gate void 5997c478bd9Sstevel@tonic-gate tsc_scalehrtime(hrtime_t *tsc) 6007c478bd9Sstevel@tonic-gate { 6017c478bd9Sstevel@tonic-gate hrtime_t hrt; 6027c478bd9Sstevel@tonic-gate hrtime_t mytsc; 6037c478bd9Sstevel@tonic-gate 6047c478bd9Sstevel@tonic-gate if (tsc == NULL) 6057c478bd9Sstevel@tonic-gate return; 6067c478bd9Sstevel@tonic-gate mytsc = *tsc; 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate TSC_CONVERT(mytsc, hrt, nsec_scale); 6097c478bd9Sstevel@tonic-gate *tsc = hrt; 6107c478bd9Sstevel@tonic-gate } 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate hrtime_t 6137c478bd9Sstevel@tonic-gate tsc_gethrtimeunscaled_delta(void) 6147c478bd9Sstevel@tonic-gate { 6157c478bd9Sstevel@tonic-gate hrtime_t hrt; 6167c478bd9Sstevel@tonic-gate int flags; 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate /* 6197c478bd9Sstevel@tonic-gate * Similarly to tsc_gethrtime_delta, we need to disable preemption 6207c478bd9Sstevel@tonic-gate * to prevent migration between the call to tsc_gethrtimeunscaled 6217c478bd9Sstevel@tonic-gate * and adding the CPU's hrtime delta. Note that disabling and 6227c478bd9Sstevel@tonic-gate * reenabling preemption is forbidden here because we may be in the 6237c478bd9Sstevel@tonic-gate * middle of a fast trap. In the amd64 kernel we cannot tolerate 6247c478bd9Sstevel@tonic-gate * preemption during a fast trap. See _update_sregs(). 6257c478bd9Sstevel@tonic-gate */ 6267c478bd9Sstevel@tonic-gate 6277c478bd9Sstevel@tonic-gate flags = clear_int_flag(); 6287c478bd9Sstevel@tonic-gate hrt = tsc_gethrtimeunscaled() + tsc_sync_tick_delta[CPU->cpu_id]; 6297c478bd9Sstevel@tonic-gate restore_int_flag(flags); 6307c478bd9Sstevel@tonic-gate 6317c478bd9Sstevel@tonic-gate return (hrt); 6327c478bd9Sstevel@tonic-gate } 633