/contrib/ntp/libntp/adjtime.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 386 lines · 204 code · 50 blank · 132 comment · 38 complexity · f9729c26362ef54a2abb8e3f95fef779 MD5 · raw file

  1. #ifdef HAVE_CONFIG_H
  2. # include <config.h>
  3. #endif
  4. #ifdef MPE
  5. /*
  6. * MPE lacks adjtime(), so we define our own. But note that time slewing has
  7. * a sub-second accuracy bug documented in SR 5003462838 which prevents ntpd
  8. * from being able to maintain clock synch. Because of the bug, this adjtime()
  9. * implementation as used by ntpd has a side-effect of screwing up the hardware
  10. * PDC clock, which will need to be reset with a reboot.
  11. *
  12. * This problem affects all versions of MPE at the time of this writing (when
  13. * MPE/iX 7.0 is the most current). It only causes bad things to happen when
  14. * doing continuous clock synchronization with ntpd; note that you CAN run ntpd
  15. * with "disable ntp" in ntp.conf if you wish to provide a time server.
  16. *
  17. * The one-time clock adjustment functionality of ntpdate and ntp_timeset can
  18. * be used without screwing up the PDC clock.
  19. *
  20. */
  21. #include <time.h>
  22. int adjtime(struct timeval *delta, struct timeval *olddelta);
  23. int adjtime(struct timeval *delta, struct timeval *olddelta)
  24. {
  25. /* Documented, supported MPE system intrinsics. */
  26. extern void GETPRIVMODE(void);
  27. extern void GETUSERMODE(void);
  28. /* Undocumented, unsupported MPE internal functions. */
  29. extern long long current_correction_usecs(void);
  30. extern long long get_time(void);
  31. extern void get_time_change_info(long long *, char *, char *);
  32. extern long long pdc_time(int *);
  33. extern void set_time_correction(long long, int, int);
  34. extern long long ticks_to_micro(long long);
  35. long long big_sec, big_usec, new_correction = 0LL;
  36. long long prev_correction;
  37. if (delta != NULL) {
  38. /* Adjustment required. Convert delta to 64-bit microseconds. */
  39. big_sec = (long)delta->tv_sec;
  40. big_usec = delta->tv_usec;
  41. new_correction = (big_sec * 1000000LL) + big_usec;
  42. }
  43. GETPRIVMODE();
  44. /* Determine how much of a previous correction (if any) we're interrupting. */
  45. prev_correction = current_correction_usecs();
  46. if (delta != NULL) {
  47. /* Adjustment required. */
  48. #if 0
  49. /* Speculative code disabled until bug SR 5003462838 is fixed. This bug
  50. prevents accurate time slewing, and indeed renders ntpd inoperable. */
  51. if (prev_correction != 0LL) {
  52. /* A previous adjustment did not complete. Since the PDC UTC clock was
  53. immediately jumped at the start of the previous adjustment, we must
  54. explicitly reset it to the value of the MPE local time clock minus the
  55. time zone offset. */
  56. char pwf_since_boot, recover_pwf_time;
  57. long long offset_ticks, offset_usecs, pdc_usecs_current, pdc_usecs_wanted;
  58. int hpe_status;
  59. get_time_change_info(&offset_ticks, &pwf_since_boot, &recover_pwf_time);
  60. offset_usecs = ticks_to_micro(offset_ticks);
  61. pdc_usecs_wanted = get_time() - offset_usecs;
  62. pdc_usecs_current = pdc_time(&hpe_status);
  63. if (hpe_status == 0)
  64. /* Force new PDC time by starting an extra correction. */
  65. set_time_correction(pdc_usecs_wanted - pdc_usecs_current,0,1);
  66. }
  67. #endif /* 0 */
  68. /* Immediately jump the PDC time to the new value, and then initiate a
  69. gradual MPE time correction slew. */
  70. set_time_correction(new_correction,0,1);
  71. }
  72. GETUSERMODE();
  73. if (olddelta != NULL) {
  74. /* Caller wants to know remaining amount of previous correction. */
  75. (long)olddelta->tv_sec = prev_correction / 1000000LL;
  76. olddelta->tv_usec = prev_correction % 1000000LL;
  77. }
  78. return 0;
  79. }
  80. #endif /* MPE */
  81. #ifdef NEED_HPUX_ADJTIME
  82. /*************************************************************************/
  83. /* (c) Copyright Tai Jin, 1988. All Rights Reserved. */
  84. /* Hewlett-Packard Laboratories. */
  85. /* */
  86. /* Permission is hereby granted for unlimited modification, use, and */
  87. /* distribution. This software is made available with no warranty of */
  88. /* any kind, express or implied. This copyright notice must remain */
  89. /* intact in all versions of this software. */
  90. /* */
  91. /* The author would appreciate it if any bug fixes and enhancements were */
  92. /* to be sent back to him for incorporation into future versions of this */
  93. /* software. Please send changes to tai@iag.hp.com or ken@sdd.hp.com. */
  94. /*************************************************************************/
  95. /*
  96. * Revision history
  97. *
  98. * 9 Jul 94 David L. Mills, Unibergity of Delabunch
  99. * Implemented variable threshold to limit age of
  100. * corrections; reformatted code for readability.
  101. */
  102. #ifndef lint
  103. static char RCSid[] = "adjtime.c,v 3.1 1993/07/06 01:04:42 jbj Exp";
  104. #endif
  105. #include <sys/types.h>
  106. #include <sys/ipc.h>
  107. #include <sys/msg.h>
  108. #include <time.h>
  109. #include <signal.h>
  110. #include "adjtime.h"
  111. #define abs(x) ((x) < 0 ? -(x) : (x))
  112. /*
  113. * The following paramters are appropriate for an NTP adjustment
  114. * interval of one second.
  115. */
  116. #define ADJ_THRESH 200 /* initial threshold */
  117. #define ADJ_DELTA 4 /* threshold decrement */
  118. static long adjthresh; /* adjustment threshold */
  119. static long saveup; /* corrections accumulator */
  120. /*
  121. * clear_adjtime - reset accumulator and threshold variables
  122. */
  123. void
  124. _clear_adjtime(void)
  125. {
  126. saveup = 0;
  127. adjthresh = ADJ_THRESH;
  128. }
  129. /*
  130. * adjtime - hp-ux copout of the standard Unix adjtime() system call
  131. */
  132. int
  133. adjtime(
  134. register struct timeval *delta,
  135. register struct timeval *olddelta
  136. )
  137. {
  138. struct timeval newdelta;
  139. /*
  140. * Corrections greater than one second are done immediately.
  141. */
  142. if (delta->tv_sec) {
  143. adjthresh = ADJ_THRESH;
  144. saveup = 0;
  145. return(_adjtime(delta, olddelta));
  146. }
  147. /*
  148. * Corrections less than one second are accumulated until
  149. * tripping a threshold, which is initially set at ADJ_THESH and
  150. * reduced in ADJ_DELTA steps to zero. The idea here is to
  151. * introduce large corrections quickly, while making sure that
  152. * small corrections are introduced without excessive delay. The
  153. * idea comes from the ARPAnet routing update algorithm.
  154. */
  155. saveup += delta->tv_usec;
  156. if (abs(saveup) >= adjthresh) {
  157. adjthresh = ADJ_THRESH;
  158. newdelta.tv_sec = 0;
  159. newdelta.tv_usec = saveup;
  160. saveup = 0;
  161. return(_adjtime(&newdelta, olddelta));
  162. } else {
  163. adjthresh -= ADJ_DELTA;
  164. }
  165. /*
  166. * While nobody uses it, return the residual before correction,
  167. * as per Unix convention.
  168. */
  169. if (olddelta)
  170. olddelta->tv_sec = olddelta->tv_usec = 0;
  171. return(0);
  172. }
  173. /*
  174. * _adjtime - does the actual work
  175. */
  176. int
  177. _adjtime(
  178. register struct timeval *delta,
  179. register struct timeval *olddelta
  180. )
  181. {
  182. register int mqid;
  183. MsgBuf msg;
  184. register MsgBuf *msgp = &msg;
  185. /*
  186. * Get the key to the adjtime message queue (note that we must
  187. * get it every time because the queue might have been removed
  188. * and recreated)
  189. */
  190. if ((mqid = msgget(KEY, 0)) == -1)
  191. return (-1);
  192. msgp->msgb.mtype = CLIENT;
  193. msgp->msgb.tv = *delta;
  194. if (olddelta)
  195. msgp->msgb.code = DELTA2;
  196. else
  197. msgp->msgb.code = DELTA1;
  198. /*
  199. * Tickle adjtimed and snatch residual, if indicated. Lots of
  200. * fanatic error checking here.
  201. */
  202. if (msgsnd(mqid, &msgp->msgp, MSGSIZE, 0) == -1)
  203. return (-1);
  204. if (olddelta) {
  205. if (msgrcv(mqid, &msgp->msgp, MSGSIZE, SERVER, 0) == -1)
  206. return (-1);
  207. *olddelta = msgp->msgb.tv;
  208. }
  209. return (0);
  210. }
  211. #else
  212. # if NEED_QNX_ADJTIME
  213. /*
  214. * Emulate adjtime() using QNX ClockAdjust().
  215. * Chris Burghart <burghart@atd.ucar.edu>, 11/2001
  216. * Miroslaw Pabich <miroslaw_pabich@o2.pl>, 09/2005
  217. *
  218. * This is an implementation of adjtime() for QNX.
  219. * ClockAdjust() is used to tweak the system clock for about
  220. * 1 second period until the desired delta is achieved.
  221. * Time correction slew is limited to reasonable value.
  222. * Internal rounding and relative errors are reduced.
  223. */
  224. # include <sys/neutrino.h>
  225. # include <sys/time.h>
  226. # include <ntp_stdlib.h>
  227. /*
  228. * Time correction slew limit. QNX is a hard real-time system,
  229. * so don't adjust system clock too fast.
  230. */
  231. #define CORR_SLEW_LIMIT 0.02 /* [s/s] */
  232. /*
  233. * Period of system clock adjustment. It should be equal to adjtime
  234. * execution period (1s). If slightly less than 1s (0.95-0.99), then olddelta
  235. * residual error (introduced by execution period jitter) will be reduced.
  236. */
  237. #define ADJUST_PERIOD 0.97 /* [s] */
  238. int
  239. adjtime (struct timeval *delta, struct timeval *olddelta)
  240. {
  241. double delta_nsec;
  242. double delta_nsec_old;
  243. struct _clockadjust adj;
  244. struct _clockadjust oldadj;
  245. /*
  246. * How many nanoseconds are we adjusting?
  247. */
  248. if (delta != NULL)
  249. delta_nsec = 1e9 * (long)delta->tv_sec + 1e3 * delta->tv_usec;
  250. else
  251. delta_nsec = 0;
  252. /*
  253. * Build the adjust structure and call ClockAdjust()
  254. */
  255. if (delta_nsec != 0)
  256. {
  257. struct _clockperiod period;
  258. long count;
  259. long increment;
  260. long increment_limit;
  261. int isneg = 0;
  262. /*
  263. * Convert to absolute value for future processing
  264. */
  265. if (delta_nsec < 0)
  266. {
  267. isneg = 1;
  268. delta_nsec = -delta_nsec;
  269. }
  270. /*
  271. * Get the current clock period (nanoseconds)
  272. */
  273. if (ClockPeriod (CLOCK_REALTIME, 0, &period, 0) < 0)
  274. return -1;
  275. /*
  276. * Compute count and nanoseconds increment
  277. */
  278. count = 1e9 * ADJUST_PERIOD / period.nsec;
  279. increment = delta_nsec / count + .5;
  280. /* Reduce relative error */
  281. if (count > increment + 1)
  282. {
  283. increment = 1 + (long)((delta_nsec - 1) / count);
  284. count = delta_nsec / increment + .5;
  285. }
  286. /*
  287. * Limit the adjust increment to appropriate value
  288. */
  289. increment_limit = CORR_SLEW_LIMIT * period.nsec;
  290. if (increment > increment_limit)
  291. {
  292. increment = increment_limit;
  293. count = delta_nsec / increment + .5;
  294. /* Reduce relative error */
  295. if (increment > count + 1)
  296. {
  297. count = 1 + (long)((delta_nsec - 1) / increment);
  298. increment = delta_nsec / count + .5;
  299. }
  300. }
  301. adj.tick_nsec_inc = isneg ? -increment : increment;
  302. adj.tick_count = count;
  303. }
  304. else
  305. {
  306. adj.tick_nsec_inc = 0;
  307. adj.tick_count = 0;
  308. }
  309. if (ClockAdjust (CLOCK_REALTIME, &adj, &oldadj) < 0)
  310. return -1;
  311. /*
  312. * Build olddelta
  313. */
  314. delta_nsec_old = (double)oldadj.tick_count * oldadj.tick_nsec_inc;
  315. if (olddelta != NULL)
  316. {
  317. if (delta_nsec_old != 0)
  318. {
  319. /* Reduce rounding error */
  320. delta_nsec_old += (delta_nsec_old < 0) ? -500 : 500;
  321. olddelta->tv_sec = delta_nsec_old / 1e9;
  322. olddelta->tv_usec = (long)(delta_nsec_old - 1e9
  323. * (long)olddelta->tv_sec) / 1000;
  324. }
  325. else
  326. {
  327. olddelta->tv_sec = 0;
  328. olddelta->tv_usec = 0;
  329. }
  330. }
  331. return 0;
  332. }
  333. # else /* no special adjtime() needed */
  334. int adjtime_bs;
  335. # endif
  336. #endif