kern_ffclock.c revision 227986
1/*-
2 * Copyright (c) 2011 The University of Melbourne
3 * All rights reserved.
4 *
5 * This software was developed by Julien Ridoux at the University of Melbourne
6 * under sponsorship from the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/kern/kern_ffclock.c 227986 2011-11-26 01:44:37Z lstewart $");
32
33#include "opt_ffclock.h"
34
35#include <sys/param.h>
36#include <sys/bus.h>
37#include <sys/kernel.h>
38#include <sys/lock.h>
39#include <sys/module.h>
40#include <sys/mutex.h>
41#include <sys/priv.h>
42#include <sys/proc.h>
43#include <sys/sbuf.h>
44#include <sys/sysent.h>
45#include <sys/sysproto.h>
46#include <sys/sysctl.h>
47#include <sys/systm.h>
48#include <sys/timeffc.h>
49
50#ifdef FFCLOCK
51
52FEATURE(ffclock, "Feed-forward clock support");
53
54extern struct ffclock_estimate ffclock_estimate;
55extern struct bintime ffclock_boottime;
56extern int8_t ffclock_updated;
57extern struct mtx ffclock_mtx;
58
59/*
60 * Feed-forward clock absolute time. This should be the preferred way to read
61 * the feed-forward clock for "wall-clock" type time. The flags allow to compose
62 * various flavours of absolute time (e.g. with or without leap seconds taken
63 * into account). If valid pointers are provided, the ffcounter value and an
64 * upper bound on clock error associated with the bintime are provided.
65 * NOTE: use ffclock_convert_abs() to differ the conversion of a ffcounter value
66 * read earlier.
67 */
68void
69ffclock_abstime(ffcounter *ffcount, struct bintime *bt,
70    struct bintime *error_bound, uint32_t flags)
71{
72	struct ffclock_estimate cest;
73	ffcounter ffc;
74	ffcounter update_ffcount;
75	ffcounter ffdelta_error;
76
77	/* Get counter and corresponding time. */
78	if ((flags & FFCLOCK_FAST) == FFCLOCK_FAST)
79		ffclock_last_tick(&ffc, bt, flags);
80	else {
81		ffclock_read_counter(&ffc);
82		ffclock_convert_abs(ffc, bt, flags);
83	}
84
85	/* Current ffclock estimate, use update_ffcount as generation number. */
86	do {
87		update_ffcount = ffclock_estimate.update_ffcount;
88		bcopy(&ffclock_estimate, &cest, sizeof(struct ffclock_estimate));
89	} while (update_ffcount != ffclock_estimate.update_ffcount);
90
91	/*
92	 * Leap second adjustment. Total as seen by synchronisation algorithm
93	 * since it started. cest.leapsec_next is the ffcounter prediction of
94	 * when the next leapsecond occurs.
95	 */
96	if ((flags & FFCLOCK_LEAPSEC) == FFCLOCK_LEAPSEC) {
97		bt->sec -= cest.leapsec_total;
98		if (ffc > cest.leapsec_next)
99			bt->sec -= cest.leapsec;
100	}
101
102	/* Boot time adjustment, for uptime/monotonic clocks. */
103	if ((flags & FFCLOCK_UPTIME) == FFCLOCK_UPTIME) {
104		bintime_sub(bt, &ffclock_boottime);
105	}
106
107	/* Compute error bound if a valid pointer has been passed. */
108	if (error_bound) {
109		ffdelta_error = ffc - cest.update_ffcount;
110		ffclock_convert_diff(ffdelta_error, error_bound);
111		/* 18446744073709 = int(2^64/1e12), err_bound_rate in [ps/s] */
112		bintime_mul(error_bound, cest.errb_rate *
113		    (uint64_t)18446744073709LL);
114		/* 18446744073 = int(2^64 / 1e9), since err_abs in [ns] */
115		bintime_addx(error_bound, cest.errb_abs *
116		    (uint64_t)18446744073LL);
117	}
118
119	if (ffcount)
120		*ffcount = ffc;
121}
122
123/*
124 * Feed-forward difference clock. This should be the preferred way to convert a
125 * time interval in ffcounter values into a time interval in seconds. If a valid
126 * pointer is passed, an upper bound on the error in computing the time interval
127 * in seconds is provided.
128 */
129void
130ffclock_difftime(ffcounter ffdelta, struct bintime *bt,
131    struct bintime *error_bound)
132{
133	ffcounter update_ffcount;
134	uint32_t err_rate;
135
136	ffclock_convert_diff(ffdelta, bt);
137
138	if (error_bound) {
139		do {
140			update_ffcount = ffclock_estimate.update_ffcount;
141			err_rate = ffclock_estimate.errb_rate;
142		} while (update_ffcount != ffclock_estimate.update_ffcount);
143
144		ffclock_convert_diff(ffdelta, error_bound);
145		/* 18446744073709 = int(2^64/1e12), err_bound_rate in [ps/s] */
146		bintime_mul(error_bound, err_rate * (uint64_t)18446744073709LL);
147	}
148}
149
150/*
151 * Sysctl for the Feed-Forward Clock.
152 */
153
154static int ffclock_version = 2;
155SYSCTL_NODE(_kern, OID_AUTO, ffclock, CTLFLAG_RW, 0,
156    "Feed-Forward Clock Support");
157SYSCTL_INT(_kern_ffclock, OID_AUTO, version, CTLFLAG_RD, &ffclock_version, 0,
158    "Version of Feed-Forward Clock Support");
159
160/*
161 * Sysctl to select which clock is read when calling any of the
162 * [get]{bin,nano,micro}[up]time() functions.
163 */
164char *sysclocks[] = {"feedback", "feed-forward"};
165
166#define	NUM_SYSCLOCKS (sizeof(sysclocks) / sizeof(*sysclocks))
167
168/* Report or change the active timecounter hardware. */
169static int
170sysctl_kern_ffclock_choice(SYSCTL_HANDLER_ARGS)
171{
172	struct sbuf *s;
173	int clk, error;
174
175	s = sbuf_new_for_sysctl(NULL, NULL, 16 * NUM_SYSCLOCKS, req);
176	if (s == NULL)
177		return (ENOMEM);
178
179	for (clk = 0; clk < NUM_SYSCLOCKS; clk++) {
180		sbuf_cat(s, sysclocks[clk]);
181		if (clk + 1 < NUM_SYSCLOCKS)
182			sbuf_cat(s, " ");
183	}
184	error = sbuf_finish(s);
185	sbuf_delete(s);
186
187	return (error);
188}
189
190SYSCTL_PROC(_kern_ffclock, OID_AUTO, choice, CTLTYPE_STRING | CTLFLAG_RD,
191    0, 0, sysctl_kern_ffclock_choice, "A", "Clock paradigms available");
192
193extern int sysclock_active;
194
195static int
196sysctl_kern_ffclock_active(SYSCTL_HANDLER_ARGS)
197{
198	char newclock[32];
199	int error;
200
201	switch (sysclock_active) {
202	case SYSCLOCK_FBCK:
203		strlcpy(newclock, sysclocks[SYSCLOCK_FBCK], sizeof(newclock));
204		break;
205	case SYSCLOCK_FFWD:
206		strlcpy(newclock, sysclocks[SYSCLOCK_FFWD], sizeof(newclock));
207		break;
208	}
209
210	error = sysctl_handle_string(oidp, &newclock[0], sizeof(newclock), req);
211	if (error != 0 || req->newptr == NULL)
212		return (error);
213	if (strncmp(newclock, sysclocks[SYSCLOCK_FBCK],
214	    sizeof(sysclocks[SYSCLOCK_FBCK])) == 0)
215		sysclock_active = SYSCLOCK_FBCK;
216	else if (strncmp(newclock, sysclocks[SYSCLOCK_FFWD],
217	    sizeof(sysclocks[SYSCLOCK_FFWD])) == 0)
218		sysclock_active = SYSCLOCK_FFWD;
219	else
220		return (EINVAL);
221
222	return (error);
223}
224
225SYSCTL_PROC(_kern_ffclock, OID_AUTO, active, CTLTYPE_STRING | CTLFLAG_RW,
226    0, 0, sysctl_kern_ffclock_active, "A", "Kernel clock selected");
227
228int sysctl_kern_ffclock_ffcounter_bypass = 0;
229
230SYSCTL_INT(_kern_ffclock, OID_AUTO, ffcounter_bypass, CTLFLAG_RW,
231    &sysctl_kern_ffclock_ffcounter_bypass, 0,
232    "Use reliable hardware timecounter as the Feed-Forward Counter");
233
234/*
235 * High level functions to access the Feed-Forward Clock.
236 */
237void
238ffclock_bintime(struct bintime *bt)
239{
240
241	ffclock_abstime(NULL, bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
242}
243
244void
245ffclock_nanotime(struct timespec *tsp)
246{
247	struct bintime bt;
248
249	ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
250	bintime2timespec(&bt, tsp);
251}
252
253void
254ffclock_microtime(struct timeval *tvp)
255{
256	struct bintime bt;
257
258	ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
259	bintime2timeval(&bt, tvp);
260}
261
262void
263ffclock_getbintime(struct bintime *bt)
264{
265
266	ffclock_abstime(NULL, bt, NULL,
267	    FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
268}
269
270void
271ffclock_getnanotime(struct timespec *tsp)
272{
273	struct bintime bt;
274
275	ffclock_abstime(NULL, &bt, NULL,
276	    FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
277	bintime2timespec(&bt, tsp);
278}
279
280void
281ffclock_getmicrotime(struct timeval *tvp)
282{
283	struct bintime bt;
284
285	ffclock_abstime(NULL, &bt, NULL,
286	    FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
287	bintime2timeval(&bt, tvp);
288}
289
290void
291ffclock_binuptime(struct bintime *bt)
292{
293
294	ffclock_abstime(NULL, bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
295}
296
297void
298ffclock_nanouptime(struct timespec *tsp)
299{
300	struct bintime bt;
301
302	ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
303	bintime2timespec(&bt, tsp);
304}
305
306void
307ffclock_microuptime(struct timeval *tvp)
308{
309	struct bintime bt;
310
311	ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
312	bintime2timeval(&bt, tvp);
313}
314
315void
316ffclock_getbinuptime(struct bintime *bt)
317{
318
319	ffclock_abstime(NULL, bt, NULL,
320	    FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
321}
322
323void
324ffclock_getnanouptime(struct timespec *tsp)
325{
326	struct bintime bt;
327
328	ffclock_abstime(NULL, &bt, NULL,
329	    FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
330	bintime2timespec(&bt, tsp);
331}
332
333void
334ffclock_getmicrouptime(struct timeval *tvp)
335{
336	struct bintime bt;
337
338	ffclock_abstime(NULL, &bt, NULL,
339	    FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
340	bintime2timeval(&bt, tvp);
341}
342
343void
344ffclock_bindifftime(ffcounter ffdelta, struct bintime *bt)
345{
346
347	ffclock_difftime(ffdelta, bt, NULL);
348}
349
350void
351ffclock_nanodifftime(ffcounter ffdelta, struct timespec *tsp)
352{
353	struct bintime bt;
354
355	ffclock_difftime(ffdelta, &bt, NULL);
356	bintime2timespec(&bt, tsp);
357}
358
359void
360ffclock_microdifftime(ffcounter ffdelta, struct timeval *tvp)
361{
362	struct bintime bt;
363
364	ffclock_difftime(ffdelta, &bt, NULL);
365	bintime2timeval(&bt, tvp);
366}
367
368/*
369 * System call allowing userland applications to retrieve the current value of
370 * the Feed-Forward Clock counter.
371 */
372#ifndef _SYS_SYSPROTO_H_
373struct ffclock_getcounter_args {
374	ffcounter *ffcount;
375};
376#endif
377/* ARGSUSED */
378int
379sys_ffclock_getcounter(struct thread *td, struct ffclock_getcounter_args *uap)
380{
381	ffcounter ffcount;
382	int error;
383
384	ffcount = 0;
385	ffclock_read_counter(&ffcount);
386	if (ffcount == 0)
387		return (EAGAIN);
388	error = copyout(&ffcount, uap->ffcount, sizeof(ffcounter));
389
390	return (error);
391}
392
393/*
394 * System call allowing the synchronisation daemon to push new feed-foward clock
395 * estimates to the kernel. Acquire ffclock_mtx to prevent concurrent updates
396 * and ensure data consistency.
397 * NOTE: ffclock_updated signals the fftimehands that new estimates are
398 * available. The updated estimates are picked up by the fftimehands on next
399 * tick, which could take as long as 1/hz seconds (if ticks are not missed).
400 */
401#ifndef _SYS_SYSPROTO_H_
402struct ffclock_setestimate_args {
403	struct ffclock_estimate *cest;
404};
405#endif
406/* ARGSUSED */
407int
408sys_ffclock_setestimate(struct thread *td, struct ffclock_setestimate_args *uap)
409{
410	struct ffclock_estimate cest;
411	int error;
412
413	/* Reuse of PRIV_CLOCK_SETTIME. */
414	if ((error = priv_check(td, PRIV_CLOCK_SETTIME)) != 0)
415		return (error);
416
417	if ((error = copyin(uap->cest, &cest, sizeof(struct ffclock_estimate)))
418	    != 0)
419		return (error);
420
421	mtx_lock(&ffclock_mtx);
422	memcpy(&ffclock_estimate, &cest, sizeof(struct ffclock_estimate));
423	ffclock_updated++;
424	mtx_unlock(&ffclock_mtx);
425	return (error);
426}
427
428/*
429 * System call allowing userland applications to retrieve the clock estimates
430 * stored within the kernel. It is useful to kickstart the synchronisation
431 * daemon with the kernel's knowledge of hardware timecounter.
432 */
433#ifndef _SYS_SYSPROTO_H_
434struct ffclock_getestimate_args {
435	struct ffclock_estimate *cest;
436};
437#endif
438/* ARGSUSED */
439int
440sys_ffclock_getestimate(struct thread *td, struct ffclock_getestimate_args *uap)
441{
442	struct ffclock_estimate cest;
443	int error;
444
445	mtx_lock(&ffclock_mtx);
446	memcpy(&cest, &ffclock_estimate, sizeof(struct ffclock_estimate));
447	mtx_unlock(&ffclock_mtx);
448	error = copyout(&cest, uap->cest, sizeof(struct ffclock_estimate));
449	return (error);
450}
451
452#else /* !FFCLOCK */
453
454int
455sys_ffclock_getcounter(struct thread *td, struct ffclock_getcounter_args *uap)
456{
457
458	return (ENOSYS);
459}
460
461int
462sys_ffclock_setestimate(struct thread *td, struct ffclock_setestimate_args *uap)
463{
464
465	return (ENOSYS);
466}
467
468int
469sys_ffclock_getestimate(struct thread *td, struct ffclock_getestimate_args *uap)
470{
471
472	return (ENOSYS);
473}
474
475#endif /* FFCLOCK */
476