adjtime.c revision 330567
1219089Spjd#ifdef HAVE_CONFIG_H
2219089Spjd# include <config.h>
3219089Spjd#endif
4219089Spjd
5219089Spjd#ifdef MPE
6219089Spjd/*
7219089Spjd * MPE lacks adjtime(), so we define our own.  But note that time slewing has
8219089Spjd * a sub-second accuracy bug documented in SR 5003462838 which prevents ntpd
9219089Spjd * from being able to maintain clock synch.  Because of the bug, this adjtime()
10219089Spjd * implementation as used by ntpd has a side-effect of screwing up the hardware
11219089Spjd * PDC clock, which will need to be reset with a reboot.
12219089Spjd *
13219089Spjd * This problem affects all versions of MPE at the time of this writing (when
14219089Spjd * MPE/iX 7.0 is the most current).  It only causes bad things to happen when
15219089Spjd * doing continuous clock synchronization with ntpd; note that you CAN run ntpd
16219089Spjd * with "disable ntp" in ntp.conf if you wish to provide a time server.
17219089Spjd *
18219089Spjd * The one-time clock adjustment functionality of ntpdate and ntp_timeset can
19219089Spjd * be used without screwing up the PDC clock.
20219089Spjd *
21219089Spjd */
22219089Spjd#include <time.h>
23219089Spjd
24219089Spjdint adjtime(struct timeval *delta, struct timeval *olddelta);
25219089Spjd
26219089Spjdint adjtime(struct timeval *delta, struct timeval *olddelta)
27219089Spjd
28219089Spjd{
29219089Spjd/* Documented, supported MPE system intrinsics. */
30219089Spjd
31219089Spjdextern void GETPRIVMODE(void);
32219089Spjdextern void GETUSERMODE(void);
33219089Spjd
34219089Spjd/* Undocumented, unsupported MPE internal functions. */
35219089Spjd
36219089Spjdextern long long current_correction_usecs(void);
37219089Spjdextern long long get_time(void);
38219089Spjdextern void get_time_change_info(long long *, char *, char *);
39219089Spjdextern long long pdc_time(int *);
40219089Spjdextern void set_time_correction(long long, int, int);
41219089Spjdextern long long ticks_to_micro(long long);
42219089Spjd
43219089Spjdlong long big_sec, big_usec, new_correction = 0LL;
44219089Spjdlong long prev_correction;
45219089Spjd
46219089Spjdif (delta != NULL) {
47219089Spjd  /* Adjustment required.  Convert delta to 64-bit microseconds. */
48219089Spjd  big_sec = (long)delta->tv_sec;
49219089Spjd  big_usec = delta->tv_usec;
50219089Spjd  new_correction = (big_sec * 1000000LL) + big_usec;
51219089Spjd}
52219089Spjd
53219089SpjdGETPRIVMODE();
54219089Spjd
55219089Spjd/* Determine how much of a previous correction (if any) we're interrupting. */
56219089Spjdprev_correction = current_correction_usecs();
57219089Spjd
58219089Spjdif (delta != NULL) {
59219089Spjd  /* Adjustment required. */
60219089Spjd
61219089Spjd#if 0
62219089Spjd  /* Speculative code disabled until bug SR 5003462838 is fixed.  This bug
63219089Spjd     prevents accurate time slewing, and indeed renders ntpd inoperable. */
64219089Spjd
65219089Spjd  if (prev_correction != 0LL) {
66219089Spjd    /* A previous adjustment did not complete.  Since the PDC UTC clock was
67219089Spjd    immediately jumped at the start of the previous adjustment, we must
68219089Spjd    explicitly reset it to the value of the MPE local time clock minus the
69219089Spjd    time zone offset. */
70219089Spjd
71219089Spjd    char pwf_since_boot, recover_pwf_time;
72219089Spjd    long long offset_ticks, offset_usecs, pdc_usecs_current, pdc_usecs_wanted;
73219089Spjd    int hpe_status;
74219089Spjd
75219089Spjd    get_time_change_info(&offset_ticks, &pwf_since_boot, &recover_pwf_time);
76219089Spjd    offset_usecs = ticks_to_micro(offset_ticks);
77219089Spjd    pdc_usecs_wanted = get_time() - offset_usecs;
78219089Spjd    pdc_usecs_current = pdc_time(&hpe_status);
79219089Spjd    if (hpe_status == 0)
80219089Spjd      /* Force new PDC time by starting an extra correction. */
81219089Spjd      set_time_correction(pdc_usecs_wanted - pdc_usecs_current,0,1);
82219089Spjd  }
83219089Spjd#endif /* 0 */
84219089Spjd
85219089Spjd  /* Immediately jump the PDC time to the new value, and then initiate a
86219089Spjd     gradual MPE time correction slew. */
87219089Spjd  set_time_correction(new_correction,0,1);
88219089Spjd}
89219089Spjd
90219089SpjdGETUSERMODE();
91219089Spjd
92219089Spjdif (olddelta != NULL) {
93219089Spjd  /* Caller wants to know remaining amount of previous correction. */
94219089Spjd  (long)olddelta->tv_sec = prev_correction / 1000000LL;
95219089Spjd  olddelta->tv_usec = prev_correction % 1000000LL;
96219089Spjd}
97219089Spjd
98219089Spjdreturn 0;
99219089Spjd}
100219089Spjd#endif /* MPE */
101219089Spjd
102219089Spjd#ifdef NEED_HPUX_ADJTIME
103219089Spjd/*************************************************************************/
104219089Spjd/* (c) Copyright Tai Jin, 1988.  All Rights Reserved.                    */
105219089Spjd/*     Hewlett-Packard Laboratories.                                     */
106219089Spjd/*                                                                       */
107219089Spjd/* Permission is hereby granted for unlimited modification, use, and     */
108219089Spjd/* distribution.  This software is made available with no warranty of    */
109219089Spjd/* any kind, express or implied.  This copyright notice must remain      */
110219089Spjd/* intact in all versions of this software.                              */
111219089Spjd/*                                                                       */
112219089Spjd/* The author would appreciate it if any bug fixes and enhancements were */
113219089Spjd/* to be sent back to him for incorporation into future versions of this */
114219089Spjd/* software.  Please send changes to tai@iag.hp.com or ken@sdd.hp.com.   */
115219089Spjd/*************************************************************************/
116219089Spjd
117219089Spjd/*
118219089Spjd * Revision history
119219089Spjd *
120219089Spjd * 9 Jul 94	David L. Mills, Unibergity of Delabunch
121219089Spjd *		Implemented variable threshold to limit age of
122219089Spjd *		corrections; reformatted code for readability.
123219089Spjd */
124219089Spjd
125219089Spjd#ifndef lint
126219089Spjdstatic char RCSid[] = "adjtime.c,v 3.1 1993/07/06 01:04:42 jbj Exp";
127219089Spjd#endif
128219089Spjd
129219089Spjd#include <sys/types.h>
130219089Spjd#include <sys/ipc.h>
131219089Spjd#include <sys/msg.h>
132219089Spjd#include <time.h>
133219089Spjd#include <signal.h>
134219089Spjd#include "adjtime.h"
135219089Spjd
136219089Spjd#define abs(x)  ((x) < 0 ? -(x) : (x))
137219089Spjd
138219089Spjd/*
139219089Spjd * The following paramters are appropriate for an NTP adjustment
140219089Spjd * interval of one second.
141219089Spjd */
142219089Spjd#define ADJ_THRESH 200		/* initial threshold */
143#define ADJ_DELTA 4		/* threshold decrement */
144
145static long adjthresh;		/* adjustment threshold */
146static long saveup;		/* corrections accumulator */
147
148/*
149 * clear_adjtime - reset accumulator and threshold variables
150 */
151void
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 */
161int
162adjtime(
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 */
209int
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
278int
279adjtime (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) == -1)
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) == -1)
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 */
384int adjtime_bs;
385# endif
386#endif
387