Annotation of embedaddon/ntp/ports/winnt/ntpd/nt_clockstuff.c, revision 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>