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