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

Cache object: 706f681fa7edf0c4d3e6011c9645e7c4


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