VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMThread.cpp@ 76553

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 35.0 KB
RevLine 
[3523]1/* $Id: PDMThread.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * PDM Thread - VM Thread Management.
4 */
5
6/*
[76553]7 * Copyright (C) 2007-2019 Oracle Corporation
[3523]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
[5999]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.
[3523]16 */
17
[57358]18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
[63560]22/// @todo \#define LOG_GROUP LOG_GROUP_PDM_THREAD
[3523]23#include "PDMInternal.h"
[35346]24#include <VBox/vmm/pdm.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/vm.h>
27#include <VBox/vmm/uvm.h>
[3523]28#include <VBox/err.h>
29
30#include <VBox/log.h>
31#include <iprt/asm.h>
[3548]32#include <iprt/semaphore.h>
[3523]33#include <iprt/assert.h>
34#include <iprt/thread.h>
35
36
[57358]37/*********************************************************************************************************************************
38* Internal Functions *
39*********************************************************************************************************************************/
[3548]40static DECLCALLBACK(int) pdmR3ThreadMain(RTTHREAD Thread, void *pvUser);
[3523]41
42
43/**
[3548]44 * Wrapper around ASMAtomicCmpXchgSize.
[3523]45 */
[3548]46DECLINLINE(bool) pdmR3AtomicCmpXchgState(PPDMTHREAD pThread, PDMTHREADSTATE enmNewState, PDMTHREADSTATE enmOldState)
[3523]47{
[3548]48 bool fRc;
49 ASMAtomicCmpXchgSize(&pThread->enmState, enmNewState, enmOldState, fRc);
50 return fRc;
51}
[3523]52
53
54/**
55 * Does the wakeup call.
56 *
57 * @returns VBox status code. Already asserted on failure.
58 * @param pThread The PDM thread.
59 */
[4421]60static DECLCALLBACK(int) pdmR3ThreadWakeUp(PPDMTHREAD pThread)
[3523]61{
[12324]62 RTSemEventMultiSignal(pThread->Internal.s.SleepEvent);
63
[3523]64 int rc;
65 switch (pThread->Internal.s.enmType)
66 {
67 case PDMTHREADTYPE_DEVICE:
[4421]68 rc = pThread->u.Dev.pfnWakeUp(pThread->u.Dev.pDevIns, pThread);
[3523]69 break;
[4455]70
[4012]71 case PDMTHREADTYPE_USB:
[4421]72 rc = pThread->u.Usb.pfnWakeUp(pThread->u.Usb.pUsbIns, pThread);
[4012]73 break;
[3523]74
75 case PDMTHREADTYPE_DRIVER:
[4421]76 rc = pThread->u.Drv.pfnWakeUp(pThread->u.Drv.pDrvIns, pThread);
[3523]77 break;
78
79 case PDMTHREADTYPE_INTERNAL:
[4421]80 rc = pThread->u.Int.pfnWakeUp(pThread->Internal.s.pVM, pThread);
[3523]81 break;
82
83 case PDMTHREADTYPE_EXTERNAL:
[4421]84 rc = pThread->u.Ext.pfnWakeUp(pThread);
[3523]85 break;
86
87 default:
88 AssertMsgFailed(("%d\n", pThread->Internal.s.enmType));
[39402]89 rc = VERR_PDM_THREAD_IPE_1;
[3523]90 break;
91 }
92 AssertRC(rc);
93 return rc;
94}
95
96
97/**
98 * Allocates new thread instance.
99 *
100 * @returns VBox status code.
[58122]101 * @param pVM The cross context VM structure.
[3523]102 * @param ppThread Where to store the pointer to the instance.
103 */
[3548]104static int pdmR3ThreadNew(PVM pVM, PPPDMTHREAD ppThread)
[3523]105{
106 PPDMTHREAD pThread;
[3548]107 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_THREAD, sizeof(*pThread), (void **)&pThread);
[3523]108 if (RT_FAILURE(rc))
109 return rc;
110
111 pThread->u32Version = PDMTHREAD_VERSION;
112 pThread->enmState = PDMTHREADSTATE_INITIALIZING;
[3548]113 pThread->Thread = NIL_RTTHREAD;
[3523]114 pThread->Internal.s.pVM = pVM;
115
116 *ppThread = pThread;
117 return VINF_SUCCESS;
118}
119
120
121
122/**
123 * Initialize a new thread, this actually creates the thread.
124 *
125 * @returns VBox status code.
[58122]126 * @param pVM The cross context VM structure.
[3548]127 * @param ppThread Where the thread instance data handle is.
128 * @param cbStack The stack size, see RTThreadCreate().
[3523]129 * @param enmType The thread type, see RTThreadCreate().
130 * @param pszName The thread name, see RTThreadCreate().
131 */
[3548]132static int pdmR3ThreadInit(PVM pVM, PPPDMTHREAD ppThread, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
[3523]133{
[28262]134 PPDMTHREAD pThread = *ppThread;
135 PUVM pUVM = pVM->pUVM;
[3548]136
[3523]137 /*
138 * Initialize the remainder of the structure.
139 */
140 pThread->Internal.s.pVM = pVM;
141
142 int rc = RTSemEventMultiCreate(&pThread->Internal.s.BlockEvent);
143 if (RT_SUCCESS(rc))
144 {
[12324]145 rc = RTSemEventMultiCreate(&pThread->Internal.s.SleepEvent);
[3523]146 if (RT_SUCCESS(rc))
147 {
[12324]148 /*
149 * Create the thread and wait for it to initialize.
150 * The newly created thread will set the PDMTHREAD::Thread member.
151 */
152 RTTHREAD Thread;
153 rc = RTThreadCreate(&Thread, pdmR3ThreadMain, pThread, cbStack, enmType, RTTHREADFLAGS_WAITABLE, pszName);
[3523]154 if (RT_SUCCESS(rc))
155 {
[12324]156 rc = RTThreadUserWait(Thread, 60*1000);
157 if ( RT_SUCCESS(rc)
158 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
[39402]159 rc = VERR_PDM_THREAD_IPE_2;
[12324]160 if (RT_SUCCESS(rc))
161 {
162 /*
163 * Insert it into the thread list.
164 */
[28265]165 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
[12324]166 pThread->Internal.s.pNext = NULL;
[28262]167 if (pUVM->pdm.s.pThreadsTail)
168 pUVM->pdm.s.pThreadsTail->Internal.s.pNext = pThread;
[12324]169 else
[28262]170 pUVM->pdm.s.pThreads = pThread;
171 pUVM->pdm.s.pThreadsTail = pThread;
[28265]172 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
[4421]173
[12324]174 rc = RTThreadUserReset(Thread);
175 AssertRC(rc);
176 return rc;
177 }
178
179 /* bailout */
180 RTThreadWait(Thread, 60*1000, NULL);
[3523]181 }
[12324]182 RTSemEventMultiDestroy(pThread->Internal.s.SleepEvent);
183 pThread->Internal.s.SleepEvent = NIL_RTSEMEVENTMULTI;
[3523]184 }
185 RTSemEventMultiDestroy(pThread->Internal.s.BlockEvent);
186 pThread->Internal.s.BlockEvent = NIL_RTSEMEVENTMULTI;
187 }
[69175]188 MMR3HeapFree(pThread);
[3523]189 *ppThread = NULL;
[3548]190
[3523]191 return rc;
192}
193
194
[4012]195/**
196 * Device Helper for creating a thread associated with a device.
[4455]197 *
[4012]198 * @returns VBox status code.
[58122]199 * @param pVM The cross context VM structure.
[4012]200 * @param pDevIns The device instance.
201 * @param ppThread Where to store the thread 'handle'.
202 * @param pvUser The user argument to the thread function.
203 * @param pfnThread The thread function.
[4421]204 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
[4012]205 * a state change is pending.
206 * @param cbStack See RTThreadCreate.
207 * @param enmType See RTThreadCreate.
208 * @param pszName See RTThreadCreate.
209 */
210int pdmR3ThreadCreateDevice(PVM pVM, PPDMDEVINS pDevIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADDEV pfnThread,
[4421]211 PFNPDMTHREADWAKEUPDEV pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
[3523]212{
213 int rc = pdmR3ThreadNew(pVM, ppThread);
214 if (RT_SUCCESS(rc))
215 {
[12986]216 PPDMTHREAD pThread = *ppThread;
217 pThread->pvUser = pvUser;
218 pThread->Internal.s.enmType = PDMTHREADTYPE_DEVICE;
219 pThread->u.Dev.pDevIns = pDevIns;
220 pThread->u.Dev.pfnThread = pfnThread;
221 pThread->u.Dev.pfnWakeUp = pfnWakeUp;
[3548]222 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
[3523]223 }
224 return rc;
225}
226
227
228/**
[4012]229 * USB Device Helper for creating a thread associated with an USB device.
[4455]230 *
[4012]231 * @returns VBox status code.
[58122]232 * @param pVM The cross context VM structure.
[4012]233 * @param pUsbIns The USB device instance.
234 * @param ppThread Where to store the thread 'handle'.
235 * @param pvUser The user argument to the thread function.
236 * @param pfnThread The thread function.
[4421]237 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
[4012]238 * a state change is pending.
239 * @param cbStack See RTThreadCreate.
240 * @param enmType See RTThreadCreate.
241 * @param pszName See RTThreadCreate.
242 */
243int pdmR3ThreadCreateUsb(PVM pVM, PPDMUSBINS pUsbIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADUSB pfnThread,
[4421]244 PFNPDMTHREADWAKEUPUSB pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
[4012]245{
246 int rc = pdmR3ThreadNew(pVM, ppThread);
247 if (RT_SUCCESS(rc))
248 {
[12986]249 PPDMTHREAD pThread = *ppThread;
250 pThread->pvUser = pvUser;
251 pThread->Internal.s.enmType = PDMTHREADTYPE_USB;
252 pThread->u.Usb.pUsbIns = pUsbIns;
253 pThread->u.Usb.pfnThread = pfnThread;
254 pThread->u.Usb.pfnWakeUp = pfnWakeUp;
[4012]255 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
256 }
257 return rc;
258}
259
260
261/**
262 * Driver Helper for creating a thread associated with a driver.
[4455]263 *
[4012]264 * @returns VBox status code.
[58122]265 * @param pVM The cross context VM structure.
[4012]266 * @param pDrvIns The driver instance.
267 * @param ppThread Where to store the thread 'handle'.
268 * @param pvUser The user argument to the thread function.
269 * @param pfnThread The thread function.
[4421]270 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
[4012]271 * a state change is pending.
272 * @param cbStack See RTThreadCreate.
273 * @param enmType See RTThreadCreate.
274 * @param pszName See RTThreadCreate.
275 */
276int pdmR3ThreadCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADDRV pfnThread,
[4421]277 PFNPDMTHREADWAKEUPDRV pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
[4012]278{
279 int rc = pdmR3ThreadNew(pVM, ppThread);
280 if (RT_SUCCESS(rc))
281 {
[12986]282 PPDMTHREAD pThread = *ppThread;
283 pThread->pvUser = pvUser;
284 pThread->Internal.s.enmType = PDMTHREADTYPE_DRIVER;
285 pThread->u.Drv.pDrvIns = pDrvIns;
286 pThread->u.Drv.pfnThread = pfnThread;
287 pThread->u.Drv.pfnWakeUp = pfnWakeUp;
[4012]288 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
289 }
290 return rc;
291}
292
293
294/**
295 * Creates a PDM thread for internal use in the VM.
[4455]296 *
[4012]297 * @returns VBox status code.
[58122]298 * @param pVM The cross context VM structure.
[4012]299 * @param ppThread Where to store the thread 'handle'.
300 * @param pvUser The user argument to the thread function.
301 * @param pfnThread The thread function.
[4421]302 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
[4012]303 * a state change is pending.
304 * @param cbStack See RTThreadCreate.
305 * @param enmType See RTThreadCreate.
306 * @param pszName See RTThreadCreate.
307 */
[12989]308VMMR3DECL(int) PDMR3ThreadCreate(PVM pVM, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADINT pfnThread,
[4421]309 PFNPDMTHREADWAKEUPINT pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
[4012]310{
311 int rc = pdmR3ThreadNew(pVM, ppThread);
312 if (RT_SUCCESS(rc))
313 {
[12986]314 PPDMTHREAD pThread = *ppThread;
315 pThread->pvUser = pvUser;
316 pThread->Internal.s.enmType = PDMTHREADTYPE_INTERNAL;
317 pThread->u.Int.pfnThread = pfnThread;
318 pThread->u.Int.pfnWakeUp = pfnWakeUp;
[4012]319 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
320 }
321 return rc;
322}
323
324
325/**
326 * Creates a PDM thread for VM use by some external party.
[4455]327 *
[4012]328 * @returns VBox status code.
[58122]329 * @param pVM The cross context VM structure.
[4012]330 * @param ppThread Where to store the thread 'handle'.
331 * @param pvUser The user argument to the thread function.
332 * @param pfnThread The thread function.
[4421]333 * @param pfnWakeUp The wakup callback. This is called on the EMT thread when
[4012]334 * a state change is pending.
335 * @param cbStack See RTThreadCreate.
336 * @param enmType See RTThreadCreate.
337 * @param pszName See RTThreadCreate.
338 */
[12989]339VMMR3DECL(int) PDMR3ThreadCreateExternal(PVM pVM, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADEXT pfnThread,
[4421]340 PFNPDMTHREADWAKEUPEXT pfnWakeUp, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
[4012]341{
342 int rc = pdmR3ThreadNew(pVM, ppThread);
343 if (RT_SUCCESS(rc))
344 {
[12986]345 PPDMTHREAD pThread = *ppThread;
346 pThread->pvUser = pvUser;
347 pThread->Internal.s.enmType = PDMTHREADTYPE_EXTERNAL;
348 pThread->u.Ext.pfnThread = pfnThread;
349 pThread->u.Ext.pfnWakeUp = pfnWakeUp;
[4012]350 rc = pdmR3ThreadInit(pVM, ppThread, cbStack, enmType, pszName);
351 }
352 return rc;
353}
354
355
356/**
[3523]357 * Destroys a PDM thread.
358 *
359 * This will wakeup the thread, tell it to terminate, and wait for it terminate.
360 *
361 * @returns VBox status code.
[3548]362 * This reflects the success off destroying the thread and not the exit code
[3523]363 * of the thread as this is stored in *pRcThread.
364 * @param pThread The thread to destroy.
365 * @param pRcThread Where to store the thread exit code. Optional.
366 * @thread The emulation thread (EMT).
367 */
[12989]368VMMR3DECL(int) PDMR3ThreadDestroy(PPDMTHREAD pThread, int *pRcThread)
[3523]369{
370 /*
371 * Assert sanity.
372 */
373 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
374 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
375 Assert(pThread->Thread != RTThreadSelf());
376 AssertPtrNullReturn(pRcThread, VERR_INVALID_POINTER);
377 PVM pVM = pThread->Internal.s.pVM;
378 VM_ASSERT_EMT(pVM);
[28262]379 PUVM pUVM = pVM->pUVM;
[3548]380
[3523]381 /*
382 * Advance the thread to the terminating state.
383 */
384 int rc = VINF_SUCCESS;
385 if (pThread->enmState <= PDMTHREADSTATE_TERMINATING)
386 {
387 for (;;)
388 {
389 PDMTHREADSTATE enmState = pThread->enmState;
390 switch (enmState)
391 {
392 case PDMTHREADSTATE_RUNNING:
[3548]393 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
[3523]394 continue;
[4421]395 rc = pdmR3ThreadWakeUp(pThread);
[3523]396 break;
397
[4421]398 case PDMTHREADSTATE_SUSPENDED:
[3523]399 case PDMTHREADSTATE_SUSPENDING:
400 case PDMTHREADSTATE_RESUMING:
401 case PDMTHREADSTATE_INITIALIZING:
[3548]402 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
[3523]403 continue;
404 break;
405
406 case PDMTHREADSTATE_TERMINATING:
407 case PDMTHREADSTATE_TERMINATED:
408 break;
409
410 default:
411 AssertMsgFailed(("enmState=%d\n", enmState));
[39402]412 rc = VERR_PDM_THREAD_IPE_2;
[3523]413 break;
414 }
415 break;
416 }
417 }
[4421]418 int rc2 = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
419 AssertRC(rc2);
[3523]420
421 /*
422 * Wait for it to terminate and the do cleanups.
423 */
[4421]424 rc2 = RTThreadWait(pThread->Thread, RT_SUCCESS(rc) ? 60*1000 : 150, pRcThread);
[3523]425 if (RT_SUCCESS(rc2))
426 {
427 /* make it invalid. */
428 pThread->u32Version = 0xffffffff;
429 pThread->enmState = PDMTHREADSTATE_INVALID;
430 pThread->Thread = NIL_RTTHREAD;
431
432 /* unlink */
[28265]433 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
[28262]434 if (pUVM->pdm.s.pThreads == pThread)
[4421]435 {
[28262]436 pUVM->pdm.s.pThreads = pThread->Internal.s.pNext;
[4421]437 if (!pThread->Internal.s.pNext)
[28262]438 pUVM->pdm.s.pThreadsTail = NULL;
[4421]439 }
[3523]440 else
441 {
[28262]442 PPDMTHREAD pPrev = pUVM->pdm.s.pThreads;
[3548]443 while (pPrev && pPrev->Internal.s.pNext != pThread)
[3523]444 pPrev = pPrev->Internal.s.pNext;
445 Assert(pPrev);
446 if (pPrev)
447 pPrev->Internal.s.pNext = pThread->Internal.s.pNext;
[4421]448 if (!pThread->Internal.s.pNext)
[28262]449 pUVM->pdm.s.pThreadsTail = pPrev;
[3523]450 }
451 pThread->Internal.s.pNext = NULL;
[28265]452 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
[3523]453
[12324]454 /* free the resources */
455 RTSemEventMultiDestroy(pThread->Internal.s.BlockEvent);
456 pThread->Internal.s.BlockEvent = NIL_RTSEMEVENTMULTI;
457
458 RTSemEventMultiDestroy(pThread->Internal.s.SleepEvent);
459 pThread->Internal.s.SleepEvent = NIL_RTSEMEVENTMULTI;
460
[3523]461 MMR3HeapFree(pThread);
462 }
463 else if (RT_SUCCESS(rc))
464 rc = rc2;
465
466 return rc;
467}
468
469
[4012]470/**
471 * Destroys all threads associated with a device.
472 *
[4455]473 * This function is called by PDMDevice when a device is
[4012]474 * destroyed (not currently implemented).
[4455]475 *
[4012]476 * @returns VBox status code of the first failure.
[58122]477 * @param pVM The cross context VM structure.
[4012]478 * @param pDevIns the device instance.
479 */
480int pdmR3ThreadDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
[3523]481{
[28262]482 int rc = VINF_SUCCESS;
483 PUVM pUVM = pVM->pUVM;
[3523]484
485 AssertPtr(pDevIns);
[28265]486
487 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
[28262]488 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
[3523]489 while (pThread)
490 {
491 PPDMTHREAD pNext = pThread->Internal.s.pNext;
492 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DEVICE
[3548]493 && pThread->u.Dev.pDevIns == pDevIns)
[3523]494 {
495 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
496 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
497 rc = rc2;
498 }
[3548]499 pThread = pNext;
[3523]500 }
[28265]501 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
[3523]502 return rc;
503}
504
505
[4012]506/**
507 * Destroys all threads associated with an USB device.
508 *
509 * This function is called by PDMUsb when a device is destroyed.
[4455]510 *
[4012]511 * @returns VBox status code of the first failure.
[58122]512 * @param pVM The cross context VM structure.
[4012]513 * @param pUsbIns The USB device instance.
514 */
515int pdmR3ThreadDestroyUsb(PVM pVM, PPDMUSBINS pUsbIns)
[3523]516{
[28262]517 int rc = VINF_SUCCESS;
518 PUVM pUVM = pVM->pUVM;
[3523]519
[4012]520 AssertPtr(pUsbIns);
[28265]521
522 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
[28262]523 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
[4012]524 while (pThread)
525 {
526 PPDMTHREAD pNext = pThread->Internal.s.pNext;
527 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DEVICE
528 && pThread->u.Usb.pUsbIns == pUsbIns)
529 {
530 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
531 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
532 rc = rc2;
533 }
534 pThread = pNext;
535 }
[28265]536 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
[4012]537 return rc;
538}
539
540
541/**
542 * Destroys all threads associated with a driver.
543 *
544 * This function is called by PDMDriver when a driver is destroyed.
[4455]545 *
[4012]546 * @returns VBox status code of the first failure.
[58122]547 * @param pVM The cross context VM structure.
[4012]548 * @param pDrvIns The driver instance.
549 */
550int pdmR3ThreadDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
551{
[28262]552 int rc = VINF_SUCCESS;
553 PUVM pUVM = pVM->pUVM;
[4012]554
[3523]555 AssertPtr(pDrvIns);
[28265]556
557 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
[28262]558 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
[3523]559 while (pThread)
560 {
561 PPDMTHREAD pNext = pThread->Internal.s.pNext;
562 if ( pThread->Internal.s.enmType == PDMTHREADTYPE_DRIVER
[3548]563 && pThread->u.Drv.pDrvIns == pDrvIns)
[3523]564 {
565 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
566 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
567 rc = rc2;
568 }
[3548]569 pThread = pNext;
[3523]570 }
[28265]571 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
[3523]572 return rc;
573}
574
575
576/**
577 * Called For VM power off.
578 *
[58122]579 * @param pVM The cross context VM structure.
[3523]580 */
581void pdmR3ThreadDestroyAll(PVM pVM)
582{
[28265]583 PUVM pUVM = pVM->pUVM;
584 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
585 PPDMTHREAD pThread = pUVM->pdm.s.pThreads;
[3523]586 while (pThread)
587 {
588 PPDMTHREAD pNext = pThread->Internal.s.pNext;
589 int rc2 = PDMR3ThreadDestroy(pThread, NULL);
[3548]590 AssertRC(rc2);
591 pThread = pNext;
[3523]592 }
[28262]593 Assert(!pUVM->pdm.s.pThreads && !pUVM->pdm.s.pThreadsTail);
[28265]594 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
[3523]595}
596
597
598/**
[3548]599 * Initiate termination of the thread (self) because something failed in a bad way.
[3523]600 *
601 * @param pThread The PDM thread.
602 */
603static void pdmR3ThreadBailMeOut(PPDMTHREAD pThread)
604{
605 for (;;)
606 {
607 PDMTHREADSTATE enmState = pThread->enmState;
608 switch (enmState)
609 {
610 case PDMTHREADSTATE_SUSPENDING:
611 case PDMTHREADSTATE_SUSPENDED:
612 case PDMTHREADSTATE_RESUMING:
613 case PDMTHREADSTATE_RUNNING:
[3548]614 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
[3523]615 continue;
616 break;
617
618 case PDMTHREADSTATE_TERMINATING:
619 case PDMTHREADSTATE_TERMINATED:
620 break;
621
622 case PDMTHREADSTATE_INITIALIZING:
623 default:
624 AssertMsgFailed(("enmState=%d\n", enmState));
625 break;
626 }
627 break;
628 }
629}
630
631
632/**
[3548]633 * Called by the PDM thread in response to a wakeup call with
[3523]634 * suspending as the new state.
635 *
[3548]636 * The thread will block in side this call until the state is changed in
637 * response to a VM state change or to the device/driver/whatever calling the
[3523]638 * PDMR3ThreadResume API.
639 *
640 * @returns VBox status code.
641 * On failure, terminate the thread.
642 * @param pThread The PDM thread.
643 */
[12989]644VMMR3DECL(int) PDMR3ThreadIAmSuspending(PPDMTHREAD pThread)
[3523]645{
646 /*
647 * Assert sanity.
648 */
649 AssertPtr(pThread);
[3548]650 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
[3523]651 Assert(pThread->Thread == RTThreadSelf() || pThread->enmState == PDMTHREADSTATE_INITIALIZING);
652 PDMTHREADSTATE enmState = pThread->enmState;
653 Assert( enmState == PDMTHREADSTATE_SUSPENDING
654 || enmState == PDMTHREADSTATE_INITIALIZING);
655
656 /*
657 * Update the state, notify the control thread (the API caller) and go to sleep.
658 */
659 int rc = VERR_WRONG_ORDER;
[3548]660 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDED, enmState))
[3523]661 {
662 rc = RTThreadUserSignal(pThread->Thread);
663 if (RT_SUCCESS(rc))
664 {
[3548]665 rc = RTSemEventMultiWait(pThread->Internal.s.BlockEvent, RT_INDEFINITE_WAIT);
[3523]666 if ( RT_SUCCESS(rc)
667 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
668 return rc;
[3548]669
[3523]670 if (RT_SUCCESS(rc))
[39402]671 rc = VERR_PDM_THREAD_IPE_2;
[3523]672 }
673 }
674
[3548]675 AssertMsgFailed(("rc=%d enmState=%d\n", rc, pThread->enmState));
[3523]676 pdmR3ThreadBailMeOut(pThread);
677 return rc;
678}
679
680
681/**
682 * Called by the PDM thread in response to a resuming state.
683 *
684 * The purpose of this API is to tell the PDMR3ThreadResume caller that
[14299]685 * the PDM thread has successfully resumed. It will also do the
[3523]686 * state transition from the resuming to the running state.
687 *
688 * @returns VBox status code.
689 * On failure, terminate the thread.
[3548]690 * @param pThread The PDM thread.
[3523]691 */
[12989]692VMMR3DECL(int) PDMR3ThreadIAmRunning(PPDMTHREAD pThread)
[3523]693{
694 /*
695 * Assert sanity.
696 */
697 Assert(pThread->enmState == PDMTHREADSTATE_RESUMING);
698 Assert(pThread->Thread == RTThreadSelf());
699
700 /*
701 * Update the state and tell the control thread (the guy calling the resume API).
702 */
703 int rc = VERR_WRONG_ORDER;
[3548]704 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RUNNING, PDMTHREADSTATE_RESUMING))
[3523]705 {
706 rc = RTThreadUserSignal(pThread->Thread);
707 if (RT_SUCCESS(rc))
708 return rc;
709 }
[3548]710
711 AssertMsgFailed(("rc=%d enmState=%d\n", rc, pThread->enmState));
[3523]712 pdmR3ThreadBailMeOut(pThread);
713 return rc;
714}
715
716
717/**
[12324]718 * Called by the PDM thread instead of RTThreadSleep.
719 *
720 * The difference is that the sleep will be interrupted on state change. The
721 * thread must be in the running state, otherwise it will return immediately.
722 *
723 * @returns VBox status code.
724 * @retval VINF_SUCCESS on success or state change.
725 * @retval VERR_INTERRUPTED on signal or APC.
726 *
727 * @param pThread The PDM thread.
728 * @param cMillies The number of milliseconds to sleep.
729 */
[25728]730VMMR3DECL(int) PDMR3ThreadSleep(PPDMTHREAD pThread, RTMSINTERVAL cMillies)
[12324]731{
732 /*
733 * Assert sanity.
734 */
[39402]735 AssertReturn(pThread->enmState > PDMTHREADSTATE_INVALID && pThread->enmState < PDMTHREADSTATE_TERMINATED, VERR_PDM_THREAD_IPE_2);
736 AssertReturn(pThread->Thread == RTThreadSelf(), VERR_PDM_THREAD_INVALID_CALLER);
[12324]737
738 /*
739 * Reset the event semaphore, check the state and sleep.
740 */
741 RTSemEventMultiReset(pThread->Internal.s.SleepEvent);
742 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
743 return VINF_SUCCESS;
744 return RTSemEventMultiWaitNoResume(pThread->Internal.s.SleepEvent, cMillies);
745}
746
747
748/**
[3523]749 * The PDM thread function.
750 *
[3548]751 * @returns return from pfnThread.
[3523]752 *
753 * @param Thread The thread handle.
754 * @param pvUser Pointer to the PDMTHREAD structure.
755 */
756static DECLCALLBACK(int) pdmR3ThreadMain(RTTHREAD Thread, void *pvUser)
757{
758 PPDMTHREAD pThread = (PPDMTHREAD)pvUser;
759 Log(("PDMThread: Initializing thread %RTthrd / %p / '%s'...\n", Thread, pThread, RTThreadGetName(Thread)));
[4455]760 pThread->Thread = Thread;
[3523]761
[36437]762 PUVM pUVM = pThread->Internal.s.pVM->pUVM;
763 if ( pUVM->pVmm2UserMethods
764 && pUVM->pVmm2UserMethods->pfnNotifyPdmtInit)
765 pUVM->pVmm2UserMethods->pfnNotifyPdmtInit(pUVM->pVmm2UserMethods, pUVM);
766
[3523]767 /*
768 * The run loop.
769 *
[3548]770 * It handles simple thread functions which returns when they see a suspending
771 * request and leaves the PDMR3ThreadIAmSuspending and PDMR3ThreadIAmRunning
[3523]772 * parts to us.
773 */
774 int rc;
775 for (;;)
776 {
777 switch (pThread->Internal.s.enmType)
778 {
779 case PDMTHREADTYPE_DEVICE:
[3548]780 rc = pThread->u.Dev.pfnThread(pThread->u.Dev.pDevIns, pThread);
[3523]781 break;
[3548]782
[4012]783 case PDMTHREADTYPE_USB:
784 rc = pThread->u.Usb.pfnThread(pThread->u.Usb.pUsbIns, pThread);
785 break;
786
[3523]787 case PDMTHREADTYPE_DRIVER:
[3548]788 rc = pThread->u.Drv.pfnThread(pThread->u.Drv.pDrvIns, pThread);
[3523]789 break;
[3548]790
[3523]791 case PDMTHREADTYPE_INTERNAL:
[3548]792 rc = pThread->u.Int.pfnThread(pThread->Internal.s.pVM, pThread);
[3523]793 break;
[3548]794
[3523]795 case PDMTHREADTYPE_EXTERNAL:
[3548]796 rc = pThread->u.Ext.pfnThread(pThread);
[3523]797 break;
[3548]798
[3523]799 default:
800 AssertMsgFailed(("%d\n", pThread->Internal.s.enmType));
[39402]801 rc = VERR_PDM_THREAD_IPE_1;
[3523]802 break;
803 }
804 if (RT_FAILURE(rc))
805 break;
806
[3548]807 /*
808 * If this is a simple thread function, the state will be suspending
[3523]809 * or initializing now. If it isn't we're supposed to terminate.
810 */
811 if ( pThread->enmState != PDMTHREADSTATE_SUSPENDING
812 && pThread->enmState != PDMTHREADSTATE_INITIALIZING)
813 {
814 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
815 break;
816 }
817 rc = PDMR3ThreadIAmSuspending(pThread);
818 if (RT_FAILURE(rc))
819 break;
820 if (pThread->enmState != PDMTHREADSTATE_RESUMING)
821 {
822 Assert(pThread->enmState == PDMTHREADSTATE_TERMINATING);
823 break;
824 }
825
826 rc = PDMR3ThreadIAmRunning(pThread);
827 if (RT_FAILURE(rc))
828 break;
829 }
830
[15540]831 if (RT_FAILURE(rc))
832 LogRel(("PDMThread: Thread '%s' (%RTthrd) quit unexpectedly with rc=%Rrc.\n", RTThreadGetName(Thread), Thread, rc));
833
[3523]834 /*
835 * Advance the state to terminating and then on to terminated.
836 */
837 for (;;)
838 {
839 PDMTHREADSTATE enmState = pThread->enmState;
840 if ( enmState == PDMTHREADSTATE_TERMINATING
[3548]841 || pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
[3523]842 break;
843 }
844
[3548]845 ASMAtomicXchgSize(&pThread->enmState, PDMTHREADSTATE_TERMINATED);
[3523]846 int rc2 = RTThreadUserSignal(Thread); AssertRC(rc2);
[3548]847
[36437]848 if ( pUVM->pVmm2UserMethods
849 && pUVM->pVmm2UserMethods->pfnNotifyPdmtTerm)
850 pUVM->pVmm2UserMethods->pfnNotifyPdmtTerm(pUVM->pVmm2UserMethods, pUVM);
[3523]851 Log(("PDMThread: Terminating thread %RTthrd / %p / '%s': %Rrc\n", Thread, pThread, RTThreadGetName(Thread), rc));
852 return rc;
853}
854
855
856/**
[3548]857 * Initiate termination of the thread because something failed in a bad way.
[3523]858 *
859 * @param pThread The PDM thread.
860 */
861static void pdmR3ThreadBailOut(PPDMTHREAD pThread)
862{
863 for (;;)
864 {
865 PDMTHREADSTATE enmState = pThread->enmState;
866 switch (enmState)
867 {
868 case PDMTHREADSTATE_SUSPENDING:
869 case PDMTHREADSTATE_SUSPENDED:
[3548]870 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
[3523]871 continue;
872 RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
873 break;
874
875 case PDMTHREADSTATE_RESUMING:
[3548]876 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
[3523]877 continue;
878 break;
879
880 case PDMTHREADSTATE_RUNNING:
[3548]881 if (!pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_TERMINATING, enmState))
[3523]882 continue;
[4421]883 pdmR3ThreadWakeUp(pThread);
[3523]884 break;
885
886 case PDMTHREADSTATE_TERMINATING:
887 case PDMTHREADSTATE_TERMINATED:
888 break;
889
890 case PDMTHREADSTATE_INITIALIZING:
891 default:
892 AssertMsgFailed(("enmState=%d\n", enmState));
893 break;
894 }
895 break;
896 }
897}
898
899
900/**
901 * Suspends the thread.
902 *
[4012]903 * This can be called at the power off / suspend notifications to suspend the
[3523]904 * PDM thread a bit early. The thread will be automatically suspend upon
[4012]905 * completion of the device/driver notification cycle.
[4455]906 *
[3548]907 * The caller is responsible for serializing the control operations on the
[3523]908 * thread. That basically means, always do these calls from the EMT.
909 *
910 * @returns VBox status code.
911 * @param pThread The PDM thread.
912 */
[12989]913VMMR3DECL(int) PDMR3ThreadSuspend(PPDMTHREAD pThread)
[3523]914{
915 /*
916 * Assert sanity.
917 */
918 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
[3548]919 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
[3523]920 Assert(pThread->Thread != RTThreadSelf());
921
922 /*
[24244]923 * This is a noop if the thread is already suspended.
924 */
925 if (pThread->enmState == PDMTHREADSTATE_SUSPENDED)
926 return VINF_SUCCESS;
927
928 /*
[3523]929 * Change the state to resuming and kick the thread.
930 */
931 int rc = RTSemEventMultiReset(pThread->Internal.s.BlockEvent);
932 if (RT_SUCCESS(rc))
933 {
934 rc = RTThreadUserReset(pThread->Thread);
935 if (RT_SUCCESS(rc))
936 {
937 rc = VERR_WRONG_ORDER;
[3548]938 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_SUSPENDING, PDMTHREADSTATE_RUNNING))
[3523]939 {
[4421]940 rc = pdmR3ThreadWakeUp(pThread);
[3523]941 if (RT_SUCCESS(rc))
942 {
943 /*
944 * Wait for the thread to reach the suspended state.
945 */
946 if (pThread->enmState != PDMTHREADSTATE_SUSPENDED)
947 rc = RTThreadUserWait(pThread->Thread, 60*1000);
[3548]948 if ( RT_SUCCESS(rc)
[3523]949 && pThread->enmState != PDMTHREADSTATE_SUSPENDED)
[39402]950 rc = VERR_PDM_THREAD_IPE_2;
[3523]951 if (RT_SUCCESS(rc))
952 return rc;
953 }
954 }
955 }
956 }
957
958 /*
959 * Something failed, initialize termination.
960 */
[43866]961 AssertMsgFailed(("PDMR3ThreadSuspend -> rc=%Rrc enmState=%d suspending '%s'\n",
962 rc, pThread->enmState, RTThreadGetName(pThread->Thread)));
[3523]963 pdmR3ThreadBailOut(pThread);
964 return rc;
965}
966
967
968/**
969 * Suspend all running threads.
970 *
[3548]971 * This is called by PDMR3Suspend() and PDMR3PowerOff() after all the devices
[3523]972 * and drivers have been notified about the suspend / power off.
[3548]973 *
[3523]974 * @return VBox status code.
[58122]975 * @param pVM The cross context VM structure.
[3523]976 */
977int pdmR3ThreadSuspendAll(PVM pVM)
978{
[28265]979 PUVM pUVM = pVM->pUVM;
980 RTCritSectEnter(&pUVM->pdm.s.ListCritSect); /* This may cause deadlocks later... */
981 for (PPDMTHREAD pThread = pUVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
[3523]982 switch (pThread->enmState)
983 {
984 case PDMTHREADSTATE_RUNNING:
985 {
986 int rc = PDMR3ThreadSuspend(pThread);
987 AssertRCReturn(rc, rc);
988 break;
989 }
990
[4421]991 /* suspend -> power off; voluntary suspend. */
992 case PDMTHREADSTATE_SUSPENDED:
993 break;
994
[3523]995 default:
996 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
997 break;
998 }
[28265]999 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
[3523]1000 return VINF_SUCCESS;
1001}
1002
1003
1004/**
1005 * Resumes the thread.
1006 *
[3548]1007 * This can be called the power on / resume notifications to resume the
[3523]1008 * PDM thread a bit early. The thread will be automatically resumed upon
1009 * return from these two notification callbacks (devices/drivers).
1010 *
[3548]1011 * The caller is responsible for serializing the control operations on the
[3523]1012 * thread. That basically means, always do these calls from the EMT.
1013 *
1014 * @returns VBox status code.
1015 * @param pThread The PDM thread.
1016 */
[12989]1017VMMR3DECL(int) PDMR3ThreadResume(PPDMTHREAD pThread)
[3523]1018{
1019 /*
1020 * Assert sanity.
1021 */
1022 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
[3548]1023 AssertReturn(pThread->u32Version == PDMTHREAD_VERSION, VERR_INVALID_MAGIC);
[3523]1024 Assert(pThread->Thread != RTThreadSelf());
1025
1026 /*
1027 * Change the state to resuming and kick the thread.
1028 */
1029 int rc = RTThreadUserReset(pThread->Thread);
1030 if (RT_SUCCESS(rc))
1031 {
1032 rc = VERR_WRONG_ORDER;
[3548]1033 if (pdmR3AtomicCmpXchgState(pThread, PDMTHREADSTATE_RESUMING, PDMTHREADSTATE_SUSPENDED))
[3523]1034 {
1035 rc = RTSemEventMultiSignal(pThread->Internal.s.BlockEvent);
1036 if (RT_SUCCESS(rc))
1037 {
1038 /*
1039 * Wait for the thread to reach the running state.
1040 */
1041 rc = RTThreadUserWait(pThread->Thread, 60*1000);
[3548]1042 if ( RT_SUCCESS(rc)
1043 && pThread->enmState != PDMTHREADSTATE_RUNNING)
[39402]1044 rc = VERR_PDM_THREAD_IPE_2;
[3523]1045 if (RT_SUCCESS(rc))
1046 return rc;
1047 }
1048 }
1049 }
1050
1051 /*
1052 * Something failed, initialize termination.
1053 */
1054 AssertMsgFailed(("PDMR3ThreadResume -> rc=%Rrc enmState=%d\n", rc, pThread->enmState));
1055 pdmR3ThreadBailOut(pThread);
1056 return rc;
1057}
1058
1059
1060/**
1061 * Resumes all threads not running.
1062 *
[3548]1063 * This is called by PDMR3Resume() and PDMR3PowerOn() after all the devices
[3523]1064 * and drivers have been notified about the resume / power on .
[3548]1065 *
[3523]1066 * @return VBox status code.
[58122]1067 * @param pVM The cross context VM structure.
[3523]1068 */
1069int pdmR3ThreadResumeAll(PVM pVM)
1070{
[28265]1071 PUVM pUVM = pVM->pUVM;
1072 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1073 for (PPDMTHREAD pThread = pUVM->pdm.s.pThreads; pThread; pThread = pThread->Internal.s.pNext)
[3523]1074 switch (pThread->enmState)
1075 {
1076 case PDMTHREADSTATE_SUSPENDED:
1077 {
1078 int rc = PDMR3ThreadResume(pThread);
1079 AssertRCReturn(rc, rc);
1080 break;
1081 }
1082
1083 default:
1084 AssertMsgFailed(("pThread=%p enmState=%d\n", pThread, pThread->enmState));
1085 break;
1086 }
[28265]1087 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
[3523]1088 return VINF_SUCCESS;
1089}
1090
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use