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: releng/10.0/sys/kern/kern_ffclock.c 228173 2011-12-01 07:19:13Z 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 
   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 (sizeof(sysclocks) / sizeof(*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 clk, error;
  207 
  208         if (req->newptr == NULL) {
  209                 /* Return the name of the current active sysclock. */
  210                 strlcpy(newclock, sysclocks[sysclock_active], sizeof(newclock));
  211                 error = sysctl_handle_string(oidp, newclock,
  212                     sizeof(newclock), req);
  213         } else {
  214                 /* Change the active sysclock to the user specified one. */
  215                 error = EINVAL;
  216                 for (clk = 0; clk < NUM_SYSCLOCKS; clk++) {
  217                         if (strncmp((char *)req->newptr, sysclocks[clk],
  218                             strlen(sysclocks[clk])) == 0) {
  219                                 sysclock_active = clk;
  220                                 error = 0;
  221                                 break;
  222                         }
  223                 }
  224         }
  225 
  226         return (error);
  227 }
  228 
  229 SYSCTL_PROC(_kern_sysclock, OID_AUTO, active, CTLTYPE_STRING | CTLFLAG_RW,
  230     0, 0, sysctl_kern_sysclock_active, "A",
  231     "Name of the active system clock which is currently serving time");
  232 
  233 static int sysctl_kern_ffclock_ffcounter_bypass = 0;
  234 SYSCTL_INT(_kern_sysclock_ffclock, OID_AUTO, ffcounter_bypass, CTLFLAG_RW,
  235     &sysctl_kern_ffclock_ffcounter_bypass, 0,
  236     "Use reliable hardware timecounter as the feed-forward counter");
  237 
  238 /*
  239  * High level functions to access the Feed-Forward Clock.
  240  */
  241 void
  242 ffclock_bintime(struct bintime *bt)
  243 {
  244 
  245         ffclock_abstime(NULL, bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
  246 }
  247 
  248 void
  249 ffclock_nanotime(struct timespec *tsp)
  250 {
  251         struct bintime bt;
  252 
  253         ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
  254         bintime2timespec(&bt, tsp);
  255 }
  256 
  257 void
  258 ffclock_microtime(struct timeval *tvp)
  259 {
  260         struct bintime bt;
  261 
  262         ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
  263         bintime2timeval(&bt, tvp);
  264 }
  265 
  266 void
  267 ffclock_getbintime(struct bintime *bt)
  268 {
  269 
  270         ffclock_abstime(NULL, bt, NULL,
  271             FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
  272 }
  273 
  274 void
  275 ffclock_getnanotime(struct timespec *tsp)
  276 {
  277         struct bintime bt;
  278 
  279         ffclock_abstime(NULL, &bt, NULL,
  280             FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
  281         bintime2timespec(&bt, tsp);
  282 }
  283 
  284 void
  285 ffclock_getmicrotime(struct timeval *tvp)
  286 {
  287         struct bintime bt;
  288 
  289         ffclock_abstime(NULL, &bt, NULL,
  290             FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
  291         bintime2timeval(&bt, tvp);
  292 }
  293 
  294 void
  295 ffclock_binuptime(struct bintime *bt)
  296 {
  297 
  298         ffclock_abstime(NULL, bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
  299 }
  300 
  301 void
  302 ffclock_nanouptime(struct timespec *tsp)
  303 {
  304         struct bintime bt;
  305 
  306         ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
  307         bintime2timespec(&bt, tsp);
  308 }
  309 
  310 void
  311 ffclock_microuptime(struct timeval *tvp)
  312 {
  313         struct bintime bt;
  314 
  315         ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
  316         bintime2timeval(&bt, tvp);
  317 }
  318 
  319 void
  320 ffclock_getbinuptime(struct bintime *bt)
  321 {
  322 
  323         ffclock_abstime(NULL, bt, NULL,
  324             FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
  325 }
  326 
  327 void
  328 ffclock_getnanouptime(struct timespec *tsp)
  329 {
  330         struct bintime bt;
  331 
  332         ffclock_abstime(NULL, &bt, NULL,
  333             FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
  334         bintime2timespec(&bt, tsp);
  335 }
  336 
  337 void
  338 ffclock_getmicrouptime(struct timeval *tvp)
  339 {
  340         struct bintime bt;
  341 
  342         ffclock_abstime(NULL, &bt, NULL,
  343             FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
  344         bintime2timeval(&bt, tvp);
  345 }
  346 
  347 void
  348 ffclock_bindifftime(ffcounter ffdelta, struct bintime *bt)
  349 {
  350 
  351         ffclock_difftime(ffdelta, bt, NULL);
  352 }
  353 
  354 void
  355 ffclock_nanodifftime(ffcounter ffdelta, struct timespec *tsp)
  356 {
  357         struct bintime bt;
  358 
  359         ffclock_difftime(ffdelta, &bt, NULL);
  360         bintime2timespec(&bt, tsp);
  361 }
  362 
  363 void
  364 ffclock_microdifftime(ffcounter ffdelta, struct timeval *tvp)
  365 {
  366         struct bintime bt;
  367 
  368         ffclock_difftime(ffdelta, &bt, NULL);
  369         bintime2timeval(&bt, tvp);
  370 }
  371 
  372 /*
  373  * System call allowing userland applications to retrieve the current value of
  374  * the Feed-Forward Clock counter.
  375  */
  376 #ifndef _SYS_SYSPROTO_H_
  377 struct ffclock_getcounter_args {
  378         ffcounter *ffcount;
  379 };
  380 #endif
  381 /* ARGSUSED */
  382 int
  383 sys_ffclock_getcounter(struct thread *td, struct ffclock_getcounter_args *uap)
  384 {
  385         ffcounter ffcount;
  386         int error;
  387 
  388         ffcount = 0;
  389         ffclock_read_counter(&ffcount);
  390         if (ffcount == 0)
  391                 return (EAGAIN);
  392         error = copyout(&ffcount, uap->ffcount, sizeof(ffcounter));
  393 
  394         return (error);
  395 }
  396 
  397 /*
  398  * System call allowing the synchronisation daemon to push new feed-foward clock
  399  * estimates to the kernel. Acquire ffclock_mtx to prevent concurrent updates
  400  * and ensure data consistency.
  401  * NOTE: ffclock_updated signals the fftimehands that new estimates are
  402  * available. The updated estimates are picked up by the fftimehands on next
  403  * tick, which could take as long as 1/hz seconds (if ticks are not missed).
  404  */
  405 #ifndef _SYS_SYSPROTO_H_
  406 struct ffclock_setestimate_args {
  407         struct ffclock_estimate *cest;
  408 };
  409 #endif
  410 /* ARGSUSED */
  411 int
  412 sys_ffclock_setestimate(struct thread *td, struct ffclock_setestimate_args *uap)
  413 {
  414         struct ffclock_estimate cest;
  415         int error;
  416 
  417         /* Reuse of PRIV_CLOCK_SETTIME. */
  418         if ((error = priv_check(td, PRIV_CLOCK_SETTIME)) != 0)
  419                 return (error);
  420 
  421         if ((error = copyin(uap->cest, &cest, sizeof(struct ffclock_estimate)))
  422             != 0)
  423                 return (error);
  424 
  425         mtx_lock(&ffclock_mtx);
  426         memcpy(&ffclock_estimate, &cest, sizeof(struct ffclock_estimate));
  427         ffclock_updated++;
  428         mtx_unlock(&ffclock_mtx);
  429         return (error);
  430 }
  431 
  432 /*
  433  * System call allowing userland applications to retrieve the clock estimates
  434  * stored within the kernel. It is useful to kickstart the synchronisation
  435  * daemon with the kernel's knowledge of hardware timecounter.
  436  */
  437 #ifndef _SYS_SYSPROTO_H_
  438 struct ffclock_getestimate_args {
  439         struct ffclock_estimate *cest;
  440 };
  441 #endif
  442 /* ARGSUSED */
  443 int
  444 sys_ffclock_getestimate(struct thread *td, struct ffclock_getestimate_args *uap)
  445 {
  446         struct ffclock_estimate cest;
  447         int error;
  448 
  449         mtx_lock(&ffclock_mtx);
  450         memcpy(&cest, &ffclock_estimate, sizeof(struct ffclock_estimate));
  451         mtx_unlock(&ffclock_mtx);
  452         error = copyout(&cest, uap->cest, sizeof(struct ffclock_estimate));
  453         return (error);
  454 }
  455 
  456 #else /* !FFCLOCK */
  457 
  458 int
  459 sys_ffclock_getcounter(struct thread *td, struct ffclock_getcounter_args *uap)
  460 {
  461 
  462         return (ENOSYS);
  463 }
  464 
  465 int
  466 sys_ffclock_setestimate(struct thread *td, struct ffclock_setestimate_args *uap)
  467 {
  468 
  469         return (ENOSYS);
  470 }
  471 
  472 int
  473 sys_ffclock_getestimate(struct thread *td, struct ffclock_getestimate_args *uap)
  474 {
  475 
  476         return (ENOSYS);
  477 }
  478 
  479 #endif /* FFCLOCK */

Cache object: 0ae48c58841438e2d8da13b747e61619


[ 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.