VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTR0ThreadPreemption.cpp@ 55931

Last change on this file since 55931 was 55863, checked in by vboxsync, 10 years ago

IPRT,SUPDrv,VMM: Revised the context switching hook interface. Do less work when enabling the hook (formerly 'registration'). Drop the reference counting (kept internally for solaris) as it complicates restrictions wrt destroying enabled hooks. Bumped support driver version.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/* $Id: tstRTR0ThreadPreemption.cpp 55863 2015-05-14 18:29:34Z vboxsync $ */
2/** @file
3 * IPRT R0 Testcase - Thread Preemption.
4 */
5
6/*
7 * Copyright (C) 2009-2015 Oracle Corporation
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/thread.h>
31
32#include <iprt/asm-amd64-x86.h>
33#include <iprt/err.h>
34#include <iprt/mem.h>
35#include <iprt/time.h>
36#include <iprt/string.h>
37#include <VBox/sup.h>
38#include "tstRTR0ThreadPreemption.h"
39
40
41#define TSTRTR0THREADCTXDATA_MAGIC 0xc01a50da
42
43/**
44 * Thread-context hook data.
45 */
46typedef struct TSTRTR0THREADCTXDATA
47{
48 uint32_t volatile u32Magic;
49 RTCPUID uSourceCpuId;
50 RTNATIVETHREAD hSourceThread;
51
52 /* For RTTHREADCTXEVENT_PREEMPTING. */
53 bool fPreemptingSuccess;
54 volatile bool fPreemptingInvoked;
55
56 /* For RTTHREADCTXEVENT_RESUMED. */
57 bool fResumedSuccess;
58 volatile bool fResumedInvoked;
59
60 char achResult[512];
61} TSTRTR0THREADCTXDATA, *PTSTRTR0THREADCTXDATA;
62
63
64/**
65 * Thread-context hook function.
66 *
67 * @param enmEvent The thread-context event.
68 * @param pvUser Pointer to the user argument.
69 */
70static DECLCALLBACK(void) tstRTR0ThreadCtxHook(RTTHREADCTXEVENT enmEvent, void *pvUser)
71{
72 PTSTRTR0THREADCTXDATA pData = (PTSTRTR0THREADCTXDATA)pvUser;
73 AssertPtrReturnVoid(pData);
74
75 if (pData->u32Magic != TSTRTR0THREADCTXDATA_MAGIC)
76 {
77 RTStrPrintf(pData->achResult, sizeof(pData->achResult), "!tstRTR0ThreadCtxHook: Invalid magic.");
78 return;
79 }
80
81 switch (enmEvent)
82 {
83 case RTTHREADCTXEVENT_OUT:
84 {
85 ASMAtomicWriteBool(&pData->fPreemptingInvoked, true);
86
87 /* We've already been called once, we now might very well be on another CPU. Nothing to do here. */
88 if (pData->fPreemptingSuccess)
89 return;
90
91 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
92 {
93 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
94 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_PREEMPTING]: Called with preemption enabled");
95 break;
96 }
97
98 RTNATIVETHREAD hCurrentThread = RTThreadNativeSelf();
99 if (pData->hSourceThread != hCurrentThread)
100 {
101 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
102 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_PREEMPTING]: Thread switched! Source=%RTnthrd Current=%RTnthrd.",
103 pData->hSourceThread, hCurrentThread);
104 break;
105 }
106
107 RTCPUID uCurrentCpuId = RTMpCpuId();
108 if (pData->uSourceCpuId != uCurrentCpuId)
109 {
110 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
111 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_PREEMPTING]: migrated uSourceCpuId=%RU32 uCurrentCpuId=%RU32",
112 pData->uSourceCpuId, uCurrentCpuId);
113 break;
114 }
115
116 pData->fPreemptingSuccess = true;
117 break;
118 }
119
120 case RTTHREADCTXEVENT_IN:
121 {
122 ASMAtomicWriteBool(&pData->fResumedInvoked, true);
123
124 /* We've already been called once successfully, nothing more to do. */
125 if (ASMAtomicReadBool(&pData->fResumedSuccess))
126 return;
127
128 if (!pData->fPreemptingSuccess)
129 {
130 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
131 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_RESUMED]: Called before preempting callback was invoked.");
132 break;
133 }
134
135 RTNATIVETHREAD hCurrentThread = RTThreadNativeSelf();
136 if (pData->hSourceThread != hCurrentThread)
137 {
138 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
139 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_RESUMED]: Thread switched! Source=%RTnthrd Current=%RTnthrd.",
140 pData->hSourceThread, hCurrentThread);
141 break;
142 }
143
144 ASMAtomicWriteBool(&pData->fResumedSuccess, true);
145 break;
146 }
147
148 default:
149 AssertMsgFailed(("Invalid event %#x\n", enmEvent));
150 break;
151 }
152}
153
154
155/**
156 * Service request callback function.
157 *
158 * @returns VBox status code.
159 * @param pSession The caller's session.
160 * @param u64Arg 64-bit integer argument.
161 * @param pReqHdr The request header. Input / Output. Optional.
162 */
163DECLEXPORT(int) TSTRTR0ThreadPreemptionSrvReqHandler(PSUPDRVSESSION pSession, uint32_t uOperation,
164 uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr)
165{
166 NOREF(pSession);
167 if (u64Arg)
168 return VERR_INVALID_PARAMETER;
169 if (!VALID_PTR(pReqHdr))
170 return VERR_INVALID_PARAMETER;
171 char *pszErr = (char *)(pReqHdr + 1);
172 size_t cchErr = pReqHdr->cbReq - sizeof(*pReqHdr);
173 if (cchErr < 32 || cchErr >= 0x10000)
174 return VERR_INVALID_PARAMETER;
175 *pszErr = '\0';
176
177 /*
178 * The big switch.
179 */
180 switch (uOperation)
181 {
182 case TSTRTR0THREADPREEMPTION_SANITY_OK:
183 break;
184
185 case TSTRTR0THREADPREEMPTION_SANITY_FAILURE:
186 RTStrPrintf(pszErr, cchErr, "!42failure42%1024s", "");
187 break;
188
189 case TSTRTR0THREADPREEMPTION_BASIC:
190 {
191 if (!ASMIntAreEnabled())
192 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
193 else if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
194 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns false by default");
195 else
196 {
197 RTTHREADPREEMPTSTATE State = RTTHREADPREEMPTSTATE_INITIALIZER;
198 RTThreadPreemptDisable(&State);
199 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
200 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after RTThreadPreemptDisable");
201 else if (!ASMIntAreEnabled())
202 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
203 RTThreadPreemptRestore(&State);
204 }
205 break;
206 }
207
208 case TSTRTR0THREADPREEMPTION_IS_TRUSTY:
209 if (!RTThreadPreemptIsPendingTrusty())
210 RTStrPrintf(pszErr, cchErr, "!Untrusty");
211 break;
212
213 case TSTRTR0THREADPREEMPTION_IS_PENDING:
214 {
215 RTTHREADPREEMPTSTATE State = RTTHREADPREEMPTSTATE_INITIALIZER;
216 RTThreadPreemptDisable(&State);
217 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
218 {
219#ifdef RT_OS_DARWIN
220 uint64_t const cNsMax = UINT64_C(8)*1000U*1000U*1000U;
221#else
222 uint64_t const cNsMax = UINT64_C(2)*1000U*1000U*1000U;
223#endif
224 if (ASMIntAreEnabled())
225 {
226 uint64_t u64StartTS = RTTimeNanoTS();
227 uint64_t u64StartSysTS = RTTimeSystemNanoTS();
228 uint64_t cLoops = 0;
229 uint64_t cNanosSysElapsed;
230 uint64_t cNanosElapsed;
231 bool fPending;
232 do
233 {
234 fPending = RTThreadPreemptIsPending(NIL_RTTHREAD);
235 cNanosElapsed = RTTimeNanoTS() - u64StartTS;
236 cNanosSysElapsed = RTTimeSystemNanoTS() - u64StartSysTS;
237 cLoops++;
238 } while ( !fPending
239 && cNanosElapsed < cNsMax
240 && cNanosSysElapsed < cNsMax
241 && cLoops < 100U*_1M);
242 if (!fPending)
243 RTStrPrintf(pszErr, cchErr, "!Preempt not pending after %'llu loops / %'llu ns / %'llu ns (sys)",
244 cLoops, cNanosElapsed, cNanosSysElapsed);
245 else if (cLoops == 1)
246 RTStrPrintf(pszErr, cchErr, "!cLoops=1\n");
247 else
248 RTStrPrintf(pszErr, cchErr, "RTThreadPreemptIsPending returned true after %'llu loops / %'llu ns / %'llu ns (sys)",
249 cLoops, cNanosElapsed, cNanosSysElapsed);
250 }
251 else
252 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
253 }
254 else
255 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after RTThreadPreemptDisable");
256 RTThreadPreemptRestore(&State);
257 break;
258 }
259
260 case TSTRTR0THREADPREEMPTION_NESTED:
261 {
262 bool const fDefault = RTThreadPreemptIsEnabled(NIL_RTTHREAD);
263 RTTHREADPREEMPTSTATE State1 = RTTHREADPREEMPTSTATE_INITIALIZER;
264 RTThreadPreemptDisable(&State1);
265 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
266 {
267 RTTHREADPREEMPTSTATE State2 = RTTHREADPREEMPTSTATE_INITIALIZER;
268 RTThreadPreemptDisable(&State2);
269 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
270 {
271 RTTHREADPREEMPTSTATE State3 = RTTHREADPREEMPTSTATE_INITIALIZER;
272 RTThreadPreemptDisable(&State3);
273 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
274 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 3rd RTThreadPreemptDisable");
275
276 RTThreadPreemptRestore(&State3);
277 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) && !*pszErr)
278 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 1st RTThreadPreemptRestore");
279 }
280 else
281 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 2nd RTThreadPreemptDisable");
282
283 RTThreadPreemptRestore(&State2);
284 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) && !*pszErr)
285 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 2nd RTThreadPreemptRestore");
286 }
287 else
288 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 1st RTThreadPreemptDisable");
289 RTThreadPreemptRestore(&State1);
290 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) != fDefault && !*pszErr)
291 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns false after 3rd RTThreadPreemptRestore");
292 break;
293 }
294
295 case TSTRTR0THREADPREEMPTION_CTXHOOKS:
296 {
297 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
298 {
299 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksCreate must be called with preemption enabled");
300 break;
301 }
302
303 bool fRegistered = RTThreadCtxHookIsEnabled(NIL_RTTHREADCTXHOOK);
304 if (fRegistered)
305 {
306 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookIsEnabled returns true before creating any hooks");
307 break;
308 }
309
310 PTSTRTR0THREADCTXDATA pCtxData = (PTSTRTR0THREADCTXDATA)RTMemAllocZ(sizeof(*pCtxData));
311 AssertReturn(pCtxData, VERR_NO_MEMORY);
312 pCtxData->u32Magic = TSTRTR0THREADCTXDATA_MAGIC;
313 pCtxData->fPreemptingSuccess = false;
314 pCtxData->fPreemptingInvoked = false;
315 pCtxData->fResumedInvoked = false;
316 pCtxData->fResumedSuccess = false;
317 pCtxData->hSourceThread = RTThreadNativeSelf();
318 RT_ZERO(pCtxData->achResult);
319
320 RTTHREADCTXHOOK hThreadCtx;
321 int rc = RTThreadCtxHookCreate(&hThreadCtx, 0, tstRTR0ThreadCtxHook, pCtxData);
322 if (RT_FAILURE(rc))
323 {
324 if (rc == VERR_NOT_SUPPORTED)
325 RTStrPrintf(pszErr, cchErr, "RTThreadCtxHooksCreate returns VERR_NOT_SUPPORTED");
326 else
327 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksCreate returns %Rrc", rc);
328 RTMemFree(pCtxData);
329 break;
330 }
331
332 fRegistered = RTThreadCtxHookIsEnabled(hThreadCtx);
333 if (fRegistered)
334 {
335 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookIsEnabled returns true before registering any hooks");
336 RTThreadCtxHookDestroy(hThreadCtx);
337 break;
338 }
339
340 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
341 RTThreadPreemptDisable(&PreemptState);
342 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
343
344 pCtxData->uSourceCpuId = RTMpCpuId();
345
346 rc = RTThreadCtxHookEnable(hThreadCtx);
347 if (RT_FAILURE(rc))
348 {
349 RTThreadPreemptRestore(&PreemptState);
350 RTMemFree(pCtxData);
351 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookEnable returns %Rrc", rc);
352 break;
353 }
354
355 fRegistered = RTThreadCtxHookIsEnabled(hThreadCtx);
356 if (!fRegistered)
357 {
358 RTThreadPreemptRestore(&PreemptState);
359 RTMemFree(pCtxData);
360 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookIsEnabled return false when hooks are supposedly registered");
361 break;
362 }
363
364 RTThreadPreemptRestore(&PreemptState);
365
366 /* Check if the preempting callback has/will been invoked. */
367 const uint32_t cMsTimeout = 10000;
368 const uint32_t cMsSleepGranularity = 50;
369 uint32_t cMsSlept = 0;
370 RTCPUID uCurrentCpuId = NIL_RTCPUID;
371 for (;;)
372 {
373 RTThreadYield();
374 RTThreadPreemptDisable(&PreemptState);
375 uCurrentCpuId = RTMpCpuId();
376 RTThreadPreemptRestore(&PreemptState);
377
378 if ( pCtxData->uSourceCpuId != uCurrentCpuId
379 || cMsSlept >= cMsTimeout)
380 {
381 break;
382 }
383
384 RTThreadSleep(cMsSleepGranularity);
385 cMsSlept += cMsSleepGranularity;
386 }
387
388 if (!ASMAtomicReadBool(&pCtxData->fPreemptingInvoked))
389 {
390 if (pCtxData->uSourceCpuId != uCurrentCpuId)
391 {
392 RTStrPrintf(pszErr, cchErr,
393 "!tstRTR0ThreadCtxHooks[RTTHREADCTXEVENT_PREEMPTING] not invoked before migrating from CPU %RU32 to %RU32",
394 pCtxData->uSourceCpuId, uCurrentCpuId);
395 }
396 else
397 {
398 RTStrPrintf(pszErr, cchErr, "!tstRTR0ThreadCtxHooks[RTTHREADCTXEVENT_PREEMPTING] not invoked after ca. %u ms",
399 cMsSlept);
400 }
401 }
402 else if (!pCtxData->fPreemptingSuccess)
403 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
404 else
405 {
406 /* Preempting callback succeeded, now check if the resumed callback has/will been invoked. */
407 cMsSlept = 0;
408 for (;;)
409 {
410 if ( ASMAtomicReadBool(&pCtxData->fResumedInvoked)
411 || cMsSlept >= cMsTimeout)
412 {
413 break;
414 }
415
416 RTThreadSleep(cMsSleepGranularity);
417 cMsSlept += cMsSleepGranularity;
418 }
419
420 if (!ASMAtomicReadBool(&pCtxData->fResumedInvoked))
421 {
422 RTStrPrintf(pszErr, cchErr, "!tstRTR0ThreadCtxHooks[RTTHREADCTXEVENT_RESUMED] not invoked after ca. %u ms",
423 cMsSlept);
424 }
425 else if (!pCtxData->fResumedSuccess)
426 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
427 }
428
429 RTThreadCtxHookDisable(hThreadCtx);
430
431 fRegistered = RTThreadCtxHookIsEnabled(hThreadCtx);
432 if (fRegistered)
433 {
434 RTMemFree(pCtxData);
435 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookIsEnabled return true when hooks are deregistered");
436 break;
437 }
438
439 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
440 rc = RTThreadCtxHookDestroy(hThreadCtx);
441 if (RT_FAILURE(rc))
442 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksRelease returns %Rrc!", rc);
443
444 RTMemFree(pCtxData);
445 break;
446 }
447
448 default:
449 RTStrPrintf(pszErr, cchErr, "!Unknown test #%d", uOperation);
450 break;
451 }
452
453 /* The error indicator is the '!' in the message buffer. */
454 return VINF_SUCCESS;
455}
456
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