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