VirtualBox

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

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

VMM/r3: Refactored VMCPU enumeration in preparation that aCpus will be replaced with a pointer array. Removed two raw-mode offset members from the CPUM and CPUMCPU sub-structures. bugref:9217 bugref:9517

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