xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_time.c (revision 1743a90d)
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
5e4b86885SCheng Sean Ye  * Common Development and Distribution License (the "License").
6e4b86885SCheng Sean Ye  * 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  */
21e4b86885SCheng Sean Ye 
227c478bd9Sstevel@tonic-gate /*
23e4b86885SCheng Sean Ye  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/fm/protocol.h>
287c478bd9Sstevel@tonic-gate #include <signal.h>
297c478bd9Sstevel@tonic-gate #include <limits.h>
307c478bd9Sstevel@tonic-gate #include <time.h>
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <fmd_time.h>
337c478bd9Sstevel@tonic-gate #include <fmd_alloc.h>
347c478bd9Sstevel@tonic-gate #include <fmd_error.h>
357c478bd9Sstevel@tonic-gate #include <fmd_subr.h>
367c478bd9Sstevel@tonic-gate #include <fmd.h>
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate void
fmd_time_gettimeofday(struct timeval * tvp)397c478bd9Sstevel@tonic-gate fmd_time_gettimeofday(struct timeval *tvp)
407c478bd9Sstevel@tonic-gate {
417c478bd9Sstevel@tonic-gate 	if (fmd.d_clockops->fto_gettimeofday(tvp, NULL) != 0)
427c478bd9Sstevel@tonic-gate 		fmd_panic("failed to read time-of-day clock");
437c478bd9Sstevel@tonic-gate }
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate hrtime_t
fmd_time_gethrtime(void)467c478bd9Sstevel@tonic-gate fmd_time_gethrtime(void)
477c478bd9Sstevel@tonic-gate {
487c478bd9Sstevel@tonic-gate 	return (fmd.d_clockops->fto_gethrtime());
497c478bd9Sstevel@tonic-gate }
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate void
fmd_time_addhrtime(hrtime_t delta)527c478bd9Sstevel@tonic-gate fmd_time_addhrtime(hrtime_t delta)
537c478bd9Sstevel@tonic-gate {
547c478bd9Sstevel@tonic-gate 	fmd.d_clockops->fto_addhrtime(delta);
557c478bd9Sstevel@tonic-gate }
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate void
fmd_time_waithrtime(hrtime_t delta)587c478bd9Sstevel@tonic-gate fmd_time_waithrtime(hrtime_t delta)
597c478bd9Sstevel@tonic-gate {
607c478bd9Sstevel@tonic-gate 	fmd.d_clockops->fto_waithrtime(delta);
617c478bd9Sstevel@tonic-gate }
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate void
fmd_time_waitcancel(pthread_t tid)647c478bd9Sstevel@tonic-gate fmd_time_waitcancel(pthread_t tid)
657c478bd9Sstevel@tonic-gate {
667c478bd9Sstevel@tonic-gate 	fmd.d_clockops->fto_waitcancel(tid);
677c478bd9Sstevel@tonic-gate }
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
707c478bd9Sstevel@tonic-gate  * To synchronize TOD with a gethrtime() source, we repeatedly sample TOD in
717c478bd9Sstevel@tonic-gate  * between two calls to gethrtime(), which places a reasonably tight bound on
727c478bd9Sstevel@tonic-gate  * the high-resolution time that matches the TOD value we sampled.  We repeat
737c478bd9Sstevel@tonic-gate  * this process several times and ultimately select the sample where the two
747c478bd9Sstevel@tonic-gate  * values of gethrtime() were closest.  We then assign the average of those
757c478bd9Sstevel@tonic-gate  * two high-resolution times to be the gethrtime() associated with that TOD.
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate void
fmd_time_sync(fmd_timeval_t * ftv,hrtime_t * hrp,uint_t samples)787c478bd9Sstevel@tonic-gate fmd_time_sync(fmd_timeval_t *ftv, hrtime_t *hrp, uint_t samples)
797c478bd9Sstevel@tonic-gate {
807c478bd9Sstevel@tonic-gate 	const fmd_timeops_t *ftop = fmd.d_clockops;
817c478bd9Sstevel@tonic-gate 	hrtime_t hrtbase, hrtmin = INT64_MAX;
827c478bd9Sstevel@tonic-gate 	struct timeval todbase;
837c478bd9Sstevel@tonic-gate 	uint_t i;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	for (i = 0; i < samples; i++) {
867c478bd9Sstevel@tonic-gate 		hrtime_t t0, t1, delta;
877c478bd9Sstevel@tonic-gate 		struct timeval tod;
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 		t0 = ftop->fto_gethrtime();
907c478bd9Sstevel@tonic-gate 		(void) ftop->fto_gettimeofday(&tod, NULL);
917c478bd9Sstevel@tonic-gate 		t1 = ftop->fto_gethrtime();
927c478bd9Sstevel@tonic-gate 		delta = t1 - t0;
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate 		if (delta < hrtmin) {
957c478bd9Sstevel@tonic-gate 			hrtmin = delta;
967c478bd9Sstevel@tonic-gate 			hrtbase = t0 + delta / 2;
977c478bd9Sstevel@tonic-gate 			todbase = tod;
987c478bd9Sstevel@tonic-gate 		}
997c478bd9Sstevel@tonic-gate 	}
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	if (ftv != NULL) {
1027c478bd9Sstevel@tonic-gate 		ftv->ftv_sec = todbase.tv_sec;
1037c478bd9Sstevel@tonic-gate 		ftv->ftv_nsec = todbase.tv_usec * (NANOSEC / MICROSEC);
1047c478bd9Sstevel@tonic-gate 	}
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	if (hrp != NULL)
1077c478bd9Sstevel@tonic-gate 		*hrp = hrtbase;
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate /*
1117c478bd9Sstevel@tonic-gate  * Convert a high-resolution timestamp into 64-bit seconds and nanoseconds.
1127c478bd9Sstevel@tonic-gate  * For efficiency, the multiplication and division are expanded using the
1137c478bd9Sstevel@tonic-gate  * clever algorithm originally designed for the kernel in hrt2ts().  Refer to
1147c478bd9Sstevel@tonic-gate  * the comments in uts/common/os/timers.c for an explanation of how it works.
1157c478bd9Sstevel@tonic-gate  */
1167c478bd9Sstevel@tonic-gate static void
fmd_time_hrt2ftv(hrtime_t hrt,fmd_timeval_t * ftv)1177c478bd9Sstevel@tonic-gate fmd_time_hrt2ftv(hrtime_t hrt, fmd_timeval_t *ftv)
1187c478bd9Sstevel@tonic-gate {
1197c478bd9Sstevel@tonic-gate 	uint32_t sec, nsec, tmp;
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	tmp = (uint32_t)(hrt >> 30);
1227c478bd9Sstevel@tonic-gate 	sec = tmp - (tmp >> 2);
1237c478bd9Sstevel@tonic-gate 	sec = tmp - (sec >> 5);
1247c478bd9Sstevel@tonic-gate 	sec = tmp + (sec >> 1);
1257c478bd9Sstevel@tonic-gate 	sec = tmp - (sec >> 6) + 7;
1267c478bd9Sstevel@tonic-gate 	sec = tmp - (sec >> 3);
1277c478bd9Sstevel@tonic-gate 	sec = tmp + (sec >> 1);
1287c478bd9Sstevel@tonic-gate 	sec = tmp + (sec >> 3);
1297c478bd9Sstevel@tonic-gate 	sec = tmp + (sec >> 4);
1307c478bd9Sstevel@tonic-gate 	tmp = (sec << 7) - sec - sec - sec;
1317c478bd9Sstevel@tonic-gate 	tmp = (tmp << 7) - tmp - tmp - tmp;
1327c478bd9Sstevel@tonic-gate 	tmp = (tmp << 7) - tmp - tmp - tmp;
1337c478bd9Sstevel@tonic-gate 	nsec = (uint32_t)hrt - (tmp << 9);
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	while (nsec >= NANOSEC) {
1367c478bd9Sstevel@tonic-gate 		nsec -= NANOSEC;
1377c478bd9Sstevel@tonic-gate 		sec++;
1387c478bd9Sstevel@tonic-gate 	}
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	ftv->ftv_sec = sec;
1417c478bd9Sstevel@tonic-gate 	ftv->ftv_nsec = nsec;
1427c478bd9Sstevel@tonic-gate }
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate /*
1457c478bd9Sstevel@tonic-gate  * Convert a high-resolution time from gethrtime() to a TOD (fmd_timeval_t).
1467c478bd9Sstevel@tonic-gate  * We convert 'tod_base' to nanoseconds, adjust it based on the difference
1477c478bd9Sstevel@tonic-gate  * between the corresponding 'hrt_base' and the event high-res time 'hrt',
1487c478bd9Sstevel@tonic-gate  * and then repack the result into ftv_sec and ftv_nsec for our output.
1497c478bd9Sstevel@tonic-gate  */
1507c478bd9Sstevel@tonic-gate void
fmd_time_hrt2tod(hrtime_t hrt_base,const fmd_timeval_t * tod_base,hrtime_t hrt,fmd_timeval_t * ftv)1517c478bd9Sstevel@tonic-gate fmd_time_hrt2tod(hrtime_t hrt_base, const fmd_timeval_t *tod_base,
1527c478bd9Sstevel@tonic-gate     hrtime_t hrt, fmd_timeval_t *ftv)
1537c478bd9Sstevel@tonic-gate {
1547c478bd9Sstevel@tonic-gate 	fmd_time_hrt2ftv(tod_base->ftv_sec * NANOSEC +
1557c478bd9Sstevel@tonic-gate 	    tod_base->ftv_nsec + (hrt - hrt_base), ftv);
1567c478bd9Sstevel@tonic-gate }
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate /*
1597c478bd9Sstevel@tonic-gate  * Convert a TOD (fmd_timeval_t) to a high-resolution time from gethrtime().
1607c478bd9Sstevel@tonic-gate  * Note that since TOD occurred in the past, the resulting value may be a
1617c478bd9Sstevel@tonic-gate  * negative number according the current gethrtime() clock value.
1627c478bd9Sstevel@tonic-gate  */
1637c478bd9Sstevel@tonic-gate void
fmd_time_tod2hrt(hrtime_t hrt_base,const fmd_timeval_t * tod_base,const fmd_timeval_t * ftv,hrtime_t * hrtp)1647c478bd9Sstevel@tonic-gate fmd_time_tod2hrt(hrtime_t hrt_base, const fmd_timeval_t *tod_base,
1657c478bd9Sstevel@tonic-gate     const fmd_timeval_t *ftv, hrtime_t *hrtp)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	hrtime_t tod_hrt = tod_base->ftv_sec * NANOSEC + tod_base->ftv_nsec;
1687c478bd9Sstevel@tonic-gate 	hrtime_t ftv_hrt = ftv->ftv_sec * NANOSEC + ftv->ftv_nsec;
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	*hrtp = hrt_base - (tod_hrt - ftv_hrt);
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate /*
1747c478bd9Sstevel@tonic-gate  * Adjust a high-resolution time based on the low bits of time stored in ENA.
1757c478bd9Sstevel@tonic-gate  * The assumption here in that ENA won't wrap between the time it is computed
1767c478bd9Sstevel@tonic-gate  * and the time the error is queued (when we capture a full 64-bits of hrtime).
1777c478bd9Sstevel@tonic-gate  * We extract the relevant ENA time bits as 't0' and subtract the difference
1787c478bd9Sstevel@tonic-gate  * between these bits and the corresponding low bits of 'hrt' from 'hrt'.
179e4b86885SCheng Sean Ye  *
180e4b86885SCheng Sean Ye  * Under xVM dom0, the UE ereport is prepared after panic, therefore
181e4b86885SCheng Sean Ye  * the full 64-bit hrtime of 't0' can be bigger than 'hrt'.  In such case,
182e4b86885SCheng Sean Ye  * we should just return 'hrt'.
183e4b86885SCheng Sean Ye  *
184e4b86885SCheng Sean Ye  * 't0' contains only the low bits of 64bit hrtime.  It is tricky to tell
185e4b86885SCheng Sean Ye  * whether 'hrt' or 't0' happened first.  We assume there should be short
186e4b86885SCheng Sean Ye  * period between 'hrt' and 't0', therefore to check which one came first, we
187e4b86885SCheng Sean Ye  * test their subtraction against the highest bit of mask, if the bit is not
188e4b86885SCheng Sean Ye  * set, then 't0' is earlier.  This is equivalent to
189*1743a90dSToomas Soome  *	((hrt - t0) & mask) < ((mask + 1) / 2)
1907c478bd9Sstevel@tonic-gate  */
1917c478bd9Sstevel@tonic-gate hrtime_t
fmd_time_ena2hrt(hrtime_t hrt,uint64_t ena)1927c478bd9Sstevel@tonic-gate fmd_time_ena2hrt(hrtime_t hrt, uint64_t ena)
1937c478bd9Sstevel@tonic-gate {
1947c478bd9Sstevel@tonic-gate 	hrtime_t t0, mask;
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	switch (ENA_FORMAT(ena)) {
1977c478bd9Sstevel@tonic-gate 	case FM_ENA_FMT1:
1987c478bd9Sstevel@tonic-gate 		t0 = (ena & ENA_FMT1_TIME_MASK) >> ENA_FMT1_TIME_SHFT;
1997c478bd9Sstevel@tonic-gate 		mask = ENA_FMT1_TIME_MASK >> ENA_FMT1_TIME_SHFT;
200e4b86885SCheng Sean Ye 		if (((hrt - t0) & ((mask + 1) >> 1)) == 0)
201e4b86885SCheng Sean Ye 			hrt -= (hrt - t0) & mask;
2027c478bd9Sstevel@tonic-gate 		break;
2037c478bd9Sstevel@tonic-gate 	case FM_ENA_FMT2:
2047c478bd9Sstevel@tonic-gate 		t0 = (ena & ENA_FMT2_TIME_MASK) >> ENA_FMT2_TIME_SHFT;
2057c478bd9Sstevel@tonic-gate 		mask = ENA_FMT2_TIME_MASK >> ENA_FMT2_TIME_SHFT;
206e4b86885SCheng Sean Ye 		if (((hrt - t0) & ((mask + 1) >> 1)) == 0)
207e4b86885SCheng Sean Ye 			hrt -= (hrt - t0) & mask;
2087c478bd9Sstevel@tonic-gate 		break;
2097c478bd9Sstevel@tonic-gate 	}
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	return (hrt);
2127c478bd9Sstevel@tonic-gate }
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate /*
2157c478bd9Sstevel@tonic-gate  * To implement a simulated clock, we keep track of an hrtime_t value which
2167c478bd9Sstevel@tonic-gate  * starts at zero and is incremented only by fmd_time_addhrtime() (i.e. when
2177c478bd9Sstevel@tonic-gate  * the driver of the simulation requests that the clock advance).  We sample
2187c478bd9Sstevel@tonic-gate  * the native time-of-day clock once at the start of the simulation and then
2197c478bd9Sstevel@tonic-gate  * return subsequent time-of-day values by adjusting TOD using the hrtime_t
2207c478bd9Sstevel@tonic-gate  * clock setting.  Simulated nanosleep (fmd_time_waithrtime() entry point) is
2217c478bd9Sstevel@tonic-gate  * implemented by waiting on fts->fts_cv for the hrtime_t to increment.
2227c478bd9Sstevel@tonic-gate  */
2237c478bd9Sstevel@tonic-gate static void *
fmd_simulator_init(void)2247c478bd9Sstevel@tonic-gate fmd_simulator_init(void)
2257c478bd9Sstevel@tonic-gate {
2267c478bd9Sstevel@tonic-gate 	fmd_timesim_t *fts = fmd_alloc(sizeof (fmd_timesim_t), FMD_SLEEP);
2277c478bd9Sstevel@tonic-gate 	struct timeval tv;
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_init(&fts->fts_lock, NULL);
2307c478bd9Sstevel@tonic-gate 	(void) pthread_cond_init(&fts->fts_cv, NULL);
2317c478bd9Sstevel@tonic-gate 	(void) gettimeofday(&tv, NULL);
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	fts->fts_tod = (hrtime_t)tv.tv_sec * NANOSEC +
2347c478bd9Sstevel@tonic-gate 	    (hrtime_t)tv.tv_usec * (NANOSEC / MICROSEC);
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	fts->fts_hrt = 0;
2377c478bd9Sstevel@tonic-gate 	fts->fts_cancel = 0;
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	fmd_dprintf(FMD_DBG_TMR, "simulator tod base tv_sec=%lx hrt=%llx\n",
2407c478bd9Sstevel@tonic-gate 	    tv.tv_sec, fts->fts_tod);
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	return (fts);
2437c478bd9Sstevel@tonic-gate }
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate static void
fmd_simulator_fini(void * fts)2467c478bd9Sstevel@tonic-gate fmd_simulator_fini(void *fts)
2477c478bd9Sstevel@tonic-gate {
2487c478bd9Sstevel@tonic-gate 	if (fts != NULL)
2497c478bd9Sstevel@tonic-gate 		fmd_free(fts, sizeof (fmd_timesim_t));
2507c478bd9Sstevel@tonic-gate }
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2537c478bd9Sstevel@tonic-gate static int
fmd_simulator_tod(struct timeval * tvp,void * tzp)2547c478bd9Sstevel@tonic-gate fmd_simulator_tod(struct timeval *tvp, void *tzp)
2557c478bd9Sstevel@tonic-gate {
2567c478bd9Sstevel@tonic-gate 	fmd_timesim_t *fts = fmd.d_clockptr;
2577c478bd9Sstevel@tonic-gate 	hrtime_t tod, hrt, sec, rem;
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fts->fts_lock);
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	tod = fts->fts_tod;
2627c478bd9Sstevel@tonic-gate 	hrt = fts->fts_hrt;
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&fts->fts_lock);
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	sec = tod / NANOSEC + hrt / NANOSEC;
2677c478bd9Sstevel@tonic-gate 	rem = tod % NANOSEC + hrt % NANOSEC;
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	tvp->tv_sec = sec + rem / NANOSEC;
2707c478bd9Sstevel@tonic-gate 	tvp->tv_usec = (rem % NANOSEC) / (NANOSEC / MICROSEC);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	return (0);
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate static hrtime_t
fmd_simulator_hrt(void)2767c478bd9Sstevel@tonic-gate fmd_simulator_hrt(void)
2777c478bd9Sstevel@tonic-gate {
2787c478bd9Sstevel@tonic-gate 	fmd_timesim_t *fts = fmd.d_clockptr;
2797c478bd9Sstevel@tonic-gate 	hrtime_t hrt;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fts->fts_lock);
2827c478bd9Sstevel@tonic-gate 	hrt = fts->fts_hrt;
2837c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&fts->fts_lock);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	return (hrt);
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate static void
fmd_simulator_add(hrtime_t delta)2897c478bd9Sstevel@tonic-gate fmd_simulator_add(hrtime_t delta)
2907c478bd9Sstevel@tonic-gate {
2917c478bd9Sstevel@tonic-gate 	fmd_timesim_t *fts = fmd.d_clockptr;
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fts->fts_lock);
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	if (fts->fts_hrt + delta < fts->fts_hrt)
2967c478bd9Sstevel@tonic-gate 		fts->fts_hrt = INT64_MAX; /* do not increment past apocalypse */
2977c478bd9Sstevel@tonic-gate 	else
2987c478bd9Sstevel@tonic-gate 		fts->fts_hrt += delta;
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	TRACE((FMD_DBG_TMR, "hrt clock set %llx", fts->fts_hrt));
3017c478bd9Sstevel@tonic-gate 	fmd_dprintf(FMD_DBG_TMR, "hrt clock set %llx\n", fts->fts_hrt);
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&fts->fts_cv);
304d9638e54Smws 	(void) pthread_mutex_unlock(&fts->fts_lock);
3057c478bd9Sstevel@tonic-gate }
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate static void
fmd_simulator_wait(hrtime_t delta)3087c478bd9Sstevel@tonic-gate fmd_simulator_wait(hrtime_t delta)
3097c478bd9Sstevel@tonic-gate {
3107c478bd9Sstevel@tonic-gate 	fmd_timesim_t *fts = fmd.d_clockptr;
3117c478bd9Sstevel@tonic-gate 	uint64_t hrt;
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fts->fts_lock);
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	/*
3167c478bd9Sstevel@tonic-gate 	 * If the delta causes time to wrap because we've reached the simulated
3177c478bd9Sstevel@tonic-gate 	 * apocalypse, then wait forever.  We make 'hrt' unsigned so that the
3187c478bd9Sstevel@tonic-gate 	 * while-loop comparison fts_hrt < UINT64_MAX will always return true.
3197c478bd9Sstevel@tonic-gate 	 */
3207c478bd9Sstevel@tonic-gate 	if (fts->fts_hrt + delta < fts->fts_hrt)
3217c478bd9Sstevel@tonic-gate 		hrt = UINT64_MAX;
3227c478bd9Sstevel@tonic-gate 	else
3237c478bd9Sstevel@tonic-gate 		hrt = fts->fts_hrt + delta;
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	while (fts->fts_hrt < hrt && fts->fts_cancel == 0)
3267c478bd9Sstevel@tonic-gate 		(void) pthread_cond_wait(&fts->fts_cv, &fts->fts_lock);
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	if (fts->fts_cancel != 0)
3297c478bd9Sstevel@tonic-gate 		fts->fts_cancel--; /* cancel has been processed */
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&fts->fts_lock);
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3357c478bd9Sstevel@tonic-gate static void
fmd_simulator_cancel(pthread_t tid)3367c478bd9Sstevel@tonic-gate fmd_simulator_cancel(pthread_t tid)
3377c478bd9Sstevel@tonic-gate {
3387c478bd9Sstevel@tonic-gate 	fmd_timesim_t *fts = fmd.d_clockptr;
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&fts->fts_lock);
3417c478bd9Sstevel@tonic-gate 	fts->fts_cancel++;
3427c478bd9Sstevel@tonic-gate 	(void) pthread_cond_signal(&fts->fts_cv);
343d9638e54Smws 	(void) pthread_mutex_unlock(&fts->fts_lock);
3447c478bd9Sstevel@tonic-gate }
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate /*
3477c478bd9Sstevel@tonic-gate  * Native time is implemented by calls to gethrtime() and gettimeofday(), which
3487c478bd9Sstevel@tonic-gate  * are stored directly in the native time ops-vector defined below.  To wait on
3497c478bd9Sstevel@tonic-gate  * the native clock we use nanosleep(), which we can abort using a signal.  The
3507c478bd9Sstevel@tonic-gate  * implementation assumes that callers will have a SIGALRM handler installed.
3517c478bd9Sstevel@tonic-gate  */
3527c478bd9Sstevel@tonic-gate static void
fmd_native_wait(hrtime_t delta)3537c478bd9Sstevel@tonic-gate fmd_native_wait(hrtime_t delta)
3547c478bd9Sstevel@tonic-gate {
3557c478bd9Sstevel@tonic-gate 	timespec_t tv;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	tv.tv_sec = delta / NANOSEC;
3587c478bd9Sstevel@tonic-gate 	tv.tv_nsec = delta % NANOSEC;
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	(void) nanosleep(&tv, NULL);
3617c478bd9Sstevel@tonic-gate }
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate static void
fmd_native_cancel(pthread_t tid)3647c478bd9Sstevel@tonic-gate fmd_native_cancel(pthread_t tid)
3657c478bd9Sstevel@tonic-gate {
3667c478bd9Sstevel@tonic-gate 	(void) pthread_kill(tid, SIGALRM);
3677c478bd9Sstevel@tonic-gate }
3687c478bd9Sstevel@tonic-gate 
369*1743a90dSToomas Soome static void
fmd_time_vnop(void)370*1743a90dSToomas Soome fmd_time_vnop(void)
371*1743a90dSToomas Soome {
372*1743a90dSToomas Soome }
373*1743a90dSToomas Soome 
3747c478bd9Sstevel@tonic-gate static void *
fmd_time_nop(void)3757c478bd9Sstevel@tonic-gate fmd_time_nop(void)
3767c478bd9Sstevel@tonic-gate {
3777c478bd9Sstevel@tonic-gate 	return (NULL);
3787c478bd9Sstevel@tonic-gate }
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate const fmd_timeops_t fmd_timeops_native = {
3817c478bd9Sstevel@tonic-gate 	(void *(*)())fmd_time_nop,	/* fto_init */
382*1743a90dSToomas Soome 	(void (*)())fmd_time_vnop,	/* fto_fini */
3837c478bd9Sstevel@tonic-gate 	gettimeofday,			/* fto_gettimeofday */
3847c478bd9Sstevel@tonic-gate 	gethrtime,			/* fto_gethrtime */
385*1743a90dSToomas Soome 	(void (*)())fmd_time_vnop,	/* fto_addhrtime */
3867c478bd9Sstevel@tonic-gate 	fmd_native_wait,		/* fto_waithrtime */
3877c478bd9Sstevel@tonic-gate 	fmd_native_cancel,		/* fto_waitcancel */
3887c478bd9Sstevel@tonic-gate };
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate const fmd_timeops_t fmd_timeops_simulated = {
3917c478bd9Sstevel@tonic-gate 	fmd_simulator_init,		/* fto_init */
3927c478bd9Sstevel@tonic-gate 	fmd_simulator_fini,		/* fto_fini */
3937c478bd9Sstevel@tonic-gate 	fmd_simulator_tod,		/* fto_gettimeofday */
3947c478bd9Sstevel@tonic-gate 	fmd_simulator_hrt,		/* fto_gethrtime */
3957c478bd9Sstevel@tonic-gate 	fmd_simulator_add,		/* fto_addhrtime */
3967c478bd9Sstevel@tonic-gate 	fmd_simulator_wait,		/* fto_waithrtime */
3977c478bd9Sstevel@tonic-gate 	fmd_simulator_cancel,		/* fto_waitcancel */
3987c478bd9Sstevel@tonic-gate };
399