4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/fm/protocol.h>
33 #include <fmd_alloc.h>
34 #include <fmd_error.h>
39 fmd_time_gettimeofday(struct timeval
*tvp
)
41 if (fmd
.d_clockops
->fto_gettimeofday(tvp
, NULL
) != 0)
42 fmd_panic("failed to read time-of-day clock");
46 fmd_time_gethrtime(void)
48 return (fmd
.d_clockops
->fto_gethrtime());
52 fmd_time_addhrtime(hrtime_t delta
)
54 fmd
.d_clockops
->fto_addhrtime(delta
);
58 fmd_time_waithrtime(hrtime_t delta
)
60 fmd
.d_clockops
->fto_waithrtime(delta
);
64 fmd_time_waitcancel(pthread_t tid
)
66 fmd
.d_clockops
->fto_waitcancel(tid
);
70 * To synchronize TOD with a gethrtime() source, we repeatedly sample TOD in
71 * between two calls to gethrtime(), which places a reasonably tight bound on
72 * the high-resolution time that matches the TOD value we sampled. We repeat
73 * this process several times and ultimately select the sample where the two
74 * values of gethrtime() were closest. We then assign the average of those
75 * two high-resolution times to be the gethrtime() associated with that TOD.
78 fmd_time_sync(fmd_timeval_t
*ftv
, hrtime_t
*hrp
, uint_t samples
)
80 const fmd_timeops_t
*ftop
= fmd
.d_clockops
;
81 hrtime_t hrtbase
, hrtmin
= INT64_MAX
;
82 struct timeval todbase
;
85 for (i
= 0; i
< samples
; i
++) {
86 hrtime_t t0
, t1
, delta
;
89 t0
= ftop
->fto_gethrtime();
90 (void) ftop
->fto_gettimeofday(&tod
, NULL
);
91 t1
= ftop
->fto_gethrtime();
96 hrtbase
= t0
+ delta
/ 2;
102 ftv
->ftv_sec
= todbase
.tv_sec
;
103 ftv
->ftv_nsec
= todbase
.tv_usec
* (NANOSEC
/ MICROSEC
);
111 * Convert a high-resolution timestamp into 64-bit seconds and nanoseconds.
112 * For efficiency, the multiplication and division are expanded using the
113 * clever algorithm originally designed for the kernel in hrt2ts(). Refer to
114 * the comments in kernel/os/timers.c for an explanation of how it works.
117 fmd_time_hrt2ftv(hrtime_t hrt
, fmd_timeval_t
*ftv
)
119 uint32_t sec
, nsec
, tmp
;
121 tmp
= (uint32_t)(hrt
>> 30);
122 sec
= tmp
- (tmp
>> 2);
123 sec
= tmp
- (sec
>> 5);
124 sec
= tmp
+ (sec
>> 1);
125 sec
= tmp
- (sec
>> 6) + 7;
126 sec
= tmp
- (sec
>> 3);
127 sec
= tmp
+ (sec
>> 1);
128 sec
= tmp
+ (sec
>> 3);
129 sec
= tmp
+ (sec
>> 4);
130 tmp
= (sec
<< 7) - sec
- sec
- sec
;
131 tmp
= (tmp
<< 7) - tmp
- tmp
- tmp
;
132 tmp
= (tmp
<< 7) - tmp
- tmp
- tmp
;
133 nsec
= (uint32_t)hrt
- (tmp
<< 9);
135 while (nsec
>= NANOSEC
) {
141 ftv
->ftv_nsec
= nsec
;
145 * Convert a high-resolution time from gethrtime() to a TOD (fmd_timeval_t).
146 * We convert 'tod_base' to nanoseconds, adjust it based on the difference
147 * between the corresponding 'hrt_base' and the event high-res time 'hrt',
148 * and then repack the result into ftv_sec and ftv_nsec for our output.
151 fmd_time_hrt2tod(hrtime_t hrt_base
, const fmd_timeval_t
*tod_base
,
152 hrtime_t hrt
, fmd_timeval_t
*ftv
)
154 fmd_time_hrt2ftv(tod_base
->ftv_sec
* NANOSEC
+
155 tod_base
->ftv_nsec
+ (hrt
- hrt_base
), ftv
);
159 * Convert a TOD (fmd_timeval_t) to a high-resolution time from gethrtime().
160 * Note that since TOD occurred in the past, the resulting value may be a
161 * negative number according the current gethrtime() clock value.
164 fmd_time_tod2hrt(hrtime_t hrt_base
, const fmd_timeval_t
*tod_base
,
165 const fmd_timeval_t
*ftv
, hrtime_t
*hrtp
)
167 hrtime_t tod_hrt
= tod_base
->ftv_sec
* NANOSEC
+ tod_base
->ftv_nsec
;
168 hrtime_t ftv_hrt
= ftv
->ftv_sec
* NANOSEC
+ ftv
->ftv_nsec
;
170 *hrtp
= hrt_base
- (tod_hrt
- ftv_hrt
);
174 * Adjust a high-resolution time based on the low bits of time stored in ENA.
175 * The assumption here in that ENA won't wrap between the time it is computed
176 * and the time the error is queued (when we capture a full 64-bits of hrtime).
177 * We extract the relevant ENA time bits as 't0' and subtract the difference
178 * between these bits and the corresponding low bits of 'hrt' from 'hrt'.
180 * Under xVM dom0, the UE ereport is prepared after panic, therefore
181 * the full 64-bit hrtime of 't0' can be bigger than 'hrt'. In such case,
182 * we should just return 'hrt'.
184 * 't0' contains only the low bits of 64bit hrtime. It is tricky to tell
185 * whether 'hrt' or 't0' happened first. We assume there should be short
186 * period between 'hrt' and 't0', therefore to check which one came first, we
187 * test their subtraction against the highest bit of mask, if the bit is not
188 * set, then 't0' is earlier. This is equivalent to
189 * ((hrt - t0) & mask) < ((mask + 1) / 2)
192 fmd_time_ena2hrt(hrtime_t hrt
, uint64_t ena
)
196 switch (ENA_FORMAT(ena
)) {
198 t0
= (ena
& ENA_FMT1_TIME_MASK
) >> ENA_FMT1_TIME_SHFT
;
199 mask
= ENA_FMT1_TIME_MASK
>> ENA_FMT1_TIME_SHFT
;
200 if (((hrt
- t0
) & ((mask
+ 1) >> 1)) == 0)
201 hrt
-= (hrt
- t0
) & mask
;
204 t0
= (ena
& ENA_FMT2_TIME_MASK
) >> ENA_FMT2_TIME_SHFT
;
205 mask
= ENA_FMT2_TIME_MASK
>> ENA_FMT2_TIME_SHFT
;
206 if (((hrt
- t0
) & ((mask
+ 1) >> 1)) == 0)
207 hrt
-= (hrt
- t0
) & mask
;
215 * To implement a simulated clock, we keep track of an hrtime_t value which
216 * starts at zero and is incremented only by fmd_time_addhrtime() (i.e. when
217 * the driver of the simulation requests that the clock advance). We sample
218 * the native time-of-day clock once at the start of the simulation and then
219 * return subsequent time-of-day values by adjusting TOD using the hrtime_t
220 * clock setting. Simulated nanosleep (fmd_time_waithrtime() entry point) is
221 * implemented by waiting on fts->fts_cv for the hrtime_t to increment.
224 fmd_simulator_init(void)
226 fmd_timesim_t
*fts
= fmd_alloc(sizeof (fmd_timesim_t
), FMD_SLEEP
);
229 (void) pthread_mutex_init(&fts
->fts_lock
, NULL
);
230 (void) pthread_cond_init(&fts
->fts_cv
, NULL
);
231 (void) gettimeofday(&tv
, NULL
);
233 fts
->fts_tod
= (hrtime_t
)tv
.tv_sec
* NANOSEC
+
234 (hrtime_t
)tv
.tv_usec
* (NANOSEC
/ MICROSEC
);
239 fmd_dprintf(FMD_DBG_TMR
, "simulator tod base tv_sec=%lx hrt=%llx\n",
240 tv
.tv_sec
, fts
->fts_tod
);
246 fmd_simulator_fini(void *fts
)
249 fmd_free(fts
, sizeof (fmd_timesim_t
));
254 fmd_simulator_tod(struct timeval
*tvp
, void *tzp
)
256 fmd_timesim_t
*fts
= fmd
.d_clockptr
;
257 hrtime_t tod
, hrt
, sec
, rem
;
259 (void) pthread_mutex_lock(&fts
->fts_lock
);
264 (void) pthread_mutex_unlock(&fts
->fts_lock
);
266 sec
= tod
/ NANOSEC
+ hrt
/ NANOSEC
;
267 rem
= tod
% NANOSEC
+ hrt
% NANOSEC
;
269 tvp
->tv_sec
= sec
+ rem
/ NANOSEC
;
270 tvp
->tv_usec
= (rem
% NANOSEC
) / (NANOSEC
/ MICROSEC
);
276 fmd_simulator_hrt(void)
278 fmd_timesim_t
*fts
= fmd
.d_clockptr
;
281 (void) pthread_mutex_lock(&fts
->fts_lock
);
283 (void) pthread_mutex_unlock(&fts
->fts_lock
);
289 fmd_simulator_add(hrtime_t delta
)
291 fmd_timesim_t
*fts
= fmd
.d_clockptr
;
293 (void) pthread_mutex_lock(&fts
->fts_lock
);
295 if (fts
->fts_hrt
+ delta
< fts
->fts_hrt
)
296 fts
->fts_hrt
= INT64_MAX
; /* do not increment past apocalypse */
298 fts
->fts_hrt
+= delta
;
300 TRACE((FMD_DBG_TMR
, "hrt clock set %llx", fts
->fts_hrt
));
301 fmd_dprintf(FMD_DBG_TMR
, "hrt clock set %llx\n", fts
->fts_hrt
);
303 (void) pthread_cond_broadcast(&fts
->fts_cv
);
304 (void) pthread_mutex_unlock(&fts
->fts_lock
);
308 fmd_simulator_wait(hrtime_t delta
)
310 fmd_timesim_t
*fts
= fmd
.d_clockptr
;
313 (void) pthread_mutex_lock(&fts
->fts_lock
);
316 * If the delta causes time to wrap because we've reached the simulated
317 * apocalypse, then wait forever. We make 'hrt' unsigned so that the
318 * while-loop comparison fts_hrt < UINT64_MAX will always return true.
320 if (fts
->fts_hrt
+ delta
< fts
->fts_hrt
)
323 hrt
= fts
->fts_hrt
+ delta
;
325 while (fts
->fts_hrt
< hrt
&& fts
->fts_cancel
== 0)
326 (void) pthread_cond_wait(&fts
->fts_cv
, &fts
->fts_lock
);
328 if (fts
->fts_cancel
!= 0)
329 fts
->fts_cancel
--; /* cancel has been processed */
331 (void) pthread_mutex_unlock(&fts
->fts_lock
);
336 fmd_simulator_cancel(pthread_t tid
)
338 fmd_timesim_t
*fts
= fmd
.d_clockptr
;
340 (void) pthread_mutex_lock(&fts
->fts_lock
);
342 (void) pthread_cond_signal(&fts
->fts_cv
);
343 (void) pthread_mutex_unlock(&fts
->fts_lock
);
347 * Native time is implemented by calls to gethrtime() and gettimeofday(), which
348 * are stored directly in the native time ops-vector defined below. To wait on
349 * the native clock we use nanosleep(), which we can abort using a signal. The
350 * implementation assumes that callers will have a SIGALRM handler installed.
353 fmd_native_wait(hrtime_t delta
)
357 tv
.tv_sec
= delta
/ NANOSEC
;
358 tv
.tv_nsec
= delta
% NANOSEC
;
360 (void) nanosleep(&tv
, NULL
);
364 fmd_native_cancel(pthread_t tid
)
366 (void) pthread_kill(tid
, SIGALRM
);
375 const fmd_timeops_t fmd_timeops_native
= {
376 (void *(*)())fmd_time_nop
, /* fto_init */
377 (void (*)())fmd_time_nop
, /* fto_fini */
378 gettimeofday
, /* fto_gettimeofday */
379 gethrtime
, /* fto_gethrtime */
380 (void (*)())fmd_time_nop
, /* fto_addhrtime */
381 fmd_native_wait
, /* fto_waithrtime */
382 fmd_native_cancel
, /* fto_waitcancel */
385 const fmd_timeops_t fmd_timeops_simulated
= {
386 fmd_simulator_init
, /* fto_init */
387 fmd_simulator_fini
, /* fto_fini */
388 fmd_simulator_tod
, /* fto_gettimeofday */
389 fmd_simulator_hrt
, /* fto_gethrtime */
390 fmd_simulator_add
, /* fto_addhrtime */
391 fmd_simulator_wait
, /* fto_waithrtime */
392 fmd_simulator_cancel
, /* fto_waitcancel */