VirtualBox

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

Last change on this file since 2981 was 2981, checked in by vboxsync, 18 years ago

InnoTek -> innotek: all the headers and comments.

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