VirtualBox

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

Last change on this file since 106924 was 106924, checked in by vboxsync, 2 months ago

IPRT/testcase: Made the R0 modules build on win.arm64. jiraref:VBP-1449

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