File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / libntp / adjtime.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 2012 UTC (12 years, 5 months ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

    1: #ifdef HAVE_CONFIG_H
    2: # include <config.h>
    3: #endif
    4: 
    5: #ifdef MPE 
    6: /*
    7:  * MPE lacks adjtime(), so we define our own.  But note that time slewing has
    8:  * a sub-second accuracy bug documented in SR 5003462838 which prevents ntpd
    9:  * from being able to maintain clock synch.  Because of the bug, this adjtime()
   10:  * implementation as used by ntpd has a side-effect of screwing up the hardware
   11:  * PDC clock, which will need to be reset with a reboot.
   12:  *
   13:  * This problem affects all versions of MPE at the time of this writing (when
   14:  * MPE/iX 7.0 is the most current).  It only causes bad things to happen when
   15:  * doing continuous clock synchronization with ntpd; note that you CAN run ntpd
   16:  * with "disable ntp" in ntp.conf if you wish to provide a time server.
   17:  *
   18:  * The one-time clock adjustment functionality of ntpdate and ntp_timeset can
   19:  * be used without screwing up the PDC clock.
   20:  * 
   21:  */
   22: #include <time.h>
   23: 
   24: int adjtime(struct timeval *delta, struct timeval *olddelta);
   25: 
   26: int adjtime(struct timeval *delta, struct timeval *olddelta)
   27: 
   28: {
   29: /* Documented, supported MPE system intrinsics. */
   30: 
   31: extern void GETPRIVMODE(void);
   32: extern void GETUSERMODE(void);
   33: 
   34: /* Undocumented, unsupported MPE internal functions. */
   35: 
   36: extern long long current_correction_usecs(void);
   37: extern long long get_time(void);
   38: extern void get_time_change_info(long long *, char *, char *);
   39: extern long long pdc_time(int *);
   40: extern void set_time_correction(long long, int, int);
   41: extern long long ticks_to_micro(long long);
   42: 
   43: long long big_sec, big_usec, new_correction = 0LL;
   44: long long prev_correction;
   45: 
   46: if (delta != NULL) {
   47:   /* Adjustment required.  Convert delta to 64-bit microseconds. */
   48:   big_sec = (long)delta->tv_sec;
   49:   big_usec = delta->tv_usec;
   50:   new_correction = (big_sec * 1000000LL) + big_usec;
   51: }
   52: 
   53: GETPRIVMODE();
   54: 
   55: /* Determine how much of a previous correction (if any) we're interrupting. */
   56: prev_correction = current_correction_usecs();
   57: 
   58: if (delta != NULL) {
   59:   /* Adjustment required. */
   60: 
   61: #if 0
   62:   /* Speculative code disabled until bug SR 5003462838 is fixed.  This bug
   63:      prevents accurate time slewing, and indeed renders ntpd inoperable. */
   64: 
   65:   if (prev_correction != 0LL) {
   66:     /* A previous adjustment did not complete.  Since the PDC UTC clock was
   67:     immediately jumped at the start of the previous adjustment, we must
   68:     explicitly reset it to the value of the MPE local time clock minus the
   69:     time zone offset. */
   70: 
   71:     char pwf_since_boot, recover_pwf_time;
   72:     long long offset_ticks, offset_usecs, pdc_usecs_current, pdc_usecs_wanted;
   73:     int hpe_status;
   74: 
   75:     get_time_change_info(&offset_ticks, &pwf_since_boot, &recover_pwf_time);
   76:     offset_usecs = ticks_to_micro(offset_ticks);
   77:     pdc_usecs_wanted = get_time() - offset_usecs;
   78:     pdc_usecs_current = pdc_time(&hpe_status);
   79:     if (hpe_status == 0) 
   80:       /* Force new PDC time by starting an extra correction. */
   81:       set_time_correction(pdc_usecs_wanted - pdc_usecs_current,0,1);
   82:   }
   83: #endif /* 0 */
   84:     
   85:   /* Immediately jump the PDC time to the new value, and then initiate a 
   86:      gradual MPE time correction slew. */
   87:   set_time_correction(new_correction,0,1);
   88: }
   89: 
   90: GETUSERMODE();
   91: 
   92: if (olddelta != NULL) {
   93:   /* Caller wants to know remaining amount of previous correction. */
   94:   (long)olddelta->tv_sec = prev_correction / 1000000LL;
   95:   olddelta->tv_usec = prev_correction % 1000000LL;
   96: }
   97: 
   98: return 0;
   99: }
  100: #endif /* MPE */
  101: 
  102: #ifdef NEED_HPUX_ADJTIME
  103: /*************************************************************************/
  104: /* (c) Copyright Tai Jin, 1988.  All Rights Reserved.                    */
  105: /*     Hewlett-Packard Laboratories.                                     */
  106: /*                                                                       */
  107: /* Permission is hereby granted for unlimited modification, use, and     */
  108: /* distribution.  This software is made available with no warranty of    */
  109: /* any kind, express or implied.  This copyright notice must remain      */
  110: /* intact in all versions of this software.                              */
  111: /*                                                                       */
  112: /* The author would appreciate it if any bug fixes and enhancements were */
  113: /* to be sent back to him for incorporation into future versions of this */
  114: /* software.  Please send changes to tai@iag.hp.com or ken@sdd.hp.com.   */
  115: /*************************************************************************/
  116: 
  117: /*
  118:  * Revision history
  119:  *
  120:  * 9 Jul 94	David L. Mills, Unibergity of Delabunch
  121:  *		Implemented variable threshold to limit age of
  122:  *		corrections; reformatted code for readability.
  123:  */
  124: 
  125: #ifndef lint
  126: static char RCSid[] = "adjtime.c,v 3.1 1993/07/06 01:04:42 jbj Exp";
  127: #endif
  128: 
  129: #include <sys/types.h>
  130: #include <sys/ipc.h>
  131: #include <sys/msg.h>
  132: #include <time.h>
  133: #include <signal.h>
  134: #include "adjtime.h"
  135: 
  136: #define abs(x)  ((x) < 0 ? -(x) : (x))
  137: 
  138: /*
  139:  * The following paramters are appropriate for an NTP adjustment
  140:  * interval of one second.
  141:  */
  142: #define ADJ_THRESH 200		/* initial threshold */
  143: #define ADJ_DELTA 4		/* threshold decrement */
  144: 
  145: static long adjthresh;		/* adjustment threshold */
  146: static long saveup;		/* corrections accumulator */
  147: 
  148: /*
  149:  * clear_adjtime - reset accumulator and threshold variables
  150:  */
  151: void
  152: _clear_adjtime(void)
  153: {
  154: 	saveup = 0;
  155: 	adjthresh = ADJ_THRESH;
  156: }
  157: 
  158: /*
  159:  * adjtime - hp-ux copout of the standard Unix adjtime() system call
  160:  */
  161: int
  162: adjtime(
  163: 	register struct timeval *delta,
  164: 	register struct timeval *olddelta
  165: 	)
  166: {
  167: 	struct timeval newdelta;
  168: 
  169: 	/*
  170: 	 * Corrections greater than one second are done immediately.
  171: 	 */
  172: 	if (delta->tv_sec) {
  173: 		adjthresh = ADJ_THRESH;
  174: 		saveup = 0;
  175: 		return(_adjtime(delta, olddelta));
  176: 	}
  177: 
  178: 	/*
  179: 	 * Corrections less than one second are accumulated until
  180: 	 * tripping a threshold, which is initially set at ADJ_THESH and
  181: 	 * reduced in ADJ_DELTA steps to zero. The idea here is to
  182: 	 * introduce large corrections quickly, while making sure that
  183: 	 * small corrections are introduced without excessive delay. The
  184: 	 * idea comes from the ARPAnet routing update algorithm.
  185: 	 */
  186: 	saveup += delta->tv_usec;
  187: 	if (abs(saveup) >= adjthresh) {
  188: 		adjthresh = ADJ_THRESH;
  189: 		newdelta.tv_sec = 0;
  190: 		newdelta.tv_usec = saveup;
  191: 		saveup = 0;
  192: 		return(_adjtime(&newdelta, olddelta));
  193: 	} else {
  194: 		adjthresh -= ADJ_DELTA;
  195: 	}
  196: 
  197: 	/*
  198: 	 * While nobody uses it, return the residual before correction,
  199: 	 * as per Unix convention.
  200: 	 */
  201: 	if (olddelta)
  202: 	    olddelta->tv_sec = olddelta->tv_usec = 0;
  203: 	return(0);
  204: }
  205: 
  206: /*
  207:  * _adjtime - does the actual work
  208:  */
  209: int
  210: _adjtime(
  211: 	register struct timeval *delta,
  212: 	register struct timeval *olddelta
  213: 	)
  214: {
  215: 	register int mqid;
  216: 	MsgBuf msg;
  217: 	register MsgBuf *msgp = &msg;
  218: 
  219: 	/*
  220: 	 * Get the key to the adjtime message queue (note that we must
  221: 	 * get it every time because the queue might have been removed
  222: 	 * and recreated)
  223: 	 */
  224: 	if ((mqid = msgget(KEY, 0)) == -1)
  225: 	    return (-1);
  226: 	msgp->msgb.mtype = CLIENT;
  227: 	msgp->msgb.tv = *delta;
  228: 	if (olddelta)
  229: 	    msgp->msgb.code = DELTA2;
  230: 	else
  231: 	    msgp->msgb.code = DELTA1;
  232: 
  233: 	/*
  234: 	 * Tickle adjtimed and snatch residual, if indicated. Lots of
  235: 	 * fanatic error checking here.
  236: 	 */
  237: 	if (msgsnd(mqid, &msgp->msgp, MSGSIZE, 0) == -1)
  238: 	    return (-1);
  239: 	if (olddelta) {
  240: 		if (msgrcv(mqid, &msgp->msgp, MSGSIZE, SERVER, 0) == -1)
  241: 		    return (-1);
  242: 		*olddelta = msgp->msgb.tv;
  243: 	}
  244: 	return (0);
  245: }
  246: 
  247: #else
  248: # if NEED_QNX_ADJTIME
  249: /*
  250:  * Emulate adjtime() using QNX ClockAdjust().
  251:  * Chris Burghart <burghart@atd.ucar.edu>, 11/2001
  252:  * Miroslaw Pabich <miroslaw_pabich@o2.pl>, 09/2005
  253:  *
  254:  * This is an implementation of adjtime() for QNX.  
  255:  * ClockAdjust() is used to tweak the system clock for about
  256:  * 1 second period until the desired delta is achieved.
  257:  * Time correction slew is limited to reasonable value.
  258:  * Internal rounding and relative errors are reduced.
  259:  */
  260: # include <sys/neutrino.h>
  261: # include <sys/time.h>
  262: 
  263: # include <ntp_stdlib.h>
  264: 
  265: /*
  266:  * Time correction slew limit. QNX is a hard real-time system,
  267:  * so don't adjust system clock too fast.
  268:  */
  269: #define CORR_SLEW_LIMIT     0.02  /* [s/s] */
  270: 
  271: /*
  272:  * Period of system clock adjustment. It should be equal to adjtime
  273:  * execution period (1s). If slightly less than 1s (0.95-0.99), then olddelta
  274:  * residual error (introduced by execution period jitter) will be reduced.
  275:  */
  276: #define ADJUST_PERIOD       0.97  /* [s] */
  277: 
  278: int 
  279: adjtime (struct timeval *delta, struct timeval *olddelta)
  280: {
  281:     double delta_nsec;
  282:     double delta_nsec_old;
  283:     struct _clockadjust adj;
  284:     struct _clockadjust oldadj;
  285: 
  286:     /*
  287:      * How many nanoseconds are we adjusting?
  288:      */
  289:     if (delta != NULL)
  290: 	delta_nsec = 1e9 * (long)delta->tv_sec + 1e3 * delta->tv_usec;
  291:     else
  292: 	delta_nsec = 0;
  293: 
  294:     /*
  295:      * Build the adjust structure and call ClockAdjust()
  296:      */
  297:     if (delta_nsec != 0)
  298:     {
  299: 	struct _clockperiod period;
  300: 	long count;
  301: 	long increment;
  302: 	long increment_limit;
  303: 	int isneg = 0;
  304: 
  305: 	/*
  306: 	 * Convert to absolute value for future processing
  307: 	 */
  308: 	if (delta_nsec < 0)
  309: 	{
  310: 	    isneg = 1;
  311: 	    delta_nsec = -delta_nsec;
  312: 	}
  313: 
  314: 	/*
  315: 	 * Get the current clock period (nanoseconds)
  316: 	 */
  317: 	if (ClockPeriod (CLOCK_REALTIME, 0, &period, 0) < 0)
  318: 	    return -1;
  319: 
  320: 	/*
  321: 	 * Compute count and nanoseconds increment
  322: 	 */
  323: 	count = 1e9 * ADJUST_PERIOD / period.nsec;
  324: 	increment = delta_nsec / count + .5;
  325: 	/* Reduce relative error */
  326: 	if (count > increment + 1)
  327: 	{
  328: 	    increment = 1 + (long)((delta_nsec - 1) / count);
  329: 	    count = delta_nsec / increment + .5;
  330: 	}
  331: 
  332: 	/*
  333: 	 * Limit the adjust increment to appropriate value
  334: 	 */
  335: 	increment_limit = CORR_SLEW_LIMIT * period.nsec;
  336: 	if (increment > increment_limit)
  337: 	{
  338: 	    increment = increment_limit;
  339: 	    count = delta_nsec / increment + .5;
  340: 	    /* Reduce relative error */
  341: 	    if (increment > count + 1)
  342: 	    {
  343: 		count =  1 + (long)((delta_nsec - 1) / increment);
  344: 		increment = delta_nsec / count + .5;
  345: 	    }
  346: 	}
  347: 
  348: 	adj.tick_nsec_inc = isneg ? -increment : increment;
  349: 	adj.tick_count = count;
  350:     }
  351:     else
  352:     {
  353: 	adj.tick_nsec_inc = 0;
  354: 	adj.tick_count = 0;
  355:     }
  356: 
  357:     if (ClockAdjust (CLOCK_REALTIME, &adj, &oldadj) < 0)
  358: 	return -1;
  359: 
  360:     /*
  361:      * Build olddelta
  362:      */
  363:     delta_nsec_old = (double)oldadj.tick_count * oldadj.tick_nsec_inc;
  364:     if (olddelta != NULL)
  365:     {
  366: 	if (delta_nsec_old != 0)
  367: 	{
  368: 	    /* Reduce rounding error */
  369: 	    delta_nsec_old += (delta_nsec_old < 0) ? -500 : 500;
  370: 	    olddelta->tv_sec = delta_nsec_old / 1e9;
  371: 	    olddelta->tv_usec = (long)(delta_nsec_old - 1e9
  372: 				 * (long)olddelta->tv_sec) / 1000;
  373: 	}
  374: 	else
  375: 	{
  376: 	    olddelta->tv_sec = 0;
  377: 	    olddelta->tv_usec = 0;
  378: 	}
  379:     }
  380: 
  381:     return 0;
  382: }
  383: # else /* no special adjtime() needed */
  384: int adjtime_bs;
  385: # endif
  386: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>