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: * Miroslaw Pabich <miroslaw_pabich@o2.pl>, 09/2005
253: *
254: * This is an implementation of adjtime() for QNX.
255: * ClockAdjust() is used to tweak the system clock for about
256: * 1 second period until the desired delta is achieved.
257: * Time correction slew is limited to reasonable value.
258: * Internal rounding and relative errors are reduced.
259: */
260: # include <sys/neutrino.h>
261: # include <sys/time.h>
262:
263: # include <ntp_stdlib.h>
264:
265: /*
266: * Time correction slew limit. QNX is a hard real-time system,
267: * so don't adjust system clock too fast.
268: */
269: #define CORR_SLEW_LIMIT 0.02 /* [s/s] */
270:
271: /*
272: * Period of system clock adjustment. It should be equal to adjtime
273: * execution period (1s). If slightly less than 1s (0.95-0.99), then olddelta
274: * residual error (introduced by execution period jitter) will be reduced.
275: */
276: #define ADJUST_PERIOD 0.97 /* [s] */
277:
278: int
279: adjtime (struct timeval *delta, struct timeval *olddelta)
280: {
281: double delta_nsec;
282: double delta_nsec_old;
283: struct _clockadjust adj;
284: struct _clockadjust oldadj;
285:
286: /*
287: * How many nanoseconds are we adjusting?
288: */
289: if (delta != NULL)
290: delta_nsec = 1e9 * (long)delta->tv_sec + 1e3 * delta->tv_usec;
291: else
292: delta_nsec = 0;
293:
294: /*
295: * Build the adjust structure and call ClockAdjust()
296: */
297: if (delta_nsec != 0)
298: {
299: struct _clockperiod period;
300: long count;
301: long increment;
302: long increment_limit;
303: int isneg = 0;
304:
305: /*
306: * Convert to absolute value for future processing
307: */
308: if (delta_nsec < 0)
309: {
310: isneg = 1;
311: delta_nsec = -delta_nsec;
312: }
313:
314: /*
315: * Get the current clock period (nanoseconds)
316: */
317: if (ClockPeriod (CLOCK_REALTIME, 0, &period, 0) < 0)
318: return -1;
319:
320: /*
321: * Compute count and nanoseconds increment
322: */
323: count = 1e9 * ADJUST_PERIOD / period.nsec;
324: increment = delta_nsec / count + .5;
325: /* Reduce relative error */
326: if (count > increment + 1)
327: {
328: increment = 1 + (long)((delta_nsec - 1) / count);
329: count = delta_nsec / increment + .5;
330: }
331:
332: /*
333: * Limit the adjust increment to appropriate value
334: */
335: increment_limit = CORR_SLEW_LIMIT * period.nsec;
336: if (increment > increment_limit)
337: {
338: increment = increment_limit;
339: count = delta_nsec / increment + .5;
340: /* Reduce relative error */
341: if (increment > count + 1)
342: {
343: count = 1 + (long)((delta_nsec - 1) / increment);
344: increment = delta_nsec / count + .5;
345: }
346: }
347:
348: adj.tick_nsec_inc = isneg ? -increment : increment;
349: adj.tick_count = count;
350: }
351: else
352: {
353: adj.tick_nsec_inc = 0;
354: adj.tick_count = 0;
355: }
356:
357: if (ClockAdjust (CLOCK_REALTIME, &adj, &oldadj) < 0)
358: return -1;
359:
360: /*
361: * Build olddelta
362: */
363: delta_nsec_old = (double)oldadj.tick_count * oldadj.tick_nsec_inc;
364: if (olddelta != NULL)
365: {
366: if (delta_nsec_old != 0)
367: {
368: /* Reduce rounding error */
369: delta_nsec_old += (delta_nsec_old < 0) ? -500 : 500;
370: olddelta->tv_sec = delta_nsec_old / 1e9;
371: olddelta->tv_usec = (long)(delta_nsec_old - 1e9
372: * (long)olddelta->tv_sec) / 1000;
373: }
374: else
375: {
376: olddelta->tv_sec = 0;
377: olddelta->tv_usec = 0;
378: }
379: }
380:
381: return 0;
382: }
383: # else /* no special adjtime() needed */
384: int adjtime_bs;
385: # endif
386: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>