VirtualBox

source: vbox/trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceTimeSync.cpp@ 5999

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

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.4 KB
Line 
1/** $Id: VBoxServiceTimeSync.cpp 5999 2007-12-07 15:05:06Z 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#include <unistd.h>
83#include <errno.h>
84#include <time.h>
85#include <sys/time.h>
86
87#include <iprt/thread.h>
88#include <iprt/string.h>
89#include <iprt/semaphore.h>
90#include <iprt/time.h>
91#include <iprt/assert.h>
92#include <VBox/VBoxGuest.h>
93#include "VBoxServiceInternal.h"
94
95
96/*******************************************************************************
97* Global Variables *
98*******************************************************************************/
99/** The timesync interval (millseconds). */
100uint32_t g_TimeSyncInterval = 0;
101/**
102 * @see pg_vboxservice_timesync
103 *
104 * @remark OS/2: There is either a 1 second resolution on the DosSetDateTime
105 * API or a but in the settimeofday implementation. Thus, don't
106 * bother unless there is at least a 1 second drift.
107 */
108#ifdef RT_OS_OS2
109static uint32_t g_TimeSyncMinAdjust = 1000;
110#else
111static uint32_t g_TimeSyncMinAdjust = 100;
112#endif
113/** @see pg_vboxservice_timesync */
114static uint32_t g_TimeSyncLatencyFactor = 8;
115/** @see pg_vboxservice_timesync */
116static uint32_t g_TimeSyncMaxLatency = 250;
117
118/** The semaphore we're blocking on. */
119static RTSEMEVENTMULTI g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
120
121
122/** @copydoc VBOXSERVICE::pfnPreInit */
123static DECLCALLBACK(int) VBoxServiceTimeSyncPreInit(void)
124{
125 return VINF_SUCCESS;
126}
127
128
129/** @copydoc VBOXSERVICE::pfnOption */
130static DECLCALLBACK(int) VBoxServiceTimeSyncOption(const char **ppszShort, int argc, char **argv, int *pi)
131{
132 int rc = -1;
133 if (ppszShort)
134 /* no short options */;
135 else if (!strcmp(argv[*pi], "--timesync-interval"))
136 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
137 &g_TimeSyncInterval, 1, UINT32_MAX - 1);
138 else if (!strcmp(argv[*pi], "--timesync-min-adjust"))
139 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
140 &g_TimeSyncMinAdjust, 0, 3600000);
141 else if (!strcmp(argv[*pi], "--timesync-latency-factor"))
142 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
143 &g_TimeSyncLatencyFactor, 1, 1024);
144 else if (!strcmp(argv[*pi], "--timesync-max-latency"))
145 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
146 &g_TimeSyncMaxLatency, 1, 3600000);
147 return rc;
148}
149
150
151/** @copydoc VBOXSERVICE::pfnInit */
152static DECLCALLBACK(int) VBoxServiceTimeSyncInit(void)
153{
154 /*
155 * If not specified, find the right interval default.
156 * Then create the event sem to block on.
157 */
158 if (!g_TimeSyncInterval)
159 g_TimeSyncInterval = g_DefaultInterval * 1000;
160 if (!g_TimeSyncInterval)
161 g_TimeSyncInterval = 10 * 1000;
162
163 int rc = RTSemEventMultiCreate(&g_TimeSyncEvent);
164 AssertRC(rc);
165 return rc;
166}
167
168
169/** @copydoc VBOXSERVICE::pfnWorker */
170DECLCALLBACK(int) VBoxServiceTimeSyncWorker(bool volatile *pfShutdown)
171{
172 RTTIME Time;
173 char sz[64];
174 int rc;
175
176 unsigned cErrors = 0;
177 for (;;)
178 {
179 /*
180 * Try get a reliable time reading.
181 */
182 int cTries = 3;
183 do
184 {
185 /* query it. */
186 RTTIMESPEC GuestNow0, GuestNow, HostNow;
187 RTTimeNow(&GuestNow0);
188 int rc2 = VbglR3GetHostTime(&HostNow);
189 if (RT_FAILURE(rc2))
190 {
191 if (cErrors++ < 10)
192 VBoxServiceError("VbglR3GetHostTime failed; rc2=%Rrc\n", rc2);
193 break;
194 }
195 RTTimeNow(&GuestNow);
196
197 /* calc latency and check if it's ok. */
198 RTTIMESPEC GuestElapsed = GuestNow;
199 RTTimeSpecSub(&GuestElapsed, &GuestNow0);
200 if ((uint32_t)RTTimeSpecGetMilli(&GuestElapsed) < g_TimeSyncMaxLatency)
201 {
202 /*
203 * Calculate the adjustment threshold and the current drift.
204 */
205 uint32_t MinAdjust = RTTimeSpecGetMilli(&GuestElapsed) * g_TimeSyncLatencyFactor;
206 if (MinAdjust < g_TimeSyncMinAdjust)
207 MinAdjust = g_TimeSyncMinAdjust;
208
209 RTTIMESPEC Drift = HostNow;
210 RTTimeSpecSub(&Drift, &GuestNow);
211 if (RTTimeSpecGetMilli(&Drift) < 0)
212 MinAdjust += g_TimeSyncMinAdjust; /* extra buffer against moving time backwards. */
213
214 RTTIMESPEC AbsDrift = Drift;
215 RTTimeSpecAbsolute(&AbsDrift);
216 if (g_cVerbosity >= 3)
217 {
218 VBoxServiceVerbose(3, "Host: %s (MinAdjust: %RU32 ms)\n",
219 RTTimeToString(RTTimeExplode(&Time, &HostNow), sz, sizeof(sz)), MinAdjust);
220 VBoxServiceVerbose(3, "Guest: - %s => %RDtimespec drift\n",
221 RTTimeToString(RTTimeExplode(&Time, &GuestNow), sz, sizeof(sz)),
222 &Drift);
223 }
224 if (RTTimeSpecGetMilli(&AbsDrift) > MinAdjust)
225 {
226 /*
227 * The drift is to big, we have to make adjustments. :-/
228 * If we've got adjtime around, try that first - most
229 * *NIX systems have it. Fall back on settimeofday.
230 */
231 struct timeval tv;
232#if !defined(RT_OS_OS2) /* PORTME */
233 RTTimeSpecGetTimeval(Drift, &tv);
234 if (adjtime(&tv, NULL) == 0)
235 {
236 if (g_cVerbosity >= 1)
237 VBoxServiceVerbose(1, "adjtime by %RDtimespec\n", &Drift);
238 cErrors = 0;
239 }
240 else
241#endif
242 {
243 errno = 0;
244 if (!gettimeofday(&tv, NULL))
245 {
246 RTTIMESPEC Tmp;
247 RTTimeSpecAdd(RTTimeSpecSetTimeval(&Tmp, &tv), &Drift);
248 if (!settimeofday(RTTimeSpecGetTimeval(&Tmp, &tv), NULL))
249 {
250 if (g_cVerbosity >= 1)
251 VBoxServiceVerbose(1, "settimeofday to %s\n",
252 RTTimeToString(RTTimeExplode(&Time, &Tmp), sz, sizeof(sz)));
253#ifdef DEBUG
254 if (g_cVerbosity >= 3)
255 VBoxServiceVerbose(2, " new time %s\n",
256 RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&Tmp)), sz, sizeof(sz)));
257#endif
258 cErrors = 0;
259 }
260 else if (cErrors++ < 10)
261 VBoxServiceError("settimeofday failed; errno=%d: %s\n", errno, strerror(errno));
262 }
263 else if (cErrors++ < 10)
264 VBoxServiceError("gettimeofday failed; errno=%d: %s\n", errno, strerror(errno));
265 }
266 }
267 break;
268 }
269 VBoxServiceVerbose(3, "%RDtimespec: latency too high (%RDtimespec) sleeping 1s\n", GuestElapsed);
270 RTThreadSleep(1000);
271 } while (--cTries > 0);
272
273 /*
274 * Block for a while.
275 *
276 * The event semaphore takes care of ignoring interruptions and it
277 * allows us to implement service wakeup later.
278 */
279 if (*pfShutdown)
280 break;
281 int rc2 = RTSemEventMultiWait(g_TimeSyncEvent, g_TimeSyncInterval);
282 if (*pfShutdown)
283 break;
284 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
285 {
286 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
287 rc = rc2;
288 break;
289 }
290 }
291
292 RTSemEventMultiDestroy(g_TimeSyncEvent);
293 g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
294 return rc;
295}
296
297
298/** @copydoc VBOXSERVICE::pfnStop */
299static DECLCALLBACK(void) VBoxServiceTimeSyncStop(void)
300{
301 RTSemEventMultiSignal(g_TimeSyncEvent);
302}
303
304
305/** @copydoc VBOXSERVICE::pfnTerm */
306static DECLCALLBACK(void) VBoxServiceTimeSyncTerm(void)
307{
308 RTSemEventMultiDestroy(g_TimeSyncEvent);
309 g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
310}
311
312
313/**
314 * The 'timesync' service description.
315 */
316VBOXSERVICE g_TimeSync =
317{
318 /* pszName. */
319 "timesync",
320 /* pszDescription. */
321 "Time synchronization",
322 /* pszUsage. */
323 "[--timesync-interval <ms>] [--timesync-min-adjust <ms>] "
324 "[--timesync-latency-factor <x>] [--time-sync-max-latency <ms>]"
325 ,
326 /* pszOptions. */
327 " --timesync-interval Specifies the interval at which to synchronize the\n"
328 " time with the host. The default is 10000 ms.\n"
329 " --timesync-min-adjust The minimum absolute drift drift value measured\n"
330 " in milliseconds to make adjustments for.\n"
331 " The default is 1000 ms on OS/2 and 100 ms elsewhere.\n"
332 " --timesync-latency-factor The factor to multiply the time query latency\n"
333 " with to calculate the dynamic minimum adjust time.\n"
334 " The default is 8 times.\n"
335 " --timesync-max-latency The max host timer query latency to accpet.\n"
336 " The default is 250 ms.\n"
337 ,
338 /* methods */
339 VBoxServiceTimeSyncPreInit,
340 VBoxServiceTimeSyncOption,
341 VBoxServiceTimeSyncInit,
342 VBoxServiceTimeSyncWorker,
343 VBoxServiceTimeSyncStop,
344 VBoxServiceTimeSyncTerm
345};
346
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