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$");
   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         int error;
  140 
  141         rtc = arg;
  142         if (!(rtc->flags & CLOCKF_SETTIME_NO_TS)) {
  143                 getnanotime(&ts);
  144                 if (!(rtc->flags & CLOCKF_SETTIME_NO_ADJ)) {
  145                         ts.tv_sec -= utc_offset();
  146                         timespecadd(&ts, &rtc->resadj);
  147                 }
  148         } else {
  149                 ts.tv_sec  = 0;
  150                 ts.tv_nsec = 0;
  151         }
  152         error = CLOCK_SETTIME(rtc->clockdev, &ts);
  153         if (error != 0 && bootverbose)
  154                 device_printf(rtc->clockdev, "CLOCK_SETTIME error %d\n", error);
  155 }
  156 
  157 static void
  158 clock_dbgprint_hdr(device_t dev, int rw)
  159 {
  160         struct timespec now;
  161 
  162         getnanotime(&now);
  163         device_printf(dev, "%s at ", (rw & CLOCK_DBG_READ) ? "read " : "write");
  164         clock_print_ts(&now, 9);
  165         printf(": "); 
  166 }
  167 
  168 void
  169 clock_dbgprint_bcd(device_t dev, int rw, const struct bcd_clocktime *bct)
  170 {
  171 
  172         if (show_io & rw) {
  173                 clock_dbgprint_hdr(dev, rw);
  174                 clock_print_bcd(bct, 9);
  175                 printf("\n");
  176         }
  177 }
  178 
  179 void
  180 clock_dbgprint_ct(device_t dev, int rw, const struct clocktime *ct)
  181 {
  182 
  183         if (show_io & rw) {
  184                 clock_dbgprint_hdr(dev, rw);
  185                 clock_print_ct(ct, 9);
  186                 printf("\n");
  187         }
  188 }
  189 
  190 void
  191 clock_dbgprint_err(device_t dev, int rw, int err)
  192 {
  193 
  194         if (show_io & rw) {
  195                 clock_dbgprint_hdr(dev, rw);
  196                 printf("error = %d\n", err);
  197         }
  198 }
  199 
  200 void
  201 clock_dbgprint_ts(device_t dev, int rw, const struct timespec *ts)
  202 {
  203 
  204         if (show_io & rw) {
  205                 clock_dbgprint_hdr(dev, rw);
  206                 clock_print_ts(ts, 9);
  207                 printf("\n");
  208         }
  209 }
  210 
  211 void
  212 clock_register_flags(device_t clockdev, long resolution, int flags)
  213 {
  214         struct rtc_instance *rtc, *newrtc;
  215 
  216         newrtc = malloc(sizeof(*newrtc), M_DEVBUF, M_WAITOK);
  217         newrtc->clockdev = clockdev;
  218         newrtc->resolution = (int)resolution;
  219         newrtc->flags = flags;
  220         newrtc->schedns = 0;
  221         newrtc->resadj.tv_sec  = newrtc->resolution / 2 / 1000000;
  222         newrtc->resadj.tv_nsec = newrtc->resolution / 2 % 1000000 * 1000;
  223         TIMEOUT_TASK_INIT(taskqueue_thread, &newrtc->stask, 0,
  224                     settime_task_func, newrtc);
  225 
  226         sx_xlock(&rtc_list_lock);
  227         if (LIST_EMPTY(&rtc_list)) {
  228                 LIST_INSERT_HEAD(&rtc_list, newrtc, rtc_entries);
  229         } else {
  230                 LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
  231                         if (rtc->resolution > newrtc->resolution) {
  232                                 LIST_INSERT_BEFORE(rtc, newrtc, rtc_entries);
  233                                 break;
  234                         } else if (LIST_NEXT(rtc, rtc_entries) == NULL) {
  235                                 LIST_INSERT_AFTER(rtc, newrtc, rtc_entries);
  236                                 break;
  237                         }
  238                 }
  239         }
  240         sx_xunlock(&rtc_list_lock);
  241 
  242         device_printf(clockdev, 
  243             "registered as a time-of-day clock, resolution %d.%6.6ds\n",
  244             newrtc->resolution / 1000000, newrtc->resolution % 1000000);
  245 }
  246 
  247 void
  248 clock_register(device_t dev, long res)
  249 {
  250 
  251         clock_register_flags(dev, res, 0);
  252 }
  253 
  254 void
  255 clock_unregister(device_t clockdev)
  256 {
  257         struct rtc_instance *rtc, *tmp;
  258 
  259         sx_xlock(&rtc_list_lock);
  260         LIST_FOREACH_SAFE(rtc, &rtc_list, rtc_entries, tmp) {
  261                 if (rtc->clockdev == clockdev) {
  262                         LIST_REMOVE(rtc, rtc_entries);
  263                         break;
  264                 }
  265         }
  266         sx_xunlock(&rtc_list_lock);
  267         if (rtc != NULL) {
  268                 taskqueue_cancel_timeout(taskqueue_thread, &rtc->stask, NULL);
  269                 taskqueue_drain_timeout(taskqueue_thread, &rtc->stask);
  270                 free(rtc, M_DEVBUF);
  271         }
  272 }
  273 
  274 void
  275 clock_schedule(device_t clockdev, u_int offsetns)
  276 {
  277         struct rtc_instance *rtc;
  278 
  279         sx_xlock(&rtc_list_lock);
  280         LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
  281                 if (rtc->clockdev == clockdev) {
  282                         rtc->schedns = offsetns;
  283                         break;
  284                 }
  285         }
  286         sx_xunlock(&rtc_list_lock);
  287 }
  288 
  289 static int
  290 read_clocks(struct timespec *ts, bool debug_read)
  291 {
  292         struct rtc_instance *rtc;
  293         int error;
  294 
  295         error = ENXIO;
  296         sx_xlock(&rtc_list_lock);
  297         LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
  298                 if ((error = CLOCK_GETTIME(rtc->clockdev, ts)) != 0)
  299                         continue;
  300                 if (ts->tv_sec < 0 || ts->tv_nsec < 0) {
  301                         error = EINVAL;
  302                         continue;
  303                 }
  304                 if (!(rtc->flags & CLOCKF_GETTIME_NO_ADJ)) {
  305                         timespecadd(ts, &rtc->resadj);
  306                         ts->tv_sec += utc_offset();
  307                 }
  308                 if (!debug_read) {
  309                         if (bootverbose)
  310                                 device_printf(rtc->clockdev,
  311                                     "providing initial system time\n");
  312                         break;
  313                 }
  314         }
  315         sx_xunlock(&rtc_list_lock);
  316         return (error);
  317 }
  318 
  319 /*
  320  * Initialize the system time.  Must be called from a context which does not
  321  * restrict any locking or sleeping that clock drivers may need to do.
  322  *
  323  * First attempt to get the time from a registered realtime clock.  The clocks
  324  * are queried in order of resolution until one provides the time.  If no clock
  325  * can provide the current time, use the 'base' time provided by the caller, if
  326  * non-zero.  The 'base' time is potentially highly inaccurate, such as the last
  327  * known good value of the system clock, or even a filesystem last-updated
  328  * timestamp.  It is used to prevent system time from appearing to move
  329  * backwards in logs.
  330  */
  331 void
  332 inittodr(time_t base)
  333 {
  334         struct timespec ts;
  335         int error;
  336 
  337         error = read_clocks(&ts, false);
  338 
  339         /*
  340          * Do not report errors from each clock; it is expected that some clocks
  341          * cannot provide results in some situations.  Only report problems when
  342          * no clocks could provide the time.
  343          */
  344         if (error != 0) {
  345                 switch (error) {
  346                 case ENXIO:
  347                         printf("Warning: no time-of-day clock registered, ");
  348                         break;
  349                 case EINVAL:
  350                         printf("Warning: bad time from time-of-day clock, ");
  351                         break;
  352                 default:
  353                         printf("Error reading time-of-day clock (%d), ", error);
  354                         break;
  355                 }
  356                 printf("system time will not be set accurately\n");
  357                 ts.tv_sec  = (base > 0) ? base : -1;
  358                 ts.tv_nsec = 0;
  359         }
  360 
  361         if (ts.tv_sec >= 0) {
  362                 tc_setclock(&ts);
  363 #ifdef FFCLOCK
  364                 ffclock_reset_clock(&ts);
  365 #endif
  366         }
  367 }
  368 
  369 /*
  370  * Write system time back to all registered clocks, unless disabled by admin.
  371  * This can be called from a context that restricts locking and/or sleeping; the
  372  * actual updating is done asynchronously on a task thread.
  373  */
  374 void
  375 resettodr(void)
  376 {
  377         struct timespec now;
  378         struct rtc_instance *rtc;
  379         sbintime_t sbt;
  380         long waitns;
  381 
  382         if (disable_rtc_set)
  383                 return;
  384 
  385         sx_xlock(&rtc_list_lock);
  386         LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
  387                 if (rtc->schedns != 0) {
  388                         getnanotime(&now);
  389                         waitns = rtc->schedns - now.tv_nsec;
  390                         if (waitns < 0)
  391                                 waitns += 1000000000;
  392                         sbt = nstosbt(waitns);
  393                 } else
  394                         sbt = 0;
  395                 taskqueue_enqueue_timeout_sbt(taskqueue_thread,
  396                     &rtc->stask, -sbt, 0, C_PREL(31));
  397         }
  398         sx_xunlock(&rtc_list_lock);
  399 }
  400 
  401 static int
  402 sysctl_clock_do_io(SYSCTL_HANDLER_ARGS)
  403 {
  404         struct timespec ts_discard;
  405         int error, value;
  406 
  407         value = 0;
  408         error = sysctl_handle_int(oidp, &value, 0, req);
  409         if (error != 0 || req->newptr == NULL)
  410                 return (error);
  411 
  412         switch (value) {
  413         case CLOCK_DBG_READ:
  414                 if (read_clocks(&ts_discard, true) == ENXIO)
  415                         printf("No registered RTC clocks\n");
  416                 break;
  417         case CLOCK_DBG_WRITE:
  418                 resettodr();
  419                 break;
  420         default:
  421                 return (EINVAL);
  422         }
  423 
  424         return (0);
  425 }

Cache object: e0f451e981c9e9c97646784a84575a54


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