154359Sroberto#ifdef HAVE_CONFIG_H 2132451Sroberto# include <config.h> 354359Sroberto#endif 454359Sroberto 5106163Sroberto#ifdef MPE 6106163Sroberto/* 7106163Sroberto * MPE lacks adjtime(), so we define our own. But note that time slewing has 8106163Sroberto * a sub-second accuracy bug documented in SR 5003462838 which prevents ntpd 9106163Sroberto * from being able to maintain clock synch. Because of the bug, this adjtime() 10106163Sroberto * implementation as used by ntpd has a side-effect of screwing up the hardware 11106163Sroberto * PDC clock, which will need to be reset with a reboot. 12106163Sroberto * 13106163Sroberto * This problem affects all versions of MPE at the time of this writing (when 14106163Sroberto * MPE/iX 7.0 is the most current). It only causes bad things to happen when 15106163Sroberto * doing continuous clock synchronization with ntpd; note that you CAN run ntpd 16106163Sroberto * with "disable ntp" in ntp.conf if you wish to provide a time server. 17106163Sroberto * 18106163Sroberto * The one-time clock adjustment functionality of ntpdate and ntp_timeset can 19106163Sroberto * be used without screwing up the PDC clock. 20106163Sroberto * 21106163Sroberto */ 22106163Sroberto#include <time.h> 23106163Sroberto 24106163Srobertoint adjtime(struct timeval *delta, struct timeval *olddelta); 25106163Sroberto 26106163Srobertoint adjtime(struct timeval *delta, struct timeval *olddelta) 27106163Sroberto 28106163Sroberto{ 29106163Sroberto/* Documented, supported MPE system intrinsics. */ 30106163Sroberto 31106163Srobertoextern void GETPRIVMODE(void); 32106163Srobertoextern void GETUSERMODE(void); 33106163Sroberto 34106163Sroberto/* Undocumented, unsupported MPE internal functions. */ 35106163Sroberto 36106163Srobertoextern long long current_correction_usecs(void); 37106163Srobertoextern long long get_time(void); 38106163Srobertoextern void get_time_change_info(long long *, char *, char *); 39106163Srobertoextern long long pdc_time(int *); 40106163Srobertoextern void set_time_correction(long long, int, int); 41106163Srobertoextern long long ticks_to_micro(long long); 42106163Sroberto 43106163Srobertolong long big_sec, big_usec, new_correction = 0LL; 44106163Srobertolong long prev_correction; 45106163Sroberto 46106163Srobertoif (delta != NULL) { 47106163Sroberto /* Adjustment required. Convert delta to 64-bit microseconds. */ 48106163Sroberto big_sec = (long)delta->tv_sec; 49106163Sroberto big_usec = delta->tv_usec; 50106163Sroberto new_correction = (big_sec * 1000000LL) + big_usec; 51106163Sroberto} 52106163Sroberto 53106163SrobertoGETPRIVMODE(); 54106163Sroberto 55106163Sroberto/* Determine how much of a previous correction (if any) we're interrupting. */ 56106163Srobertoprev_correction = current_correction_usecs(); 57106163Sroberto 58106163Srobertoif (delta != NULL) { 59106163Sroberto /* Adjustment required. */ 60106163Sroberto 61106163Sroberto#if 0 62106163Sroberto /* Speculative code disabled until bug SR 5003462838 is fixed. This bug 63106163Sroberto prevents accurate time slewing, and indeed renders ntpd inoperable. */ 64106163Sroberto 65106163Sroberto if (prev_correction != 0LL) { 66106163Sroberto /* A previous adjustment did not complete. Since the PDC UTC clock was 67106163Sroberto immediately jumped at the start of the previous adjustment, we must 68106163Sroberto explicitly reset it to the value of the MPE local time clock minus the 69106163Sroberto time zone offset. */ 70106163Sroberto 71106163Sroberto char pwf_since_boot, recover_pwf_time; 72106163Sroberto long long offset_ticks, offset_usecs, pdc_usecs_current, pdc_usecs_wanted; 73106163Sroberto int hpe_status; 74106163Sroberto 75106163Sroberto get_time_change_info(&offset_ticks, &pwf_since_boot, &recover_pwf_time); 76106163Sroberto offset_usecs = ticks_to_micro(offset_ticks); 77106163Sroberto pdc_usecs_wanted = get_time() - offset_usecs; 78106163Sroberto pdc_usecs_current = pdc_time(&hpe_status); 79106163Sroberto if (hpe_status == 0) 80106163Sroberto /* Force new PDC time by starting an extra correction. */ 81106163Sroberto set_time_correction(pdc_usecs_wanted - pdc_usecs_current,0,1); 82106163Sroberto } 83132451Sroberto#endif /* 0 */ 84106163Sroberto 85106163Sroberto /* Immediately jump the PDC time to the new value, and then initiate a 86106163Sroberto gradual MPE time correction slew. */ 87106163Sroberto set_time_correction(new_correction,0,1); 88106163Sroberto} 89106163Sroberto 90106163SrobertoGETUSERMODE(); 91106163Sroberto 92106163Srobertoif (olddelta != NULL) { 93106163Sroberto /* Caller wants to know remaining amount of previous correction. */ 94106163Sroberto (long)olddelta->tv_sec = prev_correction / 1000000LL; 95106163Sroberto olddelta->tv_usec = prev_correction % 1000000LL; 96106163Sroberto} 97106163Sroberto 98106163Srobertoreturn 0; 99106163Sroberto} 100106163Sroberto#endif /* MPE */ 101106163Sroberto 10254359Sroberto#ifdef NEED_HPUX_ADJTIME 10354359Sroberto/*************************************************************************/ 10454359Sroberto/* (c) Copyright Tai Jin, 1988. All Rights Reserved. */ 10554359Sroberto/* Hewlett-Packard Laboratories. */ 10654359Sroberto/* */ 10754359Sroberto/* Permission is hereby granted for unlimited modification, use, and */ 10854359Sroberto/* distribution. This software is made available with no warranty of */ 10954359Sroberto/* any kind, express or implied. This copyright notice must remain */ 11054359Sroberto/* intact in all versions of this software. */ 11154359Sroberto/* */ 11254359Sroberto/* The author would appreciate it if any bug fixes and enhancements were */ 11354359Sroberto/* to be sent back to him for incorporation into future versions of this */ 11454359Sroberto/* software. Please send changes to tai@iag.hp.com or ken@sdd.hp.com. */ 11554359Sroberto/*************************************************************************/ 11654359Sroberto 11754359Sroberto/* 11854359Sroberto * Revision history 11954359Sroberto * 12054359Sroberto * 9 Jul 94 David L. Mills, Unibergity of Delabunch 12154359Sroberto * Implemented variable threshold to limit age of 12254359Sroberto * corrections; reformatted code for readability. 12354359Sroberto */ 12454359Sroberto 12554359Sroberto#ifndef lint 12654359Srobertostatic char RCSid[] = "adjtime.c,v 3.1 1993/07/06 01:04:42 jbj Exp"; 12754359Sroberto#endif 12854359Sroberto 12954359Sroberto#include <sys/types.h> 13054359Sroberto#include <sys/ipc.h> 13154359Sroberto#include <sys/msg.h> 13254359Sroberto#include <time.h> 13354359Sroberto#include <signal.h> 13454359Sroberto#include "adjtime.h" 13554359Sroberto 13654359Sroberto#define abs(x) ((x) < 0 ? -(x) : (x)) 13754359Sroberto 13854359Sroberto/* 13954359Sroberto * The following paramters are appropriate for an NTP adjustment 14054359Sroberto * interval of one second. 14154359Sroberto */ 14254359Sroberto#define ADJ_THRESH 200 /* initial threshold */ 14354359Sroberto#define ADJ_DELTA 4 /* threshold decrement */ 14454359Sroberto 14554359Srobertostatic long adjthresh; /* adjustment threshold */ 14654359Srobertostatic long saveup; /* corrections accumulator */ 14754359Sroberto 14854359Sroberto/* 14954359Sroberto * clear_adjtime - reset accumulator and threshold variables 15054359Sroberto */ 15154359Srobertovoid 15254359Sroberto_clear_adjtime(void) 15354359Sroberto{ 15454359Sroberto saveup = 0; 15554359Sroberto adjthresh = ADJ_THRESH; 15654359Sroberto} 15754359Sroberto 15854359Sroberto/* 15954359Sroberto * adjtime - hp-ux copout of the standard Unix adjtime() system call 16054359Sroberto */ 16154359Srobertoint 16254359Srobertoadjtime( 16354359Sroberto register struct timeval *delta, 16454359Sroberto register struct timeval *olddelta 16554359Sroberto ) 16654359Sroberto{ 16754359Sroberto struct timeval newdelta; 16854359Sroberto 16954359Sroberto /* 17054359Sroberto * Corrections greater than one second are done immediately. 17154359Sroberto */ 17254359Sroberto if (delta->tv_sec) { 17354359Sroberto adjthresh = ADJ_THRESH; 17454359Sroberto saveup = 0; 17554359Sroberto return(_adjtime(delta, olddelta)); 17654359Sroberto } 17754359Sroberto 17854359Sroberto /* 17954359Sroberto * Corrections less than one second are accumulated until 18054359Sroberto * tripping a threshold, which is initially set at ADJ_THESH and 18154359Sroberto * reduced in ADJ_DELTA steps to zero. The idea here is to 18254359Sroberto * introduce large corrections quickly, while making sure that 18354359Sroberto * small corrections are introduced without excessive delay. The 18454359Sroberto * idea comes from the ARPAnet routing update algorithm. 18554359Sroberto */ 18654359Sroberto saveup += delta->tv_usec; 18754359Sroberto if (abs(saveup) >= adjthresh) { 18854359Sroberto adjthresh = ADJ_THRESH; 18954359Sroberto newdelta.tv_sec = 0; 19054359Sroberto newdelta.tv_usec = saveup; 19154359Sroberto saveup = 0; 19254359Sroberto return(_adjtime(&newdelta, olddelta)); 19354359Sroberto } else { 19454359Sroberto adjthresh -= ADJ_DELTA; 19554359Sroberto } 19654359Sroberto 19754359Sroberto /* 19854359Sroberto * While nobody uses it, return the residual before correction, 19954359Sroberto * as per Unix convention. 20054359Sroberto */ 20154359Sroberto if (olddelta) 20254359Sroberto olddelta->tv_sec = olddelta->tv_usec = 0; 20354359Sroberto return(0); 20454359Sroberto} 20554359Sroberto 20654359Sroberto/* 20754359Sroberto * _adjtime - does the actual work 20854359Sroberto */ 20954359Srobertoint 21054359Sroberto_adjtime( 21154359Sroberto register struct timeval *delta, 21254359Sroberto register struct timeval *olddelta 21354359Sroberto ) 21454359Sroberto{ 21554359Sroberto register int mqid; 21654359Sroberto MsgBuf msg; 21754359Sroberto register MsgBuf *msgp = &msg; 21854359Sroberto 21954359Sroberto /* 22054359Sroberto * Get the key to the adjtime message queue (note that we must 22154359Sroberto * get it every time because the queue might have been removed 22254359Sroberto * and recreated) 22354359Sroberto */ 22454359Sroberto if ((mqid = msgget(KEY, 0)) == -1) 22554359Sroberto return (-1); 22654359Sroberto msgp->msgb.mtype = CLIENT; 22754359Sroberto msgp->msgb.tv = *delta; 22854359Sroberto if (olddelta) 22954359Sroberto msgp->msgb.code = DELTA2; 23054359Sroberto else 23154359Sroberto msgp->msgb.code = DELTA1; 23254359Sroberto 23354359Sroberto /* 23454359Sroberto * Tickle adjtimed and snatch residual, if indicated. Lots of 23554359Sroberto * fanatic error checking here. 23654359Sroberto */ 23754359Sroberto if (msgsnd(mqid, &msgp->msgp, MSGSIZE, 0) == -1) 23854359Sroberto return (-1); 23954359Sroberto if (olddelta) { 24054359Sroberto if (msgrcv(mqid, &msgp->msgp, MSGSIZE, SERVER, 0) == -1) 24154359Sroberto return (-1); 24254359Sroberto *olddelta = msgp->msgb.tv; 24354359Sroberto } 24454359Sroberto return (0); 24554359Sroberto} 24654359Sroberto 247132451Sroberto#else 248132451Sroberto# if NEED_QNX_ADJTIME 249132451Sroberto/* 250132451Sroberto * Emulate adjtime() using QNX ClockAdjust(). 251132451Sroberto * Chris Burghart <burghart@atd.ucar.edu>, 11/2001 252182007Sroberto * Miroslaw Pabich <miroslaw_pabich@o2.pl>, 09/2005 253132451Sroberto * 254182007Sroberto * This is an implementation of adjtime() for QNX. 255182007Sroberto * ClockAdjust() is used to tweak the system clock for about 256182007Sroberto * 1 second period until the desired delta is achieved. 257182007Sroberto * Time correction slew is limited to reasonable value. 258182007Sroberto * Internal rounding and relative errors are reduced. 259132451Sroberto */ 260132451Sroberto# include <sys/neutrino.h> 261132451Sroberto# include <sys/time.h> 262132451Sroberto 263132451Sroberto# include <ntp_stdlib.h> 264132451Sroberto 265182007Sroberto/* 266182007Sroberto * Time correction slew limit. QNX is a hard real-time system, 267182007Sroberto * so don't adjust system clock too fast. 268182007Sroberto */ 269182007Sroberto#define CORR_SLEW_LIMIT 0.02 /* [s/s] */ 270182007Sroberto 271182007Sroberto/* 272182007Sroberto * Period of system clock adjustment. It should be equal to adjtime 273182007Sroberto * execution period (1s). If slightly less than 1s (0.95-0.99), then olddelta 274182007Sroberto * residual error (introduced by execution period jitter) will be reduced. 275182007Sroberto */ 276182007Sroberto#define ADJUST_PERIOD 0.97 /* [s] */ 277182007Sroberto 278132451Srobertoint 279132451Srobertoadjtime (struct timeval *delta, struct timeval *olddelta) 280132451Sroberto{ 281132451Sroberto double delta_nsec; 282132451Sroberto double delta_nsec_old; 283132451Sroberto struct _clockadjust adj; 284132451Sroberto struct _clockadjust oldadj; 285182007Sroberto 286132451Sroberto /* 287132451Sroberto * How many nanoseconds are we adjusting? 288132451Sroberto */ 289182007Sroberto if (delta != NULL) 290182007Sroberto delta_nsec = 1e9 * (long)delta->tv_sec + 1e3 * delta->tv_usec; 291182007Sroberto else 292182007Sroberto delta_nsec = 0; 293182007Sroberto 294132451Sroberto /* 295132451Sroberto * Build the adjust structure and call ClockAdjust() 296132451Sroberto */ 297132451Sroberto if (delta_nsec != 0) 298132451Sroberto { 299132451Sroberto struct _clockperiod period; 300132451Sroberto long count; 301132451Sroberto long increment; 302182007Sroberto long increment_limit; 303182007Sroberto int isneg = 0; 304132451Sroberto 305132451Sroberto /* 306182007Sroberto * Convert to absolute value for future processing 307182007Sroberto */ 308182007Sroberto if (delta_nsec < 0) 309182007Sroberto { 310182007Sroberto isneg = 1; 311182007Sroberto delta_nsec = -delta_nsec; 312182007Sroberto } 313182007Sroberto 314182007Sroberto /* 315132451Sroberto * Get the current clock period (nanoseconds) 316132451Sroberto */ 317132451Sroberto if (ClockPeriod (CLOCK_REALTIME, 0, &period, 0) < 0) 318132451Sroberto return -1; 319132451Sroberto 320132451Sroberto /* 321182007Sroberto * Compute count and nanoseconds increment 322132451Sroberto */ 323182007Sroberto count = 1e9 * ADJUST_PERIOD / period.nsec; 324182007Sroberto increment = delta_nsec / count + .5; 325182007Sroberto /* Reduce relative error */ 326182007Sroberto if (count > increment + 1) 327182007Sroberto { 328182007Sroberto increment = 1 + (long)((delta_nsec - 1) / count); 329182007Sroberto count = delta_nsec / increment + .5; 330182007Sroberto } 331132451Sroberto 332182007Sroberto /* 333182007Sroberto * Limit the adjust increment to appropriate value 334182007Sroberto */ 335182007Sroberto increment_limit = CORR_SLEW_LIMIT * period.nsec; 336182007Sroberto if (increment > increment_limit) 337182007Sroberto { 338182007Sroberto increment = increment_limit; 339182007Sroberto count = delta_nsec / increment + .5; 340182007Sroberto /* Reduce relative error */ 341182007Sroberto if (increment > count + 1) 342182007Sroberto { 343182007Sroberto count = 1 + (long)((delta_nsec - 1) / increment); 344182007Sroberto increment = delta_nsec / count + .5; 345182007Sroberto } 346182007Sroberto } 347182007Sroberto 348182007Sroberto adj.tick_nsec_inc = isneg ? -increment : increment; 349132451Sroberto adj.tick_count = count; 350132451Sroberto } 351132451Sroberto else 352132451Sroberto { 353132451Sroberto adj.tick_nsec_inc = 0; 354132451Sroberto adj.tick_count = 0; 355132451Sroberto } 356132451Sroberto 357132451Sroberto if (ClockAdjust (CLOCK_REALTIME, &adj, &oldadj) < 0) 358132451Sroberto return -1; 359132451Sroberto 360132451Sroberto /* 361132451Sroberto * Build olddelta 362132451Sroberto */ 363182007Sroberto delta_nsec_old = (double)oldadj.tick_count * oldadj.tick_nsec_inc; 364182007Sroberto if (olddelta != NULL) 365182007Sroberto { 366182007Sroberto if (delta_nsec_old != 0) 367182007Sroberto { 368182007Sroberto /* Reduce rounding error */ 369182007Sroberto delta_nsec_old += (delta_nsec_old < 0) ? -500 : 500; 370182007Sroberto olddelta->tv_sec = delta_nsec_old / 1e9; 371182007Sroberto olddelta->tv_usec = (long)(delta_nsec_old - 1e9 372182007Sroberto * (long)olddelta->tv_sec) / 1000; 373182007Sroberto } 374182007Sroberto else 375182007Sroberto { 376182007Sroberto olddelta->tv_sec = 0; 377182007Sroberto olddelta->tv_usec = 0; 378182007Sroberto } 379182007Sroberto } 380182007Sroberto 381132451Sroberto return 0; 382132451Sroberto} 383132451Sroberto# else /* no special adjtime() needed */ 38454359Srobertoint adjtime_bs; 385132451Sroberto# endif 386132451Sroberto#endif 387