1 /* $NetBSD: adjtime.c,v 1.2 2003/12/04 16:23:36 drochner Exp $ */
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.
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.
20 * The one-time clock adjustment functionality of ntpdate and ntp_timeset can
21 * be used without screwing up the PDC clock.
26 int adjtime(struct timeval
*delta
, struct timeval
*olddelta
);
28 int adjtime(struct timeval
*delta
, struct timeval
*olddelta
)
31 /* Documented, supported MPE system intrinsics. */
33 extern void GETPRIVMODE(void);
34 extern void GETUSERMODE(void);
36 /* Undocumented, unsupported MPE internal functions. */
38 extern long long current_correction_usecs(void);
39 extern long long get_time(void);
40 extern void get_time_change_info(long long *, char *, char *);
41 extern long long pdc_time(int *);
42 extern void set_time_correction(long long, int, int);
43 extern long long ticks_to_micro(long long);
45 long long big_sec
, big_usec
, new_correction
= 0LL;
46 long long prev_correction
;
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
;
57 /* Determine how much of a previous correction (if any) we're interrupting. */
58 prev_correction
= current_correction_usecs();
61 /* Adjustment required. */
64 /* Speculative code disabled until bug SR 5003462838 is fixed. This bug
65 prevents accurate time slewing, and indeed renders ntpd inoperable. */
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
73 char pwf_since_boot
, recover_pwf_time
;
74 long long offset_ticks
, offset_usecs
, pdc_usecs_current
, pdc_usecs_wanted
;
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
);
82 /* Force new PDC time by starting an extra correction. */
83 set_time_correction(pdc_usecs_wanted
- pdc_usecs_current
,0,1);
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);
94 if (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;
104 #ifdef NEED_HPUX_ADJTIME
105 /*************************************************************************/
106 /* (c) Copyright Tai Jin, 1988. All Rights Reserved. */
107 /* Hewlett-Packard Laboratories. */
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. */
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 /*************************************************************************/
122 * 9 Jul 94 David L. Mills, Unibergity of Delabunch
123 * Implemented variable threshold to limit age of
124 * corrections; reformatted code for readability.
128 static char RCSid
[] = "adjtime.c,v 3.1 1993/07/06 01:04:42 jbj Exp";
131 #include <sys/types.h>
138 #define abs(x) ((x) < 0 ? -(x) : (x))
141 * The following paramters are appropriate for an NTP adjustment
142 * interval of one second.
144 #define ADJ_THRESH 200 /* initial threshold */
145 #define ADJ_DELTA 4 /* threshold decrement */
147 static long adjthresh
; /* adjustment threshold */
148 static long saveup
; /* corrections accumulator */
151 * clear_adjtime - reset accumulator and threshold variables
157 adjthresh
= ADJ_THRESH
;
161 * adjtime - hp-ux copout of the standard Unix adjtime() system call
165 register struct timeval
*delta
,
166 register struct timeval
*olddelta
169 struct timeval newdelta
;
172 * Corrections greater than one second are done immediately.
175 adjthresh
= ADJ_THRESH
;
177 return(_adjtime(delta
, olddelta
));
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.
188 saveup
+= delta
->tv_usec
;
189 if (abs(saveup
) >= adjthresh
) {
190 adjthresh
= ADJ_THRESH
;
192 newdelta
.tv_usec
= saveup
;
194 return(_adjtime(&newdelta
, olddelta
));
196 adjthresh
-= ADJ_DELTA
;
200 * While nobody uses it, return the residual before correction,
201 * as per Unix convention.
204 olddelta
->tv_sec
= olddelta
->tv_usec
= 0;
209 * _adjtime - does the actual work
213 register struct timeval
*delta
,
214 register struct timeval
*olddelta
219 register MsgBuf
*msgp
= &msg
;
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
226 if ((mqid
= msgget(KEY
, 0)) == -1)
228 msgp
->msgb
.mtype
= CLIENT
;
229 msgp
->msgb
.tv
= *delta
;
231 msgp
->msgb
.code
= DELTA2
;
233 msgp
->msgb
.code
= DELTA1
;
236 * Tickle adjtimed and snatch residual, if indicated. Lots of
237 * fanatic error checking here.
239 if (msgsnd(mqid
, &msgp
->msgp
, MSGSIZE
, 0) == -1)
242 if (msgrcv(mqid
, &msgp
->msgp
, MSGSIZE
, SERVER
, 0) == -1)
244 *olddelta
= msgp
->msgb
.tv
;
250 # if NEED_QNX_ADJTIME
252 * Emulate adjtime() using QNX ClockAdjust().
253 * Chris Burghart <burghart@atd.ucar.edu>, 11/2001
254 * Miroslaw Pabich <miroslaw_pabich@o2.pl>, 09/2005
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.
262 # include <sys/neutrino.h>
263 # include <sys/time.h>
265 # include <ntp_stdlib.h>
268 * Time correction slew limit. QNX is a hard real-time system,
269 * so don't adjust system clock too fast.
271 #define CORR_SLEW_LIMIT 0.02 /* [s/s] */
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.
278 #define ADJUST_PERIOD 0.97 /* [s] */
281 adjtime (struct timeval
*delta
, struct timeval
*olddelta
)
284 double delta_nsec_old
;
285 struct _clockadjust adj
;
286 struct _clockadjust oldadj
;
289 * How many nanoseconds are we adjusting?
292 delta_nsec
= 1e9
* (long)delta
->tv_sec
+ 1e3
* delta
->tv_usec
;
297 * Build the adjust structure and call ClockAdjust()
301 struct _clockperiod period
;
304 long increment_limit
;
308 * Convert to absolute value for future processing
313 delta_nsec
= -delta_nsec
;
317 * Get the current clock period (nanoseconds)
319 if (ClockPeriod (CLOCK_REALTIME
, 0, &period
, 0) < 0)
323 * Compute count and nanoseconds increment
325 count
= 1e9
* ADJUST_PERIOD
/ period
.nsec
;
326 increment
= delta_nsec
/ count
+ .5;
327 /* Reduce relative error */
328 if (count
> increment
+ 1)
330 increment
= 1 + (long)((delta_nsec
- 1) / count
);
331 count
= delta_nsec
/ increment
+ .5;
335 * Limit the adjust increment to appropriate value
337 increment_limit
= CORR_SLEW_LIMIT
* period
.nsec
;
338 if (increment
> increment_limit
)
340 increment
= increment_limit
;
341 count
= delta_nsec
/ increment
+ .5;
342 /* Reduce relative error */
343 if (increment
> count
+ 1)
345 count
= 1 + (long)((delta_nsec
- 1) / increment
);
346 increment
= delta_nsec
/ count
+ .5;
350 adj
.tick_nsec_inc
= isneg
? -increment
: increment
;
351 adj
.tick_count
= count
;
355 adj
.tick_nsec_inc
= 0;
359 if (ClockAdjust (CLOCK_REALTIME
, &adj
, &oldadj
) < 0)
365 delta_nsec_old
= (double)oldadj
.tick_count
* oldadj
.tick_nsec_inc
;
366 if (olddelta
!= NULL
)
368 if (delta_nsec_old
!= 0)
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;
378 olddelta
->tv_sec
= 0;
379 olddelta
->tv_usec
= 0;
385 # else /* no special adjtime() needed */