VirtualBox

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

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

Moved os2/VBoxService into common space.

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