VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 53.3 KB
Line 
1/* $Id: PDMCritSect.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * PDM - Critical Sections, Ring-3.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_PDM_CRITSECT
33#include "PDMInternal.h"
34#include <VBox/vmm/pdmcritsect.h>
35#include <VBox/vmm/pdmcritsectrw.h>
36#include <VBox/vmm/mm.h>
37#include <VBox/vmm/vm.h>
38#include <VBox/vmm/uvm.h>
39
40#include <VBox/err.h>
41#include <VBox/log.h>
42#include <VBox/sup.h>
43#include <iprt/asm.h>
44#include <iprt/assert.h>
45#include <iprt/getopt.h>
46#include <iprt/lockvalidator.h>
47#include <iprt/string.h>
48#include <iprt/thread.h>
49
50
51/*********************************************************************************************************************************
52* Internal Functions *
53*********************************************************************************************************************************/
54static int pdmR3CritSectDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTINT pCritSect, PPDMCRITSECTINT pPrev, bool fFinal);
55static int pdmR3CritSectRwDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTRWINT pCritSect, PPDMCRITSECTRWINT pPrev, bool fFinal);
56static FNDBGFINFOARGVINT pdmR3CritSectInfo;
57static FNDBGFINFOARGVINT pdmR3CritSectRwInfo;
58
59
60
61/**
62 * Register statistics and info items related to the critical sections.
63 *
64 * @returns VBox status code.
65 * @param pVM The cross context VM structure.
66 */
67int pdmR3CritSectBothInitStatsAndInfo(PVM pVM)
68{
69 /*
70 * Statistics.
71 */
72 STAM_REL_REG(pVM, &pVM->pdm.s.StatQueuedCritSectLeaves, STAMTYPE_COUNTER, "/PDM/CritSects/00-QueuedLeaves", STAMUNIT_OCCURENCES,
73 "Number of times a critical section leave request needed to be queued for ring-3 execution.");
74 STAM_REL_REG(pVM, &pVM->pdm.s.StatAbortedCritSectEnters, STAMTYPE_COUNTER, "/PDM/CritSects/00-AbortedEnters", STAMUNIT_OCCURENCES,
75 "Number of times we've successfully aborted a wait in ring-0.");
76 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectEntersWhileAborting, STAMTYPE_COUNTER, "/PDM/CritSects/00-EntersWhileAborting", STAMUNIT_OCCURENCES,
77 "Number of times we've got the critical section ownership while trying to abort a wait due to VERR_INTERRUPTED.");
78 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectVerrInterrupted, STAMTYPE_COUNTER, "/PDM/CritSects/00-VERR_INTERRUPTED", STAMUNIT_OCCURENCES,
79 "Number of VERR_INTERRUPTED returns.");
80 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectVerrTimeout, STAMTYPE_COUNTER, "/PDM/CritSects/00-VERR_TIMEOUT", STAMUNIT_OCCURENCES,
81 "Number of VERR_TIMEOUT returns.");
82 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectNonInterruptibleWaits, STAMTYPE_COUNTER, "/PDM/CritSects/00-Non-interruptible-Waits-VINF_SUCCESS",
83 STAMUNIT_OCCURENCES, "Number of non-interruptible waits for rcBusy=VINF_SUCCESS");
84
85 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectRwExclVerrInterrupted, STAMTYPE_COUNTER, "/PDM/CritSectsRw/00-Excl-VERR_INTERRUPTED", STAMUNIT_OCCURENCES,
86 "Number of VERR_INTERRUPTED returns in exclusive mode.");
87 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectRwExclVerrTimeout, STAMTYPE_COUNTER, "/PDM/CritSectsRw/00-Excl-VERR_TIMEOUT", STAMUNIT_OCCURENCES,
88 "Number of VERR_TIMEOUT returns in exclusive mode.");
89 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectRwExclNonInterruptibleWaits, STAMTYPE_COUNTER, "/PDM/CritSectsRw/00-Excl-Non-interruptible-Waits-VINF_SUCCESS",
90 STAMUNIT_OCCURENCES, "Number of non-interruptible waits for rcBusy=VINF_SUCCESS in exclusive mode");
91
92 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectRwEnterSharedWhileAborting, STAMTYPE_COUNTER, "/PDM/CritSectsRw/00-EnterSharedWhileAborting", STAMUNIT_OCCURENCES,
93 "Number of times we've got the critical section ownership in shared mode while trying to abort a wait due to VERR_INTERRUPTED or VERR_TIMEOUT.");
94 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectRwSharedVerrInterrupted, STAMTYPE_COUNTER, "/PDM/CritSectsRw/00-Shared-VERR_INTERRUPTED", STAMUNIT_OCCURENCES,
95 "Number of VERR_INTERRUPTED returns in exclusive mode.");
96 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectRwSharedVerrTimeout, STAMTYPE_COUNTER, "/PDM/CritSectsRw/00-Shared-VERR_TIMEOUT", STAMUNIT_OCCURENCES,
97 "Number of VERR_TIMEOUT returns in exclusive mode.");
98 STAM_REL_REG(pVM, &pVM->pdm.s.StatCritSectRwSharedNonInterruptibleWaits, STAMTYPE_COUNTER, "/PDM/CritSectsRw/00-Shared-Non-interruptible-Waits-VINF_SUCCESS",
99 STAMUNIT_OCCURENCES, "Number of non-interruptible waits for rcBusy=VINF_SUCCESS in exclusive mode");
100
101 /*
102 * Info items.
103 */
104 DBGFR3InfoRegisterInternalArgv(pVM, "critsect", "Show critical section: critsect [-v] [pattern[...]]", pdmR3CritSectInfo, 0);
105 DBGFR3InfoRegisterInternalArgv(pVM, "critsectrw", "Show read/write critical section: critsectrw [-v] [pattern[...]]",
106 pdmR3CritSectRwInfo, 0);
107
108 return VINF_SUCCESS;
109}
110
111
112/**
113 * Deletes all remaining critical sections.
114 *
115 * This is called at the very end of the termination process. It is also called
116 * at the end of vmR3CreateU failure cleanup, which may cause it to be called
117 * twice depending on where vmR3CreateU actually failed. We have to do the
118 * latter call because other components expect the critical sections to be
119 * automatically deleted.
120 *
121 * @returns VBox status code.
122 * First error code, rest is lost.
123 * @param pVM The cross context VM structure.
124 * @remark Don't confuse this with PDMR3CritSectDelete.
125 */
126VMMR3_INT_DECL(int) PDMR3CritSectBothTerm(PVM pVM)
127{
128 PUVM pUVM = pVM->pUVM;
129 int rc = VINF_SUCCESS;
130 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
131
132 while (pUVM->pdm.s.pCritSects)
133 {
134 int rc2 = pdmR3CritSectDeleteOne(pVM, pUVM, pUVM->pdm.s.pCritSects, NULL, true /* final */);
135 AssertRC(rc2);
136 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
137 rc = rc2;
138 }
139
140 while (pUVM->pdm.s.pRwCritSects)
141 {
142 int rc2 = pdmR3CritSectRwDeleteOne(pVM, pUVM, pUVM->pdm.s.pRwCritSects, NULL, true /* final */);
143 AssertRC(rc2);
144 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
145 rc = rc2;
146 }
147
148 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
149 return rc;
150}
151
152
153/**
154 * Initializes a critical section and inserts it into the list.
155 *
156 * @returns VBox status code.
157 * @param pVM The cross context VM structure.
158 * @param pCritSect The critical section.
159 * @param pvKey The owner key.
160 * @param SRC_POS The source position.
161 * @param fUniqueClass Whether to create a unique lock validator class for
162 * it or not.
163 * @param pszNameFmt Format string for naming the critical section. For
164 * statistics and lock validation.
165 * @param va Arguments for the format string.
166 */
167static int pdmR3CritSectInitOne(PVM pVM, PPDMCRITSECTINT pCritSect, void *pvKey, RT_SRC_POS_DECL, bool fUniqueClass,
168 const char *pszNameFmt, va_list va)
169{
170 VM_ASSERT_EMT(pVM);
171 Assert(pCritSect->Core.u32Magic != RTCRITSECT_MAGIC);
172
173 /*
174 * Allocate the semaphore.
175 */
176 AssertCompile(sizeof(SUPSEMEVENT) == sizeof(pCritSect->Core.EventSem));
177 int rc = SUPSemEventCreate(pVM->pSession, (PSUPSEMEVENT)&pCritSect->Core.EventSem);
178 if (RT_SUCCESS(rc))
179 {
180 /* Only format the name once. */
181 char *pszName = RTStrAPrintf2V(pszNameFmt, va); /** @todo plug the "leak"... */
182 if (pszName)
183 {
184 RT_SRC_POS_NOREF(); RT_NOREF(fUniqueClass);
185#ifndef PDMCRITSECT_STRICT
186 pCritSect->Core.pValidatorRec = NULL;
187#else
188 rc = RTLockValidatorRecExclCreate(&pCritSect->Core.pValidatorRec,
189# ifdef RT_LOCK_STRICT_ORDER
190 fUniqueClass
191 ? RTLockValidatorClassCreateUnique(RT_SRC_POS_ARGS, "%s", pszName)
192 : RTLockValidatorClassForSrcPos(RT_SRC_POS_ARGS, "%s", pszName),
193# else
194 NIL_RTLOCKVALCLASS,
195# endif
196 RTLOCKVAL_SUB_CLASS_NONE,
197 pCritSect, true, "%s", pszName);
198#endif
199 if (RT_SUCCESS(rc))
200 {
201 /*
202 * Initialize the structure (first bit is c&p from RTCritSectInitEx).
203 */
204 pCritSect->Core.u32Magic = RTCRITSECT_MAGIC;
205 pCritSect->Core.fFlags = 0;
206 pCritSect->Core.cNestings = 0;
207 pCritSect->Core.cLockers = -1;
208 pCritSect->Core.NativeThreadOwner = NIL_RTNATIVETHREAD;
209 pCritSect->pvKey = pvKey;
210 pCritSect->fAutomaticDefaultCritsect = false;
211 pCritSect->fUsedByTimerOrSimilar = false;
212 pCritSect->hEventToSignal = NIL_SUPSEMEVENT;
213 pCritSect->pszName = pszName;
214 pCritSect->pSelfR3 = (PPDMCRITSECT)pCritSect;
215
216 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLock, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
217 STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZLock", pCritSect->pszName);
218 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLockBusy, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
219 STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZLockBusy", pCritSect->pszName);
220 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZUnlock, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
221 STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionRZUnlock", pCritSect->pszName);
222 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZWait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
223 STAMUNIT_TICKS_PER_OCCURENCE, NULL, "/PDM/CritSects/%s/ContentionRZWait", pCritSect->pszName);
224 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
225 STAMUNIT_OCCURENCES, NULL, "/PDM/CritSects/%s/ContentionR3", pCritSect->pszName);
226 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3Wait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
227 STAMUNIT_TICKS_PER_OCCURENCE, NULL, "/PDM/CritSects/%s/ContentionR3Wait", pCritSect->pszName);
228#ifdef VBOX_WITH_STATISTICS
229 STAMR3RegisterF(pVM, &pCritSect->StatLocked, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
230 STAMUNIT_TICKS_PER_OCCURENCE, NULL, "/PDM/CritSects/%s/Locked", pCritSect->pszName);
231#endif
232
233 /*
234 * Prepend to the list.
235 */
236 PUVM pUVM = pVM->pUVM;
237 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
238 pCritSect->pNext = pUVM->pdm.s.pCritSects;
239 pUVM->pdm.s.pCritSects = pCritSect;
240 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
241 Log(("pdmR3CritSectInitOne: %p %s\n", pCritSect, pszName));
242
243 return VINF_SUCCESS;
244 }
245
246 RTStrFree(pszName);
247 }
248 else
249 rc = VERR_NO_STR_MEMORY;
250 SUPSemEventClose(pVM->pSession, (SUPSEMEVENT)pCritSect->Core.EventSem);
251 }
252 return rc;
253}
254
255
256/**
257 * Initializes a read/write critical section and inserts it into the list.
258 *
259 * @returns VBox status code.
260 * @param pVM The cross context VM structure.
261 * @param pCritSect The read/write critical section.
262 * @param pvKey The owner key.
263 * @param SRC_POS The source position.
264 * @param pszNameFmt Format string for naming the critical section. For
265 * statistics and lock validation.
266 * @param va Arguments for the format string.
267 */
268static int pdmR3CritSectRwInitOne(PVM pVM, PPDMCRITSECTRWINT pCritSect, void *pvKey, RT_SRC_POS_DECL,
269 const char *pszNameFmt, va_list va)
270{
271 VM_ASSERT_EMT(pVM);
272 Assert(pCritSect->Core.u32Magic != RTCRITSECTRW_MAGIC);
273 AssertMsgReturn(((uintptr_t)&pCritSect->Core & 63) == 0, ("&Core=%p, must be 64-byte aligned!\n", &pCritSect->Core),
274 VERR_PDM_CRITSECTRW_MISALIGNED);
275 AssertMsgReturn(((uintptr_t)&pCritSect->Core.u & (sizeof(pCritSect->Core.u.u128) - 1)) == 0 /* paranoia */,
276 ("&Core.u=%p, must be 16-byte aligned!\n", &pCritSect->Core.u),
277 VERR_PDM_CRITSECTRW_MISALIGNED);
278
279 /*
280 * Allocate the semaphores.
281 */
282 AssertCompile(sizeof(SUPSEMEVENT) == sizeof(pCritSect->Core.hEvtWrite));
283 int rc = SUPSemEventCreate(pVM->pSession, (PSUPSEMEVENT)&pCritSect->Core.hEvtWrite);
284 if (RT_SUCCESS(rc))
285 {
286 AssertCompile(sizeof(SUPSEMEVENTMULTI) == sizeof(pCritSect->Core.hEvtRead));
287 rc = SUPSemEventMultiCreate(pVM->pSession, (PSUPSEMEVENT)&pCritSect->Core.hEvtRead);
288 if (RT_SUCCESS(rc))
289 {
290 /* Only format the name once. */
291 char *pszName = RTStrAPrintf2V(pszNameFmt, va); /** @todo plug the "leak"... */
292 if (pszName)
293 {
294 pCritSect->Core.pValidatorRead = NULL;
295 pCritSect->Core.pValidatorWrite = NULL;
296 RT_SRC_POS_NOREF();
297#ifdef PDMCRITSECTRW_STRICT
298# ifdef RT_LOCK_STRICT_ORDER
299 RTLOCKVALCLASS hClass = RTLockValidatorClassForSrcPos(RT_SRC_POS_ARGS, "%s", pszName);
300# else
301 RTLOCKVALCLASS hClass = NIL_RTLOCKVALCLASS;
302# endif
303 rc = RTLockValidatorRecExclCreate(&pCritSect->Core.pValidatorWrite, hClass, RTLOCKVAL_SUB_CLASS_NONE,
304 pCritSect, true, "%s", pszName);
305 if (RT_SUCCESS(rc))
306 rc = RTLockValidatorRecSharedCreate(&pCritSect->Core.pValidatorRead, hClass, RTLOCKVAL_SUB_CLASS_NONE,
307 pCritSect, false /*fSignaller*/, true, "%s", pszName);
308#endif
309 if (RT_SUCCESS(rc))
310 {
311 /*
312 * Initialize the structure (first bit is c&p from RTCritSectRwInitEx).
313 */
314 pCritSect->Core.u32Magic = RTCRITSECTRW_MAGIC;
315 pCritSect->Core.fNeedReset = false;
316 pCritSect->Core.afPadding[0] = false;
317 pCritSect->Core.fFlags = 0;
318 pCritSect->Core.u.u128.s.Lo = 0;
319 pCritSect->Core.u.u128.s.Hi = 0;
320 pCritSect->Core.u.s.hNativeWriter = NIL_RTNATIVETHREAD;
321 pCritSect->Core.cWriterReads = 0;
322 pCritSect->Core.cWriteRecursions = 0;
323 pCritSect->pvKey = pvKey;
324 pCritSect->pszName = pszName;
325 pCritSect->pSelfR3 = (PPDMCRITSECTRW)pCritSect;
326
327 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZEnterExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionRZEnterExcl", pCritSect->pszName);
328 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLeaveExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionRZLeaveExcl", pCritSect->pszName);
329 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZEnterShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionRZEnterShared", pCritSect->pszName);
330 STAMR3RegisterF(pVM, &pCritSect->StatContentionRZLeaveShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionRZLeaveShared", pCritSect->pszName);
331 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3EnterExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionR3EnterExcl", pCritSect->pszName);
332 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3LeaveExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionR3LeaveExcl", pCritSect->pszName);
333 STAMR3RegisterF(pVM, &pCritSect->StatContentionR3EnterShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/ContentionR3EnterShared", pCritSect->pszName);
334 STAMR3RegisterF(pVM, &pCritSect->StatRZEnterExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/RZEnterExcl", pCritSect->pszName);
335 STAMR3RegisterF(pVM, &pCritSect->StatRZEnterShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/RZEnterShared", pCritSect->pszName);
336 STAMR3RegisterF(pVM, &pCritSect->StatR3EnterExcl, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/R3EnterExcl", pCritSect->pszName);
337 STAMR3RegisterF(pVM, &pCritSect->StatR3EnterShared, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PDM/CritSectsRw/%s/R3EnterShared", pCritSect->pszName);
338#ifdef VBOX_WITH_STATISTICS
339 STAMR3RegisterF(pVM, &pCritSect->StatWriteLocked, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, NULL, "/PDM/CritSectsRw/%s/WriteLocked", pCritSect->pszName);
340#endif
341
342 /*
343 * Prepend to the list.
344 */
345 PUVM pUVM = pVM->pUVM;
346 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
347 pCritSect->pNext = pUVM->pdm.s.pRwCritSects;
348 pUVM->pdm.s.pRwCritSects = pCritSect;
349 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
350 LogIt(RTLOGGRPFLAGS_LEVEL_1, LOG_GROUP_PDM_CRITSECTRW, ("pdmR3CritSectRwInitOne: %p %s\n", pCritSect, pszName));
351
352 return VINF_SUCCESS;
353 }
354
355 RTStrFree(pszName);
356 }
357 else
358 rc = VERR_NO_STR_MEMORY;
359 SUPSemEventMultiClose(pVM->pSession, (SUPSEMEVENT)pCritSect->Core.hEvtRead);
360 }
361 SUPSemEventClose(pVM->pSession, (SUPSEMEVENT)pCritSect->Core.hEvtWrite);
362 }
363 return rc;
364}
365
366
367/**
368 * Initializes a PDM critical section for internal use.
369 *
370 * The PDM critical sections are derived from the IPRT critical sections, but
371 * works in ring-0 and raw-mode context as well.
372 *
373 * @returns VBox status code.
374 * @param pVM The cross context VM structure.
375 * @param pCritSect Pointer to the critical section.
376 * @param SRC_POS Use RT_SRC_POS.
377 * @param pszNameFmt Format string for naming the critical section. For
378 * statistics and lock validation.
379 * @param ... Arguments for the format string.
380 * @thread EMT
381 */
382VMMR3DECL(int) PDMR3CritSectInit(PVM pVM, PPDMCRITSECT pCritSect, RT_SRC_POS_DECL, const char *pszNameFmt, ...)
383{
384#if HC_ARCH_BITS == 64 && GC_ARCH_BITS == 32
385 AssertCompile(sizeof(pCritSect->padding) >= sizeof(pCritSect->s));
386#endif
387 Assert(RT_ALIGN_P(pCritSect, sizeof(uintptr_t)) == pCritSect);
388 va_list va;
389 va_start(va, pszNameFmt);
390 int rc = pdmR3CritSectInitOne(pVM, &pCritSect->s, pCritSect, RT_SRC_POS_ARGS, false /*fUniqueClass*/, pszNameFmt, va);
391 va_end(va);
392 return rc;
393}
394
395
396/**
397 * Initializes a PDM read/write critical section for internal use.
398 *
399 * The PDM read/write critical sections are derived from the IPRT read/write
400 * critical sections, but works in ring-0 and raw-mode context as well.
401 *
402 * @returns VBox status code.
403 * @param pVM The cross context VM structure.
404 * @param pCritSect Pointer to the read/write critical section.
405 * @param SRC_POS Use RT_SRC_POS.
406 * @param pszNameFmt Format string for naming the critical section. For
407 * statistics and lock validation.
408 * @param ... Arguments for the format string.
409 * @thread EMT
410 */
411VMMR3DECL(int) PDMR3CritSectRwInit(PVM pVM, PPDMCRITSECTRW pCritSect, RT_SRC_POS_DECL, const char *pszNameFmt, ...)
412{
413#if HC_ARCH_BITS == 64 && GC_ARCH_BITS == 32
414 AssertCompile(sizeof(pCritSect->padding) >= sizeof(pCritSect->s));
415#endif
416 Assert(RT_ALIGN_P(pCritSect, sizeof(uintptr_t)) == pCritSect);
417 va_list va;
418 va_start(va, pszNameFmt);
419 int rc = pdmR3CritSectRwInitOne(pVM, &pCritSect->s, pCritSect, RT_SRC_POS_ARGS, pszNameFmt, va);
420 va_end(va);
421 return rc;
422}
423
424
425/**
426 * Initializes a PDM critical section for a device.
427 *
428 * @returns VBox status code.
429 * @param pVM The cross context VM structure.
430 * @param pDevIns Device instance.
431 * @param pCritSect Pointer to the critical section.
432 * @param SRC_POS The source position. Optional.
433 * @param pszNameFmt Format string for naming the critical section. For
434 * statistics and lock validation.
435 * @param va Arguments for the format string.
436 */
437int pdmR3CritSectInitDevice(PVM pVM, PPDMDEVINS pDevIns, PPDMCRITSECT pCritSect, RT_SRC_POS_DECL,
438 const char *pszNameFmt, va_list va)
439{
440 return pdmR3CritSectInitOne(pVM, &pCritSect->s, pDevIns, RT_SRC_POS_ARGS, false /*fUniqueClass*/, pszNameFmt, va);
441}
442
443
444/**
445 * Initializes a PDM read/write critical section for a device.
446 *
447 * @returns VBox status code.
448 * @param pVM The cross context VM structure.
449 * @param pDevIns Device instance.
450 * @param pCritSect Pointer to the read/write critical section.
451 * @param SRC_POS The source position. Optional.
452 * @param pszNameFmt Format string for naming the critical section. For
453 * statistics and lock validation.
454 * @param va Arguments for the format string.
455 */
456int pdmR3CritSectRwInitDevice(PVM pVM, PPDMDEVINS pDevIns, PPDMCRITSECTRW pCritSect, RT_SRC_POS_DECL,
457 const char *pszNameFmt, va_list va)
458{
459 return pdmR3CritSectRwInitOne(pVM, &pCritSect->s, pDevIns, RT_SRC_POS_ARGS, pszNameFmt, va);
460}
461
462
463/**
464 * Initializes the automatic default PDM critical section for a device.
465 *
466 * @returns VBox status code.
467 * @param pVM The cross context VM structure.
468 * @param pDevIns Device instance.
469 * @param SRC_POS The source position. Optional.
470 * @param pCritSect Pointer to the critical section.
471 * @param pszNameFmt Format string for naming the critical section. For
472 * statistics and lock validation.
473 * @param ... Arguments for the format string.
474 */
475int pdmR3CritSectInitDeviceAuto(PVM pVM, PPDMDEVINS pDevIns, PPDMCRITSECT pCritSect, RT_SRC_POS_DECL,
476 const char *pszNameFmt, ...)
477{
478 va_list va;
479 va_start(va, pszNameFmt);
480 int rc = pdmR3CritSectInitOne(pVM, &pCritSect->s, pDevIns, RT_SRC_POS_ARGS, true /*fUniqueClass*/, pszNameFmt, va);
481 if (RT_SUCCESS(rc))
482 pCritSect->s.fAutomaticDefaultCritsect = true;
483 va_end(va);
484 return rc;
485}
486
487
488/**
489 * Initializes a PDM critical section for a driver.
490 *
491 * @returns VBox status code.
492 * @param pVM The cross context VM structure.
493 * @param pDrvIns Driver instance.
494 * @param pCritSect Pointer to the critical section.
495 * @param SRC_POS The source position. Optional.
496 * @param pszNameFmt Format string for naming the critical section. For
497 * statistics and lock validation.
498 * @param ... Arguments for the format string.
499 */
500int pdmR3CritSectInitDriver(PVM pVM, PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect, RT_SRC_POS_DECL,
501 const char *pszNameFmt, ...)
502{
503 va_list va;
504 va_start(va, pszNameFmt);
505 int rc = pdmR3CritSectInitOne(pVM, &pCritSect->s, pDrvIns, RT_SRC_POS_ARGS, false /*fUniqueClass*/, pszNameFmt, va);
506 va_end(va);
507 return rc;
508}
509
510
511/**
512 * Initializes a PDM read/write critical section for a driver.
513 *
514 * @returns VBox status code.
515 * @param pVM The cross context VM structure.
516 * @param pDrvIns Driver instance.
517 * @param pCritSect Pointer to the read/write critical section.
518 * @param SRC_POS The source position. Optional.
519 * @param pszNameFmt Format string for naming the critical section. For
520 * statistics and lock validation.
521 * @param ... Arguments for the format string.
522 */
523int pdmR3CritSectRwInitDriver(PVM pVM, PPDMDRVINS pDrvIns, PPDMCRITSECTRW pCritSect, RT_SRC_POS_DECL,
524 const char *pszNameFmt, ...)
525{
526 va_list va;
527 va_start(va, pszNameFmt);
528 int rc = pdmR3CritSectRwInitOne(pVM, &pCritSect->s, pDrvIns, RT_SRC_POS_ARGS, pszNameFmt, va);
529 va_end(va);
530 return rc;
531}
532
533
534/**
535 * Deletes one critical section.
536 *
537 * @returns Return code from RTCritSectDelete.
538 *
539 * @param pVM The cross context VM structure.
540 * @param pUVM The user mode VM handle.
541 * @param pCritSect The critical section.
542 * @param pPrev The previous critical section in the list.
543 * @param fFinal Set if this is the final call and statistics shouldn't be deregistered.
544 *
545 * @remarks Caller must have entered the ListCritSect.
546 */
547static int pdmR3CritSectDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTINT pCritSect, PPDMCRITSECTINT pPrev, bool fFinal)
548{
549 /*
550 * Assert free waiters and so on (c&p from RTCritSectDelete).
551 */
552 Assert(pCritSect->Core.u32Magic == RTCRITSECT_MAGIC);
553 //Assert(pCritSect->Core.cNestings == 0); - we no longer reset this when leaving.
554 Assert(pCritSect->Core.cLockers == -1);
555 Assert(pCritSect->Core.NativeThreadOwner == NIL_RTNATIVETHREAD);
556 Assert(RTCritSectIsOwner(&pUVM->pdm.s.ListCritSect));
557
558 /*
559 * Unlink it.
560 */
561 if (pPrev)
562 pPrev->pNext = pCritSect->pNext;
563 else
564 pUVM->pdm.s.pCritSects = pCritSect->pNext;
565
566 /*
567 * Delete it (parts taken from RTCritSectDelete).
568 * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
569 */
570 ASMAtomicWriteU32(&pCritSect->Core.u32Magic, 0);
571 SUPSEMEVENT hEvent = (SUPSEMEVENT)pCritSect->Core.EventSem;
572 pCritSect->Core.EventSem = NIL_RTSEMEVENT;
573 while (pCritSect->Core.cLockers-- >= 0)
574 SUPSemEventSignal(pVM->pSession, hEvent);
575 ASMAtomicWriteS32(&pCritSect->Core.cLockers, -1);
576 int rc = SUPSemEventClose(pVM->pSession, hEvent);
577 AssertRC(rc);
578 RTLockValidatorRecExclDestroy(&pCritSect->Core.pValidatorRec);
579 pCritSect->pNext = NULL;
580 pCritSect->pvKey = NULL;
581 if (!fFinal)
582 STAMR3DeregisterF(pVM->pUVM, "/PDM/CritSects/%s/*", pCritSect->pszName);
583 RTStrFree((char *)pCritSect->pszName);
584 pCritSect->pszName = NULL;
585 return rc;
586}
587
588
589/**
590 * Deletes one read/write critical section.
591 *
592 * @returns VBox status code.
593 *
594 * @param pVM The cross context VM structure.
595 * @param pUVM The user mode VM handle.
596 * @param pCritSect The read/write critical section.
597 * @param pPrev The previous critical section in the list.
598 * @param fFinal Set if this is the final call and statistics shouldn't be deregistered.
599 *
600 * @remarks Caller must have entered the ListCritSect.
601 */
602static int pdmR3CritSectRwDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTRWINT pCritSect, PPDMCRITSECTRWINT pPrev, bool fFinal)
603{
604 /*
605 * Assert free waiters and so on (c&p from RTCritSectRwDelete).
606 */
607 Assert(pCritSect->Core.u32Magic == RTCRITSECTRW_MAGIC);
608 //Assert(pCritSect->Core.cNestings == 0);
609 //Assert(pCritSect->Core.cLockers == -1);
610 Assert(pCritSect->Core.u.s.hNativeWriter == NIL_RTNATIVETHREAD);
611
612 /*
613 * Invalidate the structure and free the semaphores.
614 */
615 if (!ASMAtomicCmpXchgU32(&pCritSect->Core.u32Magic, RTCRITSECTRW_MAGIC_DEAD, RTCRITSECTRW_MAGIC))
616 AssertFailed();
617
618 /*
619 * Unlink it.
620 */
621 if (pPrev)
622 pPrev->pNext = pCritSect->pNext;
623 else
624 pUVM->pdm.s.pRwCritSects = pCritSect->pNext;
625
626 /*
627 * Delete it (parts taken from RTCritSectRwDelete).
628 * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
629 */
630 pCritSect->Core.fFlags = 0;
631 pCritSect->Core.u.s.u64State = 0;
632
633 SUPSEMEVENT hEvtWrite = (SUPSEMEVENT)pCritSect->Core.hEvtWrite;
634 pCritSect->Core.hEvtWrite = NIL_RTSEMEVENT;
635 AssertCompile(sizeof(hEvtWrite) == sizeof(pCritSect->Core.hEvtWrite));
636
637 SUPSEMEVENTMULTI hEvtRead = (SUPSEMEVENTMULTI)pCritSect->Core.hEvtRead;
638 pCritSect->Core.hEvtRead = NIL_RTSEMEVENTMULTI;
639 AssertCompile(sizeof(hEvtRead) == sizeof(pCritSect->Core.hEvtRead));
640
641 int rc1 = SUPSemEventClose(pVM->pSession, hEvtWrite); AssertRC(rc1);
642 int rc2 = SUPSemEventMultiClose(pVM->pSession, hEvtRead); AssertRC(rc2);
643
644 RTLockValidatorRecSharedDestroy(&pCritSect->Core.pValidatorRead);
645 RTLockValidatorRecExclDestroy(&pCritSect->Core.pValidatorWrite);
646
647 pCritSect->pNext = NULL;
648 pCritSect->pvKey = NULL;
649 if (!fFinal)
650 STAMR3DeregisterF(pVM->pUVM, "/PDM/CritSectsRw/%s/*", pCritSect->pszName);
651 RTStrFree((char *)pCritSect->pszName);
652 pCritSect->pszName = NULL;
653
654 return RT_SUCCESS(rc1) ? rc2 : rc1;
655}
656
657
658/**
659 * Deletes all critical sections with a give initializer key.
660 *
661 * @returns VBox status code.
662 * The entire list is processed on failure, so we'll only
663 * return the first error code. This shouldn't be a problem
664 * since errors really shouldn't happen here.
665 * @param pVM The cross context VM structure.
666 * @param pvKey The initializer key.
667 */
668static int pdmR3CritSectDeleteByKey(PVM pVM, void *pvKey)
669{
670 /*
671 * Iterate the list and match key.
672 */
673 PUVM pUVM = pVM->pUVM;
674 int rc = VINF_SUCCESS;
675 PPDMCRITSECTINT pPrev = NULL;
676 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
677 PPDMCRITSECTINT pCur = pUVM->pdm.s.pCritSects;
678 while (pCur)
679 {
680 if (pCur->pvKey == pvKey)
681 {
682 int rc2 = pdmR3CritSectDeleteOne(pVM, pUVM, pCur, pPrev, false /* not final */);
683 AssertRC(rc2);
684 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
685 rc = rc2;
686 }
687
688 /* next */
689 pPrev = pCur;
690 pCur = pCur->pNext;
691 }
692 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
693 return rc;
694}
695
696
697/**
698 * Deletes all read/write critical sections with a give initializer key.
699 *
700 * @returns VBox status code.
701 * The entire list is processed on failure, so we'll only
702 * return the first error code. This shouldn't be a problem
703 * since errors really shouldn't happen here.
704 * @param pVM The cross context VM structure.
705 * @param pvKey The initializer key.
706 */
707static int pdmR3CritSectRwDeleteByKey(PVM pVM, void *pvKey)
708{
709 /*
710 * Iterate the list and match key.
711 */
712 PUVM pUVM = pVM->pUVM;
713 int rc = VINF_SUCCESS;
714 PPDMCRITSECTRWINT pPrev = NULL;
715 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
716 PPDMCRITSECTRWINT pCur = pUVM->pdm.s.pRwCritSects;
717 while (pCur)
718 {
719 if (pCur->pvKey == pvKey)
720 {
721 int rc2 = pdmR3CritSectRwDeleteOne(pVM, pUVM, pCur, pPrev, false /* not final */);
722 AssertRC(rc2);
723 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
724 rc = rc2;
725 }
726
727 /* next */
728 pPrev = pCur;
729 pCur = pCur->pNext;
730 }
731 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
732 return rc;
733}
734
735
736/**
737 * Deletes all undeleted critical sections (both types) initialized by a given
738 * device.
739 *
740 * @returns VBox status code.
741 * @param pVM The cross context VM structure.
742 * @param pDevIns The device handle.
743 */
744int pdmR3CritSectBothDeleteDevice(PVM pVM, PPDMDEVINS pDevIns)
745{
746 int rc1 = pdmR3CritSectDeleteByKey(pVM, pDevIns);
747 int rc2 = pdmR3CritSectRwDeleteByKey(pVM, pDevIns);
748 return RT_SUCCESS(rc1) ? rc2 : rc1;
749}
750
751
752/**
753 * Deletes all undeleted critical sections (both types) initialized by a given
754 * driver.
755 *
756 * @returns VBox status code.
757 * @param pVM The cross context VM structure.
758 * @param pDrvIns The driver handle.
759 */
760int pdmR3CritSectBothDeleteDriver(PVM pVM, PPDMDRVINS pDrvIns)
761{
762 int rc1 = pdmR3CritSectDeleteByKey(pVM, pDrvIns);
763 int rc2 = pdmR3CritSectRwDeleteByKey(pVM, pDrvIns);
764 return RT_SUCCESS(rc1) ? rc2 : rc1;
765}
766
767
768/**
769 * Deletes the critical section.
770 *
771 * @returns VBox status code.
772 * @param pVM The cross context VM structure.
773 * @param pCritSect The PDM critical section to destroy.
774 */
775VMMR3DECL(int) PDMR3CritSectDelete(PVM pVM, PPDMCRITSECT pCritSect)
776{
777 if (!RTCritSectIsInitialized(&pCritSect->s.Core))
778 return VINF_SUCCESS;
779
780 /*
781 * Find and unlink it.
782 */
783 PUVM pUVM = pVM->pUVM;
784 AssertReleaseReturn(pVM, VERR_PDM_CRITSECT_IPE);
785 PPDMCRITSECTINT pPrev = NULL;
786 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
787 PPDMCRITSECTINT pCur = pUVM->pdm.s.pCritSects;
788 while (pCur)
789 {
790 if (pCur == &pCritSect->s)
791 {
792 int rc = pdmR3CritSectDeleteOne(pVM, pUVM, pCur, pPrev, false /* not final */);
793 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
794 return rc;
795 }
796
797 /* next */
798 pPrev = pCur;
799 pCur = pCur->pNext;
800 }
801 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
802 AssertReleaseMsgFailed(("pCritSect=%p wasn't found!\n", pCritSect));
803 return VERR_PDM_CRITSECT_NOT_FOUND;
804}
805
806
807/**
808 * Deletes the read/write critical section.
809 *
810 * @returns VBox status code.
811 * @param pVM The cross context VM structure.
812 * @param pCritSect The PDM read/write critical section to destroy.
813 */
814VMMR3DECL(int) PDMR3CritSectRwDelete(PVM pVM, PPDMCRITSECTRW pCritSect)
815{
816 if (!PDMCritSectRwIsInitialized(pCritSect))
817 return VINF_SUCCESS;
818
819 /*
820 * Find and unlink it.
821 */
822 PUVM pUVM = pVM->pUVM;
823 AssertReleaseReturn(pVM, VERR_PDM_CRITSECT_IPE);
824 PPDMCRITSECTRWINT pPrev = NULL;
825 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
826 PPDMCRITSECTRWINT pCur = pUVM->pdm.s.pRwCritSects;
827 while (pCur)
828 {
829 if (pCur == &pCritSect->s)
830 {
831 int rc = pdmR3CritSectRwDeleteOne(pVM, pUVM, pCur, pPrev, false /* not final */);
832 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
833 return rc;
834 }
835
836 /* next */
837 pPrev = pCur;
838 pCur = pCur->pNext;
839 }
840 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
841 AssertReleaseMsgFailed(("pCritSect=%p wasn't found!\n", pCritSect));
842 return VERR_PDM_CRITSECT_NOT_FOUND;
843}
844
845
846/**
847 * Gets the name of the critical section.
848 *
849 *
850 * @returns Pointer to the critical section name (read only) on success,
851 * NULL on failure (invalid critical section).
852 * @param pCritSect The critical section.
853 */
854VMMR3DECL(const char *) PDMR3CritSectName(PCPDMCRITSECT pCritSect)
855{
856 AssertPtrReturn(pCritSect, NULL);
857 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, NULL);
858 return pCritSect->s.pszName;
859}
860
861
862/**
863 * Gets the name of the read/write critical section.
864 *
865 *
866 * @returns Pointer to the critical section name (read only) on success,
867 * NULL on failure (invalid critical section).
868 * @param pCritSect The read/write critical section.
869 */
870VMMR3DECL(const char *) PDMR3CritSectRwName(PCPDMCRITSECTRW pCritSect)
871{
872 AssertPtrReturn(pCritSect, NULL);
873 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECTRW_MAGIC, NULL);
874 return pCritSect->s.pszName;
875}
876
877
878/**
879 * Yield the critical section if someone is waiting on it.
880 *
881 * When yielding, we'll leave the critical section and try to make sure the
882 * other waiting threads get a chance of entering before we reclaim it.
883 *
884 * @retval true if yielded.
885 * @retval false if not yielded.
886 * @param pVM The cross context VM structure.
887 * @param pCritSect The critical section.
888 */
889VMMR3DECL(bool) PDMR3CritSectYield(PVM pVM, PPDMCRITSECT pCritSect)
890{
891 AssertPtrReturn(pCritSect, false);
892 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, false);
893 Assert(pCritSect->s.Core.NativeThreadOwner == RTThreadNativeSelf());
894 Assert(!(pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NOP));
895 RT_NOREF(pVM);
896
897 /* No recursion allowed here. */
898 int32_t const cNestings = pCritSect->s.Core.cNestings;
899 AssertReturn(cNestings == 1, false);
900
901 int32_t const cLockers = ASMAtomicReadS32(&pCritSect->s.Core.cLockers);
902 if (cLockers < cNestings)
903 return false;
904
905#ifdef PDMCRITSECT_STRICT
906 RTLOCKVALSRCPOS const SrcPos = pCritSect->s.Core.pValidatorRec->SrcPos;
907#endif
908 PDMCritSectLeave(pVM, pCritSect);
909
910 /*
911 * If we're lucky, then one of the waiters has entered the lock already.
912 * We spin a little bit in hope for this to happen so we can avoid the
913 * yield detour.
914 */
915 if (ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0)
916 {
917 int cLoops = 20;
918 while ( cLoops > 0
919 && ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0
920 && ASMAtomicUoReadS32(&pCritSect->s.Core.cLockers) >= 0)
921 {
922 ASMNopPause();
923 cLoops--;
924 }
925 if (cLoops == 0)
926 RTThreadYield();
927 }
928
929#ifdef PDMCRITSECT_STRICT
930 int rc = PDMCritSectEnterDebug(pVM, pCritSect, VERR_IGNORED,
931 SrcPos.uId, SrcPos.pszFile, SrcPos.uLine, SrcPos.pszFunction);
932#else
933 int rc = PDMCritSectEnter(pVM, pCritSect, VERR_IGNORED);
934#endif
935 PDM_CRITSECT_RELEASE_ASSERT_RC(pVM, pCritSect, rc);
936 return true;
937}
938
939
940/**
941 * PDMR3CritSectBothCountOwned worker.
942 *
943 * @param pszName The critical section name.
944 * @param ppszNames Pointer to the pszNames variable.
945 * @param pcchLeft Pointer to the cchLeft variable.
946 * @param fFirst Whether this is the first name or not.
947 */
948static void pdmR3CritSectAppendNameToList(char const *pszName, char **ppszNames, size_t *pcchLeft, bool fFirst)
949{
950 size_t cchLeft = *pcchLeft;
951 if (cchLeft)
952 {
953 char *pszNames = *ppszNames;
954
955 /* try add comma. */
956 if (fFirst)
957 {
958 *pszNames++ = ',';
959 if (--cchLeft)
960 {
961 *pszNames++ = ' ';
962 cchLeft--;
963 }
964 }
965
966 /* try copy the name. */
967 if (cchLeft)
968 {
969 size_t const cchName = strlen(pszName);
970 if (cchName < cchLeft)
971 {
972 memcpy(pszNames, pszName, cchName);
973 pszNames += cchName;
974 cchLeft -= cchName;
975 }
976 else
977 {
978 if (cchLeft > 2)
979 {
980 memcpy(pszNames, pszName, cchLeft - 2);
981 pszNames += cchLeft - 2;
982 cchLeft = 2;
983 }
984 while (cchLeft-- > 0)
985 *pszNames++ = '+';
986 }
987 }
988 *pszNames = '\0';
989
990 *pcchLeft = cchLeft;
991 *ppszNames = pszNames;
992 }
993}
994
995
996/**
997 * Counts the critical sections (both type) owned by the calling thread,
998 * optionally returning a comma separated list naming them.
999 *
1000 * Read ownerships are not included in non-strict builds.
1001 *
1002 * This is for diagnostic purposes only.
1003 *
1004 * @returns Lock count.
1005 *
1006 * @param pVM The cross context VM structure.
1007 * @param pszNames Where to return the critical section names.
1008 * @param cbNames The size of the buffer.
1009 */
1010VMMR3DECL(uint32_t) PDMR3CritSectCountOwned(PVM pVM, char *pszNames, size_t cbNames)
1011{
1012 /*
1013 * Init the name buffer.
1014 */
1015 size_t cchLeft = cbNames;
1016 if (cchLeft)
1017 {
1018 cchLeft--;
1019 pszNames[0] = pszNames[cchLeft] = '\0';
1020 }
1021
1022 /*
1023 * Iterate the critical sections.
1024 */
1025 uint32_t cCritSects = 0;
1026 RTNATIVETHREAD const hNativeThread = RTThreadNativeSelf();
1027 /* This is unsafe, but wtf. */
1028 for (PPDMCRITSECTINT pCur = pVM->pUVM->pdm.s.pCritSects;
1029 pCur;
1030 pCur = pCur->pNext)
1031 {
1032 /* Same as RTCritSectIsOwner(). */
1033 if (pCur->Core.NativeThreadOwner == hNativeThread)
1034 {
1035 cCritSects++;
1036 pdmR3CritSectAppendNameToList(pCur->pszName, &pszNames, &cchLeft, cCritSects == 1);
1037 }
1038 }
1039
1040 /* This is unsafe, but wtf. */
1041 for (PPDMCRITSECTRWINT pCur = pVM->pUVM->pdm.s.pRwCritSects;
1042 pCur;
1043 pCur = pCur->pNext)
1044 {
1045 if ( pCur->Core.u.s.hNativeWriter == hNativeThread
1046 || PDMCritSectRwIsReadOwner(pVM, (PPDMCRITSECTRW)pCur, false /*fWannaHear*/) )
1047 {
1048 cCritSects++;
1049 pdmR3CritSectAppendNameToList(pCur->pszName, &pszNames, &cchLeft, cCritSects == 1);
1050 }
1051 }
1052
1053 return cCritSects;
1054}
1055
1056
1057/**
1058 * Leave all critical sections the calling thread owns.
1059 *
1060 * This is only used when entering guru meditation in order to prevent other
1061 * EMTs and I/O threads from deadlocking.
1062 *
1063 * @param pVM The cross context VM structure.
1064 */
1065VMMR3_INT_DECL(void) PDMR3CritSectLeaveAll(PVM pVM)
1066{
1067 RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
1068 PUVM pUVM = pVM->pUVM;
1069
1070 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1071 for (PPDMCRITSECTINT pCur = pUVM->pdm.s.pCritSects;
1072 pCur;
1073 pCur = pCur->pNext)
1074 {
1075 while ( pCur->Core.NativeThreadOwner == hNativeSelf
1076 && pCur->Core.cNestings > 0)
1077 PDMCritSectLeave(pVM, (PPDMCRITSECT)pCur);
1078 }
1079 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1080}
1081
1082
1083/**
1084 * Gets the address of the NOP critical section.
1085 *
1086 * The NOP critical section will not perform any thread serialization but let
1087 * all enter immediately and concurrently.
1088 *
1089 * @returns The address of the NOP critical section.
1090 * @param pVM The cross context VM structure.
1091 */
1092VMMR3DECL(PPDMCRITSECT) PDMR3CritSectGetNop(PVM pVM)
1093{
1094 VM_ASSERT_VALID_EXT_RETURN(pVM, NULL);
1095 return &pVM->pdm.s.NopCritSect;
1096}
1097
1098
1099/**
1100 * Display matching critical sections.
1101 */
1102static void pdmR3CritSectInfoWorker(PUVM pUVM, const char *pszPatterns, PCDBGFINFOHLP pHlp, unsigned cVerbosity)
1103{
1104 size_t const cchPatterns = pszPatterns ? strlen(pszPatterns) : 0;
1105 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1106
1107 for (PPDMCRITSECTINT pCritSect = pUVM->pdm.s.pCritSects; pCritSect; pCritSect = pCritSect->pNext)
1108 if ( !pszPatterns
1109 || RTStrSimplePatternMultiMatch(pszPatterns, cchPatterns, pCritSect->pszName, RTSTR_MAX, NULL))
1110 {
1111 uint32_t fFlags = pCritSect->Core.fFlags;
1112 pHlp->pfnPrintf(pHlp, "%p: '%s'%s%s%s%s%s\n", pCritSect, pCritSect->pszName,
1113 pCritSect->fAutomaticDefaultCritsect ? " default" : "",
1114 pCritSect->fUsedByTimerOrSimilar ? " used-by-timer-or-similar" : "",
1115 fFlags & RTCRITSECT_FLAGS_NO_NESTING ? " no-testing" : "",
1116 fFlags & RTCRITSECT_FLAGS_NO_LOCK_VAL ? " no-lock-val" : "",
1117 fFlags & RTCRITSECT_FLAGS_NOP ? " nop" : "");
1118
1119
1120 /*
1121 * Get the volatile data:
1122 */
1123 RTNATIVETHREAD hOwner;
1124 int32_t cLockers;
1125 int32_t cNestings;
1126 uint32_t uMagic;
1127 for (uint32_t iTry = 0; iTry < 16; iTry++)
1128 {
1129 hOwner = pCritSect->Core.NativeThreadOwner;
1130 cLockers = pCritSect->Core.cLockers;
1131 cNestings = pCritSect->Core.cNestings;
1132 fFlags = pCritSect->Core.fFlags;
1133 uMagic = pCritSect->Core.u32Magic;
1134 if ( hOwner == pCritSect->Core.NativeThreadOwner
1135 && cLockers == pCritSect->Core.cLockers
1136 && cNestings == pCritSect->Core.cNestings
1137 && fFlags == pCritSect->Core.fFlags
1138 && uMagic == pCritSect->Core.u32Magic)
1139 break;
1140 }
1141
1142 /*
1143 * Check and resolve the magic to a string, print if not RTCRITSECT_MAGIC.
1144 */
1145 const char *pszMagic;
1146 switch (uMagic)
1147 {
1148 case RTCRITSECT_MAGIC: pszMagic = NULL; break;
1149 case ~RTCRITSECT_MAGIC: pszMagic = " deleted"; break;
1150 case PDMCRITSECT_MAGIC_CORRUPTED: pszMagic = " PDMCRITSECT_MAGIC_CORRUPTED!"; break;
1151 case PDMCRITSECT_MAGIC_FAILED_ABORT: pszMagic = " PDMCRITSECT_MAGIC_FAILED_ABORT!"; break;
1152 default: pszMagic = " !unknown!"; break;
1153 }
1154 if (pszMagic || cVerbosity > 1)
1155 pHlp->pfnPrintf(pHlp, " uMagic=%#x%s\n", uMagic, pszMagic ? pszMagic : "");
1156
1157 /*
1158 * If locked, print details
1159 */
1160 if (cLockers != -1 || cNestings > 1 || cNestings < 0 || hOwner != NIL_RTNATIVETHREAD || cVerbosity > 1)
1161 {
1162 /* Translate the owner to a name if we have one and can. */
1163 const char *pszOwner = NULL;
1164 if (hOwner != NIL_RTNATIVETHREAD)
1165 {
1166 RTTHREAD hOwnerThread = RTThreadFromNative(hOwner); /* Note! Does not return a reference (crazy). */
1167 if (hOwnerThread != NIL_RTTHREAD)
1168 pszOwner = RTThreadGetName(hOwnerThread);
1169 }
1170 else
1171 pszOwner = "<no-owner>";
1172
1173 pHlp->pfnPrintf(pHlp, " cLockers=%d cNestings=%d hOwner=%p %s%s\n", cLockers, cNestings, hOwner,
1174 pszOwner ? pszOwner : "???", fFlags & PDMCRITSECT_FLAGS_PENDING_UNLOCK ? " pending-unlock" : "");
1175 }
1176 }
1177 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1178}
1179
1180
1181/**
1182 * Display matching read/write critical sections.
1183 */
1184static void pdmR3CritSectInfoRwWorker(PUVM pUVM, const char *pszPatterns, PCDBGFINFOHLP pHlp, unsigned cVerbosity)
1185{
1186 size_t const cchPatterns = pszPatterns ? strlen(pszPatterns) : 0;
1187 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1188
1189 for (PPDMCRITSECTRWINT pCritSect = pUVM->pdm.s.pRwCritSects; pCritSect; pCritSect = pCritSect->pNext)
1190 if ( !pszPatterns
1191 || RTStrSimplePatternMultiMatch(pszPatterns, cchPatterns, pCritSect->pszName, RTSTR_MAX, NULL))
1192 {
1193 uint16_t const fFlags = pCritSect->Core.fFlags;
1194 pHlp->pfnPrintf(pHlp, "%p: '%s'%s%s%s\n", pCritSect, pCritSect->pszName,
1195 fFlags & RTCRITSECT_FLAGS_NO_NESTING ? " no-testing" : "",
1196 fFlags & RTCRITSECT_FLAGS_NO_LOCK_VAL ? " no-lock-val" : "",
1197 fFlags & RTCRITSECT_FLAGS_NOP ? " nop" : "");
1198
1199 /*
1200 * Get the volatile data:
1201 */
1202 RTNATIVETHREAD hOwner;
1203 uint64_t u64State;
1204 uint32_t cWriterReads;
1205 uint32_t cWriteRecursions;
1206 bool fNeedReset;
1207 uint32_t uMagic;
1208 unsigned cTries = 16;
1209 do
1210 {
1211 u64State = pCritSect->Core.u.s.u64State;
1212 hOwner = pCritSect->Core.u.s.hNativeWriter;
1213 cWriterReads = pCritSect->Core.cWriterReads;
1214 cWriteRecursions = pCritSect->Core.cWriteRecursions;
1215 fNeedReset = pCritSect->Core.fNeedReset;
1216 uMagic = pCritSect->Core.u32Magic;
1217 } while ( cTries-- > 0
1218 && ( u64State != pCritSect->Core.u.s.u64State
1219 || hOwner != pCritSect->Core.u.s.hNativeWriter
1220 || cWriterReads != pCritSect->Core.cWriterReads
1221 || cWriteRecursions != pCritSect->Core.cWriteRecursions
1222 || fNeedReset != pCritSect->Core.fNeedReset
1223 || uMagic != pCritSect->Core.u32Magic));
1224
1225 /*
1226 * Check and resolve the magic to a string, print if not RTCRITSECT_MAGIC.
1227 */
1228 const char *pszMagic;
1229 switch (uMagic)
1230 {
1231 case RTCRITSECTRW_MAGIC: pszMagic = NULL; break;
1232 case ~RTCRITSECTRW_MAGIC: pszMagic = " deleted"; break;
1233 case PDMCRITSECTRW_MAGIC_CORRUPT: pszMagic = " PDMCRITSECTRW_MAGIC_CORRUPT!"; break;
1234 default: pszMagic = " !unknown!"; break;
1235 }
1236 if (pszMagic || cVerbosity > 1)
1237 pHlp->pfnPrintf(pHlp, " uMagic=%#x%s\n", uMagic, pszMagic ? pszMagic : "");
1238
1239 /*
1240 * If locked, print details
1241 */
1242 if ((u64State & ~RTCSRW_DIR_MASK) || hOwner != NIL_RTNATIVETHREAD || cVerbosity > 1)
1243 {
1244 /* Translate the owner to a name if we have one and can. */
1245 const char *pszOwner = NULL;
1246 if (hOwner != NIL_RTNATIVETHREAD)
1247 {
1248 RTTHREAD hOwnerThread = RTThreadFromNative(hOwner); /* Note! Does not return a reference (crazy). */
1249 if (hOwnerThread != NIL_RTTHREAD)
1250 pszOwner = RTThreadGetName(hOwnerThread);
1251 }
1252 else
1253 pszOwner = "<no-owner>";
1254
1255 pHlp->pfnPrintf(pHlp, " u64State=%#RX64 %s cReads=%u cWrites=%u cWaitingReads=%u\n",
1256 u64State, (u64State & RTCSRW_DIR_MASK) == RTCSRW_DIR_WRITE ? "writing" : "reading",
1257 (unsigned)((u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT),
1258 (unsigned)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_RD_SHIFT),
1259 (unsigned)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT));
1260 if (hOwner != NIL_RTNATIVETHREAD || cVerbosity > 2)
1261 pHlp->pfnPrintf(pHlp, " cNestings=%u cReadNestings=%u hWriter=%p %s\n",
1262 cWriteRecursions, cWriterReads, hOwner, pszOwner ? pszOwner : "???");
1263 }
1264 }
1265 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1266}
1267
1268
1269/**
1270 * Common worker for critsect and critsectrw info items.
1271 */
1272static void pdmR3CritSectInfoCommon(PVM pVM, PCDBGFINFOHLP pHlp, int cArgs, char **papszArgs, bool fReadWrite)
1273{
1274 PUVM pUVM = pVM->pUVM;
1275
1276 /*
1277 * Process arguments.
1278 */
1279 static const RTGETOPTDEF s_aOptions[] =
1280 {
1281 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1282 };
1283 RTGETOPTSTATE State;
1284 int rc = RTGetOptInit(&State, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1285 AssertRC(rc);
1286
1287 unsigned cVerbosity = 1;
1288 unsigned cProcessed = 0;
1289
1290 RTGETOPTUNION ValueUnion;
1291 while ((rc = RTGetOpt(&State, &ValueUnion)) != 0)
1292 {
1293 switch (rc)
1294 {
1295 case 'v':
1296 cVerbosity++;
1297 break;
1298
1299 case VINF_GETOPT_NOT_OPTION:
1300 if (!fReadWrite)
1301 pdmR3CritSectInfoWorker(pUVM, ValueUnion.psz, pHlp, cVerbosity);
1302 else
1303 pdmR3CritSectInfoRwWorker(pUVM, ValueUnion.psz, pHlp, cVerbosity);
1304 cProcessed++;
1305 break;
1306
1307 default:
1308 pHlp->pfnGetOptError(pHlp, rc, &ValueUnion, &State);
1309 return;
1310 }
1311 }
1312
1313 /*
1314 * If we did nothing above, dump all.
1315 */
1316 if (!cProcessed)
1317 {
1318 if (!fReadWrite)
1319 pdmR3CritSectInfoWorker(pUVM, NULL, pHlp, cVerbosity);
1320 else
1321 pdmR3CritSectInfoRwWorker(pUVM, NULL, pHlp, cVerbosity);
1322 }
1323}
1324
1325
1326/**
1327 * @callback_method_impl{FNDBGFINFOARGVINT, critsect}
1328 */
1329static DECLCALLBACK(void) pdmR3CritSectInfo(PVM pVM, PCDBGFINFOHLP pHlp, int cArgs, char **papszArgs)
1330{
1331 return pdmR3CritSectInfoCommon(pVM, pHlp, cArgs, papszArgs, false);
1332}
1333
1334
1335/**
1336 * @callback_method_impl{FNDBGFINFOARGVINT, critsectrw}
1337 */
1338static DECLCALLBACK(void) pdmR3CritSectRwInfo(PVM pVM, PCDBGFINFOHLP pHlp, int cArgs, char **papszArgs)
1339{
1340 return pdmR3CritSectInfoCommon(pVM, pHlp, cArgs, papszArgs, true);
1341}
1342
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use