Annotation of embedaddon/ntp/libntp/adjtime.c, revision 1.1
1.1 ! misho 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>