VirtualBox

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

Last change on this file since 61683 was 58123, checked in by vboxsync, 9 years ago

VMM: Made @param pVCpu more uniform and to the point.

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