VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/powernotification-r0drv.c@ 78365

Last change on this file since 78365 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.3 KB
Line 
1/* $Id: powernotification-r0drv.c 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - Power Management, Ring-0 Driver, Event Notifications.
4 */
5
6/*
7 * Copyright (C) 2008-2019 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/power.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
36# include <iprt/asm-amd64-x86.h>
37#endif
38#include <iprt/assert.h>
39#include <iprt/err.h>
40#include <iprt/mem.h>
41#include <iprt/spinlock.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include "r0drv/mp-r0drv.h"
45#include "r0drv/power-r0drv.h"
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51/**
52 * Notification registration record tracking
53 * RTPowerRegisterNotification() calls.
54 */
55typedef struct RTPOWERNOTIFYREG
56{
57 /** Pointer to the next record. */
58 struct RTPOWERNOTIFYREG * volatile pNext;
59 /** The callback. */
60 PFNRTPOWERNOTIFICATION pfnCallback;
61 /** The user argument. */
62 void *pvUser;
63 /** Bit mask indicating whether we've done this callback or not. */
64 uint8_t bmDone[sizeof(void *)];
65} RTPOWERNOTIFYREG;
66/** Pointer to a registration record. */
67typedef RTPOWERNOTIFYREG *PRTPOWERNOTIFYREG;
68
69
70/*********************************************************************************************************************************
71* Global Variables *
72*********************************************************************************************************************************/
73/** The spinlock protecting the list. */
74static RTSPINLOCK volatile g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
75/** List of callbacks, in registration order. */
76static PRTPOWERNOTIFYREG volatile g_pRTPowerCallbackHead = NULL;
77/** The current done bit. */
78static uint32_t volatile g_iRTPowerDoneBit;
79/** The list generation.
80 * This is increased whenever the list has been modified. The callback routine
81 * make use of this to avoid having restart at the list head after each callback. */
82static uint32_t volatile g_iRTPowerGeneration;
83
84
85
86
87RTDECL(int) RTPowerSignalEvent(RTPOWEREVENT enmEvent)
88{
89 PRTPOWERNOTIFYREG pCur;
90 RTSPINLOCK hSpinlock;
91
92 /*
93 * This is a little bit tricky as we cannot be holding the spinlock
94 * while calling the callback. This means that the list might change
95 * while we're walking it, and that multiple events might be running
96 * concurrently (depending on the OS).
97 *
98 * So, the first measure is to employ a 32-bitmask for each
99 * record where we'll use a bit that rotates for each call to
100 * this function to indicate which records that has been
101 * processed. This will take care of both changes to the list
102 * and a reasonable amount of concurrent events.
103 *
104 * In order to avoid having to restart the list walks for every
105 * callback we make, we'll make use a list generation number that is
106 * incremented everytime the list is changed. So, if it remains
107 * unchanged over a callback we can safely continue the iteration.
108 */
109 uint32_t iDone = ASMAtomicIncU32(&g_iRTPowerDoneBit);
110 iDone %= RT_SIZEOFMEMB(RTPOWERNOTIFYREG, bmDone) * 8;
111
112 hSpinlock = g_hRTPowerNotifySpinLock;
113 if (hSpinlock == NIL_RTSPINLOCK)
114 return VERR_ACCESS_DENIED;
115 RTSpinlockAcquire(hSpinlock);
116
117 /* Clear the bit. */
118 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
119 ASMAtomicBitClear(&pCur->bmDone[0], iDone);
120
121 /* Iterate the records and perform the callbacks. */
122 do
123 {
124 uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTPowerGeneration);
125
126 pCur = g_pRTPowerCallbackHead;
127 while (pCur)
128 {
129 if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone))
130 {
131 PFNRTPOWERNOTIFICATION pfnCallback = pCur->pfnCallback;
132 void *pvUser = pCur->pvUser;
133 pCur = pCur->pNext;
134 RTSpinlockRelease(g_hRTPowerNotifySpinLock);
135
136 pfnCallback(enmEvent, pvUser);
137
138 /* carefully require the lock here, see RTR0MpNotificationTerm(). */
139 hSpinlock = g_hRTPowerNotifySpinLock;
140 if (hSpinlock == NIL_RTSPINLOCK)
141 return VERR_ACCESS_DENIED;
142 RTSpinlockAcquire(hSpinlock);
143 if (ASMAtomicUoReadU32(&g_iRTPowerGeneration) != iGeneration)
144 break;
145 }
146 else
147 pCur = pCur->pNext;
148 }
149 } while (pCur);
150
151 RTSpinlockRelease(hSpinlock);
152 return VINF_SUCCESS;
153}
154RT_EXPORT_SYMBOL(RTPowerSignalEvent);
155
156
157RTDECL(int) RTPowerNotificationRegister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
158{
159 PRTPOWERNOTIFYREG pCur;
160 PRTPOWERNOTIFYREG pNew;
161
162 /*
163 * Validation.
164 */
165 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
166 AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
167 RT_ASSERT_PREEMPTIBLE();
168
169 RTSpinlockAcquire(g_hRTPowerNotifySpinLock);
170 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
171 if ( pCur->pvUser == pvUser
172 && pCur->pfnCallback == pfnCallback)
173 break;
174 RTSpinlockRelease(g_hRTPowerNotifySpinLock);
175 AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
176
177 /*
178 * Allocate a new record and attempt to insert it.
179 */
180 pNew = (PRTPOWERNOTIFYREG)RTMemAlloc(sizeof(*pNew));
181 if (!pNew)
182 return VERR_NO_MEMORY;
183
184 pNew->pNext = NULL;
185 pNew->pfnCallback = pfnCallback;
186 pNew->pvUser = pvUser;
187 memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
188
189 RTSpinlockAcquire(g_hRTPowerNotifySpinLock);
190
191 pCur = g_pRTPowerCallbackHead;
192 if (!pCur)
193 g_pRTPowerCallbackHead = pNew;
194 else
195 {
196 for (pCur = g_pRTPowerCallbackHead; ; pCur = pCur->pNext)
197 if ( pCur->pvUser == pvUser
198 && pCur->pfnCallback == pfnCallback)
199 break;
200 else if (!pCur->pNext)
201 {
202 pCur->pNext = pNew;
203 pCur = NULL;
204 break;
205 }
206 }
207
208 ASMAtomicIncU32(&g_iRTPowerGeneration);
209
210 RTSpinlockRelease(g_hRTPowerNotifySpinLock);
211
212 /* duplicate? */
213 if (pCur)
214 {
215 RTMemFree(pCur);
216 AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
217 }
218
219 return VINF_SUCCESS;
220}
221RT_EXPORT_SYMBOL(RTPowerNotificationRegister);
222
223
224RTDECL(int) RTPowerNotificationDeregister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
225{
226 PRTPOWERNOTIFYREG pPrev;
227 PRTPOWERNOTIFYREG pCur;
228
229 /*
230 * Validation.
231 */
232 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
233 AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
234 RT_ASSERT_INTS_ON();
235
236 /*
237 * Find and unlink the record from the list.
238 */
239 RTSpinlockAcquire(g_hRTPowerNotifySpinLock);
240 pPrev = NULL;
241 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
242 {
243 if ( pCur->pvUser == pvUser
244 && pCur->pfnCallback == pfnCallback)
245 break;
246 pPrev = pCur;
247 }
248 if (pCur)
249 {
250 if (pPrev)
251 pPrev->pNext = pCur->pNext;
252 else
253 g_pRTPowerCallbackHead = pCur->pNext;
254 ASMAtomicIncU32(&g_iRTPowerGeneration);
255 }
256 RTSpinlockRelease(g_hRTPowerNotifySpinLock);
257
258 if (!pCur)
259 return VERR_NOT_FOUND;
260
261 /*
262 * Invalidate and free the record.
263 */
264 pCur->pNext = NULL;
265 pCur->pfnCallback = NULL;
266 RTMemFree(pCur);
267
268 return VINF_SUCCESS;
269}
270RT_EXPORT_SYMBOL(RTPowerNotificationDeregister);
271
272
273DECLHIDDEN(int) rtR0PowerNotificationInit(void)
274{
275 int rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTPowerNotifySpinLock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "RTR0Power");
276 if (RT_SUCCESS(rc))
277 {
278 /** @todo OS specific init here */
279 return rc;
280#if 0
281 RTSpinlockDestroy(g_hRTPowerNotifySpinLock);
282 g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
283#endif
284 }
285 return rc;
286}
287
288
289DECLHIDDEN(void) rtR0PowerNotificationTerm(void)
290{
291 PRTPOWERNOTIFYREG pHead;
292 RTSPINLOCK hSpinlock = g_hRTPowerNotifySpinLock;
293 AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK);
294
295 /** @todo OS specific term here */
296
297 /* pick up the list and the spinlock. */
298 RTSpinlockAcquire(hSpinlock);
299 ASMAtomicWriteHandle(&g_hRTPowerNotifySpinLock, NIL_RTSPINLOCK);
300 pHead = g_pRTPowerCallbackHead;
301 g_pRTPowerCallbackHead = NULL;
302 ASMAtomicIncU32(&g_iRTPowerGeneration);
303 RTSpinlockRelease(hSpinlock);
304
305 /* free the list. */
306 while (pHead)
307 {
308 PRTPOWERNOTIFYREG pFree = pHead;
309 pHead = pHead->pNext;
310
311 pFree->pNext = NULL;
312 pFree->pfnCallback = NULL;
313 RTMemFree(pFree);
314 }
315
316 RTSpinlockDestroy(hSpinlock);
317}
318
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