VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/generic/semspinmutex-r0drv-generic.c@ 103914

Last change on this file since 103914 was 100191, checked in by vboxsync, 18 months ago

*: Some of the easy build fixes for linux.arm64 not warranting their own commit, bugref:10457

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.5 KB
Line 
1/* $Id: semspinmutex-r0drv-generic.c 100191 2023-06-16 08:04:11Z vboxsync $ */
2/** @file
3 * IPRT - Spinning Mutex Semaphores, Ring-0 Driver, Generic.
4 */
5
6/*
7 * Copyright (C) 2009-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 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#ifdef RT_OS_WINDOWS
42# include "../nt/the-nt-kernel.h"
43#endif
44#include "internal/iprt.h"
45
46#include <iprt/semaphore.h>
47#include <iprt/asm.h>
48#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
49# include <iprt/asm-amd64-x86.h>
50#elif defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32)
51# include <iprt/asm-arm.h>
52#endif
53#include <iprt/assert.h>
54#include <iprt/err.h>
55#include <iprt/mem.h>
56#include <iprt/thread.h>
57#include "internal/magics.h"
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63/**
64 * Saved state information.
65 */
66typedef struct RTSEMSPINMUTEXSTATE
67{
68 /** Saved flags register. */
69 RTCCUINTREG fSavedFlags;
70 /** Preemption state. */
71 RTTHREADPREEMPTSTATE PreemptState;
72 /** Whether to spin or sleep. */
73 bool fSpin;
74 /** Whether the flags have been saved. */
75 bool fValidFlags;
76} RTSEMSPINMUTEXSTATE;
77
78/**
79 * Spinning mutex semaphore.
80 */
81typedef struct RTSEMSPINMUTEXINTERNAL
82{
83 /** Magic value (RTSEMSPINMUTEX_MAGIC)
84 * RTCRITSECT_MAGIC is the value of an initialized & operational section. */
85 uint32_t volatile u32Magic;
86 /** Flags. This is a combination of RTSEMSPINMUTEX_FLAGS_XXX and
87 * RTSEMSPINMUTEX_INT_FLAGS_XXX. */
88 uint32_t volatile fFlags;
89 /** The owner thread.
90 * This is NIL if the semaphore is not owned by anyone. */
91 RTNATIVETHREAD volatile hOwner;
92 /** Number of threads that are fighting for the lock. */
93 int32_t volatile cLockers;
94 /** The semaphore to block on. */
95 RTSEMEVENT hEventSem;
96 /** Saved state information of the owner.
97 * This will be restored by RTSemSpinRelease. */
98 RTSEMSPINMUTEXSTATE SavedState;
99} RTSEMSPINMUTEXINTERNAL;
100
101
102/*********************************************************************************************************************************
103* Defined Constants And Macros *
104*********************************************************************************************************************************/
105/*#define RTSEMSPINMUTEX_INT_FLAGS_MUST*/
106
107/** Validates the handle, returning if invalid. */
108#define RTSEMSPINMUTEX_VALIDATE_RETURN(pThis) \
109 do \
110 { \
111 uint32_t u32Magic; \
112 AssertPtr(pThis); \
113 u32Magic = (pThis)->u32Magic; \
114 if (u32Magic != RTSEMSPINMUTEX_MAGIC) \
115 { \
116 AssertMsgFailed(("u32Magic=%#x pThis=%p\n", u32Magic, pThis)); \
117 return u32Magic == RTSEMSPINMUTEX_MAGIC_DEAD ? VERR_SEM_DESTROYED : VERR_INVALID_HANDLE; \
118 } \
119 } while (0)
120
121
122RTDECL(int) RTSemSpinMutexCreate(PRTSEMSPINMUTEX phSpinMtx, uint32_t fFlags)
123{
124 RTSEMSPINMUTEXINTERNAL *pThis;
125 int rc;
126
127 AssertReturn(!(fFlags & ~RTSEMSPINMUTEX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
128 AssertPtr(phSpinMtx);
129
130 /*
131 * Allocate and initialize the structure.
132 */
133 pThis = (RTSEMSPINMUTEXINTERNAL *)RTMemAllocZ(sizeof(*pThis));
134 if (!pThis)
135 return VERR_NO_MEMORY;
136 pThis->u32Magic = RTSEMSPINMUTEX_MAGIC;
137 pThis->fFlags = fFlags;
138 pThis->hOwner = NIL_RTNATIVETHREAD;
139 pThis->cLockers = 0;
140 rc = RTSemEventCreateEx(&pThis->hEventSem, RTSEMEVENT_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, NULL);
141 if (RT_SUCCESS(rc))
142 {
143 *phSpinMtx = pThis;
144 return VINF_SUCCESS;
145 }
146
147 RTMemFree(pThis);
148 return rc;
149}
150RT_EXPORT_SYMBOL(RTSemSpinMutexCreate);
151
152
153/**
154 * Helper for RTSemSpinMutexTryRequest and RTSemSpinMutexRequest.
155 *
156 * This will check the current context and see if it's usui
157 *
158 * @returns VINF_SUCCESS or VERR_SEM_BAD_CONTEXT.
159 * @param pState Output structure.
160 */
161static int rtSemSpinMutexEnter(RTSEMSPINMUTEXSTATE *pState, RTSEMSPINMUTEXINTERNAL *pThis)
162{
163#ifndef RT_OS_WINDOWS
164 RTTHREADPREEMPTSTATE const StateInit = RTTHREADPREEMPTSTATE_INITIALIZER;
165#endif
166 int rc = VINF_SUCCESS;
167
168 /** @todo Later #1: When entering in interrupt context and we're not able to
169 * wake up threads from it, we could try switch the lock into pure
170 * spinlock mode. This would require that there are no other threads
171 * currently waiting on it and that the RTSEMSPINMUTEX_FLAGS_IRQ_SAFE
172 * flag is set.
173 *
174 * Later #2: Similarly, it is possible to turn on the
175 * RTSEMSPINMUTEX_FLAGS_IRQ_SAFE at run time if we manage to grab the
176 * semaphore ownership at interrupt time. We might want to try delay the
177 * RTSEMSPINMUTEX_FLAGS_IRQ_SAFE even, since we're fine if we get it...
178 */
179
180#ifdef RT_OS_WINDOWS
181 /*
182 * NT: IRQL <= DISPATCH_LEVEL for waking up threads; IRQL < DISPATCH_LEVEL for sleeping.
183 */
184 pState->PreemptState.uchOldIrql = KeGetCurrentIrql();
185 if (pState->PreemptState.uchOldIrql > DISPATCH_LEVEL)
186 return VERR_SEM_BAD_CONTEXT;
187
188 if (pState->PreemptState.uchOldIrql >= DISPATCH_LEVEL)
189 pState->fSpin = true;
190 else
191 {
192 pState->fSpin = false;
193 KeRaiseIrql(DISPATCH_LEVEL, &pState->PreemptState.uchOldIrql);
194 Assert(pState->PreemptState.uchOldIrql < DISPATCH_LEVEL);
195 }
196
197#elif defined(RT_OS_SOLARIS)
198 /*
199 * Solaris: RTSemEventSignal will do bad stuff on S10 if interrupts are disabled.
200 */
201 if (!ASMIntAreEnabled())
202 return VERR_SEM_BAD_CONTEXT;
203
204 pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
205 if (RTThreadIsInInterrupt(NIL_RTTHREAD))
206 {
207 if (!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE))
208 rc = VINF_SEM_BAD_CONTEXT; /* Try, but owner might be interrupted. */
209 pState->fSpin = true;
210 }
211 pState->PreemptState = StateInit;
212 RTThreadPreemptDisable(&pState->PreemptState);
213
214#elif defined(RT_OS_LINUX) || defined(RT_OS_OS2)
215 /*
216 * OSes on which RTSemEventSignal can be called from any context.
217 */
218 pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
219 if (RTThreadIsInInterrupt(NIL_RTTHREAD))
220 {
221 if (!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE))
222 rc = VINF_SEM_BAD_CONTEXT; /* Try, but owner might be interrupted. */
223 pState->fSpin = true;
224 }
225 pState->PreemptState = StateInit;
226 RTThreadPreemptDisable(&pState->PreemptState);
227
228#else /* PORTME: Check for context where we cannot wake up threads. */
229 /*
230 * Default: ASSUME thread can be woken up if interrupts are enabled and
231 * we're not in an interrupt context.
232 * ASSUME that we can go to sleep if preemption is enabled.
233 */
234 if ( RTThreadIsInInterrupt(NIL_RTTHREAD)
235 || !ASMIntAreEnabled())
236 return VERR_SEM_BAD_CONTEXT;
237
238 pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
239 pState->PreemptState = StateInit;
240 RTThreadPreemptDisable(&pState->PreemptState);
241#endif
242
243 /*
244 * Disable interrupts if necessary.
245 */
246 pState->fValidFlags = !!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE);
247 if (pState->fValidFlags)
248 pState->fSavedFlags = ASMIntDisableFlags();
249 else
250 pState->fSavedFlags = 0;
251
252 return rc;
253}
254
255
256/**
257 * Helper for RTSemSpinMutexTryRequest, RTSemSpinMutexRequest and
258 * RTSemSpinMutexRelease.
259 *
260 * @param pState
261 */
262DECL_FORCE_INLINE(void) rtSemSpinMutexLeave(RTSEMSPINMUTEXSTATE *pState)
263{
264 /*
265 * Restore the interrupt flag.
266 */
267 if (pState->fValidFlags)
268 ASMSetFlags(pState->fSavedFlags);
269
270#ifdef RT_OS_WINDOWS
271 /*
272 * NT: Lower the IRQL if we raised it.
273 */
274 if (pState->PreemptState.uchOldIrql < DISPATCH_LEVEL)
275 KeLowerIrql(pState->PreemptState.uchOldIrql);
276#else
277 /*
278 * Default: Restore preemption.
279 */
280 RTThreadPreemptRestore(&pState->PreemptState);
281#endif
282}
283
284
285RTDECL(int) RTSemSpinMutexTryRequest(RTSEMSPINMUTEX hSpinMtx)
286{
287 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
288 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
289 RTSEMSPINMUTEXSTATE State;
290 bool fRc;
291 int rc;
292
293 Assert(hSelf != NIL_RTNATIVETHREAD);
294 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
295
296 /*
297 * Check context, disable preemption and save flags if necessary.
298 */
299 rc = rtSemSpinMutexEnter(&State, pThis);
300 if (RT_FAILURE(rc))
301 return rc;
302
303 /*
304 * Try take the ownership.
305 */
306 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
307 if (!fRc)
308 {
309 /* Busy, too bad. Check for attempts at nested access. */
310 rc = VERR_SEM_BUSY;
311 if (RT_UNLIKELY(pThis->hOwner == hSelf))
312 {
313 AssertMsgFailed(("%p attempt at nested access\n"));
314 rc = VERR_SEM_NESTED;
315 }
316
317 rtSemSpinMutexLeave(&State);
318 return rc;
319 }
320
321 /*
322 * We're the semaphore owner.
323 */
324 ASMAtomicIncS32(&pThis->cLockers);
325 pThis->SavedState = State;
326 return VINF_SUCCESS;
327}
328RT_EXPORT_SYMBOL(RTSemSpinMutexTryRequest);
329
330
331RTDECL(int) RTSemSpinMutexRequest(RTSEMSPINMUTEX hSpinMtx)
332{
333 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
334 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
335 RTSEMSPINMUTEXSTATE State;
336 bool fRc;
337 int rc;
338
339 Assert(hSelf != NIL_RTNATIVETHREAD);
340 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
341
342 /*
343 * Check context, disable preemption and save flags if necessary.
344 */
345 rc = rtSemSpinMutexEnter(&State, pThis);
346 if (RT_FAILURE(rc))
347 return rc;
348
349 /*
350 * Try take the ownership.
351 */
352 ASMAtomicIncS32(&pThis->cLockers);
353 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
354 if (!fRc)
355 {
356 uint32_t cSpins;
357
358 /*
359 * It's busy. Check if it's an attempt at nested access.
360 */
361 if (RT_UNLIKELY(pThis->hOwner == hSelf))
362 {
363 AssertMsgFailed(("%p attempt at nested access\n"));
364 rtSemSpinMutexLeave(&State);
365 return VERR_SEM_NESTED;
366 }
367
368 /*
369 * Return if we're in interrupt context and the semaphore isn't
370 * configure to be interrupt safe.
371 */
372 if (rc == VINF_SEM_BAD_CONTEXT)
373 {
374 rtSemSpinMutexLeave(&State);
375 return VERR_SEM_BAD_CONTEXT;
376 }
377
378 /*
379 * Ok, we have to wait.
380 */
381 if (State.fSpin)
382 {
383 for (cSpins = 0; ; cSpins++)
384 {
385 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
386 if (fRc)
387 break;
388 ASMNopPause();
389 if (RT_UNLIKELY(pThis->u32Magic != RTSEMSPINMUTEX_MAGIC))
390 {
391 rtSemSpinMutexLeave(&State);
392 return VERR_SEM_DESTROYED;
393 }
394
395 /*
396 * "Yield" once in a while. This may lower our IRQL/PIL which
397 * may preempting us, and it will certainly stop the hammering
398 * of hOwner for a little while.
399 */
400 if ((cSpins & 0x7f) == 0x1f)
401 {
402 rtSemSpinMutexLeave(&State);
403 rtSemSpinMutexEnter(&State, pThis);
404 Assert(State.fSpin);
405 }
406 }
407 }
408 else
409 {
410 for (cSpins = 0;; cSpins++)
411 {
412 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
413 if (fRc)
414 break;
415 ASMNopPause();
416 if (RT_UNLIKELY(pThis->u32Magic != RTSEMSPINMUTEX_MAGIC))
417 {
418 rtSemSpinMutexLeave(&State);
419 return VERR_SEM_DESTROYED;
420 }
421
422 if ((cSpins & 15) == 15) /* spin a bit before going sleep (again). */
423 {
424 rtSemSpinMutexLeave(&State);
425
426 rc = RTSemEventWait(pThis->hEventSem, RT_INDEFINITE_WAIT);
427 ASMCompilerBarrier();
428 if (RT_SUCCESS(rc))
429 AssertReturn(pThis->u32Magic == RTSEMSPINMUTEX_MAGIC, VERR_SEM_DESTROYED);
430 else if (rc == VERR_INTERRUPTED)
431 AssertRC(rc); /* shouldn't happen */
432 else
433 {
434 AssertRC(rc);
435 return rc;
436 }
437
438 rc = rtSemSpinMutexEnter(&State, pThis);
439 AssertRCReturn(rc, rc);
440 Assert(!State.fSpin);
441 }
442 }
443 }
444 }
445
446 /*
447 * We're the semaphore owner.
448 */
449 pThis->SavedState = State;
450 Assert(pThis->hOwner == hSelf);
451 return VINF_SUCCESS;
452}
453RT_EXPORT_SYMBOL(RTSemSpinMutexRequest);
454
455
456RTDECL(int) RTSemSpinMutexRelease(RTSEMSPINMUTEX hSpinMtx)
457{
458 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
459 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
460 uint32_t cLockers;
461 RTSEMSPINMUTEXSTATE State;
462 bool fRc;
463
464 Assert(hSelf != NIL_RTNATIVETHREAD);
465 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
466
467 /*
468 * Get the saved state and try release the semaphore.
469 */
470 State = pThis->SavedState;
471 ASMCompilerBarrier();
472 ASMAtomicCmpXchgHandle(&pThis->hOwner, NIL_RTNATIVETHREAD, hSelf, fRc);
473 AssertMsgReturn(fRc,
474 ("hOwner=%p hSelf=%p cLockers=%d\n", pThis->hOwner, hSelf, pThis->cLockers),
475 VERR_NOT_OWNER);
476
477 cLockers = ASMAtomicDecS32(&pThis->cLockers);
478 rtSemSpinMutexLeave(&State);
479 if (cLockers > 0)
480 {
481 int rc = RTSemEventSignal(pThis->hEventSem);
482 AssertReleaseMsg(RT_SUCCESS(rc), ("RTSemEventSignal -> %Rrc\n", rc));
483 }
484 return VINF_SUCCESS;
485}
486RT_EXPORT_SYMBOL(RTSemSpinMutexRelease);
487
488
489RTDECL(int) RTSemSpinMutexDestroy(RTSEMSPINMUTEX hSpinMtx)
490{
491 RTSEMSPINMUTEXINTERNAL *pThis;
492 RTSEMEVENT hEventSem;
493 int rc;
494
495 if (hSpinMtx == NIL_RTSEMSPINMUTEX)
496 return VINF_SUCCESS;
497 pThis = hSpinMtx;
498 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
499
500 /* No destruction races allowed! */
501 AssertMsg( pThis->cLockers == 0
502 && pThis->hOwner == NIL_RTNATIVETHREAD,
503 ("pThis=%p cLockers=%d hOwner=%p\n", pThis, pThis->cLockers, pThis->hOwner));
504
505 /*
506 * Invalidate the structure, free the mutex and free the structure.
507 */
508 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMSPINMUTEX_MAGIC_DEAD);
509 hEventSem = pThis->hEventSem;
510 pThis->hEventSem = NIL_RTSEMEVENT;
511 rc = RTSemEventDestroy(hEventSem); AssertRC(rc);
512
513 RTMemFree(pThis);
514 return rc;
515}
516RT_EXPORT_SYMBOL(RTSemSpinMutexDestroy);
517
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