VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/time/timesupref.h@ 54202

Last change on this file since 54202 was 54202, checked in by vboxsync, 10 years ago

IPRT,TM: Implemented GIP TSC delta processing in the RTTimeNanoTS code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 6.9 KB
Line 
1/* $Id: timesupref.h 54202 2015-02-13 17:13:44Z vboxsync $ */
2/** @file
3 * IPRT - Time using SUPLib, the C Code Template.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/**
29 * The C reference implementation of the assembly routines.
30 *
31 * Calculate NanoTS using the information in the global information page (GIP)
32 * which the support library (SUPLib) exports.
33 *
34 * This function guarantees that the returned timestamp is later (in time) than
35 * any previous calls in the same thread.
36 *
37 * @remark The way the ever increasing time guarantee is currently implemented means
38 * that if you call this function at a frequency higher than 1GHz you're in for
39 * trouble. We currently assume that no idiot will do that for real life purposes.
40 *
41 * @returns Nanosecond timestamp.
42 * @param pData Pointer to the data structure.
43 */
44RTDECL(uint64_t) rtTimeNanoTSInternalRef(PRTTIMENANOTSDATA pData)
45{
46 uint64_t u64Delta;
47#if IS_GIP_MODE_WITH_DELTA(GIP_MODE)
48 int64_t i64TscDelta;
49#endif
50 uint32_t u32NanoTSFactor0;
51 uint64_t u64TSC;
52 uint64_t u64NanoTS;
53 uint32_t u32UpdateIntervalTSC;
54 uint64_t u64PrevNanoTS;
55
56 /*
57 * Read the GIP data and the previous value.
58 */
59 for (;;)
60 {
61 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
62#ifdef IN_RING3
63 if (RT_UNLIKELY(!pGip || pGip->u32Magic != SUPGLOBALINFOPAGE_MAGIC))
64 return pData->pfnRediscover(pData);
65#endif
66#if GIP_MODE == GIP_MODE_ASYNC || IS_GIP_MODE_WITH_DELTA(GIP_MODE)
67 uint8_t const u8ApicId = ASMGetApicId();
68 PSUPGIPCPU pGipCpu = &pGip->aCPUs[pGip->aiCpuFromApicId[u8ApicId]];
69#endif
70
71#ifdef NEED_TRANSACTION_ID
72# if GIP_MODE == GIP_MODE_ASYNC
73 uint32_t u32TransactionId = pGipCpu->u32TransactionId;
74# else
75 uint32_t u32TransactionId = pGip->aCPUs[0].u32TransactionId;
76# endif
77# ifdef USE_LFENCE
78 ASMReadFenceSSE2();
79# else
80 ASMReadFence();
81# endif
82#endif
83
84#if GIP_MODE == GIP_MODE_ASYNC
85 u32UpdateIntervalTSC = pGipCpu->u32UpdateIntervalTSC;
86 u64NanoTS = pGipCpu->u64NanoTS;
87 u64TSC = pGipCpu->u64TSC;
88#else
89 u32UpdateIntervalTSC = pGip->aCPUs[0].u32UpdateIntervalTSC;
90 u64NanoTS = pGip->aCPUs[0].u64NanoTS;
91 u64TSC = pGip->aCPUs[0].u64TSC;
92# if IS_GIP_MODE_WITH_DELTA(GIP_MODE)
93 i64TscDelta = pGipCpu->i64TSCDelta;
94# endif
95#endif
96 u32NanoTSFactor0 = pGip->u32UpdateIntervalNS;
97 u64Delta = ASMReadTSC();
98 u64PrevNanoTS = ASMAtomicReadU64(pData->pu64Prev);
99
100#ifdef NEED_TRANSACTION_ID
101# if GIP_MODE == GIP_MODE_ASYNC || IS_GIP_MODE_WITH_DELTA(GIP_MODE)
102 if (RT_UNLIKELY(u8ApicId != ASMGetApicId()))
103 continue;
104# elif defined(USE_LFENCE)
105 ASMWriteFenceSSE();
106# else
107 ASMWriteFence();
108# endif
109# if GIP_MODE == GIP_MODE_ASYNC
110 if (RT_UNLIKELY( pGipCpu->u32TransactionId != u32TransactionId
111 || (u32TransactionId & 1)))
112 continue;
113# else
114 if (RT_UNLIKELY( pGip->aCPUs[0].u32TransactionId != u32TransactionId
115 || (u32TransactionId & 1)))
116 continue;
117# endif
118#endif
119 break;
120 }
121
122 /*
123 * Calc NanoTS delta.
124 */
125 u64Delta -= u64TSC;
126#if IS_GIP_MODE_WITH_DELTA(GIP_MODE)
127 if (RT_LIKELY(i64TscDelta != INT64_MAX))
128 u64Delta -= i64TscDelta;
129#endif
130 if (RT_UNLIKELY(u64Delta > u32UpdateIntervalTSC))
131 {
132 /*
133 * We've expired the interval, cap it. If we're here for the 2nd
134 * time without any GIP update in-between, the checks against
135 * *pu64Prev below will force 1ns stepping.
136 */
137 pData->cExpired++;
138 u64Delta = u32UpdateIntervalTSC;
139 }
140#if !defined(_MSC_VER) || defined(RT_ARCH_AMD64) /* GCC makes very pretty code from these two inline calls, while MSC cannot. */
141 u64Delta = ASMMult2xU32RetU64((uint32_t)u64Delta, u32NanoTSFactor0);
142 u64Delta = ASMDivU64ByU32RetU32(u64Delta, u32UpdateIntervalTSC);
143#else
144 __asm
145 {
146 mov eax, dword ptr [u64Delta]
147 mul dword ptr [u32NanoTSFactor0]
148 div dword ptr [u32UpdateIntervalTSC]
149 mov dword ptr [u64Delta], eax
150 xor edx, edx
151 mov dword ptr [u64Delta + 4], edx
152 }
153#endif
154
155 /*
156 * Calculate the time and compare it with the previously returned value.
157 */
158 u64NanoTS += u64Delta;
159 uint64_t u64DeltaPrev = u64NanoTS - u64PrevNanoTS;
160 if (RT_LIKELY( u64DeltaPrev > 0
161 && u64DeltaPrev < UINT64_C(86000000000000) /* 24h */))
162 /* Frequent - less than 24h since last call. */;
163 else if (RT_LIKELY( (int64_t)u64DeltaPrev <= 0
164 && (int64_t)u64DeltaPrev + u32NanoTSFactor0 * 2 >= 0))
165 {
166 /* Occasional - u64NanoTS is in the recent 'past' relative the previous call. */
167 ASMAtomicIncU32(&pData->c1nsSteps);
168 u64NanoTS = u64PrevNanoTS + 1;
169 }
170 else if (!u64PrevNanoTS)
171 /* We're resuming (see TMVirtualResume). */;
172 else
173 {
174 /* Something has gone bust, if negative offset it's real bad. */
175 ASMAtomicIncU32(&pData->cBadPrev);
176 pData->pfnBad(pData, u64NanoTS, u64DeltaPrev, u64PrevNanoTS);
177 }
178
179 if (RT_UNLIKELY(!ASMAtomicCmpXchgU64(pData->pu64Prev, u64NanoTS, u64PrevNanoTS)))
180 {
181 /*
182 * Attempt updating the previous value, provided we're still ahead of it.
183 *
184 * There is no point in recalculating u64NanoTS because we got preempted or if
185 * we raced somebody while the GIP was updated, since these are events
186 * that might occur at any point in the return path as well.
187 */
188 pData->cUpdateRaces++;
189 for (int cTries = 25; cTries > 0; cTries--)
190 {
191 u64PrevNanoTS = ASMAtomicReadU64(pData->pu64Prev);
192 if (u64PrevNanoTS >= u64NanoTS)
193 break;
194 if (ASMAtomicCmpXchgU64(pData->pu64Prev, u64NanoTS, u64PrevNanoTS))
195 break;
196 }
197 }
198 return u64NanoTS;
199}
200
Note: See TracBrowser for help on using the repository browser.

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