VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstTSC.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: 11.0 KB
Line 
1/* $Id: tstTSC.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime Testcase - SMP TSC testcase.
4 */
5
6/*
7 * Copyright (C) 2006-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 * 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* Header Files *
29*******************************************************************************/
30#include <iprt/runtime.h>
31
32/*******************************************************************************
33* Structures and Typedefs *
34*******************************************************************************/
35typedef struct TSCDATA
36{
37 /** The TSC. */
38 uint64_t volatile TSC;
39 /** The APIC ID. */
40 uint8_t volatile u8ApicId;
41 /** Did it succeed? */
42 bool volatile fRead;
43 /** Did it fail? */
44 bool volatile fFailed;
45 /** The thread handle. */
46 RTTHREAD Thread;
47} TSCDATA, *PTSCDATA;
48
49/*******************************************************************************
50* Global Variables *
51*******************************************************************************/
52/** The number of CPUs waiting on their user event semaphore. */
53static volatile uint32_t g_cWaiting;
54/** The number of CPUs ready (in spin) to do the TSC read. */
55static volatile uint32_t g_cReady;
56/** The variable the CPUs are spinning on.
57 * 0: Spin.
58 * 1: Go ahead.
59 * 2: You're too late, back to square one. */
60static volatile uint32_t g_u32Go;
61/** The number of CPUs that managed to read the TSC. */
62static volatile uint32_t g_cRead;
63/** The number of CPUs that failed to read the TSC. */
64static volatile uint32_t g_cFailed;
65
66/** Indicator forcing the threads to quit. */
67static volatile bool g_fDone;
68
69
70/*******************************************************************************
71* Internal Functions *
72*******************************************************************************/
73static DECLCALLBACK(int) ThreadFunction(RTTHREAD Thread, void *pvUser);
74
75
76/**
77 * Thread function for catching the other cpus.
78 *
79 * @returns VINF_SUCCESS (we don't care).
80 * @param Thread The thread handle.
81 * @param pvUser PTSCDATA.
82 */
83static DECLCALLBACK(int) ThreadFunction(RTTHREAD Thread, void *pvUser)
84{
85 PTSCDATA pTscData = (PTSCDATA)pvUser;
86
87 while (!g_fDone)
88 {
89 /*
90 * Wait.
91 */
92 ASMAtomicIncU32(&g_cWaiting);
93 RTThreadUserWait(Thread, RT_INDEFINITE_WAIT);
94 RTThreadUserReset(Thread);
95 ASMAtomicDecU32(&g_cWaiting);
96 if (g_fDone)
97 break;
98
99 /*
100 * Spin.
101 */
102 ASMAtomicIncU32(&g_cReady);
103 while (!g_fDone)
104 {
105 const uint8_t ApicId1 = ASMGetApicId();
106 const uint64_t TSC1 = ASMReadTSC();
107 const uint32_t u32Go = g_u32Go;
108 if (u32Go == 0)
109 continue;
110
111 if (u32Go == 1)
112 {
113 /* do the reading. */
114 const uint8_t ApicId2 = ASMGetApicId();
115 const uint64_t TSC2 = ASMReadTSC();
116 const uint8_t ApicId3 = ASMGetApicId();
117 const uint64_t TSC3 = ASMReadTSC();
118 const uint8_t ApicId4 = ASMGetApicId();
119
120 if ( ApicId1 == ApicId2
121 && ApicId1 == ApicId3
122 && ApicId1 == ApicId4
123 && TSC3 - TSC1 < 2250 /* WARNING: This is just a guess, increase if it doesn't work for you. */
124 && TSC2 - TSC1 < TSC3 - TSC1
125 )
126 {
127 /* succeeded. */
128 pTscData->TSC = TSC2;
129 pTscData->u8ApicId = ApicId1;
130 pTscData->fFailed = false;
131 pTscData->fRead = true;
132 ASMAtomicIncU32(&g_cRead);
133 break;
134 }
135 }
136
137 /* failed */
138 pTscData->fFailed = true;
139 pTscData->fRead = false;
140 ASMAtomicIncU32(&g_cFailed);
141 break;
142 }
143 }
144
145 return VINF_SUCCESS;
146}
147
148
149int main()
150{
151 RTR3Init();
152
153 /*
154 * This is only relevant to on SMP systems.
155 */
156 const unsigned cCpus = RTSystemProcessorGetCount();
157 if (cCpus <= 1)
158 {
159 RTPrintf("tstTSC: SKIPPED - Only relevant on SMP systems\n");
160 return 0;
161 }
162
163 /*
164 * Create the threads.
165 */
166 static TSCDATA s_aData[254];
167 uint32_t i;
168 if (cCpus > RT_ELEMENTS(s_aData))
169 {
170 RTPrintf("tstTSC: FAILED - too many CPUs (%u)\n", cCpus);
171 return 1;
172 }
173
174 /* ourselves. */
175 s_aData[0].Thread = RTThreadSelf();
176
177 /* the others */
178 for (i = 1; i < cCpus; i++)
179 {
180 int rc = RTThreadCreate(&s_aData[i].Thread, ThreadFunction, &s_aData[i], 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "OTHERCPU");
181 if (RT_FAILURE(rc))
182 {
183 RTPrintf("tstTSC: FAILURE - RTThreatCreate failed when creating thread #%u, rc=%Rrc!\n", i, rc);
184 ASMAtomicXchgSize(&g_fDone, true);
185 while (i-- > 1)
186 {
187 RTThreadUserSignal(s_aData[i].Thread);
188 RTThreadWait(s_aData[i].Thread, 5000, NULL);
189 }
190 return 1;
191 }
192 }
193
194 /*
195 * Retry untill we get lucky (or give up).
196 */
197 for (unsigned cTries = 0; ; cTries++)
198 {
199 if (cTries > 10240)
200 {
201 RTPrintf("tstTSC: FAILURE - %d attempts, giving.\n", cTries);
202 break;
203 }
204
205 /*
206 * Wait for the other threads to get ready (brute force active wait, I'm lazy).
207 */
208 i = 0;
209 while (g_cWaiting < cCpus - 1)
210 {
211 if (i++ > _2G32)
212 break;
213 RTThreadSleep(i & 0xf);
214 }
215 if (g_cWaiting != cCpus - 1)
216 {
217 RTPrintf("tstTSC: FAILURE - threads failed to get waiting (%d != %d (i=%d))\n", g_cWaiting + 1, cCpus, i);
218 break;
219 }
220
221 /*
222 * Send them spinning.
223 */
224 ASMAtomicXchgU32(&g_cReady, 0);
225 ASMAtomicXchgU32(&g_u32Go, 0);
226 ASMAtomicXchgU32(&g_cRead, 0);
227 ASMAtomicXchgU32(&g_cFailed, 0);
228 for (i = 1; i < cCpus; i++)
229 {
230 ASMAtomicXchgSize(&s_aData[i].fFailed, false);
231 ASMAtomicXchgSize(&s_aData[i].fRead, false);
232 ASMAtomicXchgU8(&s_aData[i].u8ApicId, 0xff);
233
234 int rc = RTThreadUserSignal(s_aData[i].Thread);
235 if (RT_FAILURE(rc))
236 RTPrintf("tstTSC: WARNING - RTThreadUserSignal(%#u) -> rc=%Rrc!\n", i, rc);
237 }
238
239 /* wait for them to get ready. */
240 i = 0;
241 while (g_cReady < cCpus - 1)
242 {
243 if (i++ > _2G32)
244 break;
245 }
246 if (g_cReady != cCpus - 1)
247 {
248 RTPrintf("tstTSC: FAILURE - threads failed to get ready (%d != %d, i=%d)\n", g_cWaiting + 1, cCpus, i);
249 break;
250 }
251
252 /*
253 * Flip the "go" switch and do our readings.
254 * We give the other threads the slack it takes to two extra TSC and APIC ID reads.
255 */
256 const uint8_t ApicId1 = ASMGetApicId();
257 const uint64_t TSC1 = ASMReadTSC();
258 ASMAtomicXchgU32(&g_u32Go, 1);
259 const uint8_t ApicId2 = ASMGetApicId();
260 const uint64_t TSC2 = ASMReadTSC();
261 const uint8_t ApicId3 = ASMGetApicId();
262 const uint64_t TSC3 = ASMReadTSC();
263 const uint8_t ApicId4 = ASMGetApicId();
264 const uint64_t TSC4 = ASMReadTSC();
265 ASMAtomicXchgU32(&g_u32Go, 2);
266 const uint8_t ApicId5 = ASMGetApicId();
267 const uint64_t TSC5 = ASMReadTSC();
268 const uint8_t ApicId6 = ASMGetApicId();
269
270 /* Compose our own result. */
271 if ( ApicId1 == ApicId2
272 && ApicId1 == ApicId3
273 && ApicId1 == ApicId4
274 && ApicId1 == ApicId5
275 && ApicId1 == ApicId6
276 && TSC5 - TSC1 < 2750 /* WARNING: This is just a guess, increase if it doesn't work for you. */
277 && TSC4 - TSC1 < TSC5 - TSC1
278 && TSC3 - TSC1 < TSC4 - TSC1
279 && TSC2 - TSC1 < TSC3 - TSC1
280 )
281 {
282 /* succeeded. */
283 s_aData[0].TSC = TSC2;
284 s_aData[0].u8ApicId = ApicId1;
285 s_aData[0].fFailed = false;
286 s_aData[0].fRead = true;
287 ASMAtomicIncU32(&g_cRead);
288 }
289 else
290 {
291 /* failed */
292 s_aData[0].fFailed = true;
293 s_aData[0].fRead = false;
294 ASMAtomicIncU32(&g_cFailed);
295 }
296
297 /*
298 * Wait a little while to let the other ones to finish.
299 */
300 i = 0;
301 while (g_cRead + g_cFailed < cCpus)
302 {
303 if (i++ > _2G32)
304 break;
305 if (i > _1M)
306 RTThreadSleep(i & 0xf);
307 }
308 if (g_cRead + g_cFailed != cCpus)
309 {
310 RTPrintf("tstTSC: FAILURE - threads failed to complete reading (%d + %d != %d)\n", g_cRead, g_cFailed, cCpus);
311 break;
312 }
313
314 /*
315 * If everone succeeded, print the results.
316 */
317 if (!g_cFailed)
318 {
319 RTPrintf(" # ID TSC\n"
320 "-----------------------\n");
321 for (i = 0; i < cCpus; i++)
322 RTPrintf("%2d %02x %RX64\n", i, s_aData[i].u8ApicId, s_aData[i].TSC);
323 RTPrintf("(Needed %u attempt%s.)\n", cTries + 1, cTries ? "s" : "");
324 break;
325 }
326 }
327
328 /*
329 * Destroy the threads.
330 */
331 ASMAtomicXchgSize(&g_fDone, true);
332 for (i = 1; i < cCpus; i++)
333 {
334 int rc = RTThreadUserSignal(s_aData[i].Thread);
335 if (RT_FAILURE(rc))
336 RTPrintf("tstTSC: WARNING - RTThreadUserSignal(%#u) -> rc=%Rrc! (2)\n", i, rc);
337 }
338 for (i = 1; i < cCpus; i++)
339 {
340 int rc = RTThreadWait(s_aData[i].Thread, 5000, NULL);
341 if (RT_FAILURE(rc))
342 RTPrintf("tstTSC: WARNING - RTThreadWait(%#u) -> rc=%Rrc!\n", i, rc);
343 }
344
345 return g_cFailed != 0 || g_cRead != cCpus;
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