#include <sys/fm/protocol.h>
#include <signal.h>
#include <limits.h>
#include <time.h>
#include <fmd_time.h>
#include <fmd_alloc.h>
#include <fmd_error.h>
#include <fmd_subr.h>
#include <fmd.h>
void
fmd_time_gettimeofday(struct timeval *tvp)
{
if (fmd.d_clockops->fto_gettimeofday(tvp, NULL) != 0)
fmd_panic("failed to read time-of-day clock");
}
hrtime_t
fmd_time_gethrtime(void)
{
return (fmd.d_clockops->fto_gethrtime());
}
void
fmd_time_addhrtime(hrtime_t delta)
{
fmd.d_clockops->fto_addhrtime(delta);
}
void
fmd_time_waithrtime(hrtime_t delta)
{
fmd.d_clockops->fto_waithrtime(delta);
}
void
fmd_time_waitcancel(pthread_t tid)
{
fmd.d_clockops->fto_waitcancel(tid);
}
void
fmd_time_sync(fmd_timeval_t *ftv, hrtime_t *hrp, uint_t samples)
{
const fmd_timeops_t *ftop = fmd.d_clockops;
hrtime_t hrtbase, hrtmin = INT64_MAX;
struct timeval todbase;
uint_t i;
for (i = 0; i < samples; i++) {
hrtime_t t0, t1, delta;
struct timeval tod;
t0 = ftop->fto_gethrtime();
(void) ftop->fto_gettimeofday(&tod, NULL);
t1 = ftop->fto_gethrtime();
delta = t1 - t0;
if (delta < hrtmin) {
hrtmin = delta;
hrtbase = t0 + delta / 2;
todbase = tod;
}
}
if (ftv != NULL) {
ftv->ftv_sec = todbase.tv_sec;
ftv->ftv_nsec = todbase.tv_usec * (NANOSEC / MICROSEC);
}
if (hrp != NULL)
*hrp = hrtbase;
}
static void
fmd_time_hrt2ftv(hrtime_t hrt, fmd_timeval_t *ftv)
{
uint32_t sec, nsec, tmp;
tmp = (uint32_t)(hrt >> 30);
sec = tmp - (tmp >> 2);
sec = tmp - (sec >> 5);
sec = tmp + (sec >> 1);
sec = tmp - (sec >> 6) + 7;
sec = tmp - (sec >> 3);
sec = tmp + (sec >> 1);
sec = tmp + (sec >> 3);
sec = tmp + (sec >> 4);
tmp = (sec << 7) - sec - sec - sec;
tmp = (tmp << 7) - tmp - tmp - tmp;
tmp = (tmp << 7) - tmp - tmp - tmp;
nsec = (uint32_t)hrt - (tmp << 9);
while (nsec >= NANOSEC) {
nsec -= NANOSEC;
sec++;
}
ftv->ftv_sec = sec;
ftv->ftv_nsec = nsec;
}
void
fmd_time_hrt2tod(hrtime_t hrt_base, const fmd_timeval_t *tod_base,
hrtime_t hrt, fmd_timeval_t *ftv)
{
fmd_time_hrt2ftv(tod_base->ftv_sec * NANOSEC +
tod_base->ftv_nsec + (hrt - hrt_base), ftv);
}
void
fmd_time_tod2hrt(hrtime_t hrt_base, const fmd_timeval_t *tod_base,
const fmd_timeval_t *ftv, hrtime_t *hrtp)
{
hrtime_t tod_hrt = tod_base->ftv_sec * NANOSEC + tod_base->ftv_nsec;
hrtime_t ftv_hrt = ftv->ftv_sec * NANOSEC + ftv->ftv_nsec;
*hrtp = hrt_base - (tod_hrt - ftv_hrt);
}
hrtime_t
fmd_time_ena2hrt(hrtime_t hrt, uint64_t ena)
{
hrtime_t t0, mask;
switch (ENA_FORMAT(ena)) {
case FM_ENA_FMT1:
t0 = (ena & ENA_FMT1_TIME_MASK) >> ENA_FMT1_TIME_SHFT;
mask = ENA_FMT1_TIME_MASK >> ENA_FMT1_TIME_SHFT;
if (((hrt - t0) & ((mask + 1) >> 1)) == 0)
hrt -= (hrt - t0) & mask;
break;
case FM_ENA_FMT2:
t0 = (ena & ENA_FMT2_TIME_MASK) >> ENA_FMT2_TIME_SHFT;
mask = ENA_FMT2_TIME_MASK >> ENA_FMT2_TIME_SHFT;
if (((hrt - t0) & ((mask + 1) >> 1)) == 0)
hrt -= (hrt - t0) & mask;
break;
}
return (hrt);
}
static void *
fmd_simulator_init(void)
{
fmd_timesim_t *fts = fmd_alloc(sizeof (fmd_timesim_t), FMD_SLEEP);
struct timeval tv;
(void) pthread_mutex_init(&fts->fts_lock, NULL);
(void) pthread_cond_init(&fts->fts_cv, NULL);
(void) gettimeofday(&tv, NULL);
fts->fts_tod = (hrtime_t)tv.tv_sec * NANOSEC +
(hrtime_t)tv.tv_usec * (NANOSEC / MICROSEC);
fts->fts_hrt = 0;
fts->fts_cancel = 0;
fmd_dprintf(FMD_DBG_TMR, "simulator tod base tv_sec=%lx hrt=%llx\n",
tv.tv_sec, fts->fts_tod);
return (fts);
}
static void
fmd_simulator_fini(void *fts)
{
if (fts != NULL)
fmd_free(fts, sizeof (fmd_timesim_t));
}
static int
fmd_simulator_tod(struct timeval *tvp, void *tzp)
{
fmd_timesim_t *fts = fmd.d_clockptr;
hrtime_t tod, hrt, sec, rem;
(void) pthread_mutex_lock(&fts->fts_lock);
tod = fts->fts_tod;
hrt = fts->fts_hrt;
(void) pthread_mutex_unlock(&fts->fts_lock);
sec = tod / NANOSEC + hrt / NANOSEC;
rem = tod % NANOSEC + hrt % NANOSEC;
tvp->tv_sec = sec + rem / NANOSEC;
tvp->tv_usec = (rem % NANOSEC) / (NANOSEC / MICROSEC);
return (0);
}
static hrtime_t
fmd_simulator_hrt(void)
{
fmd_timesim_t *fts = fmd.d_clockptr;
hrtime_t hrt;
(void) pthread_mutex_lock(&fts->fts_lock);
hrt = fts->fts_hrt;
(void) pthread_mutex_unlock(&fts->fts_lock);
return (hrt);
}
static void
fmd_simulator_add(hrtime_t delta)
{
fmd_timesim_t *fts = fmd.d_clockptr;
(void) pthread_mutex_lock(&fts->fts_lock);
if (fts->fts_hrt + delta < fts->fts_hrt)
fts->fts_hrt = INT64_MAX;
else
fts->fts_hrt += delta;
TRACE((FMD_DBG_TMR, "hrt clock set %llx", fts->fts_hrt));
fmd_dprintf(FMD_DBG_TMR, "hrt clock set %llx\n", fts->fts_hrt);
(void) pthread_cond_broadcast(&fts->fts_cv);
(void) pthread_mutex_unlock(&fts->fts_lock);
}
static void
fmd_simulator_wait(hrtime_t delta)
{
fmd_timesim_t *fts = fmd.d_clockptr;
uint64_t hrt;
(void) pthread_mutex_lock(&fts->fts_lock);
if (fts->fts_hrt + delta < fts->fts_hrt)
hrt = UINT64_MAX;
else
hrt = fts->fts_hrt + delta;
while (fts->fts_hrt < hrt && fts->fts_cancel == 0)
(void) pthread_cond_wait(&fts->fts_cv, &fts->fts_lock);
if (fts->fts_cancel != 0)
fts->fts_cancel--;
(void) pthread_mutex_unlock(&fts->fts_lock);
}
static void
fmd_simulator_cancel(pthread_t tid)
{
fmd_timesim_t *fts = fmd.d_clockptr;
(void) pthread_mutex_lock(&fts->fts_lock);
fts->fts_cancel++;
(void) pthread_cond_signal(&fts->fts_cv);
(void) pthread_mutex_unlock(&fts->fts_lock);
}
static void
fmd_native_wait(hrtime_t delta)
{
timespec_t tv;
tv.tv_sec = delta / NANOSEC;
tv.tv_nsec = delta % NANOSEC;
(void) nanosleep(&tv, NULL);
}
static void
fmd_native_cancel(pthread_t tid)
{
(void) pthread_kill(tid, SIGALRM);
}
static void
fmd_time_vnop(void)
{
}
static void *
fmd_time_nop(void)
{
return (NULL);
}
const fmd_timeops_t fmd_timeops_native = {
(void *(*)())fmd_time_nop,
(void (*)())fmd_time_vnop,
gettimeofday,
gethrtime,
(void (*)())fmd_time_vnop,
fmd_native_wait,
fmd_native_cancel,
};
const fmd_timeops_t fmd_timeops_simulated = {
fmd_simulator_init,
fmd_simulator_fini,
fmd_simulator_tod,
fmd_simulator_hrt,
fmd_simulator_add,
fmd_simulator_wait,
fmd_simulator_cancel,
};