1*263f549eSPatrick Mooney /* 2*263f549eSPatrick Mooney * This file and its contents are supplied under the terms of the 3*263f549eSPatrick Mooney * Common Development and Distribution License ("CDDL"), version 1.0. 4*263f549eSPatrick Mooney * You may only use this file in accordance with the terms of version 5*263f549eSPatrick Mooney * 1.0 of the CDDL. 6*263f549eSPatrick Mooney * 7*263f549eSPatrick Mooney * A full copy of the text of the CDDL should have accompanied this 8*263f549eSPatrick Mooney * source. A copy of the CDDL is also available via the Internet at 9*263f549eSPatrick Mooney * http://www.illumos.org/license/CDDL. 10*263f549eSPatrick Mooney */ 11*263f549eSPatrick Mooney 12*263f549eSPatrick Mooney /* 13*263f549eSPatrick Mooney * Copyright 2016 Joyent, Inc. 14*263f549eSPatrick Mooney */ 15*263f549eSPatrick Mooney 16*263f549eSPatrick Mooney #include <sys/comm_page.h> 17*263f549eSPatrick Mooney #include <sys/tsc.h> 18*263f549eSPatrick Mooney 19*263f549eSPatrick Mooney 20*263f549eSPatrick Mooney /* 21*263f549eSPatrick Mooney * Interrogate if querying the clock via the comm page is possible. 22*263f549eSPatrick Mooney */ 23*263f549eSPatrick Mooney int 24*263f549eSPatrick Mooney __cp_can_gettime(comm_page_t *cp) 25*263f549eSPatrick Mooney { 26*263f549eSPatrick Mooney switch (cp->cp_tsc_type) { 27*263f549eSPatrick Mooney case TSC_TSCP: 28*263f549eSPatrick Mooney case TSC_RDTSC_MFENCE: 29*263f549eSPatrick Mooney case TSC_RDTSC_LFENCE: 30*263f549eSPatrick Mooney case TSC_RDTSC_CPUID: 31*263f549eSPatrick Mooney return (1); 32*263f549eSPatrick Mooney default: 33*263f549eSPatrick Mooney break; 34*263f549eSPatrick Mooney } 35*263f549eSPatrick Mooney return (0); 36*263f549eSPatrick Mooney } 37*263f549eSPatrick Mooney 38*263f549eSPatrick Mooney #ifdef __amd64 39*263f549eSPatrick Mooney 40*263f549eSPatrick Mooney /* 41*263f549eSPatrick Mooney * The functions used for calculating time (both monotonic and wall-clock) are 42*263f549eSPatrick Mooney * implemented in assembly on amd64. This is primarily for stack conservation. 43*263f549eSPatrick Mooney */ 44*263f549eSPatrick Mooney 45*263f549eSPatrick Mooney #else /* i386 below */ 46*263f549eSPatrick Mooney 47*263f549eSPatrick Mooney /* 48*263f549eSPatrick Mooney * ASM-defined functions. 49*263f549eSPatrick Mooney */ 50*263f549eSPatrick Mooney extern hrtime_t __cp_tsc_read(comm_page_t *); 51*263f549eSPatrick Mooney 52*263f549eSPatrick Mooney /* 53*263f549eSPatrick Mooney * These are cloned from TSC and time related code in the kernel. The should 54*263f549eSPatrick Mooney * be kept in sync in the case that the source values are changed. 55*263f549eSPatrick Mooney */ 56*263f549eSPatrick Mooney #define NSEC_SHIFT 5 57*263f549eSPatrick Mooney #define ADJ_SHIFT 4 58*263f549eSPatrick Mooney #define NANOSEC 1000000000LL 59*263f549eSPatrick Mooney 60*263f549eSPatrick Mooney #define TSC_CONVERT_AND_ADD(tsc, hrt, scale) do { \ 61*263f549eSPatrick Mooney uint32_t *_l = (uint32_t *)&(tsc); \ 62*263f549eSPatrick Mooney uint64_t sc = (uint32_t)(scale); \ 63*263f549eSPatrick Mooney (hrt) += (uint64_t)(_l[1] * sc) << NSEC_SHIFT; \ 64*263f549eSPatrick Mooney (hrt) += (uint64_t)(_l[0] * sc) >> (32 - NSEC_SHIFT); \ 65*263f549eSPatrick Mooney } while (0) 66*263f549eSPatrick Mooney 67*263f549eSPatrick Mooney /* 68*263f549eSPatrick Mooney * Userspace version of tsc_gethrtime. 69*263f549eSPatrick Mooney * See: uts/i86pc/os/timestamp.c 70*263f549eSPatrick Mooney */ 71*263f549eSPatrick Mooney hrtime_t 72*263f549eSPatrick Mooney __cp_gethrtime(comm_page_t *cp) 73*263f549eSPatrick Mooney { 74*263f549eSPatrick Mooney uint32_t old_hres_lock; 75*263f549eSPatrick Mooney hrtime_t tsc, hrt, tsc_last; 76*263f549eSPatrick Mooney 77*263f549eSPatrick Mooney /* 78*263f549eSPatrick Mooney * Several precautions must be taken when collecting the data necessary 79*263f549eSPatrick Mooney * to perform an accurate gethrtime calculation. 80*263f549eSPatrick Mooney * 81*263f549eSPatrick Mooney * While much of the TSC state stored in the comm page is unchanging 82*263f549eSPatrick Mooney * after boot, portions of it are periodically updated during OS ticks. 83*263f549eSPatrick Mooney * Changes to hres_lock during the course of the copy indicates a 84*263f549eSPatrick Mooney * potentially inconsistent snapshot, necessitating a loop. 85*263f549eSPatrick Mooney * 86*263f549eSPatrick Mooney * Even more complicated is the handling for TSCs which require sync 87*263f549eSPatrick Mooney * offsets between different CPUs. Since userspace lacks the luxury of 88*263f549eSPatrick Mooney * disabling interrupts, a validation loop checking for CPU migrations 89*263f549eSPatrick Mooney * is used. Pathological scheduling could, in theory, "outwit" 90*263f549eSPatrick Mooney * this check. Such a possibility is considered an acceptable risk. 91*263f549eSPatrick Mooney * 92*263f549eSPatrick Mooney */ 93*263f549eSPatrick Mooney do { 94*263f549eSPatrick Mooney old_hres_lock = cp->cp_hres_lock; 95*263f549eSPatrick Mooney tsc_last = cp->cp_tsc_last; 96*263f549eSPatrick Mooney hrt = cp->cp_tsc_hrtime_base; 97*263f549eSPatrick Mooney tsc = __cp_tsc_read(cp); 98*263f549eSPatrick Mooney } while ((old_hres_lock & ~1) != cp->cp_hres_lock); 99*263f549eSPatrick Mooney 100*263f549eSPatrick Mooney if (tsc >= tsc_last) { 101*263f549eSPatrick Mooney tsc -= tsc_last; 102*263f549eSPatrick Mooney } else if (tsc >= tsc_last - (2 * cp->cp_tsc_max_delta)) { 103*263f549eSPatrick Mooney tsc = 0; 104*263f549eSPatrick Mooney } else if (tsc > cp->cp_tsc_resume_cap) { 105*263f549eSPatrick Mooney tsc = cp->cp_tsc_resume_cap; 106*263f549eSPatrick Mooney } 107*263f549eSPatrick Mooney TSC_CONVERT_AND_ADD(tsc, hrt, cp->cp_nsec_scale); 108*263f549eSPatrick Mooney 109*263f549eSPatrick Mooney return (hrt); 110*263f549eSPatrick Mooney } 111*263f549eSPatrick Mooney 112*263f549eSPatrick Mooney /* 113*263f549eSPatrick Mooney * Userspace version of pc_gethrestime. 114*263f549eSPatrick Mooney * See: uts/i86pc/os/machdep.c 115*263f549eSPatrick Mooney */ 116*263f549eSPatrick Mooney int 117*263f549eSPatrick Mooney __cp_clock_gettime_realtime(comm_page_t *cp, timespec_t *tsp) 118*263f549eSPatrick Mooney { 119*263f549eSPatrick Mooney int lock_prev, nslt; 120*263f549eSPatrick Mooney timespec_t now; 121*263f549eSPatrick Mooney int64_t hres_adj; 122*263f549eSPatrick Mooney 123*263f549eSPatrick Mooney loop: 124*263f549eSPatrick Mooney lock_prev = cp->cp_hres_lock; 125*263f549eSPatrick Mooney now.tv_sec = cp->cp_hrestime[0]; 126*263f549eSPatrick Mooney now.tv_nsec = cp->cp_hrestime[1]; 127*263f549eSPatrick Mooney nslt = (int)(__cp_gethrtime(cp) - cp->cp_hres_last_tick); 128*263f549eSPatrick Mooney hres_adj = cp->cp_hrestime_adj; 129*263f549eSPatrick Mooney if (nslt < 0) { 130*263f549eSPatrick Mooney /* 131*263f549eSPatrick Mooney * Tick came between sampling hrtime and hres_last_tick; 132*263f549eSPatrick Mooney */ 133*263f549eSPatrick Mooney goto loop; 134*263f549eSPatrick Mooney } 135*263f549eSPatrick Mooney now.tv_nsec += nslt; 136*263f549eSPatrick Mooney 137*263f549eSPatrick Mooney /* 138*263f549eSPatrick Mooney * Apply hres_adj skew, if needed. 139*263f549eSPatrick Mooney */ 140*263f549eSPatrick Mooney if (hres_adj > 0) { 141*263f549eSPatrick Mooney nslt = (nslt >> ADJ_SHIFT); 142*263f549eSPatrick Mooney if (nslt > hres_adj) 143*263f549eSPatrick Mooney nslt = (int)hres_adj; 144*263f549eSPatrick Mooney now.tv_nsec += nslt; 145*263f549eSPatrick Mooney } else if (hres_adj < 0) { 146*263f549eSPatrick Mooney nslt = -(nslt >> ADJ_SHIFT); 147*263f549eSPatrick Mooney if (nslt < hres_adj) 148*263f549eSPatrick Mooney nslt = (int)hres_adj; 149*263f549eSPatrick Mooney now.tv_nsec += nslt; 150*263f549eSPatrick Mooney } 151*263f549eSPatrick Mooney 152*263f549eSPatrick Mooney /* 153*263f549eSPatrick Mooney * Rope in tv_nsec from any excessive adjustments. 154*263f549eSPatrick Mooney */ 155*263f549eSPatrick Mooney while ((unsigned long)now.tv_nsec >= NANOSEC) { 156*263f549eSPatrick Mooney now.tv_nsec -= NANOSEC; 157*263f549eSPatrick Mooney now.tv_sec++; 158*263f549eSPatrick Mooney } 159*263f549eSPatrick Mooney 160*263f549eSPatrick Mooney if ((cp->cp_hres_lock & ~1) != lock_prev) 161*263f549eSPatrick Mooney goto loop; 162*263f549eSPatrick Mooney 163*263f549eSPatrick Mooney *tsp = now; 164*263f549eSPatrick Mooney return (0); 165*263f549eSPatrick Mooney } 166*263f549eSPatrick Mooney 167*263f549eSPatrick Mooney /* 168*263f549eSPatrick Mooney * The __cp_clock_gettime_monotonic function expects that hrt2ts be present 169*263f549eSPatrick Mooney * when the code is finally linked. 170*263f549eSPatrick Mooney * (The amd64 version has no such requirement.) 171*263f549eSPatrick Mooney */ 172*263f549eSPatrick Mooney extern void hrt2ts(hrtime_t, timespec_t *); 173*263f549eSPatrick Mooney 174*263f549eSPatrick Mooney int 175*263f549eSPatrick Mooney __cp_clock_gettime_monotonic(comm_page_t *cp, timespec_t *tsp) 176*263f549eSPatrick Mooney { 177*263f549eSPatrick Mooney hrtime_t hrt; 178*263f549eSPatrick Mooney 179*263f549eSPatrick Mooney hrt = __cp_gethrtime(cp); 180*263f549eSPatrick Mooney hrt2ts(hrt, tsp); 181*263f549eSPatrick Mooney return (0); 182*263f549eSPatrick Mooney } 183*263f549eSPatrick Mooney 184*263f549eSPatrick Mooney #endif /* __amd64 */ 185