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