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