VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/PDMAllCritSect.cpp@ 85416

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

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 29.4 KB
Line 
1/* $Id: PDMAllCritSect.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * PDM - Write-Only Critical Section, All Contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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/mm.h>
26#include <VBox/vmm/vmm.h>
27#include <VBox/vmm/vmcc.h>
28#include <VBox/err.h>
29#include <VBox/vmm/hm.h>
30
31#include <VBox/log.h>
32#include <iprt/asm.h>
33#include <iprt/asm-amd64-x86.h>
34#include <iprt/assert.h>
35#ifdef IN_RING3
36# include <iprt/lockvalidator.h>
37# include <iprt/semaphore.h>
38#endif
39#if defined(IN_RING3) || defined(IN_RING0)
40# include <iprt/thread.h>
41#endif
42
43
44/*********************************************************************************************************************************
45* Defined Constants And Macros *
46*********************************************************************************************************************************/
47/** The number loops to spin for in ring-3. */
48#define PDMCRITSECT_SPIN_COUNT_R3 20
49/** The number loops to spin for in ring-0. */
50#define PDMCRITSECT_SPIN_COUNT_R0 256
51/** The number loops to spin for in the raw-mode context. */
52#define PDMCRITSECT_SPIN_COUNT_RC 256
53
54
55/** Skips some of the overly paranoid atomic updates.
56 * Makes some assumptions about cache coherence, though not brave enough not to
57 * always end with an atomic update. */
58#define PDMCRITSECT_WITH_LESS_ATOMIC_STUFF
59
60/* Undefine the automatic VBOX_STRICT API mappings. */
61#undef PDMCritSectEnter
62#undef PDMCritSectTryEnter
63
64
65/**
66 * Gets the ring-3 native thread handle of the calling thread.
67 *
68 * @returns native thread handle (ring-3).
69 * @param pCritSect The critical section. This is used in R0 and RC.
70 */
71DECL_FORCE_INLINE(RTNATIVETHREAD) pdmCritSectGetNativeSelf(PCPDMCRITSECT pCritSect)
72{
73#ifdef IN_RING3
74 NOREF(pCritSect);
75 RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
76#else
77 AssertMsgReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, ("%RX32\n", pCritSect->s.Core.u32Magic),
78 NIL_RTNATIVETHREAD);
79 PVMCC pVM = pCritSect->s.CTX_SUFF(pVM); AssertPtr(pVM);
80 PVMCPUCC pVCpu = VMMGetCpu(pVM); AssertPtr(pVCpu);
81 RTNATIVETHREAD hNativeSelf = pVCpu->hNativeThread; Assert(hNativeSelf != NIL_RTNATIVETHREAD);
82#endif
83 return hNativeSelf;
84}
85
86
87/**
88 * Tail code called when we've won the battle for the lock.
89 *
90 * @returns VINF_SUCCESS.
91 *
92 * @param pCritSect The critical section.
93 * @param hNativeSelf The native handle of this thread.
94 * @param pSrcPos The source position of the lock operation.
95 */
96DECL_FORCE_INLINE(int) pdmCritSectEnterFirst(PPDMCRITSECT pCritSect, RTNATIVETHREAD hNativeSelf, PCRTLOCKVALSRCPOS pSrcPos)
97{
98 AssertMsg(pCritSect->s.Core.NativeThreadOwner == NIL_RTNATIVETHREAD, ("NativeThreadOwner=%p\n", pCritSect->s.Core.NativeThreadOwner));
99 Assert(!(pCritSect->s.Core.fFlags & PDMCRITSECT_FLAGS_PENDING_UNLOCK));
100
101# ifdef PDMCRITSECT_WITH_LESS_ATOMIC_STUFF
102 pCritSect->s.Core.cNestings = 1;
103# else
104 ASMAtomicWriteS32(&pCritSect->s.Core.cNestings, 1);
105# endif
106 Assert(pCritSect->s.Core.cNestings == 1);
107 ASMAtomicWriteHandle(&pCritSect->s.Core.NativeThreadOwner, hNativeSelf);
108
109# ifdef PDMCRITSECT_STRICT
110 RTLockValidatorRecExclSetOwner(pCritSect->s.Core.pValidatorRec, NIL_RTTHREAD, pSrcPos, true);
111# else
112 NOREF(pSrcPos);
113# endif
114
115 STAM_PROFILE_ADV_START(&pCritSect->s.StatLocked, l);
116 return VINF_SUCCESS;
117}
118
119
120#if defined(IN_RING3) || defined(IN_RING0)
121/**
122 * Deals with the contended case in ring-3 and ring-0.
123 *
124 * @retval VINF_SUCCESS on success.
125 * @retval VERR_SEM_DESTROYED if destroyed.
126 *
127 * @param pCritSect The critsect.
128 * @param hNativeSelf The native thread handle.
129 * @param pSrcPos The source position of the lock operation.
130 */
131static int pdmR3R0CritSectEnterContended(PPDMCRITSECT pCritSect, RTNATIVETHREAD hNativeSelf, PCRTLOCKVALSRCPOS pSrcPos)
132{
133 /*
134 * Start waiting.
135 */
136 if (ASMAtomicIncS32(&pCritSect->s.Core.cLockers) == 0)
137 return pdmCritSectEnterFirst(pCritSect, hNativeSelf, pSrcPos);
138# ifdef IN_RING3
139 STAM_COUNTER_INC(&pCritSect->s.StatContentionR3);
140# else
141 STAM_COUNTER_INC(&pCritSect->s.StatContentionRZLock);
142# endif
143
144 /*
145 * The wait loop.
146 */
147 PSUPDRVSESSION pSession = pCritSect->s.CTX_SUFF(pVM)->pSession;
148 SUPSEMEVENT hEvent = (SUPSEMEVENT)pCritSect->s.Core.EventSem;
149# ifdef IN_RING3
150# ifdef PDMCRITSECT_STRICT
151 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
152 int rc2 = RTLockValidatorRecExclCheckOrder(pCritSect->s.Core.pValidatorRec, hThreadSelf, pSrcPos, RT_INDEFINITE_WAIT);
153 if (RT_FAILURE(rc2))
154 return rc2;
155# else
156 RTTHREAD hThreadSelf = RTThreadSelf();
157# endif
158# endif
159 for (;;)
160 {
161 /*
162 * Do the wait.
163 *
164 * In ring-3 this gets cluttered by lock validation and thread state
165 * maintainence.
166 *
167 * In ring-0 we have to deal with the possibility that the thread has
168 * been signalled and the interruptible wait function returning
169 * immediately. In that case we do normal R0/RC rcBusy handling.
170 */
171# ifdef IN_RING3
172# ifdef PDMCRITSECT_STRICT
173 int rc9 = RTLockValidatorRecExclCheckBlocking(pCritSect->s.Core.pValidatorRec, hThreadSelf, pSrcPos,
174 !(pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NO_NESTING),
175 RT_INDEFINITE_WAIT, RTTHREADSTATE_CRITSECT, true);
176 if (RT_FAILURE(rc9))
177 return rc9;
178# else
179 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_CRITSECT, true);
180# endif
181 int rc = SUPSemEventWaitNoResume(pSession, hEvent, RT_INDEFINITE_WAIT);
182 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_CRITSECT);
183# else /* IN_RING0 */
184 int rc = SUPSemEventWaitNoResume(pSession, hEvent, RT_INDEFINITE_WAIT);
185# endif /* IN_RING0 */
186
187 /*
188 * Deal with the return code and critsect destruction.
189 */
190 if (RT_UNLIKELY(pCritSect->s.Core.u32Magic != RTCRITSECT_MAGIC))
191 return VERR_SEM_DESTROYED;
192 if (rc == VINF_SUCCESS)
193 return pdmCritSectEnterFirst(pCritSect, hNativeSelf, pSrcPos);
194 AssertMsg(rc == VERR_INTERRUPTED, ("rc=%Rrc\n", rc));
195
196# ifdef IN_RING0
197 /* Something is pending (signal, APC, debugger, whatever), just go back
198 to ring-3 so the kernel can deal with it when leaving kernel context.
199
200 Note! We've incremented cLockers already and cannot safely decrement
201 it without creating a race with PDMCritSectLeave, resulting in
202 spurious wakeups. */
203 PVMCC pVM = pCritSect->s.CTX_SUFF(pVM); AssertPtr(pVM);
204 PVMCPUCC pVCpu = VMMGetCpu(pVM); AssertPtr(pVCpu);
205 rc = VMMRZCallRing3(pVM, pVCpu, VMMCALLRING3_VM_R0_PREEMPT, NULL);
206 AssertRC(rc);
207# endif
208 }
209 /* won't get here */
210}
211#endif /* IN_RING3 || IN_RING0 */
212
213
214/**
215 * Common worker for the debug and normal APIs.
216 *
217 * @returns VINF_SUCCESS if entered successfully.
218 * @returns rcBusy when encountering a busy critical section in GC/R0.
219 * @retval VERR_SEM_DESTROYED if the critical section is delete before or
220 * during the operation.
221 *
222 * @param pCritSect The PDM critical section to enter.
223 * @param rcBusy The status code to return when we're in GC or R0
224 * @param pSrcPos The source position of the lock operation.
225 */
226DECL_FORCE_INLINE(int) pdmCritSectEnter(PPDMCRITSECT pCritSect, int rcBusy, PCRTLOCKVALSRCPOS pSrcPos)
227{
228 Assert(pCritSect->s.Core.cNestings < 8); /* useful to catch incorrect locking */
229 Assert(pCritSect->s.Core.cNestings >= 0);
230
231 /*
232 * If the critical section has already been destroyed, then inform the caller.
233 */
234 AssertMsgReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC,
235 ("%p %RX32\n", pCritSect, pCritSect->s.Core.u32Magic),
236 VERR_SEM_DESTROYED);
237
238 /*
239 * See if we're lucky.
240 */
241 /* NOP ... */
242 if (!(pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NOP))
243 { /* We're more likely to end up here with real critsects than a NOP one. */ }
244 else
245 return VINF_SUCCESS;
246
247 RTNATIVETHREAD hNativeSelf = pdmCritSectGetNativeSelf(pCritSect);
248 /* ... not owned ... */
249 if (ASMAtomicCmpXchgS32(&pCritSect->s.Core.cLockers, 0, -1))
250 return pdmCritSectEnterFirst(pCritSect, hNativeSelf, pSrcPos);
251
252 /* ... or nested. */
253 if (pCritSect->s.Core.NativeThreadOwner == hNativeSelf)
254 {
255 Assert(pCritSect->s.Core.cNestings >= 1);
256# ifdef PDMCRITSECT_WITH_LESS_ATOMIC_STUFF
257 pCritSect->s.Core.cNestings += 1;
258# else
259 ASMAtomicIncS32(&pCritSect->s.Core.cNestings);
260# endif
261 ASMAtomicIncS32(&pCritSect->s.Core.cLockers);
262 return VINF_SUCCESS;
263 }
264
265 /*
266 * Spin for a bit without incrementing the counter.
267 */
268 /** @todo Move this to cfgm variables since it doesn't make sense to spin on UNI
269 * cpu systems. */
270 int32_t cSpinsLeft = CTX_SUFF(PDMCRITSECT_SPIN_COUNT_);
271 while (cSpinsLeft-- > 0)
272 {
273 if (ASMAtomicCmpXchgS32(&pCritSect->s.Core.cLockers, 0, -1))
274 return pdmCritSectEnterFirst(pCritSect, hNativeSelf, pSrcPos);
275 ASMNopPause();
276 /** @todo Should use monitor/mwait on e.g. &cLockers here, possibly with a
277 cli'ed pendingpreemption check up front using sti w/ instruction fusing
278 for avoiding races. Hmm ... This is assuming the other party is actually
279 executing code on another CPU ... which we could keep track of if we
280 wanted. */
281 }
282
283#ifdef IN_RING3
284 /*
285 * Take the slow path.
286 */
287 NOREF(rcBusy);
288 return pdmR3R0CritSectEnterContended(pCritSect, hNativeSelf, pSrcPos);
289
290#else
291# ifdef IN_RING0
292 /** @todo If preemption is disabled it means we're in VT-x/AMD-V context
293 * and would be better off switching out of that while waiting for
294 * the lock. Several of the locks jumps back to ring-3 just to
295 * get the lock, the ring-3 code will then call the kernel to do
296 * the lock wait and when the call return it will call ring-0
297 * again and resume via in setjmp style. Not very efficient. */
298# if 0
299 if (ASMIntAreEnabled()) /** @todo this can be handled as well by changing
300 * callers not prepared for longjmp/blocking to
301 * use PDMCritSectTryEnter. */
302 {
303 /*
304 * Leave HM context while waiting if necessary.
305 */
306 int rc;
307 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
308 {
309 STAM_REL_COUNTER_ADD(&pCritSect->s.StatContentionRZLock, 1000000);
310 rc = pdmR3R0CritSectEnterContended(pCritSect, hNativeSelf, pSrcPos);
311 }
312 else
313 {
314 STAM_REL_COUNTER_ADD(&pCritSect->s.StatContentionRZLock, 1000000000);
315 PVMCC pVM = pCritSect->s.CTX_SUFF(pVM);
316 PVMCPUCC pVCpu = VMMGetCpu(pVM);
317 HMR0Leave(pVM, pVCpu);
318 RTThreadPreemptRestore(NIL_RTTHREAD, XXX);
319
320 rc = pdmR3R0CritSectEnterContended(pCritSect, hNativeSelf, pSrcPos);
321
322 RTThreadPreemptDisable(NIL_RTTHREAD, XXX);
323 HMR0Enter(pVM, pVCpu);
324 }
325 return rc;
326 }
327# else
328 /*
329 * We preemption hasn't been disabled, we can block here in ring-0.
330 */
331 if ( RTThreadPreemptIsEnabled(NIL_RTTHREAD)
332 && ASMIntAreEnabled())
333 return pdmR3R0CritSectEnterContended(pCritSect, hNativeSelf, pSrcPos);
334# endif
335#endif /* IN_RING0 */
336
337 STAM_REL_COUNTER_INC(&pCritSect->s.StatContentionRZLock);
338
339 /*
340 * Call ring-3 to acquire the critical section?
341 */
342 if (rcBusy == VINF_SUCCESS)
343 {
344 PVMCC pVM = pCritSect->s.CTX_SUFF(pVM); AssertPtr(pVM);
345 PVMCPUCC pVCpu = VMMGetCpu(pVM); AssertPtr(pVCpu);
346 return VMMRZCallRing3(pVM, pVCpu, VMMCALLRING3_PDM_CRIT_SECT_ENTER, MMHyperCCToR3(pVM, pCritSect));
347 }
348
349 /*
350 * Return busy.
351 */
352 LogFlow(("PDMCritSectEnter: locked => R3 (%Rrc)\n", rcBusy));
353 return rcBusy;
354#endif /* !IN_RING3 */
355}
356
357
358/**
359 * Enters a PDM critical section.
360 *
361 * @returns VINF_SUCCESS if entered successfully.
362 * @returns rcBusy when encountering a busy critical section in RC/R0.
363 * @retval VERR_SEM_DESTROYED if the critical section is delete before or
364 * during the operation.
365 *
366 * @param pCritSect The PDM critical section to enter.
367 * @param rcBusy The status code to return when we're in RC or R0
368 * and the section is busy. Pass VINF_SUCCESS to
369 * acquired the critical section thru a ring-3
370 * call if necessary.
371 */
372VMMDECL(int) PDMCritSectEnter(PPDMCRITSECT pCritSect, int rcBusy)
373{
374#ifndef PDMCRITSECT_STRICT
375 return pdmCritSectEnter(pCritSect, rcBusy, NULL);
376#else
377 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
378 return pdmCritSectEnter(pCritSect, rcBusy, &SrcPos);
379#endif
380}
381
382
383/**
384 * Enters a PDM critical section, with location information for debugging.
385 *
386 * @returns VINF_SUCCESS if entered successfully.
387 * @returns rcBusy when encountering a busy critical section in RC/R0.
388 * @retval VERR_SEM_DESTROYED if the critical section is delete before or
389 * during the operation.
390 *
391 * @param pCritSect The PDM critical section to enter.
392 * @param rcBusy The status code to return when we're in RC or R0
393 * and the section is busy. Pass VINF_SUCCESS to
394 * acquired the critical section thru a ring-3
395 * call if necessary.
396 * @param uId Some kind of locking location ID. Typically a
397 * return address up the stack. Optional (0).
398 * @param SRC_POS The source position where to lock is being
399 * acquired from. Optional.
400 */
401VMMDECL(int) PDMCritSectEnterDebug(PPDMCRITSECT pCritSect, int rcBusy, RTHCUINTPTR uId, RT_SRC_POS_DECL)
402{
403#ifdef PDMCRITSECT_STRICT
404 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
405 return pdmCritSectEnter(pCritSect, rcBusy, &SrcPos);
406#else
407 NOREF(uId); RT_SRC_POS_NOREF();
408 return pdmCritSectEnter(pCritSect, rcBusy, NULL);
409#endif
410}
411
412
413/**
414 * Common worker for the debug and normal APIs.
415 *
416 * @retval VINF_SUCCESS on success.
417 * @retval VERR_SEM_BUSY if the critsect was owned.
418 * @retval VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
419 * @retval VERR_SEM_DESTROYED if the critical section is delete before or
420 * during the operation.
421 *
422 * @param pCritSect The critical section.
423 * @param pSrcPos The source position of the lock operation.
424 */
425static int pdmCritSectTryEnter(PPDMCRITSECT pCritSect, PCRTLOCKVALSRCPOS pSrcPos)
426{
427 /*
428 * If the critical section has already been destroyed, then inform the caller.
429 */
430 AssertMsgReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC,
431 ("%p %RX32\n", pCritSect, pCritSect->s.Core.u32Magic),
432 VERR_SEM_DESTROYED);
433
434 /*
435 * See if we're lucky.
436 */
437 /* NOP ... */
438 if (!(pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NOP))
439 { /* We're more likely to end up here with real critsects than a NOP one. */ }
440 else
441 return VINF_SUCCESS;
442
443 RTNATIVETHREAD hNativeSelf = pdmCritSectGetNativeSelf(pCritSect);
444 /* ... not owned ... */
445 if (ASMAtomicCmpXchgS32(&pCritSect->s.Core.cLockers, 0, -1))
446 return pdmCritSectEnterFirst(pCritSect, hNativeSelf, pSrcPos);
447
448 /* ... or nested. */
449 if (pCritSect->s.Core.NativeThreadOwner == hNativeSelf)
450 {
451 Assert(pCritSect->s.Core.cNestings >= 1);
452# ifdef PDMCRITSECT_WITH_LESS_ATOMIC_STUFF
453 pCritSect->s.Core.cNestings += 1;
454# else
455 ASMAtomicIncS32(&pCritSect->s.Core.cNestings);
456# endif
457 ASMAtomicIncS32(&pCritSect->s.Core.cLockers);
458 return VINF_SUCCESS;
459 }
460
461 /* no spinning */
462
463 /*
464 * Return busy.
465 */
466#ifdef IN_RING3
467 STAM_REL_COUNTER_INC(&pCritSect->s.StatContentionR3);
468#else
469 STAM_REL_COUNTER_INC(&pCritSect->s.StatContentionRZLock);
470#endif
471 LogFlow(("PDMCritSectTryEnter: locked\n"));
472 return VERR_SEM_BUSY;
473}
474
475
476/**
477 * Try enter a critical section.
478 *
479 * @retval VINF_SUCCESS on success.
480 * @retval VERR_SEM_BUSY if the critsect was owned.
481 * @retval VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
482 * @retval VERR_SEM_DESTROYED if the critical section is delete before or
483 * during the operation.
484 *
485 * @param pCritSect The critical section.
486 */
487VMMDECL(int) PDMCritSectTryEnter(PPDMCRITSECT pCritSect)
488{
489#ifndef PDMCRITSECT_STRICT
490 return pdmCritSectTryEnter(pCritSect, NULL);
491#else
492 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
493 return pdmCritSectTryEnter(pCritSect, &SrcPos);
494#endif
495}
496
497
498/**
499 * Try enter a critical section, with location information for debugging.
500 *
501 * @retval VINF_SUCCESS on success.
502 * @retval VERR_SEM_BUSY if the critsect was owned.
503 * @retval VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
504 * @retval VERR_SEM_DESTROYED if the critical section is delete before or
505 * during the operation.
506 *
507 * @param pCritSect The critical section.
508 * @param uId Some kind of locking location ID. Typically a
509 * return address up the stack. Optional (0).
510 * @param SRC_POS The source position where to lock is being
511 * acquired from. Optional.
512 */
513VMMDECL(int) PDMCritSectTryEnterDebug(PPDMCRITSECT pCritSect, RTHCUINTPTR uId, RT_SRC_POS_DECL)
514{
515#ifdef PDMCRITSECT_STRICT
516 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
517 return pdmCritSectTryEnter(pCritSect, &SrcPos);
518#else
519 NOREF(uId); RT_SRC_POS_NOREF();
520 return pdmCritSectTryEnter(pCritSect, NULL);
521#endif
522}
523
524
525#ifdef IN_RING3
526/**
527 * Enters a PDM critical section.
528 *
529 * @returns VINF_SUCCESS if entered successfully.
530 * @returns rcBusy when encountering a busy critical section in GC/R0.
531 * @retval VERR_SEM_DESTROYED if the critical section is delete before or
532 * during the operation.
533 *
534 * @param pCritSect The PDM critical section to enter.
535 * @param fCallRing3 Whether this is a VMMRZCallRing3()request.
536 */
537VMMR3DECL(int) PDMR3CritSectEnterEx(PPDMCRITSECT pCritSect, bool fCallRing3)
538{
539 int rc = PDMCritSectEnter(pCritSect, VERR_IGNORED);
540 if ( rc == VINF_SUCCESS
541 && fCallRing3
542 && pCritSect->s.Core.pValidatorRec
543 && pCritSect->s.Core.pValidatorRec->hThread != NIL_RTTHREAD)
544 RTLockValidatorRecExclReleaseOwnerUnchecked(pCritSect->s.Core.pValidatorRec);
545 return rc;
546}
547#endif /* IN_RING3 */
548
549
550/**
551 * Leaves a critical section entered with PDMCritSectEnter().
552 *
553 * @returns Indication whether we really exited the critical section.
554 * @retval VINF_SUCCESS if we really exited.
555 * @retval VINF_SEM_NESTED if we only reduced the nesting count.
556 * @retval VERR_NOT_OWNER if you somehow ignore release assertions.
557 *
558 * @param pCritSect The PDM critical section to leave.
559 */
560VMMDECL(int) PDMCritSectLeave(PPDMCRITSECT pCritSect)
561{
562 AssertMsg(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, ("%p %RX32\n", pCritSect, pCritSect->s.Core.u32Magic));
563 Assert(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC);
564
565 /* Check for NOP sections before asserting ownership. */
566 if (!(pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NOP))
567 { /* We're more likely to end up here with real critsects than a NOP one. */ }
568 else
569 return VINF_SUCCESS;
570
571 /*
572 * Always check that the caller is the owner (screw performance).
573 */
574 RTNATIVETHREAD const hNativeSelf = pdmCritSectGetNativeSelf(pCritSect);
575 AssertReleaseMsgReturn(pCritSect->s.Core.NativeThreadOwner == hNativeSelf,
576 ("%p %s: %p != %p; cLockers=%d cNestings=%d\n", pCritSect, R3STRING(pCritSect->s.pszName),
577 pCritSect->s.Core.NativeThreadOwner, hNativeSelf,
578 pCritSect->s.Core.cLockers, pCritSect->s.Core.cNestings),
579 VERR_NOT_OWNER);
580
581 /*
582 * Nested leave.
583 */
584 int32_t const cNestings = pCritSect->s.Core.cNestings;
585 Assert(cNestings >= 1);
586 if (cNestings > 1)
587 {
588# ifdef PDMCRITSECT_WITH_LESS_ATOMIC_STUFF
589 pCritSect->s.Core.cNestings = cNestings - 1;
590# else
591 ASMAtomicWriteS32(&pCritSect->s.Core.cNestings, cNestings - 1);
592# endif
593 ASMAtomicDecS32(&pCritSect->s.Core.cLockers);
594 Assert(pCritSect->s.Core.cLockers >= 0);
595 return VINF_SEM_NESTED;
596 }
597
598#ifdef IN_RING0
599# if 0 /** @todo Make SUPSemEventSignal interrupt safe (handle table++) and enable this for: defined(RT_OS_LINUX) || defined(RT_OS_OS2) */
600 if (1) /* SUPSemEventSignal is safe */
601# else
602 if (ASMIntAreEnabled())
603# endif
604#endif
605#if defined(IN_RING3) || defined(IN_RING0)
606 {
607 /*
608 * Leave for real.
609 */
610 /* update members. */
611 SUPSEMEVENT hEventToSignal = pCritSect->s.hEventToSignal;
612 pCritSect->s.hEventToSignal = NIL_SUPSEMEVENT;
613# ifdef IN_RING3
614# if defined(PDMCRITSECT_STRICT)
615 if (pCritSect->s.Core.pValidatorRec->hThread != NIL_RTTHREAD)
616 RTLockValidatorRecExclReleaseOwnerUnchecked(pCritSect->s.Core.pValidatorRec);
617# endif
618 Assert(!pCritSect->s.Core.pValidatorRec || pCritSect->s.Core.pValidatorRec->hThread == NIL_RTTHREAD);
619# endif
620# ifdef PDMCRITSECT_WITH_LESS_ATOMIC_STUFF
621 //pCritSect->s.Core.cNestings = 0; /* not really needed */
622 pCritSect->s.Core.NativeThreadOwner = NIL_RTNATIVETHREAD;
623# else
624 ASMAtomicWriteS32(&pCritSect->s.Core.cNestings, 0);
625 ASMAtomicWriteHandle(&pCritSect->s.Core.NativeThreadOwner, NIL_RTNATIVETHREAD);
626# endif
627 ASMAtomicAndU32(&pCritSect->s.Core.fFlags, ~PDMCRITSECT_FLAGS_PENDING_UNLOCK);
628
629 /* stop and decrement lockers. */
630 STAM_PROFILE_ADV_STOP(&pCritSect->s.StatLocked, l);
631 ASMCompilerBarrier();
632 if (ASMAtomicDecS32(&pCritSect->s.Core.cLockers) < 0)
633 { /* hopefully likely */ }
634 else
635 {
636 /* Someone is waiting, wake up one of them. */
637 SUPSEMEVENT hEvent = (SUPSEMEVENT)pCritSect->s.Core.EventSem;
638 PSUPDRVSESSION pSession = pCritSect->s.CTX_SUFF(pVM)->pSession;
639 int rc = SUPSemEventSignal(pSession, hEvent);
640 AssertRC(rc);
641 }
642
643 /* Signal exit event. */
644 if (RT_LIKELY(hEventToSignal == NIL_SUPSEMEVENT))
645 { /* likely */ }
646 else
647 {
648 Log8(("Signalling %#p\n", hEventToSignal));
649 int rc = SUPSemEventSignal(pCritSect->s.CTX_SUFF(pVM)->pSession, hEventToSignal);
650 AssertRC(rc);
651 }
652
653# if defined(DEBUG_bird) && defined(IN_RING0)
654 VMMTrashVolatileXMMRegs();
655# endif
656 }
657#endif /* IN_RING3 || IN_RING0 */
658#ifdef IN_RING0
659 else
660#endif
661#if defined(IN_RING0) || defined(IN_RC)
662 {
663 /*
664 * Try leave it.
665 */
666 if (pCritSect->s.Core.cLockers == 0)
667 {
668# ifdef PDMCRITSECT_WITH_LESS_ATOMIC_STUFF
669 //pCritSect->s.Core.cNestings = 0; /* not really needed */
670# else
671 ASMAtomicWriteS32(&pCritSect->s.Core.cNestings, 0);
672# endif
673 RTNATIVETHREAD hNativeThread = pCritSect->s.Core.NativeThreadOwner;
674 ASMAtomicAndU32(&pCritSect->s.Core.fFlags, ~PDMCRITSECT_FLAGS_PENDING_UNLOCK);
675 STAM_PROFILE_ADV_STOP(&pCritSect->s.StatLocked, l);
676
677 ASMAtomicWriteHandle(&pCritSect->s.Core.NativeThreadOwner, NIL_RTNATIVETHREAD);
678 if (ASMAtomicCmpXchgS32(&pCritSect->s.Core.cLockers, -1, 0))
679 return VINF_SUCCESS;
680
681 /* darn, someone raced in on us. */
682 ASMAtomicWriteHandle(&pCritSect->s.Core.NativeThreadOwner, hNativeThread);
683 STAM_PROFILE_ADV_START(&pCritSect->s.StatLocked, l);
684# ifdef PDMCRITSECT_WITH_LESS_ATOMIC_STUFF
685 //pCritSect->s.Core.cNestings = 1;
686 Assert(pCritSect->s.Core.cNestings == 1);
687# else
688 //Assert(pCritSect->s.Core.cNestings == 0);
689 ASMAtomicWriteS32(&pCritSect->s.Core.cNestings, 1);
690# endif
691 }
692 ASMAtomicOrU32(&pCritSect->s.Core.fFlags, PDMCRITSECT_FLAGS_PENDING_UNLOCK);
693
694 /*
695 * Queue the request.
696 */
697 PVMCC pVM = pCritSect->s.CTX_SUFF(pVM); AssertPtr(pVM);
698 PVMCPUCC pVCpu = VMMGetCpu(pVM); AssertPtr(pVCpu);
699 uint32_t i = pVCpu->pdm.s.cQueuedCritSectLeaves++;
700 LogFlow(("PDMCritSectLeave: [%d]=%p => R3\n", i, pCritSect));
701 AssertFatal(i < RT_ELEMENTS(pVCpu->pdm.s.apQueuedCritSectLeaves));
702 pVCpu->pdm.s.apQueuedCritSectLeaves[i] = MMHyperCCToR3(pVM, pCritSect);
703 VMCPU_FF_SET(pVCpu, VMCPU_FF_PDM_CRITSECT);
704 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
705 STAM_REL_COUNTER_INC(&pVM->pdm.s.StatQueuedCritSectLeaves);
706 STAM_REL_COUNTER_INC(&pCritSect->s.StatContentionRZUnlock);
707 }
708#endif /* IN_RING0 || IN_RC */
709
710 return VINF_SUCCESS;
711}
712
713
714#if defined(IN_RING0) || defined(IN_RING3)
715/**
716 * Schedule a event semaphore for signalling upon critsect exit.
717 *
718 * @returns VINF_SUCCESS on success.
719 * @returns VERR_TOO_MANY_SEMAPHORES if an event was already scheduled.
720 * @returns VERR_NOT_OWNER if we're not the critsect owner (ring-3 only).
721 * @returns VERR_SEM_DESTROYED if RTCritSectDelete was called while waiting.
722 *
723 * @param pCritSect The critical section.
724 * @param hEventToSignal The support driver event semaphore that should be
725 * signalled.
726 */
727VMMDECL(int) PDMHCCritSectScheduleExitEvent(PPDMCRITSECT pCritSect, SUPSEMEVENT hEventToSignal)
728{
729 AssertPtr(pCritSect);
730 Assert(!(pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NOP));
731 Assert(hEventToSignal != NIL_SUPSEMEVENT);
732# ifdef IN_RING3
733 if (RT_UNLIKELY(!RTCritSectIsOwner(&pCritSect->s.Core)))
734 return VERR_NOT_OWNER;
735# endif
736 if (RT_LIKELY( pCritSect->s.hEventToSignal == NIL_RTSEMEVENT
737 || pCritSect->s.hEventToSignal == hEventToSignal))
738 {
739 pCritSect->s.hEventToSignal = hEventToSignal;
740 return VINF_SUCCESS;
741 }
742 return VERR_TOO_MANY_SEMAPHORES;
743}
744#endif /* IN_RING0 || IN_RING3 */
745
746
747/**
748 * Checks the caller is the owner of the critical section.
749 *
750 * @returns true if owner.
751 * @returns false if not owner.
752 * @param pCritSect The critical section.
753 */
754VMMDECL(bool) PDMCritSectIsOwner(PCPDMCRITSECT pCritSect)
755{
756#ifdef IN_RING3
757 return RTCritSectIsOwner(&pCritSect->s.Core);
758#else
759 PVMCC pVM = pCritSect->s.CTX_SUFF(pVM); AssertPtr(pVM);
760 PVMCPUCC pVCpu = VMMGetCpu(pVM); AssertPtr(pVCpu);
761 if (pCritSect->s.Core.NativeThreadOwner != pVCpu->hNativeThread)
762 return false;
763 return (pCritSect->s.Core.fFlags & PDMCRITSECT_FLAGS_PENDING_UNLOCK) == 0
764 || pCritSect->s.Core.cNestings > 1;
765#endif
766}
767
768
769/**
770 * Checks the specified VCPU is the owner of the critical section.
771 *
772 * @returns true if owner.
773 * @returns false if not owner.
774 * @param pCritSect The critical section.
775 * @param pVCpu The cross context virtual CPU structure.
776 */
777VMMDECL(bool) PDMCritSectIsOwnerEx(PCPDMCRITSECT pCritSect, PVMCPUCC pVCpu)
778{
779#ifdef IN_RING3
780 NOREF(pVCpu);
781 return RTCritSectIsOwner(&pCritSect->s.Core);
782#else
783 Assert(VMCC_GET_CPU(pVCpu->CTX_SUFF(pVM), pVCpu->idCpu) == pVCpu);
784 if (pCritSect->s.Core.NativeThreadOwner != pVCpu->hNativeThread)
785 return false;
786 return (pCritSect->s.Core.fFlags & PDMCRITSECT_FLAGS_PENDING_UNLOCK) == 0
787 || pCritSect->s.Core.cNestings > 1;
788#endif
789}
790
791
792/**
793 * Checks if anyone is waiting on the critical section we own.
794 *
795 * @returns true if someone is waiting.
796 * @returns false if no one is waiting.
797 * @param pCritSect The critical section.
798 */
799VMMDECL(bool) PDMCritSectHasWaiters(PCPDMCRITSECT pCritSect)
800{
801 AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, false);
802 Assert(pCritSect->s.Core.NativeThreadOwner == pdmCritSectGetNativeSelf(pCritSect));
803 return pCritSect->s.Core.cLockers >= pCritSect->s.Core.cNestings;
804}
805
806
807/**
808 * Checks if a critical section is initialized or not.
809 *
810 * @returns true if initialized.
811 * @returns false if not initialized.
812 * @param pCritSect The critical section.
813 */
814VMMDECL(bool) PDMCritSectIsInitialized(PCPDMCRITSECT pCritSect)
815{
816 return RTCritSectIsInitialized(&pCritSect->s.Core);
817}
818
819
820/**
821 * Gets the recursion depth.
822 *
823 * @returns The recursion depth.
824 * @param pCritSect The critical section.
825 */
826VMMDECL(uint32_t) PDMCritSectGetRecursion(PCPDMCRITSECT pCritSect)
827{
828 return RTCritSectGetRecursion(&pCritSect->s.Core);
829}
830
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