home *** CD-ROM | disk | FTP | other *** search
/ Handbook of Infosec Terms 2.0 / Handbook_of_Infosec_Terms_Version_2.0_ISSO.iso / text / rfcs / rfc1589.txt < prev    next >
Text File  |  1996-05-07  |  94KB  |  881 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7. Network Working Group                                           D. Mills Request for Comments: 1589                        University of Delaware Category: Informational                                       March 1994 
  8.  
  9.                  A Kernel Model for Precision Timekeeping 
  10.  
  11. Status of this Memo 
  12.  
  13.    This memo provides information for the Internet community.  This memo    does not specify an Internet standard of any kind.  Distribution of    this memo is unlimited. 
  14.  
  15. Overview 
  16.  
  17.    This memorandum describes an engineering model which implements a    precision time-of-day function for a generic operating system. The    model is based on the principles of disciplined oscillators and    phase-lock loops (PLL) often found in the engineering literature. It    has been implemented in the Unix kernel for several workstations,    including those made by Sun Microsystems and Digital Equipment. The    model changes the way the system clock is adjusted in time and    frequency, as well as provides mechanisms to discipline its frequency    to an external precision timing source. The model incorporates a    generic system-call interface for use with the Network Time Protocol    (NTP) or similar time synchronization protocol. The NTP Version 3    daemon xntpd operates with this model to provide synchronization    limited in principle only by the accuracy and stability of the    external timing source. 
  18.  
  19.    This memorandum does not obsolete or update any RFC. It does not    propose a standard protocol, specification or algorithm. It is    intended to provoke comment, refinement and alternative    implementations. While a working knowledge of NTP is not required for    an understanding of the design principles or implementation of the    model, it may be helpful in understanding how the model behaves in a    fully functional timekeeping system. The architecture and design of    NTP is described in [1], while the current NTP Version 3 protocol    specification is given in RFC-1305 [2] and a subset of the protocol,    the Simple Network Time Protocol (SNTP), in RFC-1361 [4]. 
  20.  
  21.    The model has been implemented in three Unix kernels for Sun    Microsystems and Digital Equipment workstations. In addition, for the    Digital machines the model provides improved precision to one    microsecond (us). Since these specific implementations involve    modifications to licensed code, they cannot be provided directly.    Inquiries should be directed to the manufacturer's representatives.    However, the engineering model for these implementations, including a 
  22.  
  23.  
  24.  
  25. Mills                                                           [Page 1] 
  26.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  27.  
  28.     simulator with code segments almost identical to the implementations,    but not involving licensed code, is available via anonymous FTP from    host louie.udel.edu in the directory pub/ntp and compressed tar    archive kernel.tar.Z. The NTP Version 3 distribution can be obtained    via anonymous ftp from the same host and directory in the compressed    tar archive xntp3.3g.tar.Z, where the version number shown as 3.3g    may be adjusted for new versions as they occur. 
  29.  
  30. 1. Introduction 
  31.  
  32.    This memorandum describes a model and programming interface for    generic operating system software that manages the system clock and    timer functions. The model provides improved accuracy and stability    for most workstations and servers using the Network Time Protocol    (NTP) or similar time synchronization protocol. This memorandum    describes the principles of design and implementation of the model.    Related technical reports discuss the design approach, engineering    analysis and performance evaluation of the model as implemented in    Unix kernels for Sun Microsystems and Digital Equipment workstations.    The NTP Version 3 daemon xntpd operates with these implementations to    provide improved accuracy and stability, together with diminished    overhead in the operating system and network. In addition, the model    supports the use of external timing sources, such as precision    pulse-per-second (PPS) signals and the industry standard IRIG timing    signals. The NTP daemon automatically detects the presence of the new    features and utilizes them when available. 
  33.  
  34.    There are three prototype implementations of the model presented in    this memorandum, one each for the Sun Microsystems SPARCstation with    the SunOS 4.1.x kernel, Digital Equipment DECstation 5000 with the    Ultrix 4.x kernel and Digital Equipment 3000 AXP Alpha with the OSF/1    V1.x kernel. In addition, for the DECstation 5000/240 and 3000 AXP    Alpha machines, a special feature provides improved precision to 1 us    (Sun 4.1.x kernels already do provide 1-us precision). Other than    improving the system clock accuracy, stability and precision, these    implementations do not change the operation of existing Unix system    calls which manage the system clock, such as gettimeofday(),    settimeofday() and adjtime(); however, if the new features are in    use, the operations of gettimeofday() and adjtime() can be controlled    instead by new system calls ntp_gettime() and ntp_adjtime() as    described below. 
  35.  
  36.    A detailed description of the variables and algorithms is given in    the hope that similar functionality can be incorporated in Unix    kernels for other machines. The algorithms involve only minor changes    to the system clock and interval timer routines and include    interfaces for application programs to learn the system clock status    and certain statistics of the time synchronization process. Detailed 
  37.  
  38.  
  39.  
  40. Mills                                                           [Page 2] 
  41.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  42.  
  43.     installation instructions are given in a specific README files    included in the kernel distributions. 
  44.  
  45.    In this memorandum, NTP Version 3 and the Unix implementation xntp3    are used as an example application of the new system calls for use by    a synchronization daemon. In principle, the new system calls can be    used by other protocols and implementations as well. Even in cases    where the local time is maintained by periodic exchanges of messages    at relatively long intervals, such as using the NIST Automated    Computer Time Service, the ability to precisely adjust the system    clock frequency simplifies the synchronization procedures and allows    the telephone call frequency to be considerably reduced. 
  46.  
  47. 2. Design Approach 
  48.  
  49.    While not strictly necessary for an understanding or implementation    of the model, it may be helpful to briefly describe how NTP operates    to control the system clock in a client workstation. As described in    [1], the NTP protocol exchanges timestamps with one or more peers    sharing a synchronization subnet to calculate the time offsets    between peer clocks and the local clock. These offsets are processed    by several algorithms which refine and combine the offsets to produce    an ensemble average, which is then used to adjust the local clock    time and frequency. The manner in which the local clock is adjusted    represents the main topic of this memorandum. The goal in the    enterprise is the most accurate and stable system clock possible with    the available kernel software and workstation hardware. 
  50.  
  51.    In order to understand how the new software works, it is useful to    review how most Unix kernels maintain the system time. In the Unix    design a hardware counter interrupts the kernel at a fixed rate: 100    Hz in the SunOS kernel, 256 Hz in the Ultrix kernel and 1024 Hz in    the OSF/1 kernel. Since the Ultrix timer interval (reciprocal of the    rate) does not evenly divide one second in microseconds, the Ultrix    kernel adds 64 microseconds once each second, so the timescale    consists of 255 advances of 3906 us plus one of 3970 us. Similarly,    the OSF/1 kernel adds 576 us once each second, so its timescale    consists of 1023 advances of 976 us plus one of 1552 us. 
  52.  
  53.    2.1. Mechanisms to Adjust Time and Frequency 
  54.  
  55.       In most Unix kernels it is possible to slew the system clock to a       new offset relative to the current time by using the adjtime()       system call. To do this the clock frequency is changed by adding       or subtracting a fixed amount (tickadj) at each timer interrupt       (tick) for a calculated number of ticks. Since this calculation       involves dividing the requested offset by tickadj, it is possible       to slew to a new offset with a precision only of tickadj, which is 
  56.  
  57.  
  58.  
  59. Mills                                                           [Page 3] 
  60.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  61.  
  62.        usually in the neighborhood of 5 us, but sometimes much more. This       results in a roundoff error which can accumulate to an       unacceptable degree, so that special provisions must be made in       the clock adjustment procedures of the synchronization daemon. 
  63.  
  64.       In order to implement a frequency-discipline function, it is       necessary to provide time offset adjustments to the kernel at       regular adjustment intervals using the adjtime() system call. In       order to reduce the system clock jitter to the regime considered       in this memorandum, it is necessary that the adjustment interval       be relatively small, in the neighborhood of 1 s. However, the Unix       adjtime() implementation requires each offset adjustment to       complete before another one can be begun, which means that large       adjustments must be amortized in possibly many adjustment       intervals. The requirement to implement the adjustment interval       and compensate for roundoff error considerably complicates the       synchronizing daemon implementation. 
  65.  
  66.       In the new model this scheme is replaced by another that       represents the system clock as a multiple-word, precision-time       variable in order to provide very precise clock adjustments. At       each timer interrupt a precisely calibrated quantity is added to       the kernel time variable and overflows propagated as required. The       quantity is computed as in the NTP local clock model described in       [3], which operates as an adaptive-parameter, first-order, type-II       phase-lock loop (PLL). In principle, this PLL design can provide       precision control of the system clock oscillator within 1 us and       frequency to within parts in 10^11. While precisions of this order       are surely well beyond the capabilities of the CPU clock       oscillator used in typical workstations, they are appropriate       using precision external oscillators as described below. 
  67.  
  68.       The PLL design is identical to the one originally implemented in       NTP and described in [3]. In this design the software daemon       simulates the PLL using the adjtime() system call; however, the       daemon implementation is considerably complicated by the       considerations described above. The modified kernel routines       implement the PLL in the kernel using precision time and frequency       representions, so that these complications are avoided. A new       system call ntp_adjtime() is called only as each new time update       is determined, which in NTP occurs at intervals of from 16 s to       1024 s. In addition, doing frequency compensation in the kernel       means that the system time runs true even if the daemon were to       cease operation or the network paths to the primary       synchronization source fail. 
  69.  
  70.       In the new model the new ntp_adjtime() operates in a way similar       to the original adjtime() system call, but does so independently 
  71.  
  72.  
  73.  
  74. Mills                                                           [Page 4] 
  75.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  76.  
  77.        of adjtime(), which continues to operate in its traditional       fashion. When used with NTP, it is the design intent that       settimeofday() or adjtime() be used only for system time       adjustments greater than +-128 ms, although the dynamic range of       the new model is much larger at +-512 ms. It has been the Internet       experience that the need to change the system time in increments       greater than +-128 ms is extremely rare and is usually associated       with a hardware or software malfunction or system reboot. 
  78.  
  79.       The easiest way to set the time is with the settimeofday() system       call; however, this can under some conditions cause the clock to       jump backward. If this cannot be tolerated, adjtime() can be used       to slew the clock to the new value without running backward or       affecting the frequency discipline process. Once the system clock       has been set within +-128 ms, the ntp_adjtime() system call is       used to provide periodic updates including the time offset,       maximum error, estimated error and PLL time constant. With NTP the       update interval depends on the measured dispersion and time       constant; however, the scheme is quite forgiving and neither       moderate loss of updates nor variations in the update interval are       serious. 
  80.  
  81.    2.2 Daemon and Application Interface 
  82.  
  83.       Unix application programs can read the system clock using the       gettimeofday() system call, which returns only the system time and       timezone data. For some applications it is useful to know the       maximum error of the reported time due to all causes, including       clock reading errors, oscillator frequency errors and accumulated       latencies on the path to a primary synchronization source.       However, in the new model the PLL adjusts the system clock to       compensate for its intrinsic frequency error, so that the time       errors expected in normal operation will usually be much less than       the maximum error. The programming interface includes a new system       call ntp_gettime(), which returns the system time, as well as the       maximum error and estimated error. This interface is intended to       support applications that need such things, including distributed       file systems, multimedia teleconferencing and other real-time       applications. The programming interface also includes the new       system call ntp_adjtime() mentioned previously, which can be used       to read and write kernel variables for time and frequency       adjustment, PLL time constant, leap-second warning and related       data.        In addition, the kernel adjusts the maximum error to grow by an       amount equal to the oscillator frequency tolerance times the       elapsed time since the last update. The default engineering       parameters have been optimized for update intervals in the order 
  84.  
  85.  
  86.  
  87. Mills                                                           [Page 5] 
  88.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  89.  
  90.        of 64 s. For other intervals the PLL time constant can be adjusted       to optimize the dynamic response over intervals of 16-1024 s.       Normally, this is automatically done by NTP. In any case, if       updates are suspended, the PLL coasts at the frequency last       determined, which usually results in errors increasing only to a       few tens of milliseconds over a day using room-temperature quartz       oscillators of typical modern workstations. 
  91.  
  92.       While any synchronization daemon can in principle be modified to       use the new system calls, the most likely will be users of the NTP       Version 3 daemon xntpd. The xntpd code determines whether the new       system calls are implemented and automatically reconfigures as       required. When implemented, the daemon reads the frequency offset       from a file and provides it and the initial time constant via       ntp_adjtime(). In subsequent calls to ntp_adjtime(), only the time       offset and time constant are affected. The daemon reads the       frequency from the kernel using ntp_adjtime() at intervals of       about one hour and writes it to a system file. This information is       recovered when the daemon is restarted after reboot, for example,       so the sometimes extensive training period to learn the frequency       separately for each system can be avoided. 
  93.  
  94.    2.3. Precision Clocks for DECstation 5000/240 and 3000 AXP Alpha 
  95.  
  96.       The stock microtime() routine in the Ultrix kernel returns system       time to the precision of the timer interrupt interval, which is in       the 1-4 ms range. However, in the DECstation 5000/240 and possibly       other machines of that family, there is an undocumented IOASIC       hardware register that counts system bus cycles at a rate of 25       MHz. The new microtime() routine for the Ultrix kernel uses this       register to interpolate system time between timer interrupts. This       results in a precision of 1 us for all time values obtained via       the gettimeofday() and ntp_gettime() system calls. For the Digital       Equipment 3000 AXP Alpha, the architecture provides a hardware       Process Cycle Counter and a machine instruction rpcc to read it.       This counter operates at the fundamental frequency of the CPU       clock or some submultiple of it, 133.333 MHz for the 3000/400 for       example. The new microtime() routine for the OSF/1 kernel uses       this counter in the same fashion as the Ultrix routine. 
  97.  
  98.       In both the Ultrix and OSF/1 kernels the gettimeofday() and       ntp_gettime() system call use the new microtime() routine, which       returns the actual interpolated value, but does not change the       kernel time variable. Therefore, other routines that access the       kernel time variable directly and do not call either       gettimeofday(), ntp_gettime() or microtime() will continue their       present behavior. The microtime() feature is independent of other       features described here and is operative even if the kernel PLL or 
  99.  
  100.  
  101.  
  102. Mills                                                           [Page 6] 
  103.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  104.  
  105.        new system calls have not been implemented. 
  106.  
  107.       The SunOS kernel already includes a system clock with 1-us       resolution; so, in principle, no microtime() routine is necessary.       An existing kernel routine uniqtime() implements this function,       but it is coded in the C language and is rather slow at 42-85 us       per call. A replacement microtime() routine coded in assembler       language is available in the NTP Version 3 distribution and is       much faster at about 3 us per call. 
  108.  
  109.    2.4. External Time and Frequency Discipline 
  110.  
  111.       The overall accuracy of a time synchronization subnet with respect       to Coordinated Universal Time (UTC) depends on the accuracy and       stability of the primary synchronization source, usually a radio       or satellite receiver, and the system clock oscillator of the       primary server. As discussed in [5], the traditional interface       using an RS232 protocol and serial port precludes the full       accuracy of the radio clock. In addition, the poor stability of       typical CPU clock oscillators limits the accuracy, whether or not       precision time sources are available. There are, however, several       ways in which the system clock accuracy and stability can be       improved to the degree limited only by the accuracy and stability       of the synchronization source and the jitter of the operating       system. 
  112.  
  113.       Many radio clocks produce special signals that can be used by       external equipment to precisely synchronize time and frequency.       Most produce a pulse-per-second (PPS) signal that can be read via       a modem-control lead of a serial port and some produce a special       IRIG signal that can be read directly by a bus peripheral, such as       the KSI/Odetics TPRO IRIG SBus interface, or indirectly via the       audio codec of some workstations, as described in [5]. In the NTP       Version 3 distribution, the PPS signal can be used to augment the       less precise ASCII serial timecode to improve accuracy to the       order of microseconds. Support is also included in the       distribution for the TPRO interface as well as the audio codec;       however, the latter requires a modified kernel audio driver       contained in the bsd_audio.tar.Z distribution in the same host and       directory as the NTP Version 3 distribution mentioned previously. 
  114.  
  115.       2.4.1. PPS Signal 
  116.  
  117.          The NTP Version 3 distribution includes a special ppsclock          module for the SunOS 4.1.x kernel that captures the PPS signal          presented via a modem-control lead of a serial port. Normally,          the ppsclock module produces a timestamp at each transition of          the PPS signal and provides it to the synchronization daemon 
  118.  
  119.  
  120.  
  121. Mills                                                           [Page 7] 
  122.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  123.  
  124.           for integration with the serial ASCII timecode, also produced          by the radio clock. With the conventional PLL implementation in          either the daemon or the kernel as described above, the          accuracy of this scheme is limited by the intrinsic stability          of the CPU clock oscillator to a millisecond or two, depending          on environmental temperature variations. 
  125.  
  126.          The ppsclock module has been modified to in addition call a new          kernel routine hardpps() once each second. The kernel routine          compares the timestamp with a sample of the CPU clock          oscillator to develop a frequency offset estimate. This offset          is used to discipline the oscillator frequency, nominally to          within a few parts in 10^8, which is about two orders of          magnitude better than the undisciplined oscillator. The new          feature is conditionally compiled in the code described below          only if the PPS_SYNC option is used in the kernel configuration          file. 
  127.  
  128.          When using the PPS signal to adjust the time, there is a          problem with the SunOS implementation which is very delicate to          fix. The Sun serial port interrupt routine operates at          interrupt priority level 12, while the timer interrupt routine          operates at priority 10. Thus, it is possible that the PPS          signal interrupt can occur during the timer interrupt routine,          with result that a tick increment can be missed and the          returned time early by one tick. It may happen that, if the CPU          clock oscillator is within a few ppm of the PPS oscillator,          this condition can persist for two or more successive PPS          interrupts. A useful workaround has been to use a median filter          to process the PPS sample offsets. In this filter the sample          offsets in a window of 20 samples are sorted by offset and the          six highest and six lowest outlyers discarded. The average of          the eight samples remaining becomes the output of the filter. 
  129.  
  130.          The problem is not nearly so serious when using the PPS signal          to discipline the frequency of the CPU clock oscillator. In          this case the quantity of interest is the contents of the          microseconds counter only, which does not depend on the kernel          time variable. 
  131.  
  132.       2.4.2. External Clocks 
  133.  
  134.          It is possible to replace the system clock function with an          external bus peripheral. The TPRO device mentioned previously          can be used to provide IRIG-synchronized time with a precision          of 1 us. A driver for this device tprotime.c and header file          tpro.h are included in the kernel.tar.Z distribution mentioned          previously. Using this device the system clock is read directly 
  135.  
  136.  
  137.  
  138. Mills                                                           [Page 8] 
  139.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  140.  
  141.           from the interface; however, the device does not record the          year, so special provisions have to be made to obtain the year          from the kernel time variable and initialize the driver          accordingly. This feature is conditionally compiled in the code          described below only if the EXT_CLOCK option is used in the          kernel configuration file. 
  142.  
  143.          While the system clock function is provided directly by the          microtime() routine in the driver, the kernel time variable          must be disciplined as well, since not all system timing          functions use the microtime() routine. This is done by          measuring the difference between the microtime() clock and          kernel time variable and using the difference to adjust the          kernel PLL as if the adjustment were provided by an external          peer and NTP. 
  144.  
  145.          A good deal of error checking is done in the TPRO driver, since          the system clock is vulnerable to a misbehaving radio clock,          IRIG signal source, interface cables and TPRO device itself.          Unfortunately, there is no easy way to utilize the extensive          diversity and redundancy capabilities available in the NTP          synchronization daemon. In order to avoid disruptions that          might occur if the TPRO time is far different from the kernel          time variable, the latter is used instead of the former if the          difference between the two exceeds 1000 s; presumably in that          case operator intervention is required. 
  146.  
  147.       2.4.3. External Oscillators 
  148.  
  149.          Even if a source of PPS or IRIG signals is not available, it is          still possible to improve the stability of the system clock          through the use of a specialized bus peripheral. In order to          explore the benefits of such an approach, a special SBus          peripheral caled HIGHBALL has been constructed. The device          includes a pair of 32-bit hardware counters in Unix timeval          format, together with a precision, oven-controlled quartz          oscillator with a stability of a few parts in 10^9. A driver          for this device hightime.c and header file high.h are included          in the kernel.tar.Z distribution mentioned previously. This          feature is conditionally compiled in the code described below          only if the EXT_CLOCK and HIGHBALL options are used in the          kernel configuration file. 
  150.  
  151.          Unlike the external clock case, where the system clock function          is provided directly by the microtime() routine in the driver,          the HIGHBALL counter offsets with respect to UTC must be          provided first.  This is done using the ordinary kernel PLL,          but controlling the counter offsets directly, rather than the 
  152.  
  153.  
  154.  
  155. Mills                                                           [Page 9] 
  156.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  157.  
  158.           kernel time variable. At first, this might seem to defeat the          purpose of the design, since the jitter and wander of the          synchronization source will affect the counter offsets and thus          the accuracy of the time. However, the jitter is much reduced          by the PLL and the wander is small, especially if using a radio          clock or another primary server disciplined in the same way.          In practice, the scheme works to reduce the incidental wander          to a few parts in 10^8, or about the same as using the PPS          signal. 
  159.  
  160.          As in the previous case, the kernel time variable must be          disciplined as well, since not all system timing functions use          the microtime() routine. However, the kernel PLL cannot be used          for this, since it is already in use providing offsets for the          HIGHBALL counters. Therefore, a special correction is          calculated from the difference between the microtime() clock          and the kernel time variable and used to adjust the kernel time          variable at the next timer interrupt. This somewhat roundabout          approach is necessary in order that the adjustment does not          cause the kernel time variable to jump backwards and possibly          lose or duplicate a timer event. 
  161.  
  162.    2.5 Other Features 
  163.  
  164.       It is a design feature of the NTP architecture that the system       clocks in a synchronization subnet are to read the same or nearly       the same values before during and after a leap-second event, as       declared by national standards bodies. The new model is designed       to implement the leap event upon command by an ntp_adjtime()       argument. The intricate and sometimes arcane details of the model       and implementation are discussed in [3] and [5]. Further details       are given in the technical summary later in this memorandum. 
  165.  
  166. 3. Technical Summary 
  167.  
  168.    In order to more fully understand the workings of the model, a stand-    alone simulator kern.c and header file timex.h are included in the    kernel.tar.Z distribution mentioned previously. In addition, a    complete C program kern_ntptime.c which implements the ntp_gettime()    and ntp_adjtime() functions is provided, but with the vendor-specific    argument-passing code deleted. Since the distribution is somewhat    large, due to copious comments and ornamentation, it is impractical    to include a listing of these programs in this memorandum. In any    case, implementors may choose to snip portions of the simulator for    use in new kernel designs, but, due to formatting conventions, this    would be difficult if included in this memorandum. 
  169.  
  170.  
  171.  
  172.  
  173.  
  174. Mills                                                          [Page 10] 
  175.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  176.  
  177.     The kern.c program is an implementation of an adaptive-parameter,    first-order, type-II phase-lock loop. The system clock is implemented    using a set of variables and algorithms defined in the simulator and    driven by explicit offsets generated by a driver program also    included in the program. The algorithms include code fragments almost    identical to those in the machine-specific kernel implementations and    operate in the same way, but the operations can be understood    separately from any licensed source code into which these fragments    may be integrated. The code fragments themselves are not derived from    any licensed code. The following discussion assumes that the    simulator code is available for inspection. 
  178.  
  179.    3.1. PLL Simulation 
  180.  
  181.       The simulator operates in conformance with the analytical model       described in [3]. The main() program operates as a driver for the       fragments hardupdate(), hardclock(), second_overflow(), hardpps()       and microtime(), although not all functions implemented in these       fragments are simulated. The program simulates the PLL at each       timer interrupt and prints a summary of critical program variables       at each time update. 
  182.  
  183.       There are three defined options in the kernel configuration file       specific to each implementation. The PPS_SYNC option provides       support for a pulse-per-second (PPS) signal, which is used to       discipline the frequency of the CPU clock oscillator. The       EXT_CLOCK option provides support for an external kernel-readable       clock, such as the KSI/Odetics TPRO IRIG interface or HIGHBALL       precision oscillator, both for the SBus. The TPRO option provides       support for the former, while the HIGHBALL option provides support       for the latter. External clocks are implemented as the microtime()       clock driver, with the specific source code selected by the kernel       configuration file. 
  184.  
  185.       3.1.1. The hardupdate() Fragment 
  186.  
  187.          The hardupdate() fragment is called by ntp_adjtime() as each          update is computed to adjust the system clock phase and          frequency. Note that the time constant is in units of powers of          two, so that multiplies can be done by simple shifts. The phase          variable is computed as the offset divided by the time          constant. Then, the time since the last update is computed and          clamped to a maximum (for robustness) and to zero if          initializing. The offset is multiplied (sorry about the ugly          multiply) by the result and divided by the square of the time          constant and then added to the frequency variable. Note that          all shifts are assumed to be positive and that a shift of a          signed quantity to the right requires a little dance. 
  188.  
  189.  
  190.  
  191. Mills                                                          [Page 11] 
  192.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  193.  
  194.           With the defines given, the maximum time offset is determined          by the size in bits of the long type (32 or 64) less the          SHIFT_UPDATE scale factor (12) or at least 20 bits (signed).          The scale factor is chosen so that there is no loss of          significance in later steps, which may involve a right shift up          to SHIFT_UPDATE bits. This results in a time adjustment range          over +-512 ms. Since time_constant must be greater than or          equal to zero, the maximum frequency offset is determined by          the SHIFT_USEC scale factor (16) or at least 16 bits (signed).          This results in a frequency adjustment range over +-31,500 ppm. 
  195.  
  196.          In the addition step, the value of offset * mtemp is not          greater than MAXPHASE * MAXSEC = 31 bits (signed), which will          not overflow a long add on a 32-bit machine. There could be a          loss of precision due to the right shift of up to 12 bits,          since time_constant is bounded at 6. This results in a net          worst-case frequency resolution of about .063 ppm, which is not          significant for most quartz oscillators. The worst case could          be realized only if the NTP peer misbehaves according to the          protocol specification. 
  197.  
  198.          The time_offset value is clamped upon entry. The time_phase          variable is an accumulator, so is clamped to the tolerance on          every call. This helps to damp transients before the oscillator          frequency has been determined, as well as to satisfy the          correctness assertions if the time synchronization protocol or          implementation misbehaves. 
  199.  
  200.       3.1.2. The hardclock() Fragment 
  201.  
  202.          The hardclock() fragment is inserted in the hardware timer          interrupt routine at the point the system clock is to be          incremented. Previous to this fragment the time_update variable          has been initialized to the value computed by the adjtime()          system call in the stock Unix kernel, normally plus/minus the          tickadj value, which is usually in the order of 5 us. The          time_phase variable, which represents the instantaneous phase          of the system clock, is advanced by time_adj, which is          calculated in the second_overflow() fragment described below.          If the value of time_phase exceeds 1 us in scaled units,          time_update is increased by the (signed) excess and time_phase          retains the residue. 
  203.  
  204.          Except in the case of an external oscillator such as the          HIGHBALL interface, the hardclock() fragment advances the          system clock by the value of tick plus time_update. However, in          the case of an external oscillator, the system clock is          obtained directly from the interface and time_update used to 
  205.  
  206.  
  207.  
  208. Mills                                                          [Page 12] 
  209.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  210.  
  211.           discipline that interface instead. However, the system clock          must still be disciplined as explained previously, so the value          of clock_cpu computed by the second_overflow() fragment is used          instead. 
  212.  
  213.       3.1.3. The second_overflow() Fragment 
  214.  
  215.          The second_overflow() fragment is inserted at the point where          the microseconds field of the system time variable is being          checked for overflow. Upon overflow the maximum error          time_maxerror is increased by time_tolerance to reflect the          maximum time offset due to oscillator frequency error. Then,          the increment time_adj to advance the kernel time variable is          calculated from the (scaled) time_offset and time_freq          variables updated at the last call to the hardclock() fragment. 
  216.  
  217.          The phase adjustment is calculated as a (signed) fraction of          the time_offset remaining, where the fraction is added to          time_adj, then subtracted from time_offset. This technique          provides a rapid convergence when offsets are high, together          with good resolution when offsets are low. The frequency          adjustment is the sum of the (scaled) time_freq variable, an          adjustment necessary when the tick interval does not evenly          divide one second fixtick and PPS frequency adjustment pps_ybar          (if configured). 
  218.  
  219.          The scheme of approximating exact multiply/divide operations          with shifts produces good results, except when an exact          calculation is required, such as when the PPS signal is being          used to discipling the CPU clock oscillator frequency, as          described below. As long as the actual oscillator frequency is          a power of two in seconds, no correction is required. However,          in the SunOS kernel the clock frequency is 100 Hz, which          results in an error factor of 0.78. In this case the code          increases time_adj by a factor of 1.25, which results in an          overall error less than three percent. 
  220.  
  221.          On rollover of the day, the leap-second state machine described          below  determines whether a second is to be inserted or deleted          in the timescale. The microtime() routine insures that the          reported time is always monotonically increasing. 
  222.  
  223.       3.1.4. The hardpps() Fragment 
  224.  
  225.          The hardpps() fragment is operative only if the PPS_SYNC option          is specified in the kernel configuration file. It is called          from the serial port driver or equivalent interface at the on-          time transition of the PPS signal. The fragment operates as a 
  226.  
  227.  
  228.  
  229. Mills                                                          [Page 13] 
  230.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  231.  
  232.           first-order, type-I frequency-lock loop (FLL) controlled by the          difference between the frequency represented by the pps_ybar          variable and the frequency of the hardware clock oscillator. 
  233.  
  234.          In order to avoid calling the microtime() routine more than          once for each PPS transition, the interface requires the          calling program to capture the system time and hardware counter          contents at the on-time transition of the PPS signal and          provide a pointer to the timestamp (Unix timeval) and counter          contents as arguments to the hardpps() call. The hardware          counter contents can be determined by saving the microseconds          field of the system time, calling the microtime() routine, and          subtracting the saved value. If a counter overflow has occured          during the process, the resulting microseconds value will be          negative, in which case the caller adds 1000000 to normalize          the microseconds field. 
  235.  
  236.          The frequency of the hardware oscillator can be determined from          the difference in hardware counter readings at the beginning          and end of the calibration interval divided by the duration of          the interval. However, the oscillator frequency tolerance, as          much as 100 ppm, may cause the difference to exceed the tick          value, creating an ambiguity. In order to avoid this ambiguity,          the hardware counter value at the beginning of the interval is          increased by the current pps_ybar value once each second, but          computed modulo the tick value. At the end of the interval, the          difference between this value and the value computed from the          hardware counter is used as a control signal sample for the          FLL. 
  237.  
  238.          Control signal samples which exceed the frequency tolerance are          discarded, as well as samples resulting from excessive interval          duration jitter. Surviving samples are then processed by a          three-stage median filter. The signal which drives the FLL is          derived from the median sample, while the average of          differences between the other two samples is used as a measure          of dispersion. If the dispersion is below the threshold          pps_dispmax, the median is used to correct the pps_ybar value          with a weight expressed as a shift PPS_AVG (2). In addition to          the averaging function, pps_disp is increased by the amount          pps_dispinc once each second. The result is that, should the          dispersion be exceptionally high, or if the PPS signal fails          for some reason, the pps_disp will eventually exceed          pps_dispmax and raise an alarm. 
  239.  
  240.          Initially, an approximate value for pps_ybar is not known, so          the duration of the calibration interval must be kept small to          avoid overflowing the tick. The time difference at the end of 
  241.  
  242.  
  243.  
  244. Mills                                                          [Page 14] 
  245.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  246.  
  247.           the calibration interval is measured. If greater than a          fraction tick/4, the interval is reduced by half. If less than          this fraction for four successive calibration intervals, the          interval is doubled. This design automatically adapts to          nominal jitter in the PPS signal, as well as the value of tick.          The duration of the calibration interval is set by the          pps_shift variable as a shift in powers of two. The minimum          value PPS_SHIFT (2) is chosen so that with the highest CPU          oscillator frequency 1024 Hz and frequency tolerance 100 ppm          the tick will not overflow. The maximum value PPS_SHIFTMAX (8)          is chosen such that the maximum averaging time is about 1000 s          as determined by measurements of Allan variance [5]. 
  248.  
  249.          Should the PPS signal fail, the current frequency estimate          pps_ybar continues to be used, so the nominal frequency remains          correct subject only to the instability of the undisciplined          oscillator. The procedure to save and restore the frequency          estimate works as follows. When setting the frequency from a          file, the time_freq value is set as the file value minus the          pps_ybar value; when retrieving the frequency, the two values          are added before saving in the file. This scheme provides a          seamless interface should the PPS signal fail or the kernel          configuration change. Note that the frequency discipline is          active whether or not the synchronization daemon is active.          Since all Unix systems take some time after reboot to build a          running system, usually by that time the discipline process has          already settled down and the initial transients due to          frequency discipline have damped out. 
  250.  
  251.       3.1.4. External Clock Interface 
  252.  
  253.          The external clock driver interface is implemented with two          routines, microtime(), which returns the current clock time,          and clock_set(), which furnishes the apparent system time          derived from the kernel time variable. The latter routine is          called only when the clock is set using the settimeofday()          system call, but can be called from within the driver, such as          when the year rolls over, for example. 
  254.  
  255.          In the stock SunOS kernel and modified Ultrix and OSF/1          kernels, the microtime() routine returns the kernel time          variable plus an interpolation between timer interrupts based          on the contents of a hardware counter. In the case of an          external clock, such as described above, the system clock is          read directly from the hardware clock registers. Examples of          external clock drivers are in the tprotime.c and hightime.c          routines included in the kernel.tar.Z distribution. 
  256.  
  257.  
  258.  
  259.  Mills                                                          [Page 15] 
  260.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  261.  
  262.           The external clock routines return a status code which          indicates whether the clock is operating correctly and the          nature of the problem, if not. The return code is interpreted          by the ntp_gettime() system call, which transitions the status          state machine to the TIME_ERR state if an error code is          returned. This is the only error checking implemented for the          external clock in the present version of the code. 
  263.  
  264.       The simulator has been used to check the PLL operation over the       design envelope of +-512 ms in time error and +-100 ppm in       frequency error. This confirms that no overflows occur and that       the loop initially converges in about 15 minutes for timer       interrupt rates from 50 Hz to 1024 Hz. The loop has a normal       overshoot of a few percent and a final convergence time of several       hours, depending on the initial time and frequency error. 
  265.  
  266.    3.2. Leap Seconds 
  267.  
  268.       It does not seem generally useful in the user application       interface to provide additional details private to the kernel and       synchronization protocol, such as stratum, reference identifier,       reference timestamp and so forth. It would in principle be       possible for the application to independently evaluate the quality       of time and project into the future how long this time might be       "valid." However, to do that properly would duplicate the       functionality of the synchronization protocol and require       knowledge of many mundane details of the platform architecture,       such as the subnet configuration, reachability status and related       variables. For the curious, the ntp_adjtime() system call can be       used to reveal some of these mysteries. 
  269.  
  270.       However, the user application may need to know whether a leap       second is scheduled, since this might affect interval calculations       spanning the event. A leap-warning condition is determined by the       synchronization protocol (if remotely synchronized), by the       timecode receiver (if available), or by the operator (if awake).       This condition is set by the synchronization daemon on the day the       leap second is to occur (30 June or 31 December, as announced) by       specifying in a ntp_adjtime() system call a clock status of either       TIME_DEL, if a second is to be deleted, or TIME_INS, if a second       is to be inserted. Note that, on all occasions since the inception       of the leap-second scheme, there has never been a deletion       occasion, nor is there likely to be one in future. If the value is       TIME_DEL, the kernel adds one second to the system time       immediately following second 23:59:58 and resets the clock status       to TIME_OK. If the value is TIME_INS, the kernel subtracts one       second from the system time immediately following second 23:59:59       and resets the clock status to TIME_OOP, in effect causing system 
  271.  
  272.  
  273.  
  274. Mills                                                          [Page 16] 
  275.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  276.  
  277.        time to repeat second 59. Immediately following the repeated       second, the kernel resets the clock status to TIME_OK. 
  278.  
  279.       Depending upon the system call implementation, the reported time       during a leap second may repeat (with the TIME_OOP return code set       to advertise that fact) or be monotonically adjusted until system       time "catches up" to reported time. With the latter scheme the       reported time will be correct before and shortly after the leap       second (depending on the number of microtime() calls during the       leap second), but freeze or slowly advance during the leap second       itself. However, Most programs will probably use the ctime()       library routine to convert from timeval (seconds, microseconds)       format to tm format (seconds, minutes,...). If this routine is       modified to use the ntp_gettime() system call and inspect the       return code, it could simply report the leap second as second 60. 
  280.  
  281.    3.3. Clock Status State Machine 
  282.  
  283.       The various options possible with the system clock model described       in this memorandum require a careful examination of the state       transitions, status indications and recovery procedures should a       crucial signal or interface fail. In this section is presented a       prototype state machine designed to support leap second insertion       and deletion, as well as reveal various kinds of errors in the       synchronization process. The states of this machine are decoded as       follows: 
  284.  
  285.       TIME_OK   If an external clock is present, it is working properly                 and the system clock is derived from it. If no external                 clock is present, the synchronization daemon is working                 properly and the system clock is synchronized to a radio                 clock or one or more peers. 
  286.  
  287.       TIME_INS  An insertion of one second in the system clock has been                 declared following the last second of the current day,                 but has not yet been executed. 
  288.  
  289.       TIME_DEL  A deletion of the last second of the current day has                 been declared, but not yet executed. 
  290.  
  291.       TIME_OOP  An insertion of one second in the system clock has been                 declared following the last second of the current day.                 The second is in progress, but not yet completed.                 Library conversion routines should interpret this second                 as 23:59:60. 
  292.  
  293.  
  294.  
  295.  
  296.  
  297.  Mills                                                          [Page 17] 
  298.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  299.  
  300.        TIME_BAD  Either (a) the synchronization daemon has declared the                 protocol is not working properly, (b) all sources of                 outside synchronization have been lost or (c) an                 external clock is present and it has just become                 operational following a non-operational condition. 
  301.  
  302.       TIME_ERR  An external clock is present, but is in a non-                 operational condition. 
  303.  
  304.       In all except the TIME_ERR state the system clock is derived from       either an external clock, if present, or the kernel time variable,       if not. In the TIME_ERR state the external clock is present, but       not working properly, so the system clock may be derived from the       kernel time variable. The following diagram indicates the normal       transitions of the state machine. Not all valid transitions are       shown. 
  305.  
  306.           +--------+     +--------+     +--------+     +--------+           |        |     |        |     |        |     |        |           |TIME_BAD|---->|TIME_OK |<----|TIME_OOP|<----|TIME_INS|           |        |     |        |     |        |     |        |           +--------+     +--------+     +--------+     +--------+                A              A                |              |                |              |           +--------+     +--------+           |        |     |        |           |TIME_ERR|     |TIME_DEL|           |        |     |        |           +--------+     +--------+ 
  307.  
  308.       The state machine makes a transition once each second at an       instant where the microseconds field of the kernel time variable       overflows and one second is added to the seconds field. However,       this condition is checked at each timer interrupt, which may not       exactly coincide with the actual instant of overflow. This may       lead to some interesting anomalies, such as a status indication of       a leap second in progress (TIME_OOP) when actually the leap second       had already expired. 
  309.  
  310.       The following state transitions are executed automatically by the       kernel: 
  311.  
  312.       any state -> TIME_ERR   This transition occurs when an external                               clock is present and an attempt is made to                               read it when in a non-operational                               condition. 
  313.  
  314.  
  315.  
  316.  Mills                                                          [Page 18] 
  317.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  318.  
  319.        TIME_INS -> TIME_OOP    This transition occurs immediately                               following second 86,400 of the current day                               when an insert-second event has been                               declared. 
  320.  
  321.       TIME_OOP -> TIME_OK     This transition occurs immediately                               following second 86,401 of the current                               day; that is, one second after entry to                               the TIME_OOP state. 
  322.  
  323.       TIME_DEL -> TIME_OK     This transition occurs immediately                               following second 86,399 of the current day                               when a delete-second event has been                               declared. 
  324.  
  325.       The following state transitions are executed by specific       ntp_adjtime() system calls: 
  326.  
  327.       TIME_OK -> TIME_INS     This transition occurs as the result of a                               ntp_adjtime() system call to declare an                               insert-second event. 
  328.  
  329.       TIME_OK -> TIME_DEL     This transition occurs as the result of a                               ntp_adjtime() system call to declare a                               delete-second event. 
  330.  
  331.       any state -> TIME_BAD   This transition occurs as the result of a                               ntp_adjtime() system call to declare loss                               of all sources of synchronization or in                               other cases of error. 
  332.  
  333.       The following table summarizes the actions just before, during and       just after a leap-second event. Each line in the table shows the       UTC and NTP times at the beginning of the second. The left column       shows the behavior when no leap event is to occur. In the middle       column the state machine is in TIME_INS at the end of UTC second       23:59:59 and the NTP time has just reached 400. The NTP time is       set back one second to 399 and the machine enters TIME_OOP. At the       end of the repeated second the machine enters TIME_OK and the UTC       and NTP times are again in correspondence. In the right column the       state machine is in TIME_DEL at the end of UTC second 23:59:58 and       the NTP time has just reached 399. The NTP time is incremented,       the machine enters TIME_OK and both UTC and NTP times are again in       correspondence. 
  334.  
  335.  
  336.  
  337.  
  338.  
  339.  
  340.  
  341. Mills                                                          [Page 19] 
  342.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  343.  
  344.                     No Leap       Leap Insert    Leap Delete                    UTC NTP         UTC NTP        UTC NTP               ---------------------------------------------               23:59:58|398    23:59:58|398   23:59:58|398                       |               |              |               23:59:59|399    23:59:59|399   00:00:00|400                       |               |              |               00:00:00|400    23:59:60|399   00:00:01|401                       |               |              |               00:00:01|401    00:00:00|400   00:00:02|402                       |               |              |               00:00:02|402    00:00:01|401   00:00:03|403                       |               |              | 
  345.  
  346.       To determine local midnight without fuss, the kernel code simply       finds the residue of the time.tv_sec (or time.tv_sec + 1) value       mod 86,400, but this requires a messy divide. Probably a better       way to do this is to initialize an auxiliary counter in the       settimeofday() routine using an ugly divide and increment the       counter at the same time the time.tv_sec is incremented in the       timer interrupt routine. For future embellishment. 
  347.  
  348. 4. Programming Model and Interfaces 
  349.  
  350.    This section describes the programming model for the synchronization    daemon and user application programs. The ideas are based on    suggestions from Jeff Mogul and Philip Gladstone and a similar    interface designed by the latter. It is important to point out that    the functionality of the original Unix adjtime() system call is    preserved, so that the modified kernel will work as the unmodified    one, should the new features not be in use. In this case the    ntp_adjtime() system call can still be used to read and write kernel    variables that might be used by a synchronization daemon other than    NTP, for example. 
  351.  
  352.    4.1. The ntp_gettime() System Call 
  353.  
  354.       The syntax and semantics of the ntp_gettime() call are given in       the following fragment of the timex.h header file. This file is       identical, except for the SHIFT_HZ define, in the SunOS, Ultrix       and OSF/1 kernel distributions. (The SHIFT_HZ define represents       the logarithm to the base 2 of the clock oscillator frequency       specific to each system type.) Note that the timex.h file calls       the syscall.h system header file, which must be modified to define       the SYS_ntp_gettime system call specific to each system type. The       kernel distributions include directions on how to do this. 
  355.  
  356.  
  357.  
  358.  
  359.  
  360. Mills                                                          [Page 20] 
  361.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  362.  
  363.        /*        * This header file defines the Network Time Protocol (NTP)        * interfaces for user and daemon application programs. These are        * implemented using private system calls and data structures and        * require specific kernel support.        *        * NAME        *   ntp_gettime - NTP user application interface        *        * SYNOPSIS        *   #include <sys/timex.h>        *        *   int system call(SYS_ntp_gettime, tptr)        *        *   int SYS_ntp_gettime     defined in syscall.h header file        *   struct ntptimeval *tptr pointer to ntptimeval structure        *        * NTP user interface - used to read kernel clock values        * Note: maximum error = NTP synch distance = dispersion + delay /        * 2        * estimated error = NTP dispersion.        */       struct ntptimeval {            struct timeval time;    /* current time */            long maxerror;          /* maximum error (us) */            long esterror;          /* estimated error (us) */       }; 
  364.  
  365.       The ntp_gettime() system call returns three values in the       ntptimeval structure: the current time in unix timeval format plus       the maximum and estimated errors in microseconds. While the 32-bit       long data type limits the error quantities to something more than       an hour, in practice this is not significant, since the protocol       itself will declare an unsynchronized condition well below that       limit. In the NTP Version 3 specification, if the protocol       computes either of these values in excess of 16 seconds, they are       clamped to that value and the system clock declared       unsynchronized. 
  366.  
  367.       Following is a detailed description of the ntptimeval structure       members. 
  368.  
  369.  
  370.  
  371.  
  372.  
  373.  
  374.  
  375.  
  376.  
  377.  Mills                                                          [Page 21] 
  378.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  379.  
  380.        struct timeval time;    /* current time */ 
  381.  
  382.          This member returns the current system time, expressed as a          Unix timeval structure. The timeval structure consists of two          32-bit words; the first returns the number of seconds past 1          January 1970, while the second returns the number of          microseconds. 
  383.  
  384.       long maxerror;          /* maximum error (us) */ 
  385.  
  386.          This member returns the time_maxerror kernel variable in          microseconds. See the entry for this variable in section 5 for          additional information. 
  387.  
  388.       long esterror;          /* estimated error (us) */ 
  389.  
  390.          This member returns the time_esterror kernel variable in          microseconds. See the entry for this variable in section 5 for          additional information. 
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.  
  403.  
  404.  
  405.  
  406.  
  407.  
  408.  
  409.  
  410.  
  411.  
  412.  
  413.  
  414.  
  415.  
  416.  
  417.  
  418.  
  419.  
  420.  
  421.  
  422.  Mills                                                          [Page 22] 
  423.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  424.  
  425.     4.2. The ntp_adjtime() System Call 
  426.  
  427.       The syntax and semantics of the ntp_adjtime() call are given in       the following fragment of the timex.h header file. Note that, as       in the ntp_gettime() system call, the syscall.h system header file       must be modified to define the SYS_ntp_adjtime system call       specific to each system type. 
  428.  
  429.       /*        * NAME        *   ntp_adjtime - NTP daemon application interface        *        * SYNOPSIS        *   #include <sys/timex.h>        *        *   int system call(SYS_ntp_adjtime, mode, tptr)        *        *   int SYS_ntp_adjtime     defined in syscall.h header file        *   struct timex *tptr      pointer to timex structure        *        * NTP daemon interface - used to discipline kernel clock        * oscillator        */       struct timex {           int mode;                /* mode selector */           long offset;             /* time offset (us) */           long frequency;          /* frequency offset (scaled ppm) */           long maxerror;           /* maximum error (us) */           long esterror;           /* estimated error (us) */           int status;              /* clock command/status */           long time_constant;      /* pll time constant */           long precision;          /* clock precision (us) (read only)                                     */           long tolerance;          /* clock frequency tolerance (scaled                                     * ppm) (read only) */           /*            * The following read-only structure members are implemented            * only if the PPS signal discipline is configured in the            * kernel.            */           long ybar;               /* frequency estimate (scaled ppm) */           long disp;               /* dispersion estimate (scaled ppm)                                     */           int shift;               /* interval duration (s) (shift) */           long calcnt;             /* calibration intervals */           long jitcnt;             /* jitter limit exceeded */           long discnt;             /* dispersion limit exceeded */       }; 
  430.  
  431.  
  432.  
  433. Mills                                                          [Page 23] 
  434.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  435.  
  436.        The ntp_adjtime() system call is used to read and write certain       time-related kernel variables summarized in this and subsequent       sections. Writing these variables can only be done in superuser       mode. To write a variable, the mode structure member is set with       one or more bits, one of which is assigned each of the following       variables in turn. The current values for all variables are       returned in any case; therefore, a mode argument of zero means to       return these values without changing anything. 
  437.  
  438.       Following is a description of the timex structure members. 
  439.  
  440.       int mode;               /* mode selector */ 
  441.  
  442.          This is a bit-coded variable selecting one or more structure          members, with one bit assigned each member. If a bit is set,          the value of the associated member variable is copied to the          corresponding kernel variable; if not, the member is ignored.          The bits are assigned as given in the following fragment of the          timex.h header file. Note that the precision and tolerance are          determined by the kernel and cannot be changed by          ntp_adjtime(). 
  443.  
  444.          /*           * Mode codes (timex.mode)           */          #define ADJ_OFFSET       0x0001    /* time offset */          #define ADJ_FREQUENCY    0x0002    /* frequency offset */          #define ADJ_MAXERROR     0x0004    /* maximum time error */          #define ADJ_ESTERROR     0x0008    /* estimated time error */          #define ADJ_STATUS       0x0010    /* clock status */          #define ADJ_TIMECONST    0x0020    /* pll time constant */ 
  445.  
  446.       long offset;            /* time offset (us) */ 
  447.  
  448.          If selected, this member replaces the value of the time_offset          kernel variable in microseconds. The absolute value must be          less than MAXPHASE microseconds defined in the timex.h header          file. See the entry for this variable in section 5 for          additional information. 
  449.  
  450.          If within range and the PPS signal and/or external oscillator          are configured and operating properly, the clock status is          automatically set to TIME_OK. 
  451.  
  452.  
  453.  
  454.  
  455.  
  456.  
  457.  
  458.  Mills                                                          [Page 24] 
  459.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  460.  
  461.        long time_constant;     /* pll time constant */ 
  462.  
  463.          If selected, this member replaces the value of the          time_constant kernel variable. The value must be between zero          and MAXTC defined in the timex.h header file. See the entry for          this variable in section 5 for additional information. 
  464.  
  465.       long frequency;         /* frequency offset (scaled ppm) */ 
  466.  
  467.          If selected, this member replaces the value of the          time_frequency kernel variable. The value is in ppm, with the          integer part in the high order 16 bits and fraction in the low          order 16 bits. The absolute value must be in the range less          than MAXFREQ ppm defined in the timex.h header file. See the          entry for this variable in section 5 for additional          information. 
  468.  
  469.       long maxerror;          /* maximum error (us) */ 
  470.  
  471.          If selected, this member replaces the value of the          time_maxerror kernel variable in microseconds. See the entry          for this variable in section 5 for additional information. 
  472.  
  473.       long esterror;          /* estimated error (us) */ 
  474.  
  475.          If selected, this member replaces the value of the          time_esterror kernel variable in microseconds. See the entry          for this variable in section 5 for additional information. 
  476.  
  477.       int status;             /* clock command/status */ 
  478.  
  479.          If selected, this member replaces the value of the time_status          kernel variable. See the entry for this variable in section 5          for additional information. 
  480.  
  481.          In order to set this variable by ntp_adjtime(), either (a) the          current clock status must be TIME_OK or (b) the member value is          TIME_BAD; that is, the ntp_adjtime() call can always set the          clock to the unsynchronized state or, if the clock is running          correctly, can set it to any state. In any case, the          ntp_adjtime() call always returns the current state in this          member, so the caller can determine whether or not the request          succeeded. 
  482.  
  483.  
  484.  
  485.  
  486.  
  487.  
  488.  
  489.  Mills                                                          [Page 25] 
  490.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  491.  
  492.        long time_constant;     /* pll time constant */ 
  493.  
  494.          If selected, this member replaces the value of the          time_constant kernel variable. The value must be between zero          and MAXTC defined in the timex.h header file. See the entry for          this variable in section 5 for additional information. 
  495.  
  496.       long precision;         /* clock precision (us) (read only) */ 
  497.  
  498.          This member returns the time_precision kernel variable in          microseconds. The variable can be written only by the kernel.          See the entry for this variable in section 5 for additional          information. 
  499.  
  500.       long tolerance;         /* clock frequency tolerance (scaled ppm)                                */ 
  501.  
  502.          This member returns the time_tolerance kernel variable in          microseconds. The variable can be written only by the kernel.          See the entry for this variable in section 5 for additional          information. 
  503.  
  504.       long ybar;              /* frequency estimate (scaled ppm) */ 
  505.  
  506.          This member returns the pps_ybar kernel variable in          microseconds. The variable can be written only by the kernel.          See the entry for this variable in section 5 for additional          information. 
  507.  
  508.       long disp;              /* dispersion estimate (scaled ppm) */ 
  509.  
  510.          This member returns the pps_disp kernel variable in          microseconds. The variable can be written only by the kernel.          See the entry for this variable in section 5 for additional          information. 
  511.  
  512.       int shift;              /* interval duration (s) (shift) */ 
  513.  
  514.          This member returns the pps_shift kernel variable in          microseconds. The variable can be written only by the kernel.          See the entry for this variable in section 5 for additional          information. 
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522.  
  523.  
  524. Mills                                                          [Page 26] 
  525.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  526.  
  527.        long calcnt;            /* calibration intervals */ 
  528.  
  529.          This member returns the pps_calcnt kernel variable in          microseconds. The variable can be written only by the kernel.          See the entry for this variable in section 5 for additional          information. 
  530.  
  531.       long jitcnt;            /* jitter limit exceeded */ 
  532.  
  533.          This member returns the pps_jittcnt kernel variable in          microseconds. The variable can be written only by the kernel.          See the entry for this variable in section 5 for additional          information. 
  534.  
  535.       long discnt;            /* dispersion limit exceeded */ 
  536.  
  537.          This member returns the pps_discnt kernel variable in          microseconds. The variable can be written only by the kernel.          See the entry for this variable in section 5 for additional          information. 
  538.  
  539.  
  540.  
  541.  
  542.  
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.  
  557.  
  558.  
  559.  
  560.  
  561.  
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569. Mills                                                          [Page 27] 
  570.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  571.  
  572.     4.3. Command/Status Codes 
  573.  
  574.       The kernel routines use the system clock status variable       time_status, which records whether the clock is synchronized,       waiting for a leap second, etc. The value of this variable is       returned as the result code by both the ntp_gettime() and       ntp_adjtime() system calls. In addition, it can be explicitly read       and written using the ntp_adjtime() system call, but can be       written only in superuser mode. Values presently defined in the       timex.h header file are as follows: 
  575.  
  576.       /*        * Clock command/status codes (timex.status)        */       #define TIME_OK    0    /* clock synchronized */       #define TIME_INS   1    /* insert leap second */       #define TIME_DEL   2    /* delete leap second */       #define TIME_OOP   3    /* leap second in progress */       #define TIME_BAD   4    /* kernel clock not synchronized */       #define TIME_ERR   5    /* external oscillator not                                  synchronized */ 
  577.  
  578.       A detailed description of these codes as used by the leap-second       state machine is given later in this memorandum. In case of a       negative result code, the kernel has intercepted an invalid       address or (in case of the ntp_adjtime() system call), a superuser       violation. 
  579.  
  580. 5. Kernel Variables 
  581.  
  582.    This section contains a list of kernel variables and a detailed    description of their function, initial value, scaling and limits. 
  583.  
  584.    5.1. Interface Variables 
  585.  
  586.       The following variables are read and set by the ntp_adjtime()       system call. Additional automatic variables are used as       temporaries as described in the code fragments. 
  587.  
  588.       int time_status = TIME_BAD; 
  589.  
  590.          This variable controls the state machine used to insert or          delete leap seconds and show the status of the timekeeping          system, PPS signal and external oscillator, if configured. 
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598. Mills                                                          [Page 28] 
  599.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  600.  
  601.        long time_offset = 0; 
  602.  
  603.          This variable is used by the PLL to adjust the system time in          small increments. It is scaled by (1 << SHIFT_UPDATE) (12) in          microseconds. The maximum value that can be represented is          about +-512 ms and the minimum value or precision is a few          parts in 10^10 s. 
  604.  
  605.       long time_constant = 0;      /* pll time constant */ 
  606.  
  607.          This variable determines the bandwidth or "stiffness" of the          PLL. The value is used as a shift between zero and MAXTC (6),          with the effective PLL time constant equal to a multiple of (1          << time_constant) in seconds. For room-temperature quartz          oscillator the recommended default value is 2, which          corresponds to a PLL time constant of about 900 s and a maximum          update interval of about 64 s. The maximum update interval          scales directly with the time constant, so that at the maximum          time constant of 6, the update interval can be as large as 1024          s. 
  608.  
  609.          Values of time_constant between zero and 2 can be used if quick          convergence is necessary; values between 2 and 6 can be used to          reduce network load, but at a modest cost in accuracy. Values          above 6 are appropriate only if an external oscillator is          present. 
  610.  
  611.       long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ 
  612.  
  613.          This variable represents the maximum frequency error or          tolerance in ppm of the particular CPU clock oscillator and is          a property of the architecture; however, in principle it could          change as result of the presence of external discipline          signals, for instance. It is expressed as a positive number          greater than zero in parts-per-million (ppm). 
  614.  
  615.          The recommended value of MAXFREQ is 200 ppm is appropriate for          room-temperature quartz oscillators used in typical          workstations. However, it can change due to the operating          condition of the PPS signal and/or external oscillator. With          either the PPS signal or external oscillator, the recommended          value for MAXFREQ is 100 ppm. 
  616.  
  617.  
  618.  
  619.  
  620.  
  621.  
  622.  
  623.  
  624.  
  625. Mills                                                          [Page 29] 
  626.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  627.  
  628.        long time_precision = 1000000 / HZ; /* clock precision (us) */ 
  629.  
  630.          This variable represents the maximum error in reading the          system clock in microseconds. It is usually based on the number          of microseconds between timer interrupts, 10000 us for the          SunOS kernel, 3906 us for the Ultrix kernel, 976 us for the          OSF/1 kernel. However, in cases where the time can be          interpolated between timer interrupts with microsecond          resolution, such as in the unmodified SunOS kernel and modified          Ultrix and OSF/1 kernels, the precision is specified as 1 us.          In cases where a PPS signal or external oscillator is          available, the precision can depend on the operating condition          of the signal or oscillator. This variable is determined by the          kernel for use by the synchronization daemon, but is otherwise          not used by the kernel. 
  631.  
  632.       long time_maxerror = MAXPHASE; /* maximum error */ 
  633.  
  634.          This variable establishes the maximum error of the indicated          time relative to the primary synchronization source in          microseconds. For NTP, the value is initialized by a          ntp_adjtime() call to the synchronization distance, which is          equal to the root dispersion plus one-half the root delay. It          is increased by a small amount (time_tolerance) each second to          reflect the clock frequency tolerance. This variable is          computed by the synchronization daemon and the kernel, but is          otherwise not used by the kernel. 
  635.  
  636.       long time_esterror = MAXPHASE; /* estimated error */ 
  637.  
  638.          This variable establishes the expected error of the indicated          time relative to the primary synchronization source in          microseconds. For NTP, the value is determined as the root          dispersion, which represents the best estimate of the actual          error of the system clock based on its past behavior, together          with observations of multiple clocks within the peer group.          This variable is computed by the synchronization daemon and          returned in system calls, but is otherwise not used by the          kernel. 
  639.  
  640.  
  641.  
  642.  
  643.  
  644.  
  645.  
  646.  
  647.  
  648.  
  649.  
  650.  Mills                                                          [Page 30] 
  651.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  652.  
  653.     5.2. Phase-Lock Loop Variables 
  654.  
  655.       The following variables establish the state of the PLL and the       residual time and frequency offset of the system clock. Additional       automatic variables are used as temporaries as described in the       code fragments. 
  656.  
  657.       long time_phase = 0;         /* phase offset (scaled us) */ 
  658.  
  659.          The time_phase variable represents the phase of the kernel time          variable at each tick of the clock. This variable is scaled by          (1 << SHIFT_SCALE) (23) in microseconds, giving a maximum          adjustment of about +-256 us/tick and a resolution less than          one part in 10^12. 
  660.  
  661.       long time_offset = 0;        /* time offset (scaled us) */ 
  662.  
  663.          The time_offset variable represents the time offset of the CPU          clock oscillator. It is recalculated as each update to the          system clock is received via the hardupdate() routine and at          each second in the seconds_overflow routine. This variable is          scaled by (1 << SHIFT_UPDATE) (12) in microseconds, giving a          maximum adjustment of about +-512 ms and a resolution of a few          parts in 10^10 s. 
  664.  
  665.       long time_freq = 0;          /* frequency offset (scaled ppm) */ 
  666.  
  667.          The time_freq variable represents the frequency offset of the          CPU clock oscillator. It is recalculated as each update to the          system clock is received via the hardupdate() routine. It can          also be set via ntp_adjtime() from a value stored in a file          when the synchronization daemon is first started. It can be          retrieved via ntp_adjtime() and written to the file about once          per hour by the daemon. The time_freq variable is scaled by (1          << SHIFT_KF) (16) ppm, giving it a maximum value well in excess          of the limit of +-256 ppm imposed by other constraints. The          precision of this representation (frequency resolution) is          parts in 10^11, which is adequate for all but the best external          oscillators. 
  668.  
  669.       time_adj = 0;                /* tick adjust (scaled 1 / HZ) */ 
  670.  
  671.          The time_adj variable is the adjustment added to the value of          tick at each timer interrupt. It is computed once each second          from the time_offset, time_freq and, if the PPS signal is          present, the ps_ybar variable once each second. 
  672.  
  673.  
  674.  
  675.  
  676.  
  677. Mills                                                          [Page 31] 
  678.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  679.  
  680.        long time_reftime = 0;       /* time at last adjustment (s) */ 
  681.  
  682.          This variable is the seconds portion of the system time on the          last update received by the hardupdate() routine. It is used to          compute the time_freq variable as the time since the last          update increases. 
  683.  
  684.       int fixtick = 1000000 % HZ;  /* amortization factor */ 
  685.  
  686.          In the Ultrix and OSF/1 kernels, the interval between timer          interrupts does not evenly divide the number of microseconds in          the second. In order that the clock runs at a precise rate, it          is necessary to introduce an amortization factor into the local          timescale. In the original Unix code, the value of fixtick is          amortized once each second, introducing an additional source of          jitter; in the new model the value is amortized at each tick of          the system clock, reducing the jitter by the reciprocal of the          clock oscillator frequency. This is not a new kernel variable,          but a new use of an existing kernel variable. 
  687.  
  688.    5.3. Pulse-per-second (PPS) Frequency-Lock Loop Variables 
  689.  
  690.       The following variables are used only if a pulse-per-second (PPS)       signal is available and connected via a modem-control lead, such       as produced by the optional ppsclock feature incorporated in the       serial port driver. They establish the design parameters of the       PPS frequency-lock loop used to discipline the CPU clock       oscillator to an external PPS signal. Additional automatic       variables are used as temporaries as described in the code       fragments. 
  691.  
  692.       long pps_usec;          /* microseconds at last pps */ 
  693.  
  694.          The pps_usec variable is latched from a high resolution counter          or external oscillator at each PPS interrupt. In determining          this value, only the hardware counter contents are used, not          the contents plus the kernel time variable, as returned by the          microtime() routine. 
  695.  
  696.       long pps_ybar = 0;      /* pps frequency offset estimate */ 
  697.  
  698.          The pps_ybar variable is the average CPU clock oscillator          frequency offset relative to the PPS disciplining signal. It is          scaled in the same units as the time_freq variable. 
  699.  
  700.  
  701.  
  702.  
  703.  
  704.  
  705.  
  706. Mills                                                          [Page 32] 
  707.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  708.  
  709.        pps_disp = MAXFREQ;     /* dispersion estimate (scaled ppm) */ 
  710.  
  711.          The pps_disp variable represents the average sample dispersion          measured over the last three samples. It is scaled in the same          units as the time_freq variable. 
  712.  
  713.       pps_dispmax = MAXFREQ / 2; /* dispersion threshold */ 
  714.  
  715.          The pps_dispmax variable is used as a dispersion threshold. If          pps_disp is less than this threshold, the median sample is used          to update the pps_ybar estimate; if not, the sample is          discarded. 
  716.  
  717.       pps_dispinc = MAXFREQ >> (PPS_SHIFT + 4); /* pps dispersion       increment/sec */ 
  718.  
  719.          The pps_dispinc variable is the increment to add to pps_disp          once each second. It is computed such that, if no PPS samples          have arrived for several calibration intervals, the value of          pps_disp will exceed the pps_dispmax threshold and raise an          alarm. 
  720.  
  721.       int pps_mf[] = {0, 0, 0};    /* pps median filter */ 
  722.  
  723.          The pps-mf[] array is used as a median filter to detect and          discard jitter in the PPS signal. 
  724.  
  725.       int pps_count = 0;           /* pps calibrate interval counter */ 
  726.  
  727.          The pps_count variable measures the length of the calibration          interval used to calculate the frequency. It normally counts          from zero to the value 1 << pps_shift. 
  728.  
  729.       pps_shift = PPS_SHIFT;       /* interval duration (s) (shift) */ 
  730.  
  731.          The pps_shift variable determines the duration of the          calibration interval, 1 << pps_shift s. 
  732.  
  733.       pps_intcnt = 0;              /* intervals at current duration */ 
  734.  
  735.          The pps_intcnt variable counts the number of calibration          intervals at the current interval duration. It is reset to zero          after four intervals and when the interval duration is changed. 
  736.  
  737.       long pps_calcnt = 0;         /* calibration intervals */ 
  738.  
  739.          The pps_calcnt variable counts the number of calibration          intervals. 
  740.  
  741.  
  742.  
  743. Mills                                                          [Page 33] 
  744.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  745.  
  746.        long pps_jitcnt = 0;         /* jitter limit exceeded */ 
  747.  
  748.          The pps_jitcnt variable counts the number of resets due to          excessive jitter or frequency offset. These resets are          usually due to excessive noise in the PPS signal or          interface. 
  749.  
  750.       long pps_discnt = 0;         /* dispersion limit exceeded */ 
  751.  
  752.          The pps_discnt variable counts the number of calibration          intervals where the dispersion is above the pps_dispmax          limit.  These resets are usually due to excessive frequency          wander in the PPS signal source. 
  753.  
  754.  
  755.  
  756.  
  757.  
  758.  
  759.  
  760.  
  761.  
  762.  
  763.  
  764.  
  765.  
  766.  
  767.  
  768.  
  769.  
  770.  
  771.  
  772.  
  773.  
  774.  
  775.  
  776.  
  777.  
  778.  
  779.  
  780.  
  781.  
  782.  
  783.  
  784.  
  785.  
  786.  
  787.  
  788.  
  789.  
  790.  Mills                                                          [Page 34] 
  791.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  792.  
  793.     5.4. External Oscillator Variables 
  794.  
  795.       The following variables are used only if an external oscillator       (HIGHBALL or TPRO) is present. Additional automatic variables are       used as temporaries as described in the code fragments. 
  796.  
  797.       int clock_count = 0;         /* CPU clock counter */ 
  798.  
  799.          The clock_count variable counts the seconds between adjustments          to the kernel time variable to discipline it to the external          clock. 
  800.  
  801.       struct timeval clock_offset; /* HIGHBALL clock offset */ 
  802.  
  803.          The clock_offset variable defines the offset between system          time and the HIGHBALL counters. 
  804.  
  805.       long clock_cpu = 0;          /* CPU clock adjust */ 
  806.  
  807.          The clock_cpu variable contains the offset between the system          clock and the HIGHBALL clock for use in disciplining the kernel          time variable. 
  808.  
  809. 6. Architecture Constants 
  810.  
  811.    Following is a list of the important architecture constants that    establish the response and stability of the PLL and provide maximum    bounds on behavior in order to satisfy correctness assertions made in    the protocol specification. Additional definitions are given in the    timex.h header file. 
  812.  
  813.    6.1. Phase-lock loop (PLL) definitions 
  814.  
  815.       The following defines establish the performance envelope of the       PLL. They establish the maximum phase error (MAXPHASE), maximum       frequency error (MAXFREQ), minimum interval between updates       (MINSEC) and maximum interval between updates (MAXSEC). The intent       of these bounds is to force the PLL to operate within predefined       limits in order to satisfy correctness assertions of the       synchronization protocol. An excursion which exceeds these bounds       is clamped to the bound and operation proceeds normally. In       practice, this can occur only if something has failed or is       operating out of tolerance, but otherwise the PLL continues to       operate in a stable mode. 
  816.  
  817.       MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms),       as defined in the NTP specification. CLOCK.MAX establishes the       maximum time offset allowed before the system time is reset, 
  818.  
  819.  
  820.  
  821. Mills                                                          [Page 35] 
  822.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  823.  
  824.        rather than incrementally adjusted. Here, the maximum offset is       clamped to MAXPHASE only in order to prevent overflow errors due       to defective programming. 
  825.  
  826.       MAXFREQ reflects the manufacturing frequency tolerance of the CPU       oscillator plus the maximum slew rate allowed by the protocol. It       should be set to at least the intrinsic frequency tolerance of the       oscillator plus 100 ppm for vernier frequency adjustments. If the       kernel frequency discipline code is installed (PPS_SYNC), the CPU       oscillator frequency is disciplined to an external source,       presumably with negligible frequency error. 
  827.  
  828.       #define MAXPHASE 512000      /* max phase error (us) */       #ifdef PPS_SYNC       #define MAXFREQ 100          /* max frequency error (ppm) */       #else       #define MAXFREQ 200          /* max frequency error (ppm) */       #endif /* PPS_SYNC */       #define MINSEC 16            /* min interval between updates (s)                                     */       #define MAXSEC 1200          /* max interval between updates (s)                                     */ 
  829.  
  830.    6.2. Pulse-per-second (PPS) Frequency-lock Loop (FLL) Definitions 
  831.  
  832.       The following defines and declarations are used only if a pulse-       per-second (PPS) signal is available and connected via a modem-       control lead, such as produced by the optional ppsclock feature       incorporated in the serial port driver. They establish the design       parameters of the frequency-lock loop (FLL) used to discipline the       CPU clock oscillator to the PPS oscillator. 
  833.  
  834.       PPS_AVG is the averaging constant used to update the FLL from       frequency samples measured for each calibration interval.       PPS_SHIFT and PPS_SHIFTMAX are the minimum and maximem,       respectively, of the calibration interval represented as a power       of two. The PPS_DISPINC is the initial increment to pps_disp at       each second. 
  835.  
  836.       #define PPS_AVG 2            /* pps averaging constant (shift) */       #define PPS_SHIFT 2          /* min interval duration (s) (shift)                                     */       #define PPS_SHIFTMAX 6       /* max interval duration (s) (shift)                                     */       #define PPS_DISPINC 0        /* dispersion increment (us/s) */ 
  837.  
  838.  
  839.  
  840.  
  841.  
  842.  Mills                                                          [Page 36] 
  843.  RFC 1589         Kernel Model for Precision Timekeeping       March 1994 
  844.  
  845.     6.3. External Oscillator Definitions 
  846.  
  847.       The following definitions and declarations are used only if an       external oscillator (HIGHBALL or TPRO) is configured on the       system. 
  848.  
  849.       #define CLOCK_INTERVAL 30    /* CPU clock update interval (s) */ 
  850.  
  851. 7. References 
  852.  
  853.    [1] Mills, D., "Internet time synchronization: the Network Time        Protocol", IEEE Trans. Communications COM-39, 10 (October 1991),        1482- 1493. Also in: Yang, Z., and T.A. Marsland (Eds.). Global        States and Time in Distributed Systems, IEEE Press, Los Alamitos,        CA, 91-102. 
  854.  
  855.    [2] Mills, D., "Network Time Protocol (Version 3) specification,        implementation and analysis", RFC 1305, University of Delaware,        March 1992, 113 pp. 
  856.  
  857.    [3] Mills, D., "Modelling and analysis of computer network clocks",        Electrical Engineering Department Report 92-5-2, University of        Delaware, May 1992, 29 pp. 
  858.  
  859.    [4] Mills, D., "Simple Network Time Protocol (SNTP)", RFC 1361,        University of Delaware, August 1992, 10 pp. 
  860.  
  861.    [5] Mills, D., "Precision synchronizatin of computer network clocks",        Electrical Engineering Department Report 93-11-1, University of        Delaware, November 1993, 66 pp. 
  862.  
  863. Security Considerations 
  864.  
  865.    Security issues are not discussed in this memo. 
  866.  
  867. Author's Address 
  868.  
  869.    David L. Mills    Electrical Engineering Department    University of Delaware    Newark, DE 19716 
  870.  
  871.    Phone: (302) 831-8247    EMail: mills@udel.edu 
  872.  
  873.  
  874.  
  875.  
  876.  
  877.  
  878.  
  879. Mills                                                          [Page 37] 
  880.  
  881.