diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-12-02 09:01:21 +0000 |
---|---|---|
committer | <> | 2014-12-04 16:11:25 +0000 |
commit | bdab5265fcbf3f472545073a23f8999749a9f2b9 (patch) | |
tree | c6018dd03dea906f8f1fb5f105f05b71a7dc250a /libntp/adjtime.c | |
download | ntp-bdab5265fcbf3f472545073a23f8999749a9f2b9.tar.gz |
Imported from /home/lorry/working-area/delta_ntp/ntp-dev-4.2.7p482.tar.gz.ntp-dev-4.2.7p482
Diffstat (limited to 'libntp/adjtime.c')
-rw-r--r-- | libntp/adjtime.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/libntp/adjtime.c b/libntp/adjtime.c new file mode 100644 index 0000000..a8e6580 --- /dev/null +++ b/libntp/adjtime.c @@ -0,0 +1,386 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef MPE +/* + * MPE lacks adjtime(), so we define our own. But note that time slewing has + * a sub-second accuracy bug documented in SR 5003462838 which prevents ntpd + * from being able to maintain clock synch. Because of the bug, this adjtime() + * implementation as used by ntpd has a side-effect of screwing up the hardware + * PDC clock, which will need to be reset with a reboot. + * + * This problem affects all versions of MPE at the time of this writing (when + * MPE/iX 7.0 is the most current). It only causes bad things to happen when + * doing continuous clock synchronization with ntpd; note that you CAN run ntpd + * with "disable ntp" in ntp.conf if you wish to provide a time server. + * + * The one-time clock adjustment functionality of ntpdate and ntp_timeset can + * be used without screwing up the PDC clock. + * + */ +#include <time.h> + +int adjtime(struct timeval *delta, struct timeval *olddelta); + +int adjtime(struct timeval *delta, struct timeval *olddelta) + +{ +/* Documented, supported MPE system intrinsics. */ + +extern void GETPRIVMODE(void); +extern void GETUSERMODE(void); + +/* Undocumented, unsupported MPE internal functions. */ + +extern long long current_correction_usecs(void); +extern long long get_time(void); +extern void get_time_change_info(long long *, char *, char *); +extern long long pdc_time(int *); +extern void set_time_correction(long long, int, int); +extern long long ticks_to_micro(long long); + +long long big_sec, big_usec, new_correction = 0LL; +long long prev_correction; + +if (delta != NULL) { + /* Adjustment required. Convert delta to 64-bit microseconds. */ + big_sec = (long)delta->tv_sec; + big_usec = delta->tv_usec; + new_correction = (big_sec * 1000000LL) + big_usec; +} + +GETPRIVMODE(); + +/* Determine how much of a previous correction (if any) we're interrupting. */ +prev_correction = current_correction_usecs(); + +if (delta != NULL) { + /* Adjustment required. */ + +#if 0 + /* Speculative code disabled until bug SR 5003462838 is fixed. This bug + prevents accurate time slewing, and indeed renders ntpd inoperable. */ + + if (prev_correction != 0LL) { + /* A previous adjustment did not complete. Since the PDC UTC clock was + immediately jumped at the start of the previous adjustment, we must + explicitly reset it to the value of the MPE local time clock minus the + time zone offset. */ + + char pwf_since_boot, recover_pwf_time; + long long offset_ticks, offset_usecs, pdc_usecs_current, pdc_usecs_wanted; + int hpe_status; + + get_time_change_info(&offset_ticks, &pwf_since_boot, &recover_pwf_time); + offset_usecs = ticks_to_micro(offset_ticks); + pdc_usecs_wanted = get_time() - offset_usecs; + pdc_usecs_current = pdc_time(&hpe_status); + if (hpe_status == 0) + /* Force new PDC time by starting an extra correction. */ + set_time_correction(pdc_usecs_wanted - pdc_usecs_current,0,1); + } +#endif /* 0 */ + + /* Immediately jump the PDC time to the new value, and then initiate a + gradual MPE time correction slew. */ + set_time_correction(new_correction,0,1); +} + +GETUSERMODE(); + +if (olddelta != NULL) { + /* Caller wants to know remaining amount of previous correction. */ + (long)olddelta->tv_sec = prev_correction / 1000000LL; + olddelta->tv_usec = prev_correction % 1000000LL; +} + +return 0; +} +#endif /* MPE */ + +#ifdef NEED_HPUX_ADJTIME +/*************************************************************************/ +/* (c) Copyright Tai Jin, 1988. All Rights Reserved. */ +/* Hewlett-Packard Laboratories. */ +/* */ +/* Permission is hereby granted for unlimited modification, use, and */ +/* distribution. This software is made available with no warranty of */ +/* any kind, express or implied. This copyright notice must remain */ +/* intact in all versions of this software. */ +/* */ +/* The author would appreciate it if any bug fixes and enhancements were */ +/* to be sent back to him for incorporation into future versions of this */ +/* software. Please send changes to tai@iag.hp.com or ken@sdd.hp.com. */ +/*************************************************************************/ + +/* + * Revision history + * + * 9 Jul 94 David L. Mills, Unibergity of Delabunch + * Implemented variable threshold to limit age of + * corrections; reformatted code for readability. + */ + +#ifndef lint +static char RCSid[] = "adjtime.c,v 3.1 1993/07/06 01:04:42 jbj Exp"; +#endif + +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/msg.h> +#include <time.h> +#include <signal.h> +#include "adjtime.h" + +#define abs(x) ((x) < 0 ? -(x) : (x)) + +/* + * The following paramters are appropriate for an NTP adjustment + * interval of one second. + */ +#define ADJ_THRESH 200 /* initial threshold */ +#define ADJ_DELTA 4 /* threshold decrement */ + +static long adjthresh; /* adjustment threshold */ +static long saveup; /* corrections accumulator */ + +/* + * clear_adjtime - reset accumulator and threshold variables + */ +void +_clear_adjtime(void) +{ + saveup = 0; + adjthresh = ADJ_THRESH; +} + +/* + * adjtime - hp-ux copout of the standard Unix adjtime() system call + */ +int +adjtime( + register struct timeval *delta, + register struct timeval *olddelta + ) +{ + struct timeval newdelta; + + /* + * Corrections greater than one second are done immediately. + */ + if (delta->tv_sec) { + adjthresh = ADJ_THRESH; + saveup = 0; + return(_adjtime(delta, olddelta)); + } + + /* + * Corrections less than one second are accumulated until + * tripping a threshold, which is initially set at ADJ_THESH and + * reduced in ADJ_DELTA steps to zero. The idea here is to + * introduce large corrections quickly, while making sure that + * small corrections are introduced without excessive delay. The + * idea comes from the ARPAnet routing update algorithm. + */ + saveup += delta->tv_usec; + if (abs(saveup) >= adjthresh) { + adjthresh = ADJ_THRESH; + newdelta.tv_sec = 0; + newdelta.tv_usec = saveup; + saveup = 0; + return(_adjtime(&newdelta, olddelta)); + } else { + adjthresh -= ADJ_DELTA; + } + + /* + * While nobody uses it, return the residual before correction, + * as per Unix convention. + */ + if (olddelta) + olddelta->tv_sec = olddelta->tv_usec = 0; + return(0); +} + +/* + * _adjtime - does the actual work + */ +int +_adjtime( + register struct timeval *delta, + register struct timeval *olddelta + ) +{ + register int mqid; + MsgBuf msg; + register MsgBuf *msgp = &msg; + + /* + * Get the key to the adjtime message queue (note that we must + * get it every time because the queue might have been removed + * and recreated) + */ + if ((mqid = msgget(KEY, 0)) == -1) + return (-1); + msgp->msgb.mtype = CLIENT; + msgp->msgb.tv = *delta; + if (olddelta) + msgp->msgb.code = DELTA2; + else + msgp->msgb.code = DELTA1; + + /* + * Tickle adjtimed and snatch residual, if indicated. Lots of + * fanatic error checking here. + */ + if (msgsnd(mqid, &msgp->msgp, MSGSIZE, 0) == -1) + return (-1); + if (olddelta) { + if (msgrcv(mqid, &msgp->msgp, MSGSIZE, SERVER, 0) == -1) + return (-1); + *olddelta = msgp->msgb.tv; + } + return (0); +} + +#else +# if NEED_QNX_ADJTIME +/* + * Emulate adjtime() using QNX ClockAdjust(). + * Chris Burghart <burghart@atd.ucar.edu>, 11/2001 + * Miroslaw Pabich <miroslaw_pabich@o2.pl>, 09/2005 + * + * This is an implementation of adjtime() for QNX. + * ClockAdjust() is used to tweak the system clock for about + * 1 second period until the desired delta is achieved. + * Time correction slew is limited to reasonable value. + * Internal rounding and relative errors are reduced. + */ +# include <sys/neutrino.h> +# include <sys/time.h> + +# include <ntp_stdlib.h> + +/* + * Time correction slew limit. QNX is a hard real-time system, + * so don't adjust system clock too fast. + */ +#define CORR_SLEW_LIMIT 0.02 /* [s/s] */ + +/* + * Period of system clock adjustment. It should be equal to adjtime + * execution period (1s). If slightly less than 1s (0.95-0.99), then olddelta + * residual error (introduced by execution period jitter) will be reduced. + */ +#define ADJUST_PERIOD 0.97 /* [s] */ + +int +adjtime (struct timeval *delta, struct timeval *olddelta) +{ + double delta_nsec; + double delta_nsec_old; + struct _clockadjust adj; + struct _clockadjust oldadj; + + /* + * How many nanoseconds are we adjusting? + */ + if (delta != NULL) + delta_nsec = 1e9 * (long)delta->tv_sec + 1e3 * delta->tv_usec; + else + delta_nsec = 0; + + /* + * Build the adjust structure and call ClockAdjust() + */ + if (delta_nsec != 0) + { + struct _clockperiod period; + long count; + long increment; + long increment_limit; + int isneg = 0; + + /* + * Convert to absolute value for future processing + */ + if (delta_nsec < 0) + { + isneg = 1; + delta_nsec = -delta_nsec; + } + + /* + * Get the current clock period (nanoseconds) + */ + if (ClockPeriod (CLOCK_REALTIME, 0, &period, 0) < 0) + return -1; + + /* + * Compute count and nanoseconds increment + */ + count = 1e9 * ADJUST_PERIOD / period.nsec; + increment = delta_nsec / count + .5; + /* Reduce relative error */ + if (count > increment + 1) + { + increment = 1 + (long)((delta_nsec - 1) / count); + count = delta_nsec / increment + .5; + } + + /* + * Limit the adjust increment to appropriate value + */ + increment_limit = CORR_SLEW_LIMIT * period.nsec; + if (increment > increment_limit) + { + increment = increment_limit; + count = delta_nsec / increment + .5; + /* Reduce relative error */ + if (increment > count + 1) + { + count = 1 + (long)((delta_nsec - 1) / increment); + increment = delta_nsec / count + .5; + } + } + + adj.tick_nsec_inc = isneg ? -increment : increment; + adj.tick_count = count; + } + else + { + adj.tick_nsec_inc = 0; + adj.tick_count = 0; + } + + if (ClockAdjust (CLOCK_REALTIME, &adj, &oldadj) < 0) + return -1; + + /* + * Build olddelta + */ + delta_nsec_old = (double)oldadj.tick_count * oldadj.tick_nsec_inc; + if (olddelta != NULL) + { + if (delta_nsec_old != 0) + { + /* Reduce rounding error */ + delta_nsec_old += (delta_nsec_old < 0) ? -500 : 500; + olddelta->tv_sec = delta_nsec_old / 1e9; + olddelta->tv_usec = (long)(delta_nsec_old - 1e9 + * (long)olddelta->tv_sec) / 1000; + } + else + { + olddelta->tv_sec = 0; + olddelta->tv_usec = 0; + } + } + + return 0; +} +# else /* no special adjtime() needed */ +int adjtime_bs; +# endif +#endif |