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

Cache object: 4e6dffc87600e46a96710fda344fadc4


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