VirtualBox

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

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

Double underscore cleanup.

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