VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp@ 17610

Last change on this file since 17610 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.9 KB
Line 
1/** $Id: VBoxServiceTimeSync.cpp 8155 2008-04-18 15:16:47Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions TimeSync Service.
4 */
5
6/*
7 * Copyright (C) 2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/** @page pg_vboxservice_timesync The Time Sync Service
24 *
25 * The time sync service plays along with the Time Manager (TM) in the VMM
26 * to keep the guest time accurate using the host machine as reference.
27 * TM will try its best to make sure all timer ticks gets delivered so that
28 * there isn't normally any need to adjust the guest time.
29 *
30 * There are three normal (= acceptable) cases:
31 * -# When the service starts up. This is because ticks and such might
32 * be lost during VM and OS startup. (Need to figure out exactly why!)
33 * -# When the TM is unable to deliver all the ticks and swallows a
34 * backlog of ticks. The threshold for this is configurable with
35 * a default of 60 seconds.
36 * -# The time is adjusted on the host. This can be caused manually by
37 * the user or by some time sync daemon (NTP, LAN server, etc.).
38 *
39 * There are a number of very odd case where adjusting is needed. Here
40 * are some of them:
41 * -# Timer device emulation inaccurancies (like rounding).
42 * -# Inaccurancies in time source VirtualBox uses.
43 * -# The Guest and/or Host OS doesn't perform proper time keeping. This
44 * come about as a result of OS and/or hardware issues.
45 *
46 * The TM is our source for the host time and will make adjustments for
47 * current timer delivery lag. The simplistic approach taken by TM is to
48 * adjust the host time by the current guest timer delivery lag, meaning that
49 * if the guest is behind 1 second with PIT/RTC/++ ticks this should be reflected
50 * in the guest wall time as well.
51 *
52 * Now, there is any amount of trouble we can cause by changing the time.
53 * Most applications probably uses the wall time when they need to measure
54 * things. A walltime that is being juggled about every so often, even if just
55 * a little bit, could occationally upset these measurements by for instance
56 * yielding negative results.
57 *
58 * This bottom line here is that the time sync service isn't really supposed
59 * to do anything and will try avoid having to do anything when possible.
60 *
61 * The implementation uses the latency it takes to query host time as the
62 * absolute maximum precision to avoid messing up under timer tick catchup
63 * and/or heavy host/guest load. (Rational is that a *lot* of stuff may happen
64 * on our way back from ring-3 and TM/VMMDev since we're taking the route
65 * thru the inner EM loop with it's force action processing.)
66 *
67 * But this latency has to be measured from our perspective, which means it
68 * could just as easily come out as 0. (OS/2 and Windows guest only updates
69 * the current time when the timer ticks for instance.) The good thing is
70 * that this isn't really a problem since we won't ever do anything unless
71 * the drift is noticable.
72 *
73 * It now boils down to these three (configuration) factors:
74 * -# g_TimesyncMinAdjust - The minimum drift we will ever bother with.
75 * -# g_TimesyncLatencyFactor - The factor we multiply the latency by to
76 * calculate the dynamic minimum adjust factor.
77 * -# g_TimesyncMaxLatency - When to start discarding the data as utterly
78 * useless and take a rest (someone is too busy to give us good data).
79 */
80
81
82
83/*******************************************************************************
84* Header Files *
85*******************************************************************************/
86#ifdef RT_OS_WINDOWS
87#else
88# include <unistd.h>
89# include <errno.h>
90# include <time.h>
91# include <sys/time.h>
92#endif
93
94#include <iprt/thread.h>
95#include <iprt/string.h>
96#include <iprt/semaphore.h>
97#include <iprt/time.h>
98#include <iprt/assert.h>
99#include <VBox/VBoxGuest.h>
100#include "VBoxServiceInternal.h"
101
102
103/*******************************************************************************
104* Global Variables *
105*******************************************************************************/
106/** The timesync interval (millseconds). */
107uint32_t g_TimeSyncInterval = 0;
108/**
109 * @see pg_vboxservice_timesync
110 *
111 * @remark OS/2: There is either a 1 second resolution on the DosSetDateTime
112 * API or a but in the settimeofday implementation. Thus, don't
113 * bother unless there is at least a 1 second drift.
114 */
115#ifdef RT_OS_OS2
116static uint32_t g_TimeSyncMinAdjust = 1000;
117#else
118static uint32_t g_TimeSyncMinAdjust = 100;
119#endif
120/** @see pg_vboxservice_timesync */
121static uint32_t g_TimeSyncLatencyFactor = 8;
122/** @see pg_vboxservice_timesync */
123static uint32_t g_TimeSyncMaxLatency = 250;
124
125/** The semaphore we're blocking on. */
126static RTSEMEVENTMULTI g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
127
128
129/** @copydoc VBOXSERVICE::pfnPreInit */
130static DECLCALLBACK(int) VBoxServiceTimeSyncPreInit(void)
131{
132 return VINF_SUCCESS;
133}
134
135
136/** @copydoc VBOXSERVICE::pfnOption */
137static DECLCALLBACK(int) VBoxServiceTimeSyncOption(const char **ppszShort, int argc, char **argv, int *pi)
138{
139 int rc = -1;
140 if (ppszShort)
141 /* no short options */;
142 else if (!strcmp(argv[*pi], "--timesync-interval"))
143 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
144 &g_TimeSyncInterval, 1, UINT32_MAX - 1);
145 else if (!strcmp(argv[*pi], "--timesync-min-adjust"))
146 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
147 &g_TimeSyncMinAdjust, 0, 3600000);
148 else if (!strcmp(argv[*pi], "--timesync-latency-factor"))
149 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
150 &g_TimeSyncLatencyFactor, 1, 1024);
151 else if (!strcmp(argv[*pi], "--timesync-max-latency"))
152 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
153 &g_TimeSyncMaxLatency, 1, 3600000);
154 return rc;
155}
156
157
158/** @copydoc VBOXSERVICE::pfnInit */
159static DECLCALLBACK(int) VBoxServiceTimeSyncInit(void)
160{
161 /*
162 * If not specified, find the right interval default.
163 * Then create the event sem to block on.
164 */
165 if (!g_TimeSyncInterval)
166 g_TimeSyncInterval = g_DefaultInterval * 1000;
167 if (!g_TimeSyncInterval)
168 g_TimeSyncInterval = 10 * 1000;
169
170 int rc = RTSemEventMultiCreate(&g_TimeSyncEvent);
171 AssertRC(rc);
172 return rc;
173}
174
175
176/** @copydoc VBOXSERVICE::pfnWorker */
177DECLCALLBACK(int) VBoxServiceTimeSyncWorker(bool volatile *pfShutdown)
178{
179 RTTIME Time;
180 char sz[64];
181 int rc = VINF_SUCCESS;
182
183 unsigned cErrors = 0;
184 for (;;)
185 {
186 /*
187 * Try get a reliable time reading.
188 */
189 int cTries = 3;
190 do
191 {
192 /* query it. */
193 RTTIMESPEC GuestNow0, GuestNow, HostNow;
194 RTTimeNow(&GuestNow0);
195 int rc2 = VbglR3GetHostTime(&HostNow);
196 if (RT_FAILURE(rc2))
197 {
198 if (cErrors++ < 10)
199 VBoxServiceError("VbglR3GetHostTime failed; rc2=%Rrc\n", rc2);
200 break;
201 }
202 RTTimeNow(&GuestNow);
203
204 /* calc latency and check if it's ok. */
205 RTTIMESPEC GuestElapsed = GuestNow;
206 RTTimeSpecSub(&GuestElapsed, &GuestNow0);
207 if ((uint32_t)RTTimeSpecGetMilli(&GuestElapsed) < g_TimeSyncMaxLatency)
208 {
209 /*
210 * Calculate the adjustment threshold and the current drift.
211 */
212 uint32_t MinAdjust = RTTimeSpecGetMilli(&GuestElapsed) * g_TimeSyncLatencyFactor;
213 if (MinAdjust < g_TimeSyncMinAdjust)
214 MinAdjust = g_TimeSyncMinAdjust;
215
216 RTTIMESPEC Drift = HostNow;
217 RTTimeSpecSub(&Drift, &GuestNow);
218 if (RTTimeSpecGetMilli(&Drift) < 0)
219 MinAdjust += g_TimeSyncMinAdjust; /* extra buffer against moving time backwards. */
220
221 RTTIMESPEC AbsDrift = Drift;
222 RTTimeSpecAbsolute(&AbsDrift);
223 if (g_cVerbosity >= 3)
224 {
225 VBoxServiceVerbose(3, "Host: %s (MinAdjust: %RU32 ms)\n",
226 RTTimeToString(RTTimeExplode(&Time, &HostNow), sz, sizeof(sz)), MinAdjust);
227 VBoxServiceVerbose(3, "Guest: - %s => %RDtimespec drift\n",
228 RTTimeToString(RTTimeExplode(&Time, &GuestNow), sz, sizeof(sz)),
229 &Drift);
230 }
231 if (RTTimeSpecGetMilli(&AbsDrift) > MinAdjust)
232 {
233 /*
234 * The drift is to big, we have to make adjustments. :-/
235 * If we've got adjtime around, try that first - most
236 * *NIX systems have it. Fall back on settimeofday.
237 */
238#ifdef RT_OS_WINDOWS
239 /* just make sure it compiles for now, but later:
240 SetSystemTimeAdjustment and fall back on SetSystemTime.
241 */
242 AssertFatalFailed();
243#else
244 struct timeval tv;
245# if !defined(RT_OS_OS2) /* PORTME */
246 RTTimeSpecGetTimeval(&Drift, &tv);
247 if (adjtime(&tv, NULL) == 0)
248 {
249 if (g_cVerbosity >= 1)
250 VBoxServiceVerbose(1, "adjtime by %RDtimespec\n", &Drift);
251 cErrors = 0;
252 }
253 else
254# endif
255 {
256 errno = 0;
257 if (!gettimeofday(&tv, NULL))
258 {
259 RTTIMESPEC Tmp;
260 RTTimeSpecAdd(RTTimeSpecSetTimeval(&Tmp, &tv), &Drift);
261 if (!settimeofday(RTTimeSpecGetTimeval(&Tmp, &tv), NULL))
262 {
263 if (g_cVerbosity >= 1)
264 VBoxServiceVerbose(1, "settimeofday to %s\n",
265 RTTimeToString(RTTimeExplode(&Time, &Tmp), sz, sizeof(sz)));
266# ifdef DEBUG
267 if (g_cVerbosity >= 3)
268 VBoxServiceVerbose(2, " new time %s\n",
269 RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&Tmp)), sz, sizeof(sz)));
270# endif
271 cErrors = 0;
272 }
273 else if (cErrors++ < 10)
274 VBoxServiceError("settimeofday failed; errno=%d: %s\n", errno, strerror(errno));
275 }
276 else if (cErrors++ < 10)
277 VBoxServiceError("gettimeofday failed; errno=%d: %s\n", errno, strerror(errno));
278 }
279#endif /* !RT_OS_WINDOWS */
280 }
281 break;
282 }
283 VBoxServiceVerbose(3, "%RDtimespec: latency too high (%RDtimespec) sleeping 1s\n", GuestElapsed);
284 RTThreadSleep(1000);
285 } while (--cTries > 0);
286
287 /*
288 * Block for a while.
289 *
290 * The event semaphore takes care of ignoring interruptions and it
291 * allows us to implement service wakeup later.
292 */
293 if (*pfShutdown)
294 break;
295 int rc2 = RTSemEventMultiWait(g_TimeSyncEvent, g_TimeSyncInterval);
296 if (*pfShutdown)
297 break;
298 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
299 {
300 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
301 rc = rc2;
302 break;
303 }
304 }
305
306 RTSemEventMultiDestroy(g_TimeSyncEvent);
307 g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
308 return rc;
309}
310
311
312/** @copydoc VBOXSERVICE::pfnStop */
313static DECLCALLBACK(void) VBoxServiceTimeSyncStop(void)
314{
315 RTSemEventMultiSignal(g_TimeSyncEvent);
316}
317
318
319/** @copydoc VBOXSERVICE::pfnTerm */
320static DECLCALLBACK(void) VBoxServiceTimeSyncTerm(void)
321{
322 RTSemEventMultiDestroy(g_TimeSyncEvent);
323 g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
324}
325
326
327/**
328 * The 'timesync' service description.
329 */
330VBOXSERVICE g_TimeSync =
331{
332 /* pszName. */
333 "timesync",
334 /* pszDescription. */
335 "Time synchronization",
336 /* pszUsage. */
337 "[--timesync-interval <ms>] [--timesync-min-adjust <ms>] "
338 "[--timesync-latency-factor <x>] [--time-sync-max-latency <ms>]"
339 ,
340 /* pszOptions. */
341 " --timesync-interval Specifies the interval at which to synchronize the\n"
342 " time with the host. The default is 10000 ms.\n"
343 " --timesync-min-adjust The minimum absolute drift drift value measured\n"
344 " in milliseconds to make adjustments for.\n"
345 " The default is 1000 ms on OS/2 and 100 ms elsewhere.\n"
346 " --timesync-latency-factor The factor to multiply the time query latency\n"
347 " with to calculate the dynamic minimum adjust time.\n"
348 " The default is 8 times.\n"
349 " --timesync-max-latency The max host timer query latency to accept.\n"
350 " The default is 250 ms.\n"
351 ,
352 /* methods */
353 VBoxServiceTimeSyncPreInit,
354 VBoxServiceTimeSyncOption,
355 VBoxServiceTimeSyncInit,
356 VBoxServiceTimeSyncWorker,
357 VBoxServiceTimeSyncStop,
358 VBoxServiceTimeSyncTerm
359};
360
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette