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