VirtualBox

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

Last change on this file since 12247 was 11326, checked in by vboxsync, 16 years ago

iprt/tstTSC: Use mp.h

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