12428aadPatrick Mooney/*
22428aadPatrick Mooney * This file and its contents are supplied under the terms of the
32428aadPatrick Mooney * Common Development and Distribution License ("CDDL"), version 1.0.
42428aadPatrick Mooney * You may only use this file in accordance with the terms of version
52428aadPatrick Mooney * 1.0 of the CDDL.
62428aadPatrick Mooney *
72428aadPatrick Mooney * A full copy of the text of the CDDL should have accompanied this
82428aadPatrick Mooney * source.  A copy of the CDDL is also available via the Internet at
92428aadPatrick Mooney * http://www.illumos.org/license/CDDL.
102428aadPatrick Mooney */
112428aadPatrick Mooney
122428aadPatrick Mooney/*
13e121b61Patrick Mooney * Copyright 2019 Joyent, Inc.
142428aadPatrick Mooney */
152428aadPatrick Mooney
162428aadPatrick Mooney#include <sys/comm_page.h>
172428aadPatrick Mooney#include <sys/tsc.h>
182428aadPatrick Mooney
192428aadPatrick Mooney
202428aadPatrick Mooney/*
212428aadPatrick Mooney * Interrogate if querying the clock via the comm page is possible.
222428aadPatrick Mooney */
232428aadPatrick Mooneyint
242428aadPatrick Mooney__cp_can_gettime(comm_page_t *cp)
252428aadPatrick Mooney{
262428aadPatrick Mooney	switch (cp->cp_tsc_type) {
272428aadPatrick Mooney	case TSC_TSCP:
282428aadPatrick Mooney	case TSC_RDTSC_MFENCE:
292428aadPatrick Mooney	case TSC_RDTSC_LFENCE:
302428aadPatrick Mooney	case TSC_RDTSC_CPUID:
312428aadPatrick Mooney		return (1);
322428aadPatrick Mooney	default:
332428aadPatrick Mooney		break;
342428aadPatrick Mooney	}
352428aadPatrick Mooney	return (0);
362428aadPatrick Mooney}
372428aadPatrick Mooney
382428aadPatrick Mooney#ifdef __amd64
392428aadPatrick Mooney
402428aadPatrick Mooney/*
412428aadPatrick Mooney * The functions used for calculating time (both monotonic and wall-clock) are
422428aadPatrick Mooney * implemented in assembly on amd64.  This is primarily for stack conservation.
432428aadPatrick Mooney */
442428aadPatrick Mooney
452428aadPatrick Mooney#else /* i386 below */
462428aadPatrick Mooney
472428aadPatrick Mooney/*
482428aadPatrick Mooney * ASM-defined functions.
492428aadPatrick Mooney */
502428aadPatrick Mooneyextern hrtime_t __cp_tsc_read(comm_page_t *);
51e121b61Patrick Mooneyextern hrtime_t __cp_gethrtime_fasttrap();
522428aadPatrick Mooney
532428aadPatrick Mooney/*
542428aadPatrick Mooney * These are cloned from TSC and time related code in the kernel.  The should
552428aadPatrick Mooney * be kept in sync in the case that the source values are changed.
562428aadPatrick Mooney */
572428aadPatrick Mooney#define	NSEC_SHIFT	5
582428aadPatrick Mooney#define	ADJ_SHIFT	4
592428aadPatrick Mooney#define	NANOSEC		1000000000LL
602428aadPatrick Mooney
612428aadPatrick Mooney#define	TSC_CONVERT_AND_ADD(tsc, hrt, scale) do {		\
62e121b61Patrick Mooney	uint32_t *_l = (uint32_t *)&(tsc);			\
632428aadPatrick Mooney	uint64_t sc = (uint32_t)(scale);			\
642428aadPatrick Mooney	(hrt) += (uint64_t)(_l[1] * sc) << NSEC_SHIFT;		\
652428aadPatrick Mooney	(hrt) += (uint64_t)(_l[0] * sc) >> (32 - NSEC_SHIFT);	\
662428aadPatrick Mooney} while (0)
672428aadPatrick Mooney
682428aadPatrick Mooney/*
692428aadPatrick Mooney * Userspace version of tsc_gethrtime.
702428aadPatrick Mooney * See: uts/i86pc/os/timestamp.c
712428aadPatrick Mooney */
722428aadPatrick Mooneyhrtime_t
732428aadPatrick Mooney__cp_gethrtime(comm_page_t *cp)
742428aadPatrick Mooney{
752428aadPatrick Mooney	uint32_t old_hres_lock;
762428aadPatrick Mooney	hrtime_t tsc, hrt, tsc_last;
772428aadPatrick Mooney
782428aadPatrick Mooney	/*
792428aadPatrick Mooney	 * Several precautions must be taken when collecting the data necessary
802428aadPatrick Mooney	 * to perform an accurate gethrtime calculation.
812428aadPatrick Mooney	 *
822428aadPatrick Mooney	 * While much of the TSC state stored in the comm page is unchanging
832428aadPatrick Mooney	 * after boot, portions of it are periodically updated during OS ticks.
842428aadPatrick Mooney	 * Changes to hres_lock during the course of the copy indicates a
852428aadPatrick Mooney	 * potentially inconsistent snapshot, necessitating a loop.
862428aadPatrick Mooney	 *
872428aadPatrick Mooney	 * Even more complicated is the handling for TSCs which require sync
882428aadPatrick Mooney	 * offsets between different CPUs.  Since userspace lacks the luxury of
892428aadPatrick Mooney	 * disabling interrupts, a validation loop checking for CPU migrations
902428aadPatrick Mooney	 * is used.  Pathological scheduling could, in theory, "outwit"
912428aadPatrick Mooney	 * this check.  Such a possibility is considered an acceptable risk.
922428aadPatrick Mooney	 *
932428aadPatrick Mooney	 */
942428aadPatrick Mooney	do {
952428aadPatrick Mooney		old_hres_lock = cp->cp_hres_lock;
962428aadPatrick Mooney		tsc_last = cp->cp_tsc_last;
972428aadPatrick Mooney		hrt = cp->cp_tsc_hrtime_base;
982428aadPatrick Mooney		tsc = __cp_tsc_read(cp);
99e121b61Patrick Mooney
100e121b61Patrick Mooney		/*
101e121b61Patrick Mooney		 * A TSC reading of 0 indicates the special case of an error
102e121b61Patrick Mooney		 * bail-out.  Rely on the fasttrap to supply an hrtime value.
103e121b61Patrick Mooney		 */
104e121b61Patrick Mooney		if (tsc == 0) {
105e121b61Patrick Mooney			return (__cp_gethrtime_fasttrap());
106e121b61Patrick Mooney		}
1072428aadPatrick Mooney	} while ((old_hres_lock & ~1) != cp->cp_hres_lock);
1082428aadPatrick Mooney
1092428aadPatrick Mooney	if (tsc >= tsc_last) {
1102428aadPatrick Mooney		tsc -= tsc_last;
1112428aadPatrick Mooney	} else if (tsc >= tsc_last - (2 * cp->cp_tsc_max_delta)) {
1122428aadPatrick Mooney		tsc = 0;
1132428aadPatrick Mooney	} else if (tsc > cp->cp_tsc_resume_cap) {
1142428aadPatrick Mooney		tsc = cp->cp_tsc_resume_cap;
1152428aadPatrick Mooney	}
1162428aadPatrick Mooney	TSC_CONVERT_AND_ADD(tsc, hrt, cp->cp_nsec_scale);
1172428aadPatrick Mooney
1182428aadPatrick Mooney	return (hrt);
1192428aadPatrick Mooney}
1202428aadPatrick Mooney
1212428aadPatrick Mooney/*
1222428aadPatrick Mooney * Userspace version of pc_gethrestime.
1232428aadPatrick Mooney * See: uts/i86pc/os/machdep.c
1242428aadPatrick Mooney */
1252428aadPatrick Mooneyint
1262428aadPatrick Mooney__cp_clock_gettime_realtime(comm_page_t *cp, timespec_t *tsp)
1272428aadPatrick Mooney{
1282428aadPatrick Mooney	int lock_prev, nslt;
1292428aadPatrick Mooney	timespec_t now;
1302428aadPatrick Mooney	int64_t hres_adj;
1312428aadPatrick Mooney
1322428aadPatrick Mooneyloop:
1332428aadPatrick Mooney	lock_prev = cp->cp_hres_lock;
1342428aadPatrick Mooney	now.tv_sec = cp->cp_hrestime[0];
1352428aadPatrick Mooney	now.tv_nsec = cp->cp_hrestime[1];
1362428aadPatrick Mooney	nslt = (int)(__cp_gethrtime(cp) - cp->cp_hres_last_tick);
1372428aadPatrick Mooney	hres_adj = cp->cp_hrestime_adj;
1382428aadPatrick Mooney	if (nslt < 0) {
1392428aadPatrick Mooney		/*
1402428aadPatrick Mooney		 * Tick came between sampling hrtime and hres_last_tick;
1412428aadPatrick Mooney		 */
1422428aadPatrick Mooney		goto loop;
1432428aadPatrick Mooney	}
1442428aadPatrick Mooney	now.tv_nsec += nslt;
1452428aadPatrick Mooney
1462428aadPatrick Mooney	/*
1472428aadPatrick Mooney	 * Apply hres_adj skew, if needed.
1482428aadPatrick Mooney	 */
1492428aadPatrick Mooney	if (hres_adj > 0) {
1502428aadPatrick Mooney		nslt = (nslt >> ADJ_SHIFT);
1512428aadPatrick Mooney		if (nslt > hres_adj)
1522428aadPatrick Mooney			nslt = (int)hres_adj;
1532428aadPatrick Mooney		now.tv_nsec += nslt;
1542428aadPatrick Mooney	} else if (hres_adj < 0) {
1552428aadPatrick Mooney		nslt = -(nslt >> ADJ_SHIFT);
1562428aadPatrick Mooney		if (nslt < hres_adj)
1572428aadPatrick Mooney			nslt = (int)hres_adj;
1582428aadPatrick Mooney		now.tv_nsec += nslt;
1592428aadPatrick Mooney	}
1602428aadPatrick Mooney
1612428aadPatrick Mooney	/*
1622428aadPatrick Mooney	 * Rope in tv_nsec from any excessive adjustments.
1632428aadPatrick Mooney	 */
1642428aadPatrick Mooney	while ((unsigned long)now.tv_nsec >= NANOSEC) {
1652428aadPatrick Mooney		now.tv_nsec -= NANOSEC;
1662428aadPatrick Mooney		now.tv_sec++;
1672428aadPatrick Mooney	}
1682428aadPatrick Mooney
1692428aadPatrick Mooney	if ((cp->cp_hres_lock & ~1) != lock_prev)
1702428aadPatrick Mooney		goto loop;
1712428aadPatrick Mooney
1722428aadPatrick Mooney	*tsp = now;
1732428aadPatrick Mooney	return (0);
1742428aadPatrick Mooney}
1752428aadPatrick Mooney
1762428aadPatrick Mooney/*
1772428aadPatrick Mooney * The __cp_clock_gettime_monotonic function expects that hrt2ts be present
1782428aadPatrick Mooney * when the code is finally linked.
1792428aadPatrick Mooney * (The amd64 version has no such requirement.)
1802428aadPatrick Mooney */
1812428aadPatrick Mooneyextern void hrt2ts(hrtime_t, timespec_t *);
1822428aadPatrick Mooney
1832428aadPatrick Mooneyint
1842428aadPatrick Mooney__cp_clock_gettime_monotonic(comm_page_t *cp, timespec_t *tsp)
1852428aadPatrick Mooney{
1862428aadPatrick Mooney	hrtime_t hrt;
1872428aadPatrick Mooney
1882428aadPatrick Mooney	hrt = __cp_gethrtime(cp);
1892428aadPatrick Mooney	hrt2ts(hrt, tsp);
1902428aadPatrick Mooney	return (0);
1912428aadPatrick Mooney}
1922428aadPatrick Mooney
1932428aadPatrick Mooney#endif /* __amd64 */