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