2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright 2016 Joyent, Inc.
16 #include <sys/asm_linkage.h>
17 #include <sys/segments.h>
18 #include <sys/time_impl.h>
20 #include <cp_offsets.h>
22 #define GETCPU_GDT_OFFSET SEL_GDT(GDT_CPUID, SEL_UPL)
27 * These are cloned from TSC and time related code in the kernel. They should
28 * be kept in sync in the case that the source values are changed.
29 * See: uts/i86pc/os/timestamp.c
33 #define NANOSEC 0x3b9aca00
37 * __cp_tsc_read(comm_page_t *cp)
39 * Stack usage: 0 bytes
41 ENTRY_NP
(__cp_tsc_read
)
42 movl CP_TSC_TYPE
(%rdi
), %esi
43 movl CP_TSC_NCPU
(%rdi
), %r8d
44 leaq CP_TSC_SYNC_TICK_DELTA
(%rdi
), %r9
50 * When the TSC is read, the low 32 bits are placed in %eax while the
51 * high 32 bits are placed in %edx. They are shifted and ORed together
52 * to obtain the full 64-bit value.
61 * When cp_tsc_ncpu is non-zero, it indicates the length of the
62 * cp_tsc_sync_tick_delta array, which contains per-CPU offsets for the
63 * TSC. The CPU ID furnished by the IA32_TSC_AUX register via rdtscp
64 * is used to look up an offset value in that array and apply it to the
67 movq
(%r9, %rcx
, 8), %rdx
73 * Without rdtscp, there is no way to perform a TSC reading and
74 * simultaneously query the current CPU. If tsc_ncpu indicates that
75 * per-CPU TSC offsets are present, the ID of the current CPU is
76 * queried before performing a TSC reading. It will be later compared
77 * to a second CPU ID lookup to catch CPU migrations.
79 * This method will catch all but the most pathological scheduling.
83 movl $GETCPU_GDT_OFFSET
, %edx
87 /* Save the most recently queried CPU ID for later comparison. */
90 cmpl $TSC_RDTSC_MFENCE
, %esi
97 cmpl $TSC_RDTSC_LFENCE
, %esi
104 cmpl $TSC_RDTSC_CPUID
, %esi
107 * Since the amd64 ABI dictates that %rbx is callee-saved, it must be
108 * preserved here. Its contents will be overwritten when cpuid is used
109 * as a serializing instruction.
120 * Other protections should have prevented this function from being
121 * called in the first place. The only sane action is to abort.
122 * The easiest means in this context is via SIGILL.
131 * Query the current CPU again if a per-CPU offset is being applied to
132 * the TSC reading. If the result differs from the earlier reading,
133 * then a migration has occured and the TSC must be read again.
137 movl $GETCPU_GDT_OFFSET
, %edx
141 movq
(%r9, %rdx
, 8), %rdx
145 SET_SIZE
(__cp_tsc_read
)
150 * __cp_getcpu(comm_page_t *)
152 * Stack usage: 0 bytes
154 ENTRY_NP
(__cp_getcpu
)
155 movl CP_TSC_TYPE
(%rdi
), %edi
157 * If RDTSCP is available, it is a quick way to grab the cpu_id which
158 * is stored in the TSC_AUX MSR by the kernel.
166 mov $GETCPU_GDT_OFFSET
, %eax
169 SET_SIZE
(__cp_getcpu
)
173 * __cp_gethrtime(comm_page_t *cp)
175 * Stack usage: 0x20 local + 0x8 call = 0x28 bytes
177 * %rsp+0x00 - hrtime_t tsc_last
178 * %rsp+0x08 - hrtime_t hrtime_base
179 * %rsp+0x10 - commpage_t *cp
180 * %rsp+0x18 - int hres_lock
182 ENTRY_NP
(__cp_gethrtime
)
184 movq
%rdi
, 0x10(%rsp
)
186 movl CP_HRES_LOCK
(%rdi
), %r9d
187 movl
%r9d
, 0x18(%rsp
)
189 movq CP_TSC_LAST
(%rdi
), %rax
190 movq CP_TSC_HRTIME_BASE
(%rdi
), %rdx
195 movq
0x10(%rsp
), %rdi
197 movl
0x18(%rsp
), %r9d
198 movl CP_HRES_LOCK
(%rdi
), %edx
199 andl $
0xfffffffe, %r9d
204 * The in-kernel logic for calculating hrtime performs several checks
205 * to protect against edge cases. That logic is summarized as:
206 * if (tsc >= tsc_last) {
208 * } else if (tsc >= tsc_last - 2*tsc_max_delta) {
211 * delta = MIN(tsc, tsc_resume_cap);
214 * The below implementation achieves the same result, although it is
215 * structured for speed and optimized for the fast path:
217 * delta = tsc - tsc_last;
219 * delta += (tsc_max_delta << 1);
223 * delta = MIN(tsc, tsc_resume_cap);
228 subq
%rdx
, %rax
/* delta = tsc - tsc_last */
229 jbe
3f
/* if (delta < 0) */
233 * Optimized TSC_CONVERT_AND_ADD:
234 * hrtime_base += (tsc_delta * nsec_scale) >> (32 - NSEC_SHIFT)
236 * Since the multiply and shift are done in 128-bit, there is no need
237 * to worry about overflow.
239 movl CP_NSEC_SCALE
(%rdi
), %ecx
241 shrdq $_CONST
(32 - NSEC_SHIFT
), %rdx
, %rax
249 movq
%rax
, %r9 /* save (tsc - tsc_last) in r9 */
250 movl CP_TSC_MAX_DELTA
(%rdi
), %ecx
252 addq
%rcx
, %rax
/* delta += (tsc_max_delta << 1) */
253 jae
4f
/* delta < 0 */
259 * Repopulate %rax with the TSC reading by adding tsc_last to %r9
260 * (which holds tsc - tsc_last)
265 /* delta = MIN(tsc, resume_cap) */
266 movq CP_TSC_RESUME_CAP
(%rdi
), %rcx
273 SET_SIZE
(__cp_gethrtime
)
277 * __cp_clock_gettime_monotonic(comm_page_t *cp, timespec_t *tsp)
279 * Stack usage: 0x8 local + 0x8 call + 0x28 called func. = 0x38 bytes
281 * %rsp+0x00 - timespec_t *tsp
283 ENTRY_NP
(__cp_clock_gettime_monotonic
)
290 * Convert from hrtime_t (int64_t in nanoseconds) to timespec_t.
291 * This uses the same approach as hrt2ts, although it has been updated
292 * to utilize 64-bit math.
293 * 1 / 1,000,000,000 =
294 * 1000100101110000010111110100000100110110101101001010110110011B-26
295 * = 0x112e0be826d694b3 * 2^-26
297 * secs = (nsecs * 0x112e0be826d694b3) >> 26
299 * In order to account for the 2s-compliment of negative inputs, a
300 * final operation completes the process:
302 * secs -= (nsecs >> 63)
305 movq $
0x112e0be826d694b3, %rdx
314 * Populating tv_nsec is easier:
315 * tv_nsec = nsecs - (secs * NANOSEC)
317 imulq $NANOSEC
, %rdx
, %rdx
324 SET_SIZE
(__cp_clock_gettime_monotonic
)
328 * __cp_clock_gettime_realtime(comm_page_t *cp, timespec_t *tsp)
330 * Stack usage: 0x18 local + 0x8 call + 0x28 called func. = 0x48 bytes
332 * %rsp+0x00 - commpage_t *cp
333 * %rsp+0x08 - timespec_t *tsp
334 * %rsp+0x10 - int hres_lock
336 ENTRY_NP
(__cp_clock_gettime_realtime
)
342 movl CP_HRES_LOCK
(%rdi
), %eax
343 movl
%eax
, 0x10(%rsp
)
347 movq CP_HRES_LAST_TICK
(%rdi
), %rdx
348 subq
%rdx
, %rax
/* nslt = hrtime - last_tick */
350 movq CP_HRESTIME
(%rdi
), %r9
351 movq _CONST
(CP_HRESTIME
+ CP_HRESTIME_INCR
)(%rdi
), %r10
352 movl CP_HRESTIME_ADJ
(%rdi
), %r11d
354 addq
%rax
, %r10 /* now.tv_nsec += nslt */
357 jb
4f
/* hres_adj > 0 */
358 ja
6f
/* hres_adj < 0 */
362 jae
8f
/* tv_nsec >= NANOSEC */
365 movl
0x10(%rsp
), %eax
366 movl CP_HRES_LOCK
(%rdi
), %edx
367 andl $
0xfffffffe, %edx
380 4: /* hres_adj > 0 */
381 sarq $ADJ_SHIFT
, %rax
389 6: /* hres_adj < 0 */
390 sarq $ADJ_SHIFT
, %rax
399 8: /* tv_nsec >= NANOSEC */
406 SET_SIZE
(__cp_clock_gettime_realtime
)