From f8fe689a81f906d1b91bb3220acde2a4ecb14c5b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 6 May 2024 05:01:46 +0200 Subject: Adding upstream version 6.0.4-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Runtime/r0drv/powernotification-r0drv.c | 318 +++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 src/VBox/Runtime/r0drv/powernotification-r0drv.c (limited to 'src/VBox/Runtime/r0drv/powernotification-r0drv.c') diff --git a/src/VBox/Runtime/r0drv/powernotification-r0drv.c b/src/VBox/Runtime/r0drv/powernotification-r0drv.c new file mode 100644 index 00000000..71e3c5ca --- /dev/null +++ b/src/VBox/Runtime/r0drv/powernotification-r0drv.c @@ -0,0 +1,318 @@ +/* $Id: powernotification-r0drv.c $ */ +/** @file + * IPRT - Power Management, Ring-0 Driver, Event Notifications. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include "internal/iprt.h" + +#include +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include +#include +#include +#include +#include "r0drv/mp-r0drv.h" +#include "r0drv/power-r0drv.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Notification registration record tracking + * RTPowerRegisterNotification() calls. + */ +typedef struct RTPOWERNOTIFYREG +{ + /** Pointer to the next record. */ + struct RTPOWERNOTIFYREG * volatile pNext; + /** The callback. */ + PFNRTPOWERNOTIFICATION pfnCallback; + /** The user argument. */ + void *pvUser; + /** Bit mask indicating whether we've done this callback or not. */ + uint8_t bmDone[sizeof(void *)]; +} RTPOWERNOTIFYREG; +/** Pointer to a registration record. */ +typedef RTPOWERNOTIFYREG *PRTPOWERNOTIFYREG; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The spinlock protecting the list. */ +static RTSPINLOCK volatile g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK; +/** List of callbacks, in registration order. */ +static PRTPOWERNOTIFYREG volatile g_pRTPowerCallbackHead = NULL; +/** The current done bit. */ +static uint32_t volatile g_iRTPowerDoneBit; +/** The list generation. + * This is increased whenever the list has been modified. The callback routine + * make use of this to avoid having restart at the list head after each callback. */ +static uint32_t volatile g_iRTPowerGeneration; + + + + +RTDECL(int) RTPowerSignalEvent(RTPOWEREVENT enmEvent) +{ + PRTPOWERNOTIFYREG pCur; + RTSPINLOCK hSpinlock; + + /* + * This is a little bit tricky as we cannot be holding the spinlock + * while calling the callback. This means that the list might change + * while we're walking it, and that multiple events might be running + * concurrently (depending on the OS). + * + * So, the first measure is to employ a 32-bitmask for each + * record where we'll use a bit that rotates for each call to + * this function to indicate which records that has been + * processed. This will take care of both changes to the list + * and a reasonable amount of concurrent events. + * + * In order to avoid having to restart the list walks for every + * callback we make, we'll make use a list generation number that is + * incremented everytime the list is changed. So, if it remains + * unchanged over a callback we can safely continue the iteration. + */ + uint32_t iDone = ASMAtomicIncU32(&g_iRTPowerDoneBit); + iDone %= RT_SIZEOFMEMB(RTPOWERNOTIFYREG, bmDone) * 8; + + hSpinlock = g_hRTPowerNotifySpinLock; + if (hSpinlock == NIL_RTSPINLOCK) + return VERR_ACCESS_DENIED; + RTSpinlockAcquire(hSpinlock); + + /* Clear the bit. */ + for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext) + ASMAtomicBitClear(&pCur->bmDone[0], iDone); + + /* Iterate the records and perform the callbacks. */ + do + { + uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTPowerGeneration); + + pCur = g_pRTPowerCallbackHead; + while (pCur) + { + if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone)) + { + PFNRTPOWERNOTIFICATION pfnCallback = pCur->pfnCallback; + void *pvUser = pCur->pvUser; + pCur = pCur->pNext; + RTSpinlockRelease(g_hRTPowerNotifySpinLock); + + pfnCallback(enmEvent, pvUser); + + /* carefully require the lock here, see RTR0MpNotificationTerm(). */ + hSpinlock = g_hRTPowerNotifySpinLock; + if (hSpinlock == NIL_RTSPINLOCK) + return VERR_ACCESS_DENIED; + RTSpinlockAcquire(hSpinlock); + if (ASMAtomicUoReadU32(&g_iRTPowerGeneration) != iGeneration) + break; + } + else + pCur = pCur->pNext; + } + } while (pCur); + + RTSpinlockRelease(hSpinlock); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTPowerSignalEvent); + + +RTDECL(int) RTPowerNotificationRegister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser) +{ + PRTPOWERNOTIFYREG pCur; + PRTPOWERNOTIFYREG pNew; + + /* + * Validation. + */ + AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER); + AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER); + RT_ASSERT_PREEMPTIBLE(); + + RTSpinlockAcquire(g_hRTPowerNotifySpinLock); + for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext) + if ( pCur->pvUser == pvUser + && pCur->pfnCallback == pfnCallback) + break; + RTSpinlockRelease(g_hRTPowerNotifySpinLock); + AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS); + + /* + * Allocate a new record and attempt to insert it. + */ + pNew = (PRTPOWERNOTIFYREG)RTMemAlloc(sizeof(*pNew)); + if (!pNew) + return VERR_NO_MEMORY; + + pNew->pNext = NULL; + pNew->pfnCallback = pfnCallback; + pNew->pvUser = pvUser; + memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone)); + + RTSpinlockAcquire(g_hRTPowerNotifySpinLock); + + pCur = g_pRTPowerCallbackHead; + if (!pCur) + g_pRTPowerCallbackHead = pNew; + else + { + for (pCur = g_pRTPowerCallbackHead; ; pCur = pCur->pNext) + if ( pCur->pvUser == pvUser + && pCur->pfnCallback == pfnCallback) + break; + else if (!pCur->pNext) + { + pCur->pNext = pNew; + pCur = NULL; + break; + } + } + + ASMAtomicIncU32(&g_iRTPowerGeneration); + + RTSpinlockRelease(g_hRTPowerNotifySpinLock); + + /* duplicate? */ + if (pCur) + { + RTMemFree(pCur); + AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS); + } + + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTPowerNotificationRegister); + + +RTDECL(int) RTPowerNotificationDeregister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser) +{ + PRTPOWERNOTIFYREG pPrev; + PRTPOWERNOTIFYREG pCur; + + /* + * Validation. + */ + AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER); + AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER); + RT_ASSERT_INTS_ON(); + + /* + * Find and unlink the record from the list. + */ + RTSpinlockAcquire(g_hRTPowerNotifySpinLock); + pPrev = NULL; + for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext) + { + if ( pCur->pvUser == pvUser + && pCur->pfnCallback == pfnCallback) + break; + pPrev = pCur; + } + if (pCur) + { + if (pPrev) + pPrev->pNext = pCur->pNext; + else + g_pRTPowerCallbackHead = pCur->pNext; + ASMAtomicIncU32(&g_iRTPowerGeneration); + } + RTSpinlockRelease(g_hRTPowerNotifySpinLock); + + if (!pCur) + return VERR_NOT_FOUND; + + /* + * Invalidate and free the record. + */ + pCur->pNext = NULL; + pCur->pfnCallback = NULL; + RTMemFree(pCur); + + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTPowerNotificationDeregister); + + +DECLHIDDEN(int) rtR0PowerNotificationInit(void) +{ + int rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTPowerNotifySpinLock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "RTR0Power"); + if (RT_SUCCESS(rc)) + { + /** @todo OS specific init here */ + return rc; +#if 0 + RTSpinlockDestroy(g_hRTPowerNotifySpinLock); + g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK; +#endif + } + return rc; +} + + +DECLHIDDEN(void) rtR0PowerNotificationTerm(void) +{ + PRTPOWERNOTIFYREG pHead; + RTSPINLOCK hSpinlock = g_hRTPowerNotifySpinLock; + AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK); + + /** @todo OS specific term here */ + + /* pick up the list and the spinlock. */ + RTSpinlockAcquire(hSpinlock); + ASMAtomicWriteHandle(&g_hRTPowerNotifySpinLock, NIL_RTSPINLOCK); + pHead = g_pRTPowerCallbackHead; + g_pRTPowerCallbackHead = NULL; + ASMAtomicIncU32(&g_iRTPowerGeneration); + RTSpinlockRelease(hSpinlock); + + /* free the list. */ + while (pHead) + { + PRTPOWERNOTIFYREG pFree = pHead; + pHead = pHead->pNext; + + pFree->pNext = NULL; + pFree->pfnCallback = NULL; + RTMemFree(pFree); + } + + RTSpinlockDestroy(hSpinlock); +} + -- cgit v1.2.3