Annotation of embedaddon/ntp/ports/winnt/ntpd/nt_clockstuff.c, revision 1.1.1.1
1.1 misho 1: /* Windows NT Clock Routines
2: *
3: * Created by Sven Dietrich sven@inter-yacht.com
4: *
5: * New interpolation scheme by Dave Hart <davehart@davehart.com> February 2009
6: * overcomes 500us-1ms inherent jitter with the older scheme, first identified
7: * by Peter Rosin (nee Ekberg) <peda@lysator.liu.se> in 2003 [Bug 216].
8: */
9:
10:
11: #ifdef HAVE_CONFIG_H
12: #include "config.h"
13: #endif
14:
15: #include <sys/resource.h> /* our private version */
16:
17: #if defined(_MSC_VER) && _MSC_VER >= 1400 /* VS 2005 */
18: #include <intrin.h> /* for __rdtsc() */
19: #endif
20:
21: #ifdef HAVE_PPSAPI
22: #include <timepps.h>
23: /*
24: * ports/winnt/include/timepps.h defines EOPNOTSUPP for compatibility
25: * with PPSAPI on other platforms. ports/winnt/include/isc/net.h has
26: * #define EOPNOTSUPP WSAEOPNOTSUPP, so to avoid a macro redefinition
27: * warning undefine it.
28: */
29: #undef EOPNOTSUPP
30: #endif /* HAVE_PPSAPI */
31:
32: #include "ntp_stdlib.h"
33: #include "ntp_unixtime.h"
34: #include "ntp_timer.h"
35: #include "ntp_assert.h"
36: #include "clockstuff.h"
37: #include "ntservice.h"
38: #include "ntpd.h"
39: #include "../../../ntpd/ntpd-opts.h"
40:
41: extern double sys_residual; /* residual from previous adjustment */
42:
43: /*
44: * Include code to possibly modify the MM timer while the service is active.
45: */
46:
47: /*
48: * Whether or not MM timer modifications takes place is still controlled
49: * by the variable below which is initialized by a default value but
50: * might be changed depending on a command line switch.
51: */
52: static int modify_mm_timer = MM_TIMER_LORES;
53:
54: #define MM_TIMER_INTV 1 /* the interval we'd want to set the MM timer to [ms] */
55:
56: static UINT wTimerRes;
57:
58: BOOL init_randfile();
59:
60: static long last_Adj = 0;
61:
62: #define LS_CORR_INTV_SECS 2 /* seconds to apply leap second correction */
63: #define LS_CORR_INTV ( (LONGLONG) HECTONANOSECONDS * LS_CORR_INTV_SECS )
64: #define LS_CORR_LIMIT ( (LONGLONG) HECTONANOSECONDS / 2 ) // half a second
65:
66: typedef union ft_ull {
67: FILETIME ft;
68: ULONGLONG ull;
69: LONGLONG ll;
70: LARGE_INTEGER li;
71: } FT_ULL;
72:
73: /* leap second stuff */
74: static FT_ULL ls_ft;
75: static DWORD ls_time_adjustment;
76: static ULONGLONG ls_ref_perf_cnt;
77: static LONGLONG ls_elapsed;
78:
79: static BOOL winnt_time_initialized = FALSE;
80: static BOOL winnt_use_interpolation = FALSE;
81: static ULONGLONG last_interp_time;
82: static unsigned clock_thread_id;
83:
84:
85: void WINAPI GetInterpTimeAsFileTime(LPFILETIME pft);
86: static void StartClockThread(void);
87: static void tune_ctr_freq(LONGLONG, LONGLONG);
88: void StopClockThread(void);
89: void atexit_revert_mm_timer(void);
90: void time_stepped(void);
91:
92: static HANDLE clock_thread = NULL;
93: static HANDLE TimerThreadExitRequest = NULL;
94:
95: /*
96: * interp_time estimates time in 100ns units
97: * based on a performance counter value given.
98: * The 2nd parameter indicates if this is
99: * part of a current time-of-day calculation.
100: */
101: ULONGLONG interp_time(ULONGLONG, BOOL);
102:
103: /*
104: * add_counter_time_pair is called by the
105: * high priority clock thread with a new
106: * sample.
107: */
108: void add_counter_time_pair(ULONGLONG, LONGLONG);
109:
110: /*
111: * globals used by the above two functions to
112: * implement the counter/time history
113: */
114: #define BASELINES_TOT 256
115: #define BASELINES_USED 64
116:
117: static volatile int newest_baseline = 0;
118: static volatile int newest_baseline_gen = 0;
119: static ULONGLONG baseline_counts[BASELINES_TOT] = {0};
120: static LONGLONG baseline_times[BASELINES_TOT] = {0};
121:
122: static int clock_backward_count;
123: static ULONGLONG clock_backward_max;
124:
125:
126: /*
127: * clockperiod is the period used for SetSystemTimeAdjustment
128: * slewing calculations but does not necessarily correspond
129: * to the precision of the OS clock. Prior to Windows Vista
130: * (6.0) the two were identical. In 100ns units.
131: */
132: static DWORD clockperiod;
133:
134: /*
135: * os_clock_precision is the observed precision of the OS
136: * clock, meaning the increment between discrete values. This
137: * is currently calculated once at startup. 100ns units.
138: */
139: static ULONGLONG os_clock_precision;
140:
141: /*
142: * NomPerfCtrFreq is from QueryPerformanceFrequency and is the
143: * number of performance counter beats per second. PerfCtrFreq
144: * starts from NomPerfCtrFreq but is maintained using a sliding
145: * window average based on actual performance counter behavior,
146: * to allow us to better tolerate powersaving measures that
147: * alter the effective frequency of the processor cycle counter
148: * (TSC) which sometimes underlies QueryPerformanceCounter.
149: *
150: * Note that the OS is unlikely to be so subtle in its internal
151: * scheduling of waitable timers, presumably done using the
152: * performance counter. Therefore our calculations for
153: * interpolated time should be based on PerfCtrFreq but our
154: * calculations for SetWaitableTimer should assume the OS will
155: * convert from FILETIME 100ns units to performance counter
156: * beats using the nominal frequency.
157: */
158:
159: volatile ULONGLONG PerfCtrFreq = 0;
160: ULONGLONG NomPerfCtrFreq = 0;
161:
162: /*
163: * If we're using RDTSC beating at the same rate as
164: * QueryPerformanceCounter, there is a systemic
165: * offset we need to account for when using
166: * counterstamps from serialpps.sys, which are
167: * always from QPC (actually KeQueryPerformanceCounter).
168: */
169: static LONGLONG QPC_offset = 0;
170:
171: /*
172: * Substitute RDTSC for QueryPerformanceCounter()?
173: */
174: static int use_pcc = -1;
175:
176: /*
177: * Restrict threads that call QPC/RDTSC to one CPU?
178: */
179: static int lock_interp_threads = -1;
180:
181: static void choose_interp_counter(void);
182: static int is_qpc_built_on_pcc(void);
183:
184: /*
185: * ppm_per_adjust_unit is parts per million effect on the OS
186: * clock per slewing adjustment unit per second. Per haps.
187: */
188: static DOUBLE ppm_per_adjust_unit = 0.0;
189:
190: /*
191: * wintickadj emulates the functionality provided by unix tickadj,
192: * providing a baseline clock correction if needed to get the
193: * clock within a few hundred PPM of correct frequency.
194: */
195: static long wintickadj;
196:
197: /*
198: * performance counter frequency observations
199: */
200: #define TUNE_CTR_DEPTH 3 /* running avg depth */
201:
202: static HANDLE ctr_freq_timer = INVALID_HANDLE_VALUE;
203: static ULONGLONG tune_ctr_freq_max_interval;
204: static unsigned tune_ctr_period;
205: void start_ctr_freq_timer(ULONGLONG now_time);
206: void reset_ctr_freq_timer(ULONGLONG when, ULONGLONG now);
207: void reset_ctr_freq_timer_abs(ULONGLONG when);
208:
209: /* round a Windows time to the next bottom of the second */
210:
211: #define ROUND_TO_NEXT_SEC_BOTTOM(t) \
212: do { \
213: (t) += 3 * HECTONANOSECONDS / 2 - 1; \
214: (t) /= HECTONANOSECONDS; \
215: (t) *= HECTONANOSECONDS; \
216: (t) -= HECTONANOSECONDS / 2; \
217: } while (0)
218:
219: /*
220: * workaround for VC6 inability to convert unsigned __int64 to double
221: */
222: #define LL_HNS ((LONGLONG)HECTONANOSECONDS)
223:
224: /*
225: * NT native time format is 100's of nanoseconds since 1601-01-01.
226: * Helpers for converting between "hectonanoseconds" and the
227: * performance counter scale from which interpolated time is
228: * derived.
229: *
230: * Once support for VC6 is dropped, the cast of PerfCtrFreq to
231: * LONGLONG can come out of PERF2HNS(). It avoids the VC6 error
232: * message:
233: *
234: * conversion from unsigned __int64 to double not implemented, use
235: * signed __int64
236: */
237: #define HNS2PERF(hns) ((hns) * PerfCtrFreq / LL_HNS)
238: #define PERF2HNS(ctr) ((ctr) * LL_HNS / (LONGLONG)PerfCtrFreq)
239:
240:
241: #if defined(_MSC_VER) && _MSC_VER >= 1400 /* VS 2005 */
242: #define get_pcc() __rdtsc()
243: #else
244: /*
245: * something like this can be used for a compiler without __rdtsc()
246: */
247: ULONGLONG __forceinline
248: get_pcc(void)
249: {
250: /* RDTSC returns in EDX:EAX, same as C compiler */
251: __asm {
252: RDTSC
253: }
254: }
255: #endif
256:
257:
258: /*
259: * perf_ctr() returns the current performance counter value,
260: * from QueryPerformanceCounter or RDTSC.
261: */
262: ULONGLONG WINAPI
263: perf_ctr(void)
264: {
265: FT_ULL ft;
266:
267: if (use_pcc)
268: return get_pcc();
269: else {
270: QueryPerformanceCounter(&ft.li);
271: return ft.ull;
272: }
273: }
274:
275:
276: /*
277: * choose_interp_counter - select between QueryPerformanceCounter and
278: * the x86 processor cycle counter (TSC).
279: */
280: static void
281: choose_interp_counter(void)
282: {
283: const char * ntpd_pcc_freq_text;
284: int qpc_built_on_pcc;
285:
286: /*
287: * Regardless of whether we actually use RDTSC, first determine
288: * if QueryPerformanceCounter is built on it, so that we can
289: * decide whether it's prudent to lock QPC-consuming threads to
290: * a particular CPU.
291: */
292: qpc_built_on_pcc = is_qpc_built_on_pcc();
293: lock_interp_threads = qpc_built_on_pcc;
294:
295: /*
296: * It's time to make some more permanent knobs,
297: * but for right now the RDTSC aka PCC dance on x86 is:
298: *
299: * 1. With none of these variables defined, only QPC
300: * is used because there is no reliable way to
301: * detect counter frequency variation after ntpd
302: * startup implemented.
303: * 2. We need a better knob, but for now if you know
304: * your RDTSC / CPU frequency is invariant, set
305: * NTPD_PCC and assuming your QPC is based on the
306: * PCC as well, RDTSC will be substituted.
307: * 3. More forcefully, you can jam in a desired exact
308: * processor frequency, expressed in cycles per
309: * second by setting NTPD_PCC_FREQ=398125000, for
310: * example, if yor actual known CPU frequency is
311: * 398.125 MHz, and NTPD_PCC doesn't work because
312: * QueryPerformanceCounter is implemented using
313: * another counter. It is very easy to make ntpd
314: * fall down if the NTPD_PCC_FREQ value isn't very
315: * close to the observed RDTSC units per second.
316: *
317: * Items 2 and 3 could probably best be combined into one
318: * new windows-specific command line switch such as
319: * ntpd --pcc
320: * or
321: * ntpd --pcc=398125000
322: *
323: * They are currently tied to Windows because that is
324: * the only ntpd port with its own interpolation, and
325: * to x86/x64 because no one has ported the Windows
326: * ntpd port to the sole remaining alternative, Intel
327: * Itanium.
328: */
329: if (HAVE_OPT(PCCFREQ))
330: ntpd_pcc_freq_text = OPT_ARG(PCCFREQ);
331: else
332: ntpd_pcc_freq_text = getenv("NTPD_PCC_FREQ");
333:
334: if (!HAVE_OPT(USEPCC)
335: && NULL == ntpd_pcc_freq_text
336: && NULL == getenv("NTPD_PCC")) {
337: use_pcc = 0;
338: return;
339: }
340:
341: if (!qpc_built_on_pcc && NULL == ntpd_pcc_freq_text) {
342: use_pcc = 0;
343: return;
344: }
345:
346: use_pcc = 1;
347: if (ntpd_pcc_freq_text != NULL)
348: sscanf(ntpd_pcc_freq_text,
349: "%I64u",
350: &NomPerfCtrFreq);
351:
352: NLOG(NLOG_CLOCKINFO)
353: msyslog(LOG_INFO,
354: "using processor cycle counter "
355: "%.3f MHz",
356: (LONGLONG)NomPerfCtrFreq / 1e6);
357: return;
358: }
359:
360:
361: /*
362: * is_qpc_built_on_pcc - test if QueryPerformanceCounter runs at the
363: * same rate as the processor cycle counter (TSC).
364: */
365: static int
366: is_qpc_built_on_pcc(void)
367: {
368: LONGLONG offset;
369: FT_ULL ft1;
370: FT_ULL ft2;
371: FT_ULL ft3;
372: FT_ULL ft4;
373: FT_ULL ft5;
374:
375: NTP_REQUIRE(NomPerfCtrFreq != 0);
376:
377: QueryPerformanceCounter(&ft1.li);
378: ft2.ull = get_pcc();
379: Sleep(1);
380: QueryPerformanceCounter(&ft3.li);
381: Sleep(1);
382: ft4.ull = get_pcc();
383: Sleep(1);
384: QueryPerformanceCounter(&ft5.li);
385:
386: offset = ft2.ull - ft1.ull;
387: ft3.ull += offset;
388: ft5.ull += offset;
389:
390: if (ft2.ull <= ft3.ull &&
391: ft3.ull <= ft4.ull &&
392: ft4.ull <= ft5.ull) {
393:
394: QPC_offset = offset;
395: return TRUE;
396: }
397:
398: return FALSE;
399: }
400:
401:
402: /*
403: * Request Multimedia Timer
404: */
405: void
406: set_mm_timer(
407: int timerres
408: )
409: {
410: modify_mm_timer = timerres;
411: }
412:
413: /*
414: * adj_systime - called once every second to make system time adjustments.
415: * Returns 1 if okay, 0 if trouble.
416: */
417: int
418: adj_systime(
419: double now
420: )
421: {
422: double dtemp;
423: u_char isneg = 0;
424: int rc;
425: long TimeAdjustment;
426:
427: /*
428: * Add the residual from the previous adjustment to the new
429: * adjustment, bound and round.
430: */
431: dtemp = sys_residual + now;
432: sys_residual = 0;
433: if (dtemp < 0)
434: {
435: isneg = 1;
436: dtemp = -dtemp;
437: }
438:
439: if (dtemp > NTP_MAXFREQ)
440: dtemp = NTP_MAXFREQ;
441:
442: dtemp = dtemp * 1e6;
443:
444: if (isneg)
445: dtemp = -dtemp;
446:
447: /*
448: * dtemp is in micro seconds. NT uses 100 ns units,
449: * so a unit change in TimeAdjustment corresponds
450: * to slewing 10 ppm on a 100 Hz system. Calculate
451: * the number of 100ns units to add, using OS tick
452: * frequency as per suggestion from Harry Pyle,
453: * and leave the remainder in dtemp
454: */
455: TimeAdjustment = (long) (dtemp / ppm_per_adjust_unit + (isneg ? -0.5 : 0.5));
456: dtemp -= (double) TimeAdjustment * ppm_per_adjust_unit;
457:
458:
459: /*
460: * If a leap second is pending then determine the UTC time stamp
461: * of when the insertion must take place
462: */
463: if (leapsec > 0)
464: {
465: if ( ls_ft.ull == 0 ) /* time stamp has not yet been computed */
466: {
467: SYSTEMTIME st;
468:
469: GetSystemTime(&st);
470:
471: /*
472: * Accept leap announcement only 1 month in advance,
473: * for end of March, June, September, or December.
474: */
475: if ( ( st.wMonth % 3 ) == 0 )
476: {
477: /*
478: * The comparison time stamp is computed according
479: * to 0:00h UTC of the following day
480: */
481: if ( ++st.wMonth > 12 )
482: {
483: st.wMonth -= 12;
484: st.wYear++;
485: }
486:
487: st.wDay = 1;
488: st.wHour = 0;
489: st.wMinute = 0;
490: st.wSecond = 0;
491: st.wMilliseconds = 0;
492:
493: SystemTimeToFileTime(&st, &ls_ft.ft);
494: msyslog(LOG_NOTICE,
495: "Detected positive leap second announcement "
496: "for %04d-%02d-%02d %02d:%02d:%02d UTC",
497: st.wYear, st.wMonth, st.wDay,
498: st.wHour, st.wMinute, st.wSecond);
499: }
500: }
501: }
502: else
503: {
504: if ( ls_ft.ull ) /* Leap second has been armed before */
505: {
506: /*
507: * Disarm leap second only if the leap second
508: * is not already in progress.
509: */
510: if ( !ls_time_adjustment )
511: {
512: ls_ft.ull = 0;
513: msyslog( LOG_NOTICE, "Leap second announcement disarmed" );
514: }
515: }
516: }
517:
518:
519: /*
520: * If the time stamp for the next leap second has been set
521: * then check if the leap second must be handled
522: */
523: if ( ls_ft.ull )
524: {
525: ULONGLONG this_perf_count;
526:
527: this_perf_count = perf_ctr();
528:
529: if ( ls_time_adjustment == 0 ) /* has not yet been scheduled */
530: {
531: FT_ULL curr_ft;
532:
533: GetSystemTimeAsFileTime(&curr_ft.ft);
534: if ( curr_ft.ull >= ls_ft.ull )
535: {
536: ls_time_adjustment = clockperiod / LS_CORR_INTV_SECS;
537: ls_ref_perf_cnt = this_perf_count;
538: ls_elapsed = 0;
539: msyslog(LOG_NOTICE, "Inserting positive leap second.");
540: }
541: }
542: else /* leap sec adjustment has been scheduled previously */
543: {
544: ls_elapsed = ( this_perf_count - ls_ref_perf_cnt )
545: * HECTONANOSECONDS / PerfCtrFreq;
546: }
547:
548: if ( ls_time_adjustment ) /* leap second adjustment is currently active */
549: {
550: if ( ls_elapsed > ( LS_CORR_INTV - LS_CORR_LIMIT ) )
551: {
552: ls_time_adjustment = 0; /* leap second adjustment done */
553: ls_ft.ull = 0;
554: }
555:
556: /*
557: * NOTE: While the system time is slewed during the leap second
558: * the interpolation function which is based on the performance
559: * counter does not account for the slew.
560: */
561: TimeAdjustment -= ls_time_adjustment;
562: }
563: }
564:
565:
566: /* only adjust the clock if adjustment changes */
567: TimeAdjustment += wintickadj;
568: if (last_Adj != TimeAdjustment) {
569: last_Adj = TimeAdjustment;
570: DPRINTF(1, ("SetSystemTimeAdjustment(%+ld)\n", TimeAdjustment));
571: rc = !SetSystemTimeAdjustment(clockperiod + TimeAdjustment, FALSE);
572: }
573: else rc = 0;
574: if (rc)
575: {
576: msyslog(LOG_ERR, "Can't adjust time: %m");
577: return 0;
578: }
579: else {
580: sys_residual = dtemp / 1e6;
581: }
582:
583: DPRINTF(6, ("adj_systime: adj %.9f -> remaining residual %.9f\n",
584: now, sys_residual));
585:
586: return 1;
587: }
588:
589:
590: void
591: init_winnt_time(void)
592: {
593: static const char settod[] = "settimeofday=\"SetSystemTime\"";
594: char szMsgPath[MAX_PATH+1];
595: HANDLE hToken = INVALID_HANDLE_VALUE;
596: TOKEN_PRIVILEGES tkp;
597: TIMECAPS tc;
598: BOOL noslew;
599: DWORD adjclockperiod;
600: LARGE_INTEGER Freq;
601: FT_ULL initial_hectonanosecs;
602: FT_ULL next_hectonanosecs;
603: double adjppm;
604: double rawadj;
605: char * pch;
606:
607: if (winnt_time_initialized)
608: return;
609:
610: /*
611: * Make sure the service is initialized
612: * before we do anything else
613: */
614: ntservice_init();
615:
616: /* Set up the Console Handler */
617: if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE))
618: {
619: msyslog(LOG_ERR, "Can't set console control handler: %m");
620: }
621:
622: /* Set the Event-ID message-file name. */
623: if (!GetModuleFileName(NULL, szMsgPath, sizeof(szMsgPath)))
624: {
625: msyslog(LOG_ERR, "GetModuleFileName(PGM_EXE_FILE) failed: %m\n");
626: exit(1);
627: }
628:
629: /* Initialize random file before OpenSSL checks */
630: if (!init_randfile())
631: msyslog(LOG_ERR, "Unable to initialize .rnd file\n");
632:
633: #pragma warning(push)
634: #pragma warning(disable: 4127) /* conditional expression is constant */
635:
636: #ifdef DEBUG
637: if (SIZEOF_TIME_T != sizeof(time_t)
638: || SIZEOF_INT != sizeof(int)
639: || SIZEOF_SIGNED_CHAR != sizeof(char)) {
640:
641: msyslog(LOG_ERR, "config.h SIZEOF_* macros wrong, fatal");
642: exit(1);
643: }
644: #endif
645:
646: #pragma warning(pop)
647:
648: /*
649: * Get privileges needed for fiddling with the clock
650: */
651:
652: /* get the current process token handle */
653: if (!OpenProcessToken(GetCurrentProcess(),
654: TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
655: {
656: msyslog(LOG_ERR, "OpenProcessToken failed: %m");
657: exit(-1);
658: }
659: /* get the LUID for system-time privilege. */
660: LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
661: tkp.PrivilegeCount = 1; /* one privilege to set */
662: tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
663:
664: /* get set-time privilege for this process. */
665: AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
666: (PTOKEN_PRIVILEGES) NULL, 0);
667:
668: /* cannot use return value of AdjustTokenPrivileges. */
669: /* (success does not indicate all privileges were set) */
670: if (GetLastError() != ERROR_SUCCESS)
671: {
672: msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
673: /* later set time call will probably fail */
674: }
675:
676: CloseHandle(hToken);
677: hToken = INVALID_HANDLE_VALUE;
678:
679: /*
680: * Say how we're setting the time of day
681: */
682: set_sys_var(settod, sizeof(settod), RO);
683:
684: /*
685: * ntpd on Windows has always raised its priority, without
686: * requiring -N as on Unix. Since Windows ntpd doesn't share
687: * the history of unix ntpd of once having no -N and therefore
688: * needing to be invoked under nice, there is no reason to
689: * bring it in line with the Unix version in this regard.
690: * Instsrv assumes ntpd is invoked with no arguments, and
691: * upgrading users would be negatively surprised by the
692: * poor timekeeping if they failed to add -N as part of
693: * upgrading were we to correct this platform difference.
694: */
695: if (-1 == setpriority(PRIO_PROCESS, 0, NTP_PRIO))
696: exit(-1);
697:
698: /*
699: * register with libntp ntp_set_tod() to call us back
700: * when time is stepped.
701: */
702: step_callback = time_stepped;
703:
704: /*
705: * before we start looking at clock period, do any multimedia
706: * timer manipulation requested via -M option.
707: */
708: if (modify_mm_timer) {
709:
710: if (timeGetDevCaps(&tc, sizeof(tc)) == TIMERR_NOERROR) {
711:
712: wTimerRes = min(max(tc.wPeriodMin, MM_TIMER_INTV), tc.wPeriodMax);
713: timeBeginPeriod(wTimerRes);
714: atexit(atexit_revert_mm_timer);
715:
716: msyslog(LOG_INFO, "MM timer resolution: %u..%u msec, set to %u msec",
717: tc.wPeriodMin, tc.wPeriodMax, wTimerRes );
718: } else
719: msyslog(LOG_ERR, "Multimedia timer unavailable");
720: }
721:
722: /* get the performance counter ticks per second */
723: if (!QueryPerformanceFrequency(&Freq) || !Freq.QuadPart)
724: {
725: msyslog(LOG_ERR, "QueryPerformanceFrequency failed: %m\n");
726: exit(-1);
727: }
728:
729: NomPerfCtrFreq = PerfCtrFreq = Freq.QuadPart;
730: /*
731: * the cast to LONGLONG is for VC6 compatibility:
732: * nt_clockstuff.c(586) : error C2520: conversion from
733: * unsigned __int64 to double not implemented, use signed
734: * __int64
735: */
736: msyslog(LOG_INFO,
737: "Performance counter frequency %.3f MHz",
738: (LONGLONG)PerfCtrFreq / 1e6);
739:
740: /* Determine the existing system time slewing */
741: if (!GetSystemTimeAdjustment(&adjclockperiod, &clockperiod, &noslew))
742: {
743: msyslog(LOG_ERR, "GetSystemTimeAdjustment failed: %m\n");
744: exit(-1);
745: }
746:
747: /*
748: * If there is no slewing before ntpd, adjclockperiod and clockperiod
749: * will be equal. Any difference is carried into adj_systime's first
750: * pass as the previous adjustment.
751: */
752: last_Adj = adjclockperiod - clockperiod;
753:
754: if (last_Adj)
755: msyslog(LOG_INFO,
756: "Clock interrupt period %.3f msec "
757: "(startup slew %.1f usec/period)",
758: clockperiod / 1e4,
759: last_Adj / 10.);
760: else
761: msyslog(LOG_INFO,
762: "Clock interrupt period %.3f msec",
763: clockperiod / 1e4);
764:
765: /*
766: * Calculate the time adjustment resulting from incrementing
767: * units per tick by 1 unit for 1 second
768: */
769: ppm_per_adjust_unit = 1e6 / clockperiod;
770:
771: /*
772: * Spin on GetSystemTimeAsFileTime to determine its
773: * granularity. Prior to Windows Vista this is
774: * typically the same as the clock period.
775: */
776: GetSystemTimeAsFileTime(&initial_hectonanosecs.ft);
777: do {
778: GetSystemTimeAsFileTime(&next_hectonanosecs.ft);
779: } while (initial_hectonanosecs.ull == next_hectonanosecs.ull);
780:
781: os_clock_precision = next_hectonanosecs.ull -
782: initial_hectonanosecs.ull;
783:
784: msyslog(LOG_INFO,
785: "Windows clock precision %.3f msec, min. slew %.3f ppm/s",
786: (LONGLONG)os_clock_precision / 1e4,
787: ppm_per_adjust_unit);
788:
789: pch = getenv("NTPD_TICKADJ_PPM");
790: if (pch != NULL && 1 == sscanf(pch, "%lf", &adjppm)) {
791: rawadj = adjppm / ppm_per_adjust_unit;
792: rawadj += (rawadj < 0)
793: ? -0.5
794: : 0.5;
795: wintickadj = (long)rawadj;
796: msyslog(LOG_INFO,
797: "Using NTPD_TICKADJ_PPM %+g ppm (%+ld)",
798: adjppm, wintickadj);
799: }
800:
801: winnt_time_initialized = TRUE;
802:
803: choose_interp_counter();
804:
805: if (getenv("NTPD_USE_SYSTEM_CLOCK") ||
806: (os_clock_precision < 4 * 10000 &&
807: !getenv("NTPD_USE_INTERP_DANGEROUS"))) {
808: msyslog(LOG_INFO, "using Windows clock directly");
809: } else {
810: winnt_use_interpolation = TRUE;
811: get_sys_time_as_filetime = GetInterpTimeAsFileTime;
812: StartClockThread();
813: }
814: }
815:
816:
817: void
818: atexit_revert_mm_timer(void)
819: {
820: timeEndPeriod(wTimerRes);
821: DPRINTF(1, ("MM timer resolution reset\n"));
822: }
823:
824:
825: void
826: reset_winnt_time(void)
827: {
828: SYSTEMTIME st;
829:
830: /*
831: * If we're in the 2-second slew right after a leap second,
832: * we don't want to continue that extreme slew, in that case
833: * disable our slewing and return clock discipline to the
834: * kernel. Similarly if we are not yet synchronized,
835: * our current slew may not be a good ongoing trim.
836: * Otherwise, our leave in place the last SetSystemTimeAdjustment
837: * as an ongoing frequency correction, better than nothing.
838: * TODO:
839: * Verify this will not call SetSystemTimeAdjustment if
840: * ntpd is running in ntpdate mode.
841: */
842: if (sys_leap == LEAP_NOTINSYNC || ls_time_adjustment)
843: SetSystemTimeAdjustment(0, TRUE);
844:
845: /*
846: * Read the current system time, and write it back to
847: * force CMOS update, only if we are exiting because
848: * the computer is shutting down and we are already
849: * synchronized.
850: */
851: if (ntservice_systemisshuttingdown() && sys_leap != LEAP_NOTINSYNC) {
852: GetSystemTime(&st);
853: SetSystemTime(&st);
854: NLOG(NLOG_SYSEVENT | NLOG_CLOCKINFO)
855: msyslog(LOG_NOTICE, "system is shutting down, CMOS time reset.");
856: }
857: }
858:
859:
860: /*
861: * GetSystemTimeAsFileTime() interface clone is used by getclock() in ntpd.
862: */
863:
864: void WINAPI
865: GetInterpTimeAsFileTime(
866: LPFILETIME pft
867: )
868: {
869: FT_ULL now_time;
870: FT_ULL now_count;
871:
872: /* Mark a mark ASAP. The latency to here should
873: * be reasonably deterministic
874: */
875:
876: now_count.ull = perf_ctr();
877: now_time.ull = interp_time(now_count.ull, TRUE);
878:
879: if (last_interp_time > now_time.ull) {
880:
881: clock_backward_count++;
882: if (last_interp_time - now_time.ull > clock_backward_max)
883: clock_backward_max = last_interp_time - now_time.ull;
884: now_time.ull = last_interp_time;
885: } else
886: last_interp_time = now_time.ull;
887:
888: *pft = now_time.ft;
889: return;
890: }
891:
892:
893: /*
894: * TimerApcFunction is invoked on the high-priority clock
895: * thread to capture a new baseline system time and
896: * performance counter correlation every 43 msec (64Hz
897: * OS clock precision).
898: */
899: static void CALLBACK
900: TimerApcFunction(
901: LPVOID lpArgToCompletionRoutine,
902: DWORD dwTimerLowValue,
903: DWORD dwTimerHighValue
904: )
905: {
906: static BOOL ctr_freq_timer_started = FALSE;
907: static ULONGLONG prev_count;
908: ULONGLONG now_time;
909: FT_ULL now_count;
910:
911: /* Grab the counter first of all */
912: now_count.ull = perf_ctr();
913:
914: now_time = (((ULONGLONG)dwTimerHighValue << 32) |
915: dwTimerLowValue);
916:
917: /*
918: * Save this correlation in the history.
919: */
920: add_counter_time_pair(now_count.ull, now_time);
921:
922: /*
923: * Once we're synchronized start the counter frequency
924: * tuning timer.
925: */
926: if (INVALID_HANDLE_VALUE == ctr_freq_timer &&
927: LEAP_NOTINSYNC != sys_leap)
928:
929: start_ctr_freq_timer(now_time);
930: }
931:
932:
933: unsigned WINAPI
934: ClockThread(
935: void *arg
936: )
937: {
938: LARGE_INTEGER DueTime;
939: HANDLE timer;
940: double HZ;
941: double TimerHz;
942: DWORD timer_period_msec;
943: DWORD res;
944: char *ntpd_int_int_text;
945:
946: UNUSED_ARG(arg);
947:
948: timer = CreateWaitableTimer(NULL, FALSE, NULL);
949:
950: ntpd_int_int_text = getenv("NTPD_INT_INT");
951:
952: HZ = (double)HECTONANOSECONDS / clockperiod;
953:
954: if (HZ > 63 && HZ < 65) {
955: timer_period_msec = 43;
956: } else if (HZ > 98 && HZ < 102) {
957: timer_period_msec = 27;
958: if (!ntpd_int_int_text)
959: msyslog(LOG_WARNING,
960: "%.3f Hz system clock may benefit from "
961: "custom NTPD_INT_INT env var timer interval "
962: "override between approx. 20 and 50 msecs.",
963: HZ);
964: } else {
965: timer_period_msec = (DWORD)(0.5 + (2.752 * clockperiod / 10000));
966: if (!ntpd_int_int_text)
967: msyslog(LOG_WARNING,
968: "unfamiliar %.3f Hz system clock may benefit "
969: "from custom NTPD_INT_INT env var timer "
970: "interval override between approx. 20 and 50 "
971: "msecs.",
972: HZ);
973: }
974:
975: if (ntpd_int_int_text) {
976: timer_period_msec = atoi(ntpd_int_int_text);
977: timer_period_msec = max(9, timer_period_msec);
978: msyslog(LOG_NOTICE,
979: "using NTPD_INT_INT env var override %u",
980: timer_period_msec);
981: }
982:
983: TimerHz = 1e3 / timer_period_msec;
984: msyslog(LOG_NOTICE, "HZ %.3f using %u msec timer %.3f Hz %d deep",
985: HZ,
986: timer_period_msec,
987: TimerHz,
988: BASELINES_USED);
989:
990: /* negative DueTime means relative to now */
991: DueTime.QuadPart = -(int)timer_period_msec;
992:
993: SetWaitableTimer(
994: timer,
995: &DueTime, /* first fire */
996: timer_period_msec, /* period thereafter */
997: TimerApcFunction, /* callback routine */
998: &timer, /* context for callback */
999: FALSE); /* do not interfere with power saving */
1000:
1001: /*
1002: * The clock thread spends the rest of its life in the TimerApcFunction
1003: * and ctr_freq_timer_fired timer APC callbacks, which can only occur
1004: * while this thread is in an alertable wait. Note the Ex on
1005: * WaitForSingleObjectEx and TRUE for fAlertable. The wait will return
1006: * after each APC callback in which case we simply wait again. We will
1007: * break out of the loop when StopClockThread signals our exit event.
1008: */
1009: do res = WaitForSingleObjectEx(
1010: TimerThreadExitRequest,
1011: INFINITE,
1012: TRUE);
1013: while (WAIT_OBJECT_0 != res);
1014:
1015: CloseHandle(timer);
1016:
1017: if (ctr_freq_timer != INVALID_HANDLE_VALUE) {
1018: CloseHandle(ctr_freq_timer);
1019: ctr_freq_timer = INVALID_HANDLE_VALUE;
1020: }
1021:
1022: return 0;
1023: }
1024:
1025:
1026: static void
1027: StartClockThread(void)
1028: {
1029: static BOOL done_once = FALSE;
1030: FT_ULL StartTime;
1031:
1032: /* init variables with the time now */
1033: GetSystemTimeAsFileTime(&StartTime.ft);
1034: baseline_times[0] = StartTime.ull;
1035: baseline_counts[0] = perf_ctr();
1036:
1037: /* init sync objects */
1038: TimerThreadExitRequest = CreateEvent(NULL, FALSE, FALSE, NULL);
1039:
1040: clock_thread =
1041: (HANDLE)_beginthreadex(
1042: NULL,
1043: 0,
1044: ClockThread,
1045: NULL,
1046: CREATE_SUSPENDED,
1047: &clock_thread_id);
1048:
1049: if (clock_thread != NULL) {
1050: /* remember the thread priority is only within the process class */
1051: if (!SetThreadPriority(clock_thread, THREAD_PRIORITY_TIME_CRITICAL)) {
1052: DPRINTF(1, ("Error setting thread priority\n"));
1053: }
1054:
1055: lock_thread_to_processor(clock_thread);
1056: ResumeThread(clock_thread);
1057:
1058: if (FALSE == done_once) {
1059: done_once = TRUE;
1060: lock_thread_to_processor(GetCurrentThread());
1061: atexit( StopClockThread );
1062: }
1063:
1064: /*
1065: * Give the clock thread time to fill its counter/time
1066: * sample buffer. This will underfill the buffer a
1067: * bit for sample periods over 43 msec.
1068: */
1069: Sleep(BASELINES_USED * 43);
1070: }
1071: }
1072:
1073:
1074: void
1075: StopClockThread(void)
1076: {
1077: /*
1078: * if the clock thread exit()s this routine
1079: * will be called on the clock thread and
1080: * we need not (and can't) use the normal
1081: * TimerThreadExitRequest event.
1082: */
1083: if (GetCurrentThreadId() != clock_thread_id) {
1084:
1085: if (!SetEvent(TimerThreadExitRequest) ||
1086: WaitForSingleObject(clock_thread, 2 * 1000) !=
1087: WAIT_OBJECT_0) {
1088: msyslog(LOG_ERR, "Failed to stop clock thread.");
1089: }
1090: }
1091: CloseHandle(TimerThreadExitRequest);
1092: TimerThreadExitRequest = NULL;
1093: CloseHandle(clock_thread);
1094: clock_thread = NULL;
1095: }
1096:
1097:
1098: void
1099: lock_thread_to_processor(HANDLE thread)
1100: {
1101: static DWORD_PTR ProcessAffinityMask;
1102: static DWORD_PTR ThreadAffinityMask;
1103: DWORD_PTR SystemAffinityMask;
1104: char *cputext;
1105: unsigned int cpu;
1106:
1107: if ( ! winnt_time_initialized) {
1108: DPRINTF(1, ("init_winnt_time() must be called before "
1109: "lock_thread_to_processor(), exiting\n"));
1110: exit(-1);
1111: }
1112:
1113: if ( ! winnt_use_interpolation)
1114: return;
1115:
1116: if (-1 == lock_interp_threads) {
1117: DPRINTF(1, ("choose_interp_counter() is not called "
1118: "before lock_thread_to_processor()\n"));
1119: exit(-1);
1120: } else if (!lock_interp_threads)
1121: return;
1122:
1123: /*
1124: * Calculate the ThreadAffinityMask we'll use once on the
1125: * first invocation.
1126: */
1127: if ( ! ProcessAffinityMask) {
1128:
1129: /*
1130: * Choose which processor to nail the main and clock threads to.
1131: * If we have more than one, we simply choose the 2nd.
1132: * Randomly choosing from 2 to n would be better, but in
1133: * either case with clock and network interrupts more likely
1134: * to be serviced by the first procecssor, let's stay away
1135: * from it. QueryPerformanceCounter is not necessarily
1136: * consistent across CPUs, hence the need to nail the two
1137: * threads involved in QPC-based interpolation to the same
1138: * CPU.
1139: */
1140:
1141: GetProcessAffinityMask(
1142: GetCurrentProcess(),
1143: &ProcessAffinityMask,
1144: &SystemAffinityMask);
1145:
1146: /*
1147: * respect NTPD_CPU environment variable if present
1148: * for testing. NTPD_CPU=0 means use all CPUs, 1-64
1149: * means lock threads involved in interpolation to
1150: * that CPU. Default to 2nd if more than 1.
1151: */
1152:
1153: cpu = 2;
1154: cputext = getenv("NTPD_CPU");
1155: if (cputext) {
1156: cpu = (unsigned int) atoi(cputext);
1157: cpu = min((8 * sizeof(DWORD_PTR)), cpu);
1158: }
1159:
1160: /*
1161: * Clear all bits except the 2nd. If we have only one proc
1162: * that leaves ThreadAffinityMask zeroed and we won't bother
1163: * with SetThreadAffinityMask.
1164: */
1165:
1166: ThreadAffinityMask = (0 == cpu) ? 0 : (1 << (cpu - 1));
1167:
1168: if (ThreadAffinityMask &&
1169: !(ThreadAffinityMask & ProcessAffinityMask))
1170:
1171: DPRINTF(1, ("Selected CPU %u (mask %x) is outside "
1172: "process mask %x, using all CPUs.\n",
1173: cpu, ThreadAffinityMask,
1174: ProcessAffinityMask));
1175: else
1176: DPRINTF(1, ("Wiring to processor %u (0 means all) "
1177: "affinity mask %x\n",
1178: cpu, ThreadAffinityMask));
1179:
1180: ThreadAffinityMask &= ProcessAffinityMask;
1181: }
1182:
1183: if (ThreadAffinityMask &&
1184: !SetThreadAffinityMask(thread, ThreadAffinityMask))
1185:
1186: msyslog(LOG_ERR,
1187: "Unable to wire thread to mask %x: %m\n",
1188: ThreadAffinityMask);
1189: }
1190:
1191:
1192: #ifdef HAVE_PPSAPI
1193: /*
1194: * helper routine for serial PPS which returns QueryPerformanceCounter
1195: * timestamp and needs to interpolate it to an NTP timestamp.
1196: */
1197: void
1198: pps_ntp_timestamp_from_counter(
1199: ntp_fp_t *result,
1200: ULONGLONG Timestamp,
1201: ULONGLONG Counterstamp
1202: )
1203: {
1204: /*
1205: * convert between equivalent l_fp and PPSAPI ntp_fp_t
1206: */
1207: ntp_timestamp_from_counter(
1208: (l_fp *)result,
1209: Timestamp,
1210: Counterstamp);
1211: }
1212:
1213:
1214: void
1215: ntp_timestamp_from_counter(
1216: l_fp *result,
1217: ULONGLONG Timestamp,
1218: ULONGLONG Counterstamp
1219: )
1220: {
1221: #ifdef DEBUG
1222: FT_ULL Now;
1223: #endif
1224: ULONGLONG InterpTimestamp;
1225:
1226: if (winnt_use_interpolation) {
1227: InterpTimestamp = interp_time(Counterstamp + QPC_offset, FALSE);
1228:
1229: #ifdef DEBUG
1230: /* sanity check timestamp is within 1 minute of now */
1231: GetSystemTimeAsFileTime(&Now.ft);
1232: Now.ll -= InterpTimestamp;
1233: if (debug &&
1234: Now.ll > 60 * HECTONANOSECONDS ||
1235: Now.ll < -60 * (LONGLONG) HECTONANOSECONDS) {
1236: DPRINTF(1, ("ntp_timestamp_from_counter interpolated time %.6fs from current\n",
1237: Now.ll / (double)LL_HNS));
1238: DPRINTF(1, ("interpol time %llx from %llx\n",
1239: InterpTimestamp,
1240: Counterstamp));
1241: msyslog(LOG_ERR,
1242: "ntp_timestamp_from_counter interpolated time %.6fs from current\n",
1243: Now.ll / (double)LL_HNS);
1244: exit(-1);
1245: }
1246: #endif
1247: } else { /* ! winnt_use_interpolation */
1248: /* have to simply use the driver's system time timestamp */
1249: InterpTimestamp = Timestamp;
1250: #ifdef DEBUG
1251: /* sanity check timestamp is within 1 minute of now */
1252: GetSystemTimeAsFileTime(&Now.ft);
1253: Now.ll -= InterpTimestamp;
1254: if (Now.ll > 60 * HECTONANOSECONDS ||
1255: Now.ll < -60 * (LONGLONG) HECTONANOSECONDS) {
1256: DPRINTF(1, ("ntp_timestamp_from_counter serial driver system time %.6fs from current\n",
1257: Now.ll / (double)LL_HNS));
1258: msyslog(LOG_ERR,
1259: "ntp_timestamp_from_counter serial driver system time %.6fs from current\n",
1260: Now.ll / (double)LL_HNS);
1261: exit(-1);
1262: }
1263: #endif
1264: }
1265:
1266: /* convert from 100ns units to NTP fixed point format */
1267:
1268: InterpTimestamp -= FILETIME_1970;
1269: result->l_ui = JAN_1970 + (u_int32)(InterpTimestamp / HECTONANOSECONDS);
1270: result->l_uf = (u_int32)((InterpTimestamp % HECTONANOSECONDS) *
1271: (ULONGLONG)FRAC / HECTONANOSECONDS);
1272: }
1273: #endif /* HAVE_PPSAPI */
1274:
1275:
1276: void
1277: time_stepped(void)
1278: {
1279: /*
1280: * called back by ntp_set_tod after the system
1281: * time has been stepped (set).
1282: *
1283: * We normally prevent the reported time from going backwards
1284: * but need to allow it in this case.
1285: */
1286: if (FALSE == winnt_use_interpolation)
1287: return;
1288:
1289:
1290: /*
1291: * Restart the clock thread to get a new baseline
1292: * time/counter correlation.
1293: */
1294: StopClockThread();
1295:
1296: /*
1297: * newest_baseline_gen is a generation counter
1298: * incremented once each time newest_baseline
1299: * is reset.
1300: */
1301: newest_baseline_gen++;
1302:
1303: last_interp_time =
1304: clock_backward_max =
1305: clock_backward_count =
1306: newest_baseline = 0;
1307:
1308: memset(baseline_counts, 0, sizeof(baseline_counts));
1309: memset(baseline_times, 0, sizeof(baseline_times));
1310:
1311: StartClockThread();
1312: }
1313:
1314:
1315: /*
1316: * log2ull - log base 2 of a unsigned 64-bit number
1317: */
1318: int
1319: log2ull(
1320: ULONGLONG n
1321: )
1322: {
1323: const ULONGLONG one = 1;
1324: int log = 0;
1325:
1326: if (n >= one<<32) { n >>= 32; log += 32; }
1327: if (n >= one<<16) { n >>= 16; log += 16; }
1328: if (n >= one<< 8) { n >>= 8; log += 8; }
1329: if (n >= one<< 4) { n >>= 4; log += 4; }
1330: if (n >= one<< 2) { n >>= 2; log += 2; }
1331: if (n >= one<< 1) { log += 1; }
1332:
1333: return (n) ? log : (-1);
1334: }
1335:
1336:
1337: /*
1338: * ctr_freq_timer_fired is called once a few seconds before
1339: * tune_ctr_period seconds have elapsed, to reset the timer
1340: * and hopefully minimize error due to the system using the
1341: * nominal performance counter frequency to set the timer
1342: * internally, which is typically dozens of PPM from the
1343: * actual performance counter rate. A few seconds later
1344: * it is called again to observe the counter and estimate the
1345: * counter frequency.
1346: */
1347: static void CALLBACK
1348: ctr_freq_timer_fired(
1349: LPVOID arg,
1350: DWORD dwTimeLow,
1351: DWORD dwTimeHigh
1352: )
1353: {
1354: static FT_ULL begin_time = {0};
1355: static FT_ULL begin_count = {0};
1356: static ULONGLONG next_period_time = 0;
1357: static ULONGLONG report_systemtime = 0;
1358: const ULONGLONG five_minutes = 5ui64 * 60 * HECTONANOSECONDS;
1359: FT_ULL now_time;
1360: FT_ULL now_count;
1361:
1362: if (!begin_time.ull) {
1363: begin_count.ull = perf_ctr();
1364: begin_time.ft.dwLowDateTime = dwTimeLow;
1365: begin_time.ft.dwHighDateTime = dwTimeHigh;
1366:
1367: /*
1368: * adapt perf ctr observation interval to the
1369: * counter frequency
1370: */
1371: tune_ctr_period = 22680 / log2ull(NomPerfCtrFreq);
1372:
1373: /*
1374: * reset timer 2s before period ends to minimize
1375: * error from OS timer routines using nominal
1376: * performance frequency internally.
1377: */
1378: tune_ctr_freq_max_interval = tune_ctr_period - 2;
1379:
1380: next_period_time = begin_time.ull +
1381: (ULONGLONG)tune_ctr_period * HECTONANOSECONDS;
1382:
1383: ROUND_TO_NEXT_SEC_BOTTOM(next_period_time);
1384:
1385: reset_ctr_freq_timer(next_period_time, begin_time.ull);
1386:
1387: return;
1388: }
1389:
1390: now_time.ft.dwLowDateTime = dwTimeLow;
1391: now_time.ft.dwHighDateTime = dwTimeHigh;
1392:
1393: if (now_time.ull >= next_period_time) {
1394:
1395: now_count.ull = perf_ctr();
1396: tune_ctr_freq(
1397: now_count.ull - begin_count.ull,
1398: now_time.ull - begin_time.ull);
1399:
1400: next_period_time += (ULONGLONG)tune_ctr_period * HECTONANOSECONDS;
1401: begin_count.ull = now_count.ull;
1402: begin_time.ull = now_time.ull;
1403: }
1404:
1405: /*
1406: * Log clock backward events no more often than 5 minutes.
1407: */
1408: if (!report_systemtime)
1409:
1410: report_systemtime = now_time.ull + five_minutes;
1411:
1412: else if (report_systemtime <= now_time.ull) {
1413:
1414: report_systemtime += five_minutes;
1415:
1416: if (clock_backward_count) {
1417: msyslog(LOG_WARNING,
1418: "clock would have gone backward %d times, "
1419: "max %.1f usec",
1420: clock_backward_count,
1421: (LONGLONG)clock_backward_max / 10.);
1422:
1423: clock_backward_max = clock_backward_count = 0;
1424: }
1425: }
1426:
1427: reset_ctr_freq_timer(next_period_time, now_time.ull);
1428: }
1429:
1430:
1431: void
1432: reset_ctr_freq_timer_abs(
1433: ULONGLONG when
1434: )
1435: {
1436: FT_ULL fire_time;
1437:
1438: fire_time.ull = when;
1439:
1440: SetWaitableTimer(
1441: ctr_freq_timer,
1442: &fire_time.li, /* first fire */
1443: 0, /* not periodic */
1444: ctr_freq_timer_fired, /* callback routine */
1445: NULL, /* context for callback */
1446: FALSE); /* do not interfere with power saving */
1447: }
1448:
1449:
1450: void
1451: reset_ctr_freq_timer(
1452: ULONGLONG when,
1453: ULONGLONG now
1454: )
1455: {
1456: if (when - now >
1457: (tune_ctr_freq_max_interval * HECTONANOSECONDS + HECTONANOSECONDS))
1458:
1459: when = now + tune_ctr_freq_max_interval * HECTONANOSECONDS;
1460:
1461: reset_ctr_freq_timer_abs(when);
1462: }
1463:
1464:
1465: void
1466: start_ctr_freq_timer(
1467: ULONGLONG now_time
1468: )
1469: {
1470: ULONGLONG when;
1471:
1472: ctr_freq_timer = CreateWaitableTimer(NULL, FALSE, NULL);
1473:
1474: when = now_time;
1475: ROUND_TO_NEXT_SEC_BOTTOM(when);
1476:
1477: reset_ctr_freq_timer_abs(when);
1478: }
1479:
1480:
1481: /*
1482: * tune_ctr_freq is called once per tune_ctr_period seconds
1483: * with a counter difference and time difference.
1484: */
1485: void
1486: tune_ctr_freq(
1487: LONGLONG ctr_delta,
1488: LONGLONG time_delta
1489: )
1490: {
1491: static unsigned count = 0;
1492: static unsigned dispcount = 0;
1493: static unsigned report_at_count = 0;
1494: static int disbelieved = 0;
1495: static int i = 0;
1496: static double nom_freq = 0;
1497: static LONGLONG diffs[TUNE_CTR_DEPTH] = {0};
1498: static LONGLONG sum = 0;
1499:
1500: LONGLONG delta;
1501: LONGLONG deltadiff;
1502: ULONGLONG ObsPerfCtrFreq;
1503: double obs_freq;
1504: double this_freq;
1505: int isneg;
1506:
1507: /* one-time initialization */
1508: if (!report_at_count) {
1509: report_at_count = 24 * 60 * 60 / tune_ctr_period;
1510: nom_freq = (LONGLONG)NomPerfCtrFreq / 1e6;
1511: }
1512:
1513: /* delta is the per-second observed frequency this time */
1514: delta = (LONGLONG)((double)ctr_delta * HECTONANOSECONDS /
1515: time_delta);
1516:
1517: /* disbelieve any delta more than +/- 976 PPM from nominal */
1518: deltadiff = delta - NomPerfCtrFreq;
1519: if (0 > deltadiff) {
1520: isneg = 1;
1521: deltadiff = -deltadiff;
1522: } else
1523: isneg = 0;
1524:
1525: if ((ULONGLONG)deltadiff > (NomPerfCtrFreq / 1024)) {
1526: disbelieved++;
1527: dispcount++;
1528: #ifdef DEBUG
1529: msyslog(LOG_DEBUG, "ctr delta %s%lld exceeds limit %llu",
1530: (isneg) ? "-" : "",
1531: deltadiff,
1532: NomPerfCtrFreq / 1024);
1533: #endif
1534: } else {
1535:
1536: /*
1537: * collect average over TUNE_CTR_DEPTH samples
1538: * for our PerfCtrFreq trimming.
1539: */
1540:
1541: if (isneg)
1542: deltadiff = -deltadiff;
1543: sum -= diffs[i];
1544: diffs[i] = deltadiff;
1545: sum += deltadiff;
1546: i = (i + 1) % COUNTOF(diffs);
1547: count++;
1548: dispcount++;
1549: }
1550:
1551: this_freq = delta / 1e6;
1552:
1553: ObsPerfCtrFreq = NomPerfCtrFreq + (sum / COUNTOF(diffs));
1554:
1555: #if 1 /* #if 0 to disable changing freq used */
1556: /* get rid of ObsPerfCtrFreq when removing the #ifdef */
1557: PerfCtrFreq = ObsPerfCtrFreq;
1558: #endif
1559: obs_freq = (LONGLONG)ObsPerfCtrFreq / 1e6;
1560:
1561: /*
1562: * report observed ctr freq each time the estimate used during
1563: * startup moves toward the observed freq from the nominal,
1564: * and once a day afterward.
1565: */
1566:
1567: if (count > COUNTOF(diffs) &&
1568: /* (count % COUNTOF(diffs)) && */ /* enables reporting each */
1569: dispcount < report_at_count) /* TUNE_CTR_DEPTH samples */
1570: return;
1571:
1572: NLOG(NLOG_CLOCKINFO)
1573: if (count <= COUNTOF(diffs))
1574: /* moving to observed freq. from nominal (startup) */
1575: msyslog(LOG_INFO,
1576: (obs_freq > 100)
1577: ? "ctr %.3f MHz %+6.2f PPM using "
1578: "%.3f MHz %+6.2f PPM"
1579: : "ctr %.6f MHz %+6.2f PPM using "
1580: "%.6f MHz %+6.2f PPM",
1581: this_freq,
1582: 1e6 * (this_freq - nom_freq) / nom_freq,
1583: obs_freq,
1584: 1e6 * (obs_freq - nom_freq) / nom_freq);
1585: else
1586: /* steady state */
1587: msyslog(LOG_INFO,
1588: (obs_freq > 100)
1589: ? "ctr %.3f MHz %+.2f PPM"
1590: : "ctr %.6f MHz %+.2f PPM",
1591: obs_freq,
1592: 1e6 * (obs_freq - nom_freq) / nom_freq);
1593:
1594: if (disbelieved) {
1595: msyslog(LOG_ERR,
1596: "%d ctr samples exceed +/- 976 PPM range gate",
1597: disbelieved);
1598: disbelieved = 0;
1599: }
1600:
1601: dispcount = 0;
1602: }
1603:
1604:
1605: /*
1606: * add_counter_time_pair is called by the
1607: * high priority clock thread with each new
1608: * baseline counter/time correlation.
1609: */
1610: void
1611: add_counter_time_pair(
1612: ULONGLONG ctr,
1613: LONGLONG time
1614: )
1615: {
1616: int i;
1617:
1618: i = (newest_baseline + 1) % BASELINES_TOT;
1619:
1620: baseline_counts[i] = ctr;
1621: baseline_times[i] = time;
1622:
1623: newest_baseline = i;
1624: }
1625:
1626:
1627: /*
1628: * interp_time estimates NT time in 100ns units
1629: * based on a performance counter value given.
1630: * This must tolerate recent historical counters
1631: * as well as current. When current is FALSE
1632: * we can't assume ctr is the latest/highest
1633: * seen.
1634: */
1635: ULONGLONG
1636: interp_time(
1637: ULONGLONG ctr,
1638: BOOL current
1639: )
1640: {
1641: static __declspec(thread) int last_newest = -1;
1642: static __declspec(thread) int last_newest_gen;
1643: static __declspec(thread) int best_index;
1644: ULONGLONG this_ctr;
1645: LONGLONG this_time;
1646: LONGLONG latest_time;
1647: LONGLONG ctr_diff;
1648: int i;
1649: int i_gen;
1650: int c;
1651:
1652: /*
1653: * Use the system time (roughly synchronised to the tick, and
1654: * extrapolated using the system performance counter.
1655: *
1656: * Cache the results per thread and only repeat the
1657: * calculation when new data has arrived.
1658: */
1659: i = newest_baseline;
1660: i_gen = newest_baseline_gen;
1661:
1662: if (last_newest == i && last_newest_gen == i_gen) {
1663: this_time = baseline_times[best_index];
1664: ctr_diff = ctr - baseline_counts[best_index];
1665: this_time += (LONGLONG)PERF2HNS((double)ctr_diff);
1666:
1667: return this_time;
1668: }
1669:
1670: last_newest = i;
1671: last_newest_gen = i_gen;
1672:
1673: latest_time = 0;
1674:
1675: /*
1676: * Run through the history calculating the interpolated
1677: * time based on each counter/time correlation in turn,
1678: * and believe the latest one. This is akin to the NTP
1679: * protocol minimum delay clock filter. Errors due to
1680: * counter/time correlations with stale time are all
1681: * negative.
1682: */
1683: for (c = 0; c < BASELINES_USED; c++) {
1684: if (baseline_times[i]) {
1685: this_time = baseline_times[i];
1686: this_ctr = baseline_counts[i];
1687:
1688: ctr_diff = ctr - this_ctr;
1689:
1690: if (current && ctr_diff < 0) {
1691: /*
1692: * The performance counter apparently went
1693: * backwards without rolling over. It might
1694: * be nice to complain but we don't want
1695: * to do it repeatedly.
1696: */
1697: ctr_diff = 0;
1698: }
1699:
1700: this_time += (LONGLONG)PERF2HNS((double)ctr_diff);
1701:
1702: if (this_time > latest_time) {
1703: latest_time = this_time;
1704: best_index = i;
1705: }
1706: }
1707: i = i ? (i - 1) : (BASELINES_TOT - 1);
1708: }
1709:
1710: return latest_time;
1711: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>