The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_ffclock.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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$");
   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 
   52 FEATURE(ffclock, "Feed-forward clock support");
   53 
   54 extern struct ffclock_estimate ffclock_estimate;
   55 extern struct bintime ffclock_boottime;
   56 extern int8_t ffclock_updated;
   57 extern 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  */
   68 void
   69 ffclock_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  */
  129 void
  130 ffclock_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  * Create a new kern.sysclock sysctl node, which will be home to some generic
  152  * sysclock configuration variables. Feed-forward clock specific variables will
  153  * live under the ffclock subnode.
  154  */
  155 
  156 SYSCTL_NODE(_kern, OID_AUTO, sysclock, CTLFLAG_RW, 0,
  157     "System clock related configuration");
  158 SYSCTL_NODE(_kern_sysclock, OID_AUTO, ffclock, CTLFLAG_RW, 0,
  159     "Feed-forward clock configuration");
  160 
  161 static char *sysclocks[] = {"feedback", "feed-forward"};
  162 #define MAX_SYSCLOCK_NAME_LEN 16
  163 #define NUM_SYSCLOCKS nitems(sysclocks)
  164 
  165 static int ffclock_version = 2;
  166 SYSCTL_INT(_kern_sysclock_ffclock, OID_AUTO, version, CTLFLAG_RD,
  167     &ffclock_version, 0, "Feed-forward clock kernel version");
  168 
  169 /* List available sysclocks. */
  170 static int
  171 sysctl_kern_sysclock_available(SYSCTL_HANDLER_ARGS)
  172 {
  173         struct sbuf *s;
  174         int clk, error;
  175 
  176         s = sbuf_new_for_sysctl(NULL, NULL,
  177             MAX_SYSCLOCK_NAME_LEN * NUM_SYSCLOCKS, req);
  178         if (s == NULL)
  179                 return (ENOMEM);
  180 
  181         for (clk = 0; clk < NUM_SYSCLOCKS; clk++) {
  182                 sbuf_cat(s, sysclocks[clk]);
  183                 if (clk + 1 < NUM_SYSCLOCKS)
  184                         sbuf_cat(s, " ");
  185         }
  186         error = sbuf_finish(s);
  187         sbuf_delete(s);
  188 
  189         return (error);
  190 }
  191 
  192 SYSCTL_PROC(_kern_sysclock, OID_AUTO, available, CTLTYPE_STRING | CTLFLAG_RD,
  193     0, 0, sysctl_kern_sysclock_available, "A",
  194     "List of available system clocks");
  195 
  196 /*
  197  * Return the name of the active system clock if read, or attempt to change
  198  * the active system clock to the user specified one if written to. The active
  199  * system clock is read when calling any of the [get]{bin,nano,micro}[up]time()
  200  * functions.
  201  */
  202 static int
  203 sysctl_kern_sysclock_active(SYSCTL_HANDLER_ARGS)
  204 {
  205         char newclock[MAX_SYSCLOCK_NAME_LEN];
  206         int error;
  207         int clk;
  208 
  209         /* Return the name of the current active sysclock. */
  210         strlcpy(newclock, sysclocks[sysclock_active], sizeof(newclock));
  211         error = sysctl_handle_string(oidp, newclock, sizeof(newclock), req);
  212 
  213         /* Check for error or no change */
  214         if (error != 0 || req->newptr == NULL)
  215                 goto done;
  216 
  217         /* Change the active sysclock to the user specified one: */
  218         error = EINVAL;
  219         for (clk = 0; clk < NUM_SYSCLOCKS; clk++) {
  220                 if (strncmp(newclock, sysclocks[clk],
  221                     MAX_SYSCLOCK_NAME_LEN - 1)) {
  222                         continue;
  223                 }
  224                 sysclock_active = clk;
  225                 error = 0;
  226                 break;
  227         }
  228 done:
  229         return (error);
  230 }
  231 
  232 SYSCTL_PROC(_kern_sysclock, OID_AUTO, active, CTLTYPE_STRING | CTLFLAG_RW,
  233     0, 0, sysctl_kern_sysclock_active, "A",
  234     "Name of the active system clock which is currently serving time");
  235 
  236 static int sysctl_kern_ffclock_ffcounter_bypass = 0;
  237 SYSCTL_INT(_kern_sysclock_ffclock, OID_AUTO, ffcounter_bypass, CTLFLAG_RW,
  238     &sysctl_kern_ffclock_ffcounter_bypass, 0,
  239     "Use reliable hardware timecounter as the feed-forward counter");
  240 
  241 /*
  242  * High level functions to access the Feed-Forward Clock.
  243  */
  244 void
  245 ffclock_bintime(struct bintime *bt)
  246 {
  247 
  248         ffclock_abstime(NULL, bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
  249 }
  250 
  251 void
  252 ffclock_nanotime(struct timespec *tsp)
  253 {
  254         struct bintime bt;
  255 
  256         ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
  257         bintime2timespec(&bt, tsp);
  258 }
  259 
  260 void
  261 ffclock_microtime(struct timeval *tvp)
  262 {
  263         struct bintime bt;
  264 
  265         ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
  266         bintime2timeval(&bt, tvp);
  267 }
  268 
  269 void
  270 ffclock_getbintime(struct bintime *bt)
  271 {
  272 
  273         ffclock_abstime(NULL, bt, NULL,
  274             FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
  275 }
  276 
  277 void
  278 ffclock_getnanotime(struct timespec *tsp)
  279 {
  280         struct bintime bt;
  281 
  282         ffclock_abstime(NULL, &bt, NULL,
  283             FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
  284         bintime2timespec(&bt, tsp);
  285 }
  286 
  287 void
  288 ffclock_getmicrotime(struct timeval *tvp)
  289 {
  290         struct bintime bt;
  291 
  292         ffclock_abstime(NULL, &bt, NULL,
  293             FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
  294         bintime2timeval(&bt, tvp);
  295 }
  296 
  297 void
  298 ffclock_binuptime(struct bintime *bt)
  299 {
  300 
  301         ffclock_abstime(NULL, bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
  302 }
  303 
  304 void
  305 ffclock_nanouptime(struct timespec *tsp)
  306 {
  307         struct bintime bt;
  308 
  309         ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
  310         bintime2timespec(&bt, tsp);
  311 }
  312 
  313 void
  314 ffclock_microuptime(struct timeval *tvp)
  315 {
  316         struct bintime bt;
  317 
  318         ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
  319         bintime2timeval(&bt, tvp);
  320 }
  321 
  322 void
  323 ffclock_getbinuptime(struct bintime *bt)
  324 {
  325 
  326         ffclock_abstime(NULL, bt, NULL,
  327             FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
  328 }
  329 
  330 void
  331 ffclock_getnanouptime(struct timespec *tsp)
  332 {
  333         struct bintime bt;
  334 
  335         ffclock_abstime(NULL, &bt, NULL,
  336             FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
  337         bintime2timespec(&bt, tsp);
  338 }
  339 
  340 void
  341 ffclock_getmicrouptime(struct timeval *tvp)
  342 {
  343         struct bintime bt;
  344 
  345         ffclock_abstime(NULL, &bt, NULL,
  346             FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
  347         bintime2timeval(&bt, tvp);
  348 }
  349 
  350 void
  351 ffclock_bindifftime(ffcounter ffdelta, struct bintime *bt)
  352 {
  353 
  354         ffclock_difftime(ffdelta, bt, NULL);
  355 }
  356 
  357 void
  358 ffclock_nanodifftime(ffcounter ffdelta, struct timespec *tsp)
  359 {
  360         struct bintime bt;
  361 
  362         ffclock_difftime(ffdelta, &bt, NULL);
  363         bintime2timespec(&bt, tsp);
  364 }
  365 
  366 void
  367 ffclock_microdifftime(ffcounter ffdelta, struct timeval *tvp)
  368 {
  369         struct bintime bt;
  370 
  371         ffclock_difftime(ffdelta, &bt, NULL);
  372         bintime2timeval(&bt, tvp);
  373 }
  374 
  375 /*
  376  * System call allowing userland applications to retrieve the current value of
  377  * the Feed-Forward Clock counter.
  378  */
  379 #ifndef _SYS_SYSPROTO_H_
  380 struct ffclock_getcounter_args {
  381         ffcounter *ffcount;
  382 };
  383 #endif
  384 /* ARGSUSED */
  385 int
  386 sys_ffclock_getcounter(struct thread *td, struct ffclock_getcounter_args *uap)
  387 {
  388         ffcounter ffcount;
  389         int error;
  390 
  391         ffcount = 0;
  392         ffclock_read_counter(&ffcount);
  393         if (ffcount == 0)
  394                 return (EAGAIN);
  395         error = copyout(&ffcount, uap->ffcount, sizeof(ffcounter));
  396 
  397         return (error);
  398 }
  399 
  400 /*
  401  * System call allowing the synchronisation daemon to push new feed-foward clock
  402  * estimates to the kernel. Acquire ffclock_mtx to prevent concurrent updates
  403  * and ensure data consistency.
  404  * NOTE: ffclock_updated signals the fftimehands that new estimates are
  405  * available. The updated estimates are picked up by the fftimehands on next
  406  * tick, which could take as long as 1/hz seconds (if ticks are not missed).
  407  */
  408 #ifndef _SYS_SYSPROTO_H_
  409 struct ffclock_setestimate_args {
  410         struct ffclock_estimate *cest;
  411 };
  412 #endif
  413 /* ARGSUSED */
  414 int
  415 sys_ffclock_setestimate(struct thread *td, struct ffclock_setestimate_args *uap)
  416 {
  417         struct ffclock_estimate cest;
  418         int error;
  419 
  420         /* Reuse of PRIV_CLOCK_SETTIME. */
  421         if ((error = priv_check(td, PRIV_CLOCK_SETTIME)) != 0)
  422                 return (error);
  423 
  424         if ((error = copyin(uap->cest, &cest, sizeof(struct ffclock_estimate)))
  425             != 0)
  426                 return (error);
  427 
  428         mtx_lock(&ffclock_mtx);
  429         memcpy(&ffclock_estimate, &cest, sizeof(struct ffclock_estimate));
  430         ffclock_updated++;
  431         mtx_unlock(&ffclock_mtx);
  432         return (error);
  433 }
  434 
  435 /*
  436  * System call allowing userland applications to retrieve the clock estimates
  437  * stored within the kernel. It is useful to kickstart the synchronisation
  438  * daemon with the kernel's knowledge of hardware timecounter.
  439  */
  440 #ifndef _SYS_SYSPROTO_H_
  441 struct ffclock_getestimate_args {
  442         struct ffclock_estimate *cest;
  443 };
  444 #endif
  445 /* ARGSUSED */
  446 int
  447 sys_ffclock_getestimate(struct thread *td, struct ffclock_getestimate_args *uap)
  448 {
  449         struct ffclock_estimate cest;
  450         int error;
  451 
  452         mtx_lock(&ffclock_mtx);
  453         memcpy(&cest, &ffclock_estimate, sizeof(struct ffclock_estimate));
  454         mtx_unlock(&ffclock_mtx);
  455         error = copyout(&cest, uap->cest, sizeof(struct ffclock_estimate));
  456         return (error);
  457 }
  458 
  459 #else /* !FFCLOCK */
  460 
  461 int
  462 sys_ffclock_getcounter(struct thread *td, struct ffclock_getcounter_args *uap)
  463 {
  464 
  465         return (ENOSYS);
  466 }
  467 
  468 int
  469 sys_ffclock_setestimate(struct thread *td, struct ffclock_setestimate_args *uap)
  470 {
  471 
  472         return (ENOSYS);
  473 }
  474 
  475 int
  476 sys_ffclock_getestimate(struct thread *td, struct ffclock_getestimate_args *uap)
  477 {
  478 
  479         return (ENOSYS);
  480 }
  481 
  482 #endif /* FFCLOCK */

Cache object: 3de4790944cf774c76523f4d9e8b8ea9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.