xref: /freebsd/sys/kern/kern_ffclock.c (revision f464c5ccb5f0bc406ee1c57dae8cfbb283ef79dd)
1*f464c5ccSLawrence Stewart /*-
2*f464c5ccSLawrence Stewart  * Copyright (c) 2011 The University of Melbourne
3*f464c5ccSLawrence Stewart  * All rights reserved.
4*f464c5ccSLawrence Stewart  *
5*f464c5ccSLawrence Stewart  * This software was developed by Julien Ridoux at the University of Melbourne
6*f464c5ccSLawrence Stewart  * under sponsorship from the FreeBSD Foundation.
7*f464c5ccSLawrence Stewart  *
8*f464c5ccSLawrence Stewart  * Redistribution and use in source and binary forms, with or without
9*f464c5ccSLawrence Stewart  * modification, are permitted provided that the following conditions
10*f464c5ccSLawrence Stewart  * are met:
11*f464c5ccSLawrence Stewart  * 1. Redistributions of source code must retain the above copyright
12*f464c5ccSLawrence Stewart  *    notice, this list of conditions and the following disclaimer.
13*f464c5ccSLawrence Stewart  * 2. Redistributions in binary form must reproduce the above copyright
14*f464c5ccSLawrence Stewart  *    notice, this list of conditions and the following disclaimer in the
15*f464c5ccSLawrence Stewart  *    documentation and/or other materials provided with the distribution.
16*f464c5ccSLawrence Stewart  *
17*f464c5ccSLawrence Stewart  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18*f464c5ccSLawrence Stewart  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*f464c5ccSLawrence Stewart  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*f464c5ccSLawrence Stewart  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21*f464c5ccSLawrence Stewart  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*f464c5ccSLawrence Stewart  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*f464c5ccSLawrence Stewart  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*f464c5ccSLawrence Stewart  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*f464c5ccSLawrence Stewart  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*f464c5ccSLawrence Stewart  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*f464c5ccSLawrence Stewart  * SUCH DAMAGE.
28*f464c5ccSLawrence Stewart  */
29*f464c5ccSLawrence Stewart 
30*f464c5ccSLawrence Stewart #include <sys/cdefs.h>
31*f464c5ccSLawrence Stewart __FBSDID("$FreeBSD$");
32*f464c5ccSLawrence Stewart 
33*f464c5ccSLawrence Stewart #include <sys/param.h>
34*f464c5ccSLawrence Stewart #include <sys/systm.h>
35*f464c5ccSLawrence Stewart #include <sys/timeffc.h>
36*f464c5ccSLawrence Stewart 
37*f464c5ccSLawrence Stewart extern struct ffclock_estimate ffclock_estimate;
38*f464c5ccSLawrence Stewart extern struct bintime ffclock_boottime;
39*f464c5ccSLawrence Stewart 
40*f464c5ccSLawrence Stewart /*
41*f464c5ccSLawrence Stewart  * Feed-forward clock absolute time. This should be the preferred way to read
42*f464c5ccSLawrence Stewart  * the feed-forward clock for "wall-clock" type time. The flags allow to compose
43*f464c5ccSLawrence Stewart  * various flavours of absolute time (e.g. with or without leap seconds taken
44*f464c5ccSLawrence Stewart  * into account). If valid pointers are provided, the ffcounter value and an
45*f464c5ccSLawrence Stewart  * upper bound on clock error associated with the bintime are provided.
46*f464c5ccSLawrence Stewart  * NOTE: use ffclock_convert_abs() to differ the conversion of a ffcounter value
47*f464c5ccSLawrence Stewart  * read earlier.
48*f464c5ccSLawrence Stewart  */
49*f464c5ccSLawrence Stewart void
50*f464c5ccSLawrence Stewart ffclock_abstime(ffcounter *ffcount, struct bintime *bt,
51*f464c5ccSLawrence Stewart     struct bintime *error_bound, uint32_t flags)
52*f464c5ccSLawrence Stewart {
53*f464c5ccSLawrence Stewart 	struct ffclock_estimate cest;
54*f464c5ccSLawrence Stewart 	ffcounter ffc;
55*f464c5ccSLawrence Stewart 	ffcounter update_ffcount;
56*f464c5ccSLawrence Stewart 	ffcounter ffdelta_error;
57*f464c5ccSLawrence Stewart 
58*f464c5ccSLawrence Stewart 	/* Get counter and corresponding time. */
59*f464c5ccSLawrence Stewart 	if ((flags & FFCLOCK_FAST) == FFCLOCK_FAST)
60*f464c5ccSLawrence Stewart 		ffclock_last_tick(&ffc, bt, flags);
61*f464c5ccSLawrence Stewart 	else {
62*f464c5ccSLawrence Stewart 		ffclock_read_counter(&ffc);
63*f464c5ccSLawrence Stewart 		ffclock_convert_abs(ffc, bt, flags);
64*f464c5ccSLawrence Stewart 	}
65*f464c5ccSLawrence Stewart 
66*f464c5ccSLawrence Stewart 	/* Current ffclock estimate, use update_ffcount as generation number. */
67*f464c5ccSLawrence Stewart 	do {
68*f464c5ccSLawrence Stewart 		update_ffcount = ffclock_estimate.update_ffcount;
69*f464c5ccSLawrence Stewart 		bcopy(&ffclock_estimate, &cest, sizeof(struct ffclock_estimate));
70*f464c5ccSLawrence Stewart 	} while (update_ffcount != ffclock_estimate.update_ffcount);
71*f464c5ccSLawrence Stewart 
72*f464c5ccSLawrence Stewart 	/*
73*f464c5ccSLawrence Stewart 	 * Leap second adjustment. Total as seen by synchronisation algorithm
74*f464c5ccSLawrence Stewart 	 * since it started. cest.leapsec_next is the ffcounter prediction of
75*f464c5ccSLawrence Stewart 	 * when the next leapsecond occurs.
76*f464c5ccSLawrence Stewart 	 */
77*f464c5ccSLawrence Stewart 	if ((flags & FFCLOCK_LEAPSEC) == FFCLOCK_LEAPSEC) {
78*f464c5ccSLawrence Stewart 		bt->sec -= cest.leapsec_total;
79*f464c5ccSLawrence Stewart 		if (ffc > cest.leapsec_next)
80*f464c5ccSLawrence Stewart 			bt->sec -= cest.leapsec;
81*f464c5ccSLawrence Stewart 	}
82*f464c5ccSLawrence Stewart 
83*f464c5ccSLawrence Stewart 	/* Boot time adjustment, for uptime/monotonic clocks. */
84*f464c5ccSLawrence Stewart 	if ((flags & FFCLOCK_UPTIME) == FFCLOCK_UPTIME) {
85*f464c5ccSLawrence Stewart 		bintime_sub(bt, &ffclock_boottime);
86*f464c5ccSLawrence Stewart 	}
87*f464c5ccSLawrence Stewart 
88*f464c5ccSLawrence Stewart 	/* Compute error bound if a valid pointer has been passed. */
89*f464c5ccSLawrence Stewart 	if (error_bound) {
90*f464c5ccSLawrence Stewart 		ffdelta_error = ffc - cest.update_ffcount;
91*f464c5ccSLawrence Stewart 		ffclock_convert_diff(ffdelta_error, error_bound);
92*f464c5ccSLawrence Stewart 		/* 18446744073709 = int(2^64/1e12), err_bound_rate in [ps/s] */
93*f464c5ccSLawrence Stewart 		bintime_mul(error_bound, cest.errb_rate *
94*f464c5ccSLawrence Stewart 		    (uint64_t)18446744073709LL);
95*f464c5ccSLawrence Stewart 		/* 18446744073 = int(2^64 / 1e9), since err_abs in [ns] */
96*f464c5ccSLawrence Stewart 		bintime_addx(error_bound, cest.errb_abs *
97*f464c5ccSLawrence Stewart 		    (uint64_t)18446744073LL);
98*f464c5ccSLawrence Stewart 	}
99*f464c5ccSLawrence Stewart 
100*f464c5ccSLawrence Stewart 	if (ffcount)
101*f464c5ccSLawrence Stewart 		*ffcount = ffc;
102*f464c5ccSLawrence Stewart }
103*f464c5ccSLawrence Stewart 
104*f464c5ccSLawrence Stewart /*
105*f464c5ccSLawrence Stewart  * Feed-forward difference clock. This should be the preferred way to convert a
106*f464c5ccSLawrence Stewart  * time interval in ffcounter values into a time interval in seconds. If a valid
107*f464c5ccSLawrence Stewart  * pointer is passed, an upper bound on the error in computing the time interval
108*f464c5ccSLawrence Stewart  * in seconds is provided.
109*f464c5ccSLawrence Stewart  */
110*f464c5ccSLawrence Stewart void
111*f464c5ccSLawrence Stewart ffclock_difftime(ffcounter ffdelta, struct bintime *bt,
112*f464c5ccSLawrence Stewart     struct bintime *error_bound)
113*f464c5ccSLawrence Stewart {
114*f464c5ccSLawrence Stewart 	ffcounter update_ffcount;
115*f464c5ccSLawrence Stewart 	uint32_t err_rate;
116*f464c5ccSLawrence Stewart 
117*f464c5ccSLawrence Stewart 	ffclock_convert_diff(ffdelta, bt);
118*f464c5ccSLawrence Stewart 
119*f464c5ccSLawrence Stewart 	if (error_bound) {
120*f464c5ccSLawrence Stewart 		do {
121*f464c5ccSLawrence Stewart 			update_ffcount = ffclock_estimate.update_ffcount;
122*f464c5ccSLawrence Stewart 			err_rate = ffclock_estimate.errb_rate;
123*f464c5ccSLawrence Stewart 		} while (update_ffcount != ffclock_estimate.update_ffcount);
124*f464c5ccSLawrence Stewart 
125*f464c5ccSLawrence Stewart 		ffclock_convert_diff(ffdelta, error_bound);
126*f464c5ccSLawrence Stewart 		/* 18446744073709 = int(2^64/1e12), err_bound_rate in [ps/s] */
127*f464c5ccSLawrence Stewart 		bintime_mul(error_bound, err_rate * (uint64_t)18446744073709LL);
128*f464c5ccSLawrence Stewart 	}
129*f464c5ccSLawrence Stewart }
130