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