xref: /freebsd/contrib/ntp/libntp/adjtime.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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