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/subr_rtc.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) 1988 University of Utah.
    3  * Copyright (c) 1982, 1990, 1993
    4  *      The Regents of the University of California.
    5  * Copyright (c) 2011 The FreeBSD Foundation
    6  * All rights reserved.
    7  *
    8  * This code is derived from software contributed to Berkeley by
    9  * the Systems Programming Group of the University of Utah Computer
   10  * Science Department.
   11  *
   12  * Portions of this software were developed by Julien Ridoux at the University
   13  * of Melbourne under sponsorship from the FreeBSD Foundation.
   14  *
   15  * Redistribution and use in source and binary forms, with or without
   16  * modification, are permitted provided that the following conditions
   17  * are met:
   18  * 1. Redistributions of source code must retain the above copyright
   19  *    notice, this list of conditions and the following disclaimer.
   20  * 2. Redistributions in binary form must reproduce the above copyright
   21  *    notice, this list of conditions and the following disclaimer in the
   22  *    documentation and/or other materials provided with the distribution.
   23  * 4. Neither the name of the University nor the names of its contributors
   24  *    may be used to endorse or promote products derived from this software
   25  *    without specific prior written permission.
   26  *
   27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   37  * SUCH DAMAGE.
   38  *
   39  *      from: Utah $Hdr: clock.c 1.18 91/01/21$
   40  *      from: @(#)clock.c       8.2 (Berkeley) 1/12/94
   41  *      from: NetBSD: clock_subr.c,v 1.6 2001/07/07 17:04:02 thorpej Exp
   42  *      and
   43  *      from: src/sys/i386/isa/clock.c,v 1.176 2001/09/04
   44  */
   45 
   46 /*
   47  * Helpers for time-of-day clocks. This is useful for architectures that need
   48  * support multiple models of such clocks, and generally serves to make the
   49  * code more machine-independent.
   50  * If the clock in question can also be used as a time counter, the driver
   51  * needs to initiate this.
   52  * This code is not yet used by all architectures.
   53  */
   54 
   55 #include <sys/cdefs.h>
   56 __FBSDID("$FreeBSD: releng/11.2/sys/kern/subr_rtc.c 331722 2018-03-29 02:50:57Z eadler $");
   57 
   58 #include "opt_ffclock.h"
   59 
   60 #include <sys/param.h>
   61 #include <sys/systm.h>
   62 #include <sys/kernel.h>
   63 #include <sys/bus.h>
   64 #include <sys/clock.h>
   65 #include <sys/lock.h>
   66 #include <sys/malloc.h>
   67 #include <sys/sx.h>
   68 #include <sys/sysctl.h>
   69 #include <sys/taskqueue.h>
   70 #ifdef FFCLOCK
   71 #include <sys/timeffc.h>
   72 #endif
   73 #include <sys/timetc.h>
   74 
   75 #include "clock_if.h"
   76 
   77 static int show_io;
   78 SYSCTL_INT(_debug, OID_AUTO, clock_show_io, CTLFLAG_RWTUN, &show_io, 0,
   79     "Enable debug printing of RTC clock I/O; 1=reads, 2=writes, 3=both.");
   80 
   81 static int sysctl_clock_do_io(SYSCTL_HANDLER_ARGS);
   82 SYSCTL_PROC(_debug, OID_AUTO, clock_do_io, CTLTYPE_INT | CTLFLAG_RW,
   83     0, 0, sysctl_clock_do_io, "I",
   84     "Trigger one-time IO on RTC clocks; 1=read (and discard), 2=write");
   85 
   86 /* XXX: should be kern. now, it's no longer machdep.  */
   87 static int disable_rtc_set;
   88 SYSCTL_INT(_machdep, OID_AUTO, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set,
   89     0, "Disallow adjusting time-of-day clock");
   90 
   91 /*
   92  * An instance of a realtime clock.  A list of these tracks all the registered
   93  * clocks in the system.
   94  *
   95  * The resadj member is used to apply a "resolution adjustment" equal to half
   96  * the clock's resolution, which is useful mainly on clocks with a whole-second
   97  * resolution.  Because the clock truncates the fractional part, adding half the
   98  * resolution performs 4/5 rounding.  The same adjustment is applied to the
   99  * times returned from clock_gettime(), because the fraction returned will
  100  * always be zero, but on average the actual fraction at the time of the call
  101  * should be about .5.
  102  */
  103 struct rtc_instance {
  104         device_t        clockdev;
  105         int             resolution;
  106         int             flags;
  107         u_int           schedns;
  108         struct timespec resadj;
  109         struct timeout_task
  110                         stask;
  111         LIST_ENTRY(rtc_instance)
  112                         rtc_entries;
  113 };
  114 
  115 /*
  116  * Clocks are updated using a task running on taskqueue_thread.
  117  */
  118 static void settime_task_func(void *arg, int pending);
  119 
  120 /*
  121  * Registered clocks are kept in a list which is sorted by resolution; the more
  122  * accurate clocks get the first shot at providing the time.
  123  */
  124 LIST_HEAD(rtc_listhead, rtc_instance);
  125 static struct rtc_listhead rtc_list = LIST_HEAD_INITIALIZER(rtc_list);
  126 static struct sx rtc_list_lock;
  127 SX_SYSINIT(rtc_list_lock_init, &rtc_list_lock, "rtc list");
  128 
  129 /*
  130  * On the task thread, invoke the clock_settime() method of the clock.  Do so
  131  * holding no locks, so that clock drivers are free to do whatever kind of
  132  * locking or sleeping they need to.
  133  */
  134 static void
  135 settime_task_func(void *arg, int pending)
  136 {
  137         struct timespec ts;
  138         struct rtc_instance *rtc;
  139 
  140         rtc = arg;
  141         if (!(rtc->flags & CLOCKF_SETTIME_NO_TS)) {
  142                 getnanotime(&ts);
  143                 if (!(rtc->flags & CLOCKF_SETTIME_NO_ADJ)) {
  144                         ts.tv_sec -= utc_offset();
  145                         timespecadd(&ts, &rtc->resadj);
  146                 }
  147         } else {
  148                 ts.tv_sec  = 0;
  149                 ts.tv_nsec = 0;
  150         }
  151         CLOCK_SETTIME(rtc->clockdev, &ts);
  152 }
  153 
  154 static void
  155 clock_dbgprint_hdr(device_t dev, int rw)
  156 {
  157         struct timespec now;
  158 
  159         getnanotime(&now);
  160         device_printf(dev, "%s at ", (rw & CLOCK_DBG_READ) ? "read " : "write");
  161         clock_print_ts(&now, 9);
  162         printf(": "); 
  163 }
  164 
  165 void
  166 clock_dbgprint_bcd(device_t dev, int rw, const struct bcd_clocktime *bct)
  167 {
  168 
  169         if (show_io & rw) {
  170                 clock_dbgprint_hdr(dev, rw);
  171                 clock_print_bcd(bct, 9);
  172                 printf("\n");
  173         }
  174 }
  175 
  176 void
  177 clock_dbgprint_ct(device_t dev, int rw, const struct clocktime *ct)
  178 {
  179 
  180         if (show_io & rw) {
  181                 clock_dbgprint_hdr(dev, rw);
  182                 clock_print_ct(ct, 9);
  183                 printf("\n");
  184         }
  185 }
  186 
  187 void
  188 clock_dbgprint_err(device_t dev, int rw, int err)
  189 {
  190 
  191         if (show_io & rw) {
  192                 clock_dbgprint_hdr(dev, rw);
  193                 printf("error = %d\n", err);
  194         }
  195 }
  196 
  197 void
  198 clock_dbgprint_ts(device_t dev, int rw, const struct timespec *ts)
  199 {
  200 
  201         if (show_io & rw) {
  202                 clock_dbgprint_hdr(dev, rw);
  203                 clock_print_ts(ts, 9);
  204                 printf("\n");
  205         }
  206 }
  207 
  208 void
  209 clock_register_flags(device_t clockdev, long resolution, int flags)
  210 {
  211         struct rtc_instance *rtc, *newrtc;
  212 
  213         newrtc = malloc(sizeof(*newrtc), M_DEVBUF, M_WAITOK);
  214         newrtc->clockdev = clockdev;
  215         newrtc->resolution = (int)resolution;
  216         newrtc->flags = flags;
  217         newrtc->schedns = 0;
  218         newrtc->resadj.tv_sec  = newrtc->resolution / 2 / 1000000;
  219         newrtc->resadj.tv_nsec = newrtc->resolution / 2 % 1000000 * 1000;
  220         TIMEOUT_TASK_INIT(taskqueue_thread, &newrtc->stask, 0,
  221                     settime_task_func, newrtc);
  222 
  223         sx_xlock(&rtc_list_lock);
  224         if (LIST_EMPTY(&rtc_list)) {
  225                 LIST_INSERT_HEAD(&rtc_list, newrtc, rtc_entries);
  226         } else {
  227                 LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
  228                         if (rtc->resolution > newrtc->resolution) {
  229                                 LIST_INSERT_BEFORE(rtc, newrtc, rtc_entries);
  230                                 break;
  231                         } else if (LIST_NEXT(rtc, rtc_entries) == NULL) {
  232                                 LIST_INSERT_AFTER(rtc, newrtc, rtc_entries);
  233                                 break;
  234                         }
  235                 }
  236         }
  237         sx_xunlock(&rtc_list_lock);
  238 
  239         device_printf(clockdev, 
  240             "registered as a time-of-day clock, resolution %d.%6.6ds\n",
  241             newrtc->resolution / 1000000, newrtc->resolution % 1000000);
  242 }
  243 
  244 void
  245 clock_register(device_t dev, long res)
  246 {
  247 
  248         clock_register_flags(dev, res, 0);
  249 }
  250 
  251 void
  252 clock_unregister(device_t clockdev)
  253 {
  254         struct rtc_instance *rtc, *tmp;
  255 
  256         sx_xlock(&rtc_list_lock);
  257         LIST_FOREACH_SAFE(rtc, &rtc_list, rtc_entries, tmp) {
  258                 if (rtc->clockdev == clockdev) {
  259                         LIST_REMOVE(rtc, rtc_entries);
  260                         break;
  261                 }
  262         }
  263         sx_xunlock(&rtc_list_lock);
  264         if (rtc != NULL) {
  265                 taskqueue_cancel_timeout(taskqueue_thread, &rtc->stask, NULL);
  266                 taskqueue_drain_timeout(taskqueue_thread, &rtc->stask);
  267                 free(rtc, M_DEVBUF);
  268         }
  269 }
  270 
  271 void
  272 clock_schedule(device_t clockdev, u_int offsetns)
  273 {
  274         struct rtc_instance *rtc;
  275 
  276         sx_xlock(&rtc_list_lock);
  277         LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
  278                 if (rtc->clockdev == clockdev) {
  279                         rtc->schedns = offsetns;
  280                         break;
  281                 }
  282         }
  283         sx_xunlock(&rtc_list_lock);
  284 }
  285 
  286 static int
  287 read_clocks(struct timespec *ts, bool debug_read)
  288 {
  289         struct rtc_instance *rtc;
  290         int error;
  291 
  292         error = ENXIO;
  293         sx_xlock(&rtc_list_lock);
  294         LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
  295                 if ((error = CLOCK_GETTIME(rtc->clockdev, ts)) != 0)
  296                         continue;
  297                 if (ts->tv_sec < 0 || ts->tv_nsec < 0) {
  298                         error = EINVAL;
  299                         continue;
  300                 }
  301                 if (!(rtc->flags & CLOCKF_GETTIME_NO_ADJ)) {
  302                         timespecadd(ts, &rtc->resadj);
  303                         ts->tv_sec += utc_offset();
  304                 }
  305                 if (!debug_read) {
  306                         if (bootverbose)
  307                                 device_printf(rtc->clockdev,
  308                                     "providing initial system time\n");
  309                         break;
  310                 }
  311         }
  312         sx_xunlock(&rtc_list_lock);
  313         return (error);
  314 }
  315 
  316 /*
  317  * Initialize the system time.  Must be called from a context which does not
  318  * restrict any locking or sleeping that clock drivers may need to do.
  319  *
  320  * First attempt to get the time from a registered realtime clock.  The clocks
  321  * are queried in order of resolution until one provides the time.  If no clock
  322  * can provide the current time, use the 'base' time provided by the caller, if
  323  * non-zero.  The 'base' time is potentially highly inaccurate, such as the last
  324  * known good value of the system clock, or even a filesystem last-updated
  325  * timestamp.  It is used to prevent system time from appearing to move
  326  * backwards in logs.
  327  */
  328 void
  329 inittodr(time_t base)
  330 {
  331         struct timespec ts;
  332         int error;
  333 
  334         error = read_clocks(&ts, false);
  335 
  336         /*
  337          * Do not report errors from each clock; it is expected that some clocks
  338          * cannot provide results in some situations.  Only report problems when
  339          * no clocks could provide the time.
  340          */
  341         if (error != 0) {
  342                 switch (error) {
  343                 case ENXIO:
  344                         printf("Warning: no time-of-day clock registered, ");
  345                         break;
  346                 case EINVAL:
  347                         printf("Warning: bad time from time-of-day clock, ");
  348                         break;
  349                 default:
  350                         printf("Error reading time-of-day clock (%d), ", error);
  351                         break;
  352                 }
  353                 printf("system time will not be set accurately\n");
  354                 ts.tv_sec  = (base > 0) ? base : -1;
  355                 ts.tv_nsec = 0;
  356         }
  357 
  358         if (ts.tv_sec >= 0) {
  359                 tc_setclock(&ts);
  360 #ifdef FFCLOCK
  361                 ffclock_reset_clock(&ts);
  362 #endif
  363         }
  364 }
  365 
  366 /*
  367  * Write system time back to all registered clocks, unless disabled by admin.
  368  * This can be called from a context that restricts locking and/or sleeping; the
  369  * actual updating is done asynchronously on a task thread.
  370  */
  371 void
  372 resettodr(void)
  373 {
  374         struct timespec now;
  375         struct rtc_instance *rtc;
  376         sbintime_t sbt;
  377         long waitns;
  378 
  379         if (disable_rtc_set)
  380                 return;
  381 
  382         sx_xlock(&rtc_list_lock);
  383         LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
  384                 if (rtc->schedns != 0) {
  385                         getnanotime(&now);
  386                         waitns = rtc->schedns - now.tv_nsec;
  387                         if (waitns < 0)
  388                                 waitns += 1000000000;
  389                         sbt = nstosbt(waitns);
  390                 } else
  391                         sbt = 0;
  392                 taskqueue_enqueue_timeout_sbt(taskqueue_thread,
  393                     &rtc->stask, -sbt, 0, C_PREL(31));
  394         }
  395         sx_xunlock(&rtc_list_lock);
  396 }
  397 
  398 static int
  399 sysctl_clock_do_io(SYSCTL_HANDLER_ARGS)
  400 {
  401         struct timespec ts_discard;
  402         int error, value;
  403 
  404         value = 0;
  405         error = sysctl_handle_int(oidp, &value, 0, req);
  406         if (error != 0 || req->newptr == NULL)
  407                 return (error);
  408 
  409         switch (value) {
  410         case CLOCK_DBG_READ:
  411                 if (read_clocks(&ts_discard, true) == ENXIO)
  412                         printf("No registered RTC clocks\n");
  413                 break;
  414         case CLOCK_DBG_WRITE:
  415                 resettodr();
  416                 break;
  417         default:
  418                 return (EINVAL);
  419         }
  420 
  421         return (0);
  422 }

Cache object: 217c448020d29eb9b09562775ba128fe


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