VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstTSC.cpp@ 11118

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

rebranding: IPRT files again.

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