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