VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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