VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMCritSect.cpp@ 80673

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

PDM/DevHlp: Need to wrap the crit sect methods so we can pass on pVM to the crit sect code later, as we won't be able to sore pointers in the internal critical section data anymore. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 38.8 KB
Line 
1/* $Id: PDMCritSect.cpp 80673 2019-09-09 14:02:22Z vboxsync $ */
2/** @file
3 * PDM - Critical Sections, Ring-3.
4 */
5
6/*
7 * Copyright (C) 2006-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 LOG_GROUP LOG_GROUP_PDM//_CRITSECT
23#include "PDMInternal.h"
24#include <VBox/vmm/pdmcritsect.h>
25#include <VBox/vmm/pdmcritsectrw.h>
26#include <VBox/vmm/mm.h>
27#include <VBox/vmm/vm.h>
28#include <VBox/vmm/uvm.h>
29
30#include <VBox/err.h>
31#include <VBox/log.h>
32#include <VBox/sup.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/lockvalidator.h>
36#include <iprt/string.h>
37#include <iprt/thread.h>
38
39
40/*********************************************************************************************************************************
41* Internal Functions *
42*********************************************************************************************************************************/
43static int pdmR3CritSectDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTINT pCritSect, PPDMCRITSECTINT pPrev, bool fFinal);
44static int pdmR3CritSectRwDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTRWINT pCritSect, PPDMCRITSECTRWINT pPrev, bool fFinal);
45
46
47
48/**
49 * Register statistics related to the critical sections.
50 *
51 * @returns VBox status code.
52 * @param pVM The cross context VM structure.
53 */
54int pdmR3CritSectBothInitStats(PVM pVM)
55{
56 RT_NOREF_PV(pVM);
57 STAM_REG(pVM, &pVM->pdm.s.StatQueuedCritSectLeaves, STAMTYPE_COUNTER, "/PDM/QueuedCritSectLeaves", STAMUNIT_OCCURENCES,
58 "Number of times a critical section leave request needed to be queued for ring-3 execution.");
59 return VINF_SUCCESS;
60}
61
62
63/**
64 * Relocates all the critical sections.
65 *
66 * @param pVM The cross context VM structure.
67 */
68void pdmR3CritSectBothRelocate(PVM pVM)
69{
70 PUVM pUVM = pVM->pUVM;
71 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
72
73 for (PPDMCRITSECTINT pCur = pUVM->pdm.s.pCritSects;
74 pCur;
75 pCur = pCur->pNext)
76 pCur->pVMRC = pVM->pVMRC;
77
78 for (PPDMCRITSECTRWINT pCur = pUVM->pdm.s.pRwCritSects;
79 pCur;
80 pCur = pCur->pNext)
81 pCur->pVMRC = pVM->pVMRC;
82
83 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
84}
85
86
87/**
88 * Deletes all remaining critical sections.
89 *
90 * This is called at the very end of the termination process. It is also called
91 * at the end of vmR3CreateU failure cleanup, which may cause it to be called
92 * twice depending on where vmR3CreateU actually failed. We have to do the
93 * latter call because other components expect the critical sections to be
94 * automatically deleted.
95 *
96 * @returns VBox status code.
97 * First error code, rest is lost.
98 * @param pVM The cross context VM structure.
99 * @remark Don't confuse this with PDMR3CritSectDelete.
100 */
101VMMR3_INT_DECL(int) PDMR3CritSectBothTerm(PVM pVM)
102{
103 PUVM pUVM = pVM->pUVM;
104 int rc = VINF_SUCCESS;
105 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
106
107 while (pUVM->pdm.s.pCritSects)
108 {
109 int rc2 = pdmR3CritSectDeleteOne(pVM, pUVM, pUVM->pdm.s.pCritSects, NULL, true /* final */);
110 AssertRC(rc2);
111 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
112 rc = rc2;
113 }
114
115 while (pUVM->pdm.s.pRwCritSects)
116 {
117 int rc2 = pdmR3CritSectRwDeleteOne(pVM, pUVM, pUVM->pdm.s.pRwCritSects, NULL, true /* final */);
118 AssertRC(rc2);
119 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
120 rc = rc2;
121 }
122
123 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
124 return rc;
125}
126
127
128/**
129 * Initializes a critical section and inserts it into the list.
130 *
131 * @returns VBox status code.
132 * @param pVM The cross context VM structure.
133 * @param pCritSect The critical section.
134 * @param pvKey The owner key.
135 * @param SRC_POS The source position.
136 * @param pszNameFmt Format string for naming the critical section. For
137 * statistics and lock validation.
138 * @param va Arguments for the format string.
139 */
140static int pdmR3CritSectInitOne(PVM pVM, PPDMCRITSECTINT pCritSect, void *pvKey, RT_SRC_POS_DECL,
141 const char *pszNameFmt, va_list va)
142{
143 VM_ASSERT_EMT(pVM);
144 Assert(pCritSect->Core.u32Magic != RTCRITSECT_MAGIC);
145
146 /*
147 * Allocate the semaphore.
148 */
149 AssertCompile(sizeof(SUPSEMEVENT) == sizeof(pCritSect->Core.EventSem));
150 int rc = SUPSemEventCreate(pVM->pSession, (PSUPSEMEVENT)&pCritSect->Core.EventSem);
151 if (RT_SUCCESS(rc))
152 {
153 /* Only format the name once. */
154 char *pszName = RTStrAPrintf2V(pszNameFmt, va); /** @todo plug the "leak"... */
155 if (pszName)
156 {
157 RT_SRC_POS_NOREF();
158#ifndef PDMCRITSECT_STRICT
159 pCritSect->Core.pValidatorRec = NULL;
160#else
161 rc = RTLockValidatorRecExclCreate(&pCritSect->Core.pValidatorRec,
162# ifdef RT_LOCK_STRICT_ORDER
163 RTLockValidatorClassForSrcPos(RT_SRC_POS_ARGS, "%s", pszName),
164# else
165 NIL_RTLOCKVALCLASS,
166# endif
167 RTLOCKVAL_SUB_CLASS_NONE,
168 pCritSect, true, "%s", pszName);
169#endif
170 if (RT_SUCCESS(rc))
171 {
172 /*
173 * Initialize the structure (first bit is c&p from RTCritSectInitEx).
174 */
175 pCritSect->Core.u32Magic = RTCRITSECT_MAGIC;
176 pCritSect->Core.fFlags = 0;
177 pCritSect->Core.cNestings = 0;
178 pCritSect->Core.cLockers = -1;
179 pCritSect->Core.NativeThreadOwner = NIL_RTNATIVETHREAD;
180 pCritSect->pVMR3 = pVM;
181 pCritSect->pVMR0 = pVM->pVMR0ForCall;
182 pCritSect->pVMRC = pVM->pVMRC;
183 pCritSect->pvKey = pvKey;
184 pCritSect->fAutomaticDefaultCritsect = false;
185 pCritSect->fUsedByTimerOrSimilar = false;
186 pCritSect->hEventToSignal = NIL_SUPSEMEVENT;
187 pCritSect->pszName = pszName;
188
189 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLock, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZLock", pCritSect->pszName);
190 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZUnlock,STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZUnlock", pCritSect->pszName);
191 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionR3", pCritSect->pszName);
192#ifdef VBOX_WITH_STATISTICS
193 STAMR3RegisterF(pVM, &pCritSect->StatLocked, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, NULL, "/PDM/CritSects/%s/Locked", pCritSect->pszName);
194#endif
195
196 PUVM pUVM = pVM->pUVM;
197 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
198 pCritSect->pNext = pUVM->pdm.s.pCritSects;
199 pUVM->pdm.s.pCritSects = pCritSect;
200 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
201
202 return VINF_SUCCESS;
203 }
204
205 RTStrFree(pszName);
206 }
207 else
208 rc = VERR_NO_STR_MEMORY;
209 SUPSemEventClose(pVM->pSession, (SUPSEMEVENT)pCritSect->Core.EventSem);
210 }
211 return rc;
212}
213
214
215/**
216 * Initializes a read/write critical section and inserts it into the list.
217 *
218 * @returns VBox status code.
219 * @param pVM The cross context VM structure.
220 * @param pCritSect The read/write critical section.
221 * @param pvKey The owner key.
222 * @param SRC_POS The source position.
223 * @param pszNameFmt Format string for naming the critical section. For
224 * statistics and lock validation.
225 * @param va Arguments for the format string.
226 */
227static int pdmR3CritSectRwInitOne(PVM pVM, PPDMCRITSECTRWINT pCritSect, void *pvKey, RT_SRC_POS_DECL,
228 const char *pszNameFmt, va_list va)
229{
230 VM_ASSERT_EMT(pVM);
231 Assert(pCritSect->Core.u32Magic != RTCRITSECTRW_MAGIC);
232
233 /*
234 * Allocate the semaphores.
235 */
236 AssertCompile(sizeof(SUPSEMEVENT) == sizeof(pCritSect->Core.hEvtWrite));
237 int rc = SUPSemEventCreate(pVM->pSession, (PSUPSEMEVENT)&pCritSect->Core.hEvtWrite);
238 if (RT_SUCCESS(rc))
239 {
240 AssertCompile(sizeof(SUPSEMEVENTMULTI) == sizeof(pCritSect->Core.hEvtRead));
241 rc = SUPSemEventMultiCreate(pVM->pSession, (PSUPSEMEVENT)&pCritSect->Core.hEvtRead);
242 if (RT_SUCCESS(rc))
243 {
244 /* Only format the name once. */
245 char *pszName = RTStrAPrintf2V(pszNameFmt, va); /** @todo plug the "leak"... */
246 if (pszName)
247 {
248 pCritSect->Core.pValidatorRead = NULL;
249 pCritSect->Core.pValidatorWrite = NULL;
250 RT_SRC_POS_NOREF();
251#ifdef PDMCRITSECTRW_STRICT
252# ifdef RT_LOCK_STRICT_ORDER
253 RTLOCKVALCLASS hClass = RTLockValidatorClassForSrcPos(RT_SRC_POS_ARGS, "%s", pszName);
254# else
255 RTLOCKVALCLASS hClass = NIL_RTLOCKVALCLASS;
256# endif
257 rc = RTLockValidatorRecExclCreate(&pCritSect->Core.pValidatorWrite, hClass, RTLOCKVAL_SUB_CLASS_NONE,
258 pCritSect, true, "%s", pszName);
259 if (RT_SUCCESS(rc))
260 rc = RTLockValidatorRecSharedCreate(&pCritSect->Core.pValidatorRead, hClass, RTLOCKVAL_SUB_CLASS_NONE,
261 pCritSect, false /*fSignaller*/, true, "%s", pszName);
262#endif
263 if (RT_SUCCESS(rc))
264 {
265 /*
266 * Initialize the structure (first bit is c&p from RTCritSectRwInitEx).
267 */
268 pCritSect->Core.u32Magic = RTCRITSECTRW_MAGIC;
269 pCritSect->Core.fNeedReset = false;
270 pCritSect->Core.u64State = 0;
271 pCritSect->Core.hNativeWriter = NIL_RTNATIVETHREAD;
272 pCritSect->Core.cWriterReads = 0;
273 pCritSect->Core.cWriteRecursions = 0;
274#if HC_ARCH_BITS == 32
275 pCritSect->Core.HCPtrPadding = NIL_RTHCPTR;
276#endif
277 pCritSect->pVMR3 = pVM;
278 pCritSect->pVMR0 = pVM->pVMR0ForCall;
279 pCritSect->pVMRC = pVM->pVMRC;
280 pCritSect->pvKey = pvKey;
281 pCritSect->pszName = pszName;
282
283 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZEnterExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionRZEnterExcl", pCritSect->pszName);
284 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLeaveExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionRZLeaveExcl", pCritSect->pszName);
285 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZEnterShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionRZEnterShared", pCritSect->pszName);
286 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLeaveShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionRZLeaveShared", pCritSect->pszName);
287 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3EnterExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionR3EnterExcl", pCritSect->pszName);
288 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3EnterShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionR3EnterShared", pCritSect->pszName);
289 STAMR3RegisterF(pVM, &pCritSect->StatRZEnterExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/RZEnterExcl", pCritSect->pszName);
290 STAMR3RegisterF(pVM, &pCritSect->StatRZEnterShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/RZEnterShared", pCritSect->pszName);
291 STAMR3RegisterF(pVM, &pCritSect->StatR3EnterExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/R3EnterExcl", pCritSect->pszName);
292 STAMR3RegisterF(pVM, &pCritSect->StatR3EnterShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/R3EnterShared", pCritSect->pszName);
293#ifdef VBOX_WITH_STATISTICS
294 STAMR3RegisterF(pVM, &pCritSect->StatWriteLocked, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, NULL, "/PDM/CritSectsRw/%s/WriteLocked", pCritSect->pszName);
295#endif
296
297 PUVM pUVM = pVM->pUVM;
298 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
299 pCritSect->pNext = pUVM->pdm.s.pRwCritSects;
300 pUVM->pdm.s.pRwCritSects = pCritSect;
301 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
302
303 return VINF_SUCCESS;
304 }
305
306 RTStrFree(pszName);
307 }
308 else
309 rc = VERR_NO_STR_MEMORY;
310 SUPSemEventMultiClose(pVM->pSession, (SUPSEMEVENT)pCritSect->Core.hEvtRead);
311 }
312 SUPSemEventClose(pVM->pSession, (SUPSEMEVENT)pCritSect->Core.hEvtWrite);
313 }
314 return rc;
315}
316
317
318/**
319 * Initializes a PDM critical section for internal use.
320 *
321 * The PDM critical sections are derived from the IPRT critical sections, but
322 * works in ring-0 and raw-mode context as well.
323 *
324 * @returns VBox status code.
325 * @param pVM The cross context VM structure.
326 * @param pCritSect Pointer to the critical section.
327 * @param SRC_POS Use RT_SRC_POS.
328 * @param pszNameFmt Format string for naming the critical section. For
329 * statistics and lock validation.
330 * @param ... Arguments for the format string.
331 * @thread EMT
332 */
333VMMR3DECL(int) PDMR3CritSectInit(PVM pVM, PPDMCRITSECT pCritSect, RT_SRC_POS_DECL, const char *pszNameFmt, ...)
334{
335#if HC_ARCH_BITS == 64 && GC_ARCH_BITS == 32
336 AssertCompile(sizeof(pCritSect->padding) >= sizeof(pCritSect->s));
337#endif
338 Assert(RT_ALIGN_P(pCritSect, sizeof(uintptr_t)) == pCritSect);
339 va_list va;
340 va_start(va, pszNameFmt);
341 int rc = pdmR3CritSectInitOne(pVM, &pCritSect->s, pCritSect, RT_SRC_POS_ARGS, pszNameFmt, va);
342 va_end(va);
343 return rc;
344}
345
346
347/**
348 * Initializes a PDM read/write critical section for internal use.
349 *
350 * The PDM read/write critical sections are derived from the IPRT read/write
351 * critical sections, but works in ring-0 and raw-mode context as well.
352 *
353 * @returns VBox status code.
354 * @param pVM The cross context VM structure.
355 * @param pCritSect Pointer to the read/write critical section.
356 * @param SRC_POS Use RT_SRC_POS.
357 * @param pszNameFmt Format string for naming the critical section. For
358 * statistics and lock validation.
359 * @param ... Arguments for the format string.
360 * @thread EMT
361 */
362VMMR3DECL(int) PDMR3CritSectRwInit(PVM pVM, PPDMCRITSECTRW pCritSect, RT_SRC_POS_DECL, const char *pszNameFmt, ...)
363{
364#if HC_ARCH_BITS == 64 && GC_ARCH_BITS == 32
365 AssertCompile(sizeof(pCritSect->padding) >= sizeof(pCritSect->s));
366#endif
367 Assert(RT_ALIGN_P(pCritSect, sizeof(uintptr_t)) == pCritSect);
368 va_list va;
369 va_start(va, pszNameFmt);
370 int rc = pdmR3CritSectRwInitOne(pVM, &pCritSect->s, pCritSect, RT_SRC_POS_ARGS, pszNameFmt, va);
371 va_end(va);
372 return rc;
373}
374
375
376/**
377 * Initializes a PDM critical section for a device.
378 *
379 * @returns VBox status code.
380 * @param pVM The cross context VM structure.
381 * @param pDevIns Device instance.
382 * @param pCritSect Pointer to the critical section.
383 * @param SRC_POS The source position. Optional.
384 * @param pszNameFmt Format string for naming the critical section. For
385 * statistics and lock validation.
386 * @param va Arguments for the format string.
387 */
388int pdmR3CritSectInitDevice(PVM pVM, PPDMDEVINS pDevIns, PPDMCRITSECT pCritSect, RT_SRC_POS_DECL,
389 const char *pszNameFmt, va_list va)
390{
391 return pdmR3CritSectInitOne(pVM, &pCritSect->s, pDevIns, RT_SRC_POS_ARGS, pszNameFmt, va);
392}
393
394
395/**
396 * Initializes a PDM read/write critical section for a device.
397 *
398 * @returns VBox status code.
399 * @param pVM The cross context VM structure.
400 * @param pDevIns Device instance.
401 * @param pCritSect Pointer to the read/write critical section.
402 * @param SRC_POS The source position. Optional.
403 * @param pszNameFmt Format string for naming the critical section. For
404 * statistics and lock validation.
405 * @param va Arguments for the format string.
406 */
407int pdmR3CritSectRwInitDevice(PVM pVM, PPDMDEVINS pDevIns, PPDMCRITSECTRW pCritSect, RT_SRC_POS_DECL,
408 const char *pszNameFmt, va_list va)
409{
410 return pdmR3CritSectRwInitOne(pVM, &pCritSect->s, pDevIns, RT_SRC_POS_ARGS, pszNameFmt, va);
411}
412
413
414/**
415 * Initializes the automatic default PDM critical section for a device.
416 *
417 * @returns VBox status code.
418 * @param pVM The cross context VM structure.
419 * @param pDevIns Device instance.
420 * @param SRC_POS The source position. Optional.
421 * @param pCritSect Pointer to the critical section.
422 * @param pszNameFmt Format string for naming the critical section. For
423 * statistics and lock validation.
424 * @param ... Arguments for the format string.
425 */
426int pdmR3CritSectInitDeviceAuto(PVM pVM, PPDMDEVINS pDevIns, PPDMCRITSECT pCritSect, RT_SRC_POS_DECL,
427 const char *pszNameFmt, ...)
428{
429 va_list va;
430 va_start(va, pszNameFmt);
431 int rc = pdmR3CritSectInitOne(pVM, &pCritSect->s, pDevIns, RT_SRC_POS_ARGS, pszNameFmt, va);
432 if (RT_SUCCESS(rc))
433 pCritSect->s.fAutomaticDefaultCritsect = true;
434 va_end(va);
435 return rc;
436}
437
438
439/**
440 * Initializes a PDM critical section for a driver.
441 *
442 * @returns VBox status code.
443 * @param pVM The cross context VM structure.
444 * @param pDrvIns Driver instance.
445 * @param pCritSect Pointer to the critical section.
446 * @param SRC_POS The source position. Optional.
447 * @param pszNameFmt Format string for naming the critical section. For
448 * statistics and lock validation.
449 * @param ... Arguments for the format string.
450 */
451int pdmR3CritSectInitDriver(PVM pVM, PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect, RT_SRC_POS_DECL,
452 const char *pszNameFmt, ...)
453{
454 va_list va;
455 va_start(va, pszNameFmt);
456 int rc = pdmR3CritSectInitOne(pVM, &pCritSect->s, pDrvIns, RT_SRC_POS_ARGS, pszNameFmt, va);
457 va_end(va);
458 return rc;
459}
460
461
462/**
463 * Initializes a PDM read/write critical section for a driver.
464 *
465 * @returns VBox status code.
466 * @param pVM The cross context VM structure.
467 * @param pDrvIns Driver instance.
468 * @param pCritSect Pointer to the read/write critical section.
469 * @param SRC_POS The source position. Optional.
470 * @param pszNameFmt Format string for naming the critical section. For
471 * statistics and lock validation.
472 * @param ... Arguments for the format string.
473 */
474int pdmR3CritSectRwInitDriver(PVM pVM, PPDMDRVINS pDrvIns, PPDMCRITSECTRW pCritSect, RT_SRC_POS_DECL,
475 const char *pszNameFmt, ...)
476{
477 va_list va;
478 va_start(va, pszNameFmt);
479 int rc = pdmR3CritSectRwInitOne(pVM, &pCritSect->s, pDrvIns, RT_SRC_POS_ARGS, pszNameFmt, va);
480 va_end(va);
481 return rc;
482}
483
484
485/**
486 * Deletes one critical section.
487 *
488 * @returns Return code from RTCritSectDelete.
489 *
490 * @param pVM The cross context VM structure.
491 * @param pUVM The user mode VM handle.
492 * @param pCritSect The critical section.
493 * @param pPrev The previous critical section in the list.
494 * @param fFinal Set if this is the final call and statistics shouldn't be deregistered.
495 *
496 * @remarks Caller must have entered the ListCritSect.
497 */
498static int pdmR3CritSectDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTINT pCritSect, PPDMCRITSECTINT pPrev, bool fFinal)
499{
500 /*
501 * Assert free waiters and so on (c&p from RTCritSectDelete).
502 */
503 Assert(pCritSect->Core.u32Magic == RTCRITSECT_MAGIC);
504 //Assert(pCritSect->Core.cNestings == 0); - we no longer reset this when leaving.
505 Assert(pCritSect->Core.cLockers == -1);
506 Assert(pCritSect->Core.NativeThreadOwner == NIL_RTNATIVETHREAD);
507 Assert(RTCritSectIsOwner(&pUVM->pdm.s.ListCritSect));
508
509 /*
510 * Unlink it.
511 */
512 if (pPrev)
513 pPrev->pNext = pCritSect->pNext;
514 else
515 pUVM->pdm.s.pCritSects = pCritSect->pNext;
516
517 /*
518 * Delete it (parts taken from RTCritSectDelete).
519 * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
520 */
521 ASMAtomicWriteU32(&pCritSect->Core.u32Magic, 0);
522 SUPSEMEVENT hEvent = (SUPSEMEVENT)pCritSect->Core.EventSem;
523 pCritSect->Core.EventSem = NIL_RTSEMEVENT;
524 while (pCritSect->Core.cLockers-- >= 0)
525 SUPSemEventSignal(pVM->pSession, hEvent);
526 ASMAtomicWriteS32(&pCritSect->Core.cLockers, -1);
527 int rc = SUPSemEventClose(pVM->pSession, hEvent);
528 AssertRC(rc);
529 RTLockValidatorRecExclDestroy(&pCritSect->Core.pValidatorRec);
530 pCritSect->pNext = NULL;
531 pCritSect->pvKey = NULL;
532 pCritSect->pVMR3 = NULL;
533 pCritSect->pVMR0 = NIL_RTR0PTR;
534 pCritSect->pVMRC = NIL_RTRCPTR;
535 if (!fFinal)
536 STAMR3DeregisterF(pVM->pUVM, "/PDM/CritSects/%s/*", pCritSect->pszName);
537 RTStrFree((char *)pCritSect->pszName);
538 pCritSect->pszName = NULL;
539 return rc;
540}
541
542
543/**
544 * Deletes one read/write critical section.
545 *
546 * @returns VBox status code.
547 *
548 * @param pVM The cross context VM structure.
549 * @param pUVM The user mode VM handle.
550 * @param pCritSect The read/write critical section.
551 * @param pPrev The previous critical section in the list.
552 * @param fFinal Set if this is the final call and statistics shouldn't be deregistered.
553 *
554 * @remarks Caller must have entered the ListCritSect.
555 */
556static int pdmR3CritSectRwDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTRWINT pCritSect, PPDMCRITSECTRWINT pPrev, bool fFinal)
557{
558 /*
559 * Assert free waiters and so on (c&p from RTCritSectRwDelete).
560 */
561 Assert(pCritSect->Core.u32Magic == RTCRITSECTRW_MAGIC);
562 //Assert(pCritSect->Core.cNestings == 0);
563 //Assert(pCritSect->Core.cLockers == -1);
564 Assert(pCritSect->Core.hNativeWriter == NIL_RTNATIVETHREAD);
565
566 /*
567 * Invalidate the structure and free the semaphores.
568 */
569 if (!ASMAtomicCmpXchgU32(&pCritSect->Core.u32Magic, RTCRITSECTRW_MAGIC_DEAD, RTCRITSECTRW_MAGIC))
570 AssertFailed();
571
572 /*
573 * Unlink it.
574 */
575 if (pPrev)
576 pPrev->pNext = pCritSect->pNext;
577 else
578 pUVM->pdm.s.pRwCritSects = pCritSect->pNext;
579
580 /*
581 * Delete it (parts taken from RTCritSectRwDelete).
582 * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
583 */
584 pCritSect->Core.fFlags = 0;
585 pCritSect->Core.u64State = 0;
586
587 SUPSEMEVENT hEvtWrite = (SUPSEMEVENT)pCritSect->Core.hEvtWrite;
588 pCritSect->Core.hEvtWrite = NIL_RTSEMEVENT;
589 AssertCompile(sizeof(hEvtWrite) == sizeof(pCritSect->Core.hEvtWrite));
590
591 SUPSEMEVENTMULTI hEvtRead = (SUPSEMEVENTMULTI)pCritSect->Core.hEvtRead;
592 pCritSect->Core.hEvtRead = NIL_RTSEMEVENTMULTI;
593 AssertCompile(sizeof(hEvtRead) == sizeof(pCritSect->Core.hEvtRead));
594
595 int rc1 = SUPSemEventClose(pVM->pSession, hEvtWrite); AssertRC(rc1);
596 int rc2 = SUPSemEventMultiClose(pVM->pSession, hEvtRead); AssertRC(rc2);
597
598 RTLockValidatorRecSharedDestroy(&pCritSect->Core.pValidatorRead);
599 RTLockValidatorRecExclDestroy(&pCritSect->Core.pValidatorWrite);
600
601 pCritSect->pNext = NULL;
602 pCritSect->pvKey = NULL;
603 pCritSect->pVMR3 = NULL;
604 pCritSect->pVMR0 = NIL_RTR0PTR;
605 pCritSect->pVMRC = NIL_RTRCPTR;
606 if (!fFinal)
607 STAMR3DeregisterF(pVM->pUVM, "/PDM/CritSectsRw/%s/*", pCritSect->pszName);
608 RTStrFree((char *)pCritSect->pszName);
609 pCritSect->pszName = NULL;
610
611 return RT_SUCCESS(rc1) ? rc2 : rc1;
612}
613
614
615/**
616 * Deletes all critical sections with a give initializer key.
617 *
618 * @returns VBox status code.
619 * The entire list is processed on failure, so we'll only
620 * return the first error code. This shouldn't be a problem
621 * since errors really shouldn't happen here.
622 * @param pVM The cross context VM structure.
623 * @param pvKey The initializer key.
624 */
625static int pdmR3CritSectDeleteByKey(PVM pVM, void *pvKey)
626{
627 /*
628 * Iterate the list and match key.
629 */
630 PUVM pUVM = pVM->pUVM;
631 int rc = VINF_SUCCESS;
632 PPDMCRITSECTINT pPrev = NULL;
633 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
634 PPDMCRITSECTINT pCur = pUVM->pdm.s.pCritSects;
635 while (pCur)
636 {
637 if (pCur->pvKey == pvKey)
638 {
639 int rc2 = pdmR3CritSectDeleteOne(pVM, pUVM, pCur, pPrev, false /* not final */);
640 AssertRC(rc2);
641 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
642 rc = rc2;
643 }
644
645 /* next */
646 pPrev = pCur;
647 pCur = pCur->pNext;
648 }
649 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
650 return rc;
651}
652
653
654/**
655 * Deletes all read/write critical sections with a give initializer key.
656 *
657 * @returns VBox status code.
658 * The entire list is processed on failure, so we'll only
659 * return the first error code. This shouldn't be a problem
660 * since errors really shouldn't happen here.
661 * @param pVM The cross context VM structure.
662 * @param pvKey The initializer key.
663 */
664static int pdmR3CritSectRwDeleteByKey(PVM pVM, void *pvKey)
665{
666 /*
667 * Iterate the list and match key.
668 */
669 PUVM pUVM = pVM->pUVM;
670 int rc = VINF_SUCCESS;
671 PPDMCRITSECTRWINT pPrev = NULL;
672 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
673 PPDMCRITSECTRWINT pCur = pUVM->pdm.s.pRwCritSects;
674 while (pCur)
675 {
676 if (pCur->pvKey == pvKey)
677 {
678 int rc2 = pdmR3CritSectRwDeleteOne(pVM, pUVM, pCur, pPrev, false /* not final */);
679 AssertRC(rc2);
680 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
681 rc = rc2;
682 }
683
684 /* next */
685 pPrev = pCur;
686 pCur = pCur->pNext;
687 }
688 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
689 return rc;
690}
691
692
693/**
694 * Deletes all undeleted critical sections (both types) initialized by a given
695 * device.
696 *
697 * @returns VBox status code.
698 * @param pVM The cross context VM structure.
699 * @param pDevIns The device handle.
700 */
701int pdmR3CritSectBothDeleteDevice(PVM pVM, PPDMDEVINS pDevIns)
702{
703 int rc1 = pdmR3CritSectDeleteByKey(pVM, pDevIns);
704 int rc2 = pdmR3CritSectRwDeleteByKey(pVM, pDevIns);
705 return RT_SUCCESS(rc1) ? rc2 : rc1;
706}
707
708
709/**
710 * Deletes all undeleted critical sections (both types) initialized by a given
711 * driver.
712 *
713 * @returns VBox status code.
714 * @param pVM The cross context VM structure.
715 * @param pDrvIns The driver handle.
716 */
717int pdmR3CritSectBothDeleteDriver(PVM pVM, PPDMDRVINS pDrvIns)
718{
719 int rc1 = pdmR3CritSectDeleteByKey(pVM, pDrvIns);
720 int rc2 = pdmR3CritSectRwDeleteByKey(pVM, pDrvIns);
721 return RT_SUCCESS(rc1) ? rc2 : rc1;
722}
723
724
725/**
726 * Deletes the critical section.
727 *
728 * @returns VBox status code.
729 * @param pCritSect The PDM critical section to destroy.
730 */
731VMMR3DECL(int) PDMR3CritSectDelete(PPDMCRITSECT pCritSect)
732{
733 if (!RTCritSectIsInitialized(&pCritSect->s.Core))
734 return VINF_SUCCESS;
735
736 /*
737 * Find and unlink it.
738 */
739 PVM pVM = pCritSect->s.pVMR3;
740 PUVM pUVM = pVM->pUVM;
741 AssertReleaseReturn(pVM, VERR_PDM_CRITSECT_IPE);
742 PPDMCRITSECTINT pPrev = NULL;
743 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
744 PPDMCRITSECTINT pCur = pUVM->pdm.s.pCritSects;
745 while (pCur)
746 {
747 if (pCur == &pCritSect->s)
748 {
749 int rc = pdmR3CritSectDeleteOne(pVM, pUVM, pCur, pPrev, false /* not final */);
750 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
751 return rc;
752 }
753
754 /* next */
755 pPrev = pCur;
756 pCur = pCur->pNext;
757 }
758 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
759 AssertReleaseMsgFailed(("pCritSect=%p wasn't found!\n", pCritSect));
760 return VERR_PDM_CRITSECT_NOT_FOUND;
761}
762
763
764/**
765 * Deletes the read/write critical section.
766 *
767 * @returns VBox status code.
768 * @param pCritSect The PDM read/write critical section to destroy.
769 */
770VMMR3DECL(int) PDMR3CritSectRwDelete(PPDMCRITSECTRW pCritSect)
771{
772 if (!PDMCritSectRwIsInitialized(pCritSect))
773 return VINF_SUCCESS;
774
775 /*
776 * Find and unlink it.
777 */
778 PVM pVM = pCritSect->s.pVMR3;
779 PUVM pUVM = pVM->pUVM;
780 AssertReleaseReturn(pVM, VERR_PDM_CRITSECT_IPE);
781 PPDMCRITSECTRWINT pPrev = NULL;
782 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
783 PPDMCRITSECTRWINT pCur = pUVM->pdm.s.pRwCritSects;
784 while (pCur)
785 {
786 if (pCur == &pCritSect->s)
787 {
788 int rc = pdmR3CritSectRwDeleteOne(pVM, pUVM, pCur, pPrev, false /* not final */);
789 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
790 return rc;
791 }
792
793 /* next */
794 pPrev = pCur;
795 pCur = pCur->pNext;
796 }
797 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
798 AssertReleaseMsgFailed(("pCritSect=%p wasn't found!\n", pCritSect));
799 return VERR_PDM_CRITSECT_NOT_FOUND;
800}
801
802
803/**
804 * Gets the name of the critical section.
805 *
806 *
807 * @returns Pointer to the critical section name (read only) on success,
808 * NULL on failure (invalid critical section).
809 * @param pCritSect The critical section.
810 */
811VMMR3DECL(const char *) PDMR3CritSectName(PCPDMCRITSECT pCritSect)
812{
813 AssertPtrReturn(pCritSect, NULL);
814 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, NULL);
815 return pCritSect->s.pszName;
816}
817
818
819/**
820 * Gets the name of the read/write critical section.
821 *
822 *
823 * @returns Pointer to the critical section name (read only) on success,
824 * NULL on failure (invalid critical section).
825 * @param pCritSect The read/write critical section.
826 */
827VMMR3DECL(const char *) PDMR3CritSectRwName(PCPDMCRITSECTRW pCritSect)
828{
829 AssertPtrReturn(pCritSect, NULL);
830 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECTRW_MAGIC, NULL);
831 return pCritSect->s.pszName;
832}
833
834
835/**
836 * Yield the critical section if someone is waiting on it.
837 *
838 * When yielding, we'll leave the critical section and try to make sure the
839 * other waiting threads get a chance of entering before we reclaim it.
840 *
841 * @retval true if yielded.
842 * @retval false if not yielded.
843 * @param pVM The cross context VM structure.
844 * @param pCritSect The critical section.
845 */
846VMMR3DECL(bool) PDMR3CritSectYield(PVM pVM, PPDMCRITSECT pCritSect)
847{
848 AssertPtrReturn(pCritSect, false);
849 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, false);
850 Assert(pCritSect->s.Core.NativeThreadOwner == RTThreadNativeSelf());
851 Assert(!(pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NOP));
852 RT_NOREF(pVM);
853
854 /* No recursion allowed here. */
855 int32_t const cNestings = pCritSect->s.Core.cNestings;
856 AssertReturn(cNestings == 1, false);
857
858 int32_t const cLockers = ASMAtomicReadS32(&pCritSect->s.Core.cLockers);
859 if (cLockers < cNestings)
860 return false;
861
862#ifdef PDMCRITSECT_STRICT
863 RTLOCKVALSRCPOS const SrcPos = pCritSect->s.Core.pValidatorRec->SrcPos;
864#endif
865 PDMCritSectLeave(pCritSect);
866
867 /*
868 * If we're lucky, then one of the waiters has entered the lock already.
869 * We spin a little bit in hope for this to happen so we can avoid the
870 * yield detour.
871 */
872 if (ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0)
873 {
874 int cLoops = 20;
875 while ( cLoops > 0
876 && ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0
877 && ASMAtomicUoReadS32(&pCritSect->s.Core.cLockers) >= 0)
878 {
879 ASMNopPause();
880 cLoops--;
881 }
882 if (cLoops == 0)
883 RTThreadYield();
884 }
885
886#ifdef PDMCRITSECT_STRICT
887 int rc = PDMCritSectEnterDebug(pCritSect, VERR_IGNORED,
888 SrcPos.uId, SrcPos.pszFile, SrcPos.uLine, SrcPos.pszFunction);
889#else
890 int rc = PDMCritSectEnter(pCritSect, VERR_IGNORED);
891#endif
892 AssertLogRelRC(rc);
893 return true;
894}
895
896
897/**
898 * PDMR3CritSectBothCountOwned worker.
899 *
900 * @param pszName The critical section name.
901 * @param ppszNames Pointer to the pszNames variable.
902 * @param pcchLeft Pointer to the cchLeft variable.
903 * @param fFirst Whether this is the first name or not.
904 */
905static void pdmR3CritSectAppendNameToList(char const *pszName, char **ppszNames, size_t *pcchLeft, bool fFirst)
906{
907 size_t cchLeft = *pcchLeft;
908 if (cchLeft)
909 {
910 char *pszNames = *ppszNames;
911
912 /* try add comma. */
913 if (fFirst)
914 {
915 *pszNames++ = ',';
916 if (--cchLeft)
917 {
918 *pszNames++ = ' ';
919 cchLeft--;
920 }
921 }
922
923 /* try copy the name. */
924 if (cchLeft)
925 {
926 size_t const cchName = strlen(pszName);
927 if (cchName < cchLeft)
928 {
929 memcpy(pszNames, pszName, cchName);
930 pszNames += cchName;
931 cchLeft -= cchName;
932 }
933 else
934 {
935 if (cchLeft > 2)
936 {
937 memcpy(pszNames, pszName, cchLeft - 2);
938 pszNames += cchLeft - 2;
939 cchLeft = 2;
940 }
941 while (cchLeft-- > 0)
942 *pszNames++ = '+';
943 }
944 }
945 *pszNames = '\0';
946
947 *pcchLeft = cchLeft;
948 *ppszNames = pszNames;
949 }
950}
951
952
953/**
954 * Counts the critical sections (both type) owned by the calling thread,
955 * optionally returning a comma separated list naming them.
956 *
957 * Read ownerships are not included in non-strict builds.
958 *
959 * This is for diagnostic purposes only.
960 *
961 * @returns Lock count.
962 *
963 * @param pVM The cross context VM structure.
964 * @param pszNames Where to return the critical section names.
965 * @param cbNames The size of the buffer.
966 */
967VMMR3DECL(uint32_t) PDMR3CritSectCountOwned(PVM pVM, char *pszNames, size_t cbNames)
968{
969 /*
970 * Init the name buffer.
971 */
972 size_t cchLeft = cbNames;
973 if (cchLeft)
974 {
975 cchLeft--;
976 pszNames[0] = pszNames[cchLeft] = '\0';
977 }
978
979 /*
980 * Iterate the critical sections.
981 */
982 uint32_t cCritSects = 0;
983 RTNATIVETHREAD const hNativeThread = RTThreadNativeSelf();
984 /* This is unsafe, but wtf. */
985 for (PPDMCRITSECTINT pCur = pVM->pUVM->pdm.s.pCritSects;
986 pCur;
987 pCur = pCur->pNext)
988 {
989 /* Same as RTCritSectIsOwner(). */
990 if (pCur->Core.NativeThreadOwner == hNativeThread)
991 {
992 cCritSects++;
993 pdmR3CritSectAppendNameToList(pCur->pszName, &pszNames, &cchLeft, cCritSects == 1);
994 }
995 }
996
997 /* This is unsafe, but wtf. */
998 for (PPDMCRITSECTRWINT pCur = pVM->pUVM->pdm.s.pRwCritSects;
999 pCur;
1000 pCur = pCur->pNext)
1001 {
1002 if ( pCur->Core.hNativeWriter == hNativeThread
1003 || PDMCritSectRwIsReadOwner((PPDMCRITSECTRW)pCur, false /*fWannaHear*/) )
1004 {
1005 cCritSects++;
1006 pdmR3CritSectAppendNameToList(pCur->pszName, &pszNames, &cchLeft, cCritSects == 1);
1007 }
1008 }
1009
1010 return cCritSects;
1011}
1012
1013
1014/**
1015 * Leave all critical sections the calling thread owns.
1016 *
1017 * This is only used when entering guru meditation in order to prevent other
1018 * EMTs and I/O threads from deadlocking.
1019 *
1020 * @param pVM The cross context VM structure.
1021 */
1022VMMR3_INT_DECL(void) PDMR3CritSectLeaveAll(PVM pVM)
1023{
1024 RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
1025 PUVM pUVM = pVM->pUVM;
1026
1027 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1028 for (PPDMCRITSECTINT pCur = pUVM->pdm.s.pCritSects;
1029 pCur;
1030 pCur = pCur->pNext)
1031 {
1032 while ( pCur->Core.NativeThreadOwner == hNativeSelf
1033 && pCur->Core.cNestings > 0)
1034 PDMCritSectLeave((PPDMCRITSECT)pCur);
1035 }
1036 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1037}
1038
1039
1040/**
1041 * Gets the address of the NOP critical section.
1042 *
1043 * The NOP critical section will not perform any thread serialization but let
1044 * all enter immediately and concurrently.
1045 *
1046 * @returns The address of the NOP critical section.
1047 * @param pVM The cross context VM structure.
1048 */
1049VMMR3DECL(PPDMCRITSECT) PDMR3CritSectGetNop(PVM pVM)
1050{
1051 VM_ASSERT_VALID_EXT_RETURN(pVM, NULL);
1052 return &pVM->pdm.s.NopCritSect;
1053}
1054
1055
1056/**
1057 * Gets the ring-0 address of the NOP critical section.
1058 *
1059 * @returns The ring-0 address of the NOP critical section.
1060 * @param pVM The cross context VM structure.
1061 */
1062VMMR3DECL(R0PTRTYPE(PPDMCRITSECT)) PDMR3CritSectGetNopR0(PVM pVM)
1063{
1064 VM_ASSERT_VALID_EXT_RETURN(pVM, NIL_RTR0PTR);
1065 return MMHyperR3ToR0(pVM, &pVM->pdm.s.NopCritSect);
1066}
1067
1068
1069/**
1070 * Gets the raw-mode context address of the NOP critical section.
1071 *
1072 * @returns The raw-mode context address of the NOP critical section.
1073 * @param pVM The cross context VM structure.
1074 */
1075VMMR3DECL(RCPTRTYPE(PPDMCRITSECT)) PDMR3CritSectGetNopRC(PVM pVM)
1076{
1077 VM_ASSERT_VALID_EXT_RETURN(pVM, NIL_RTRCPTR);
1078 return MMHyperR3ToRC(pVM, &pVM->pdm.s.NopCritSect);
1079}
1080
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use