VirtualBox

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

Last change on this file since 36555 was 36555, checked in by vboxsync, 13 years ago

Use DECLHIDDEN, especially in IPRT.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.1 KB
Line 
1/* $Id: powernotification-r0drv.c 36555 2011-04-05 12:34:09Z vboxsync $ */
2/** @file
3 * IPRT - Power Management, Ring-0 Driver, Event Notifications.
4 */
5
6/*
7 * Copyright (C) 2008 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 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
92
93 /*
94 * This is a little bit tricky as we cannot be holding the spinlock
95 * while calling the callback. This means that the list might change
96 * while we're walking it, and that multiple events might be running
97 * concurrently (depending on the OS).
98 *
99 * So, the first measure is to employ a 32-bitmask for each
100 * record where we'll use a bit that rotates for each call to
101 * this function to indicate which records that has been
102 * processed. This will take care of both changes to the list
103 * and a reasonable amount of concurrent events.
104 *
105 * In order to avoid having to restart the list walks for every
106 * callback we make, we'll make use a list generation number that is
107 * incremented everytime the list is changed. So, if it remains
108 * unchanged over a callback we can safely continue the iteration.
109 */
110 uint32_t iDone = ASMAtomicIncU32(&g_iRTPowerDoneBit);
111 iDone %= RT_SIZEOFMEMB(RTPOWERNOTIFYREG, bmDone) * 8;
112
113 hSpinlock = g_hRTPowerNotifySpinLock;
114 if (hSpinlock == NIL_RTSPINLOCK)
115 return VERR_ACCESS_DENIED;
116 RTSpinlockAcquire(hSpinlock, &Tmp);
117
118 /* Clear the bit. */
119 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
120 ASMAtomicBitClear(&pCur->bmDone[0], iDone);
121
122 /* Iterate the records and perform the callbacks. */
123 do
124 {
125 uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTPowerGeneration);
126
127 pCur = g_pRTPowerCallbackHead;
128 while (pCur)
129 {
130 if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone))
131 {
132 PFNRTPOWERNOTIFICATION pfnCallback = pCur->pfnCallback;
133 void *pvUser = pCur->pvUser;
134 pCur = pCur->pNext;
135 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
136
137 pfnCallback(enmEvent, pvUser);
138
139 /* carefully require the lock here, see RTR0MpNotificationTerm(). */
140 hSpinlock = g_hRTPowerNotifySpinLock;
141 if (hSpinlock == NIL_RTSPINLOCK)
142 return VERR_ACCESS_DENIED;
143 RTSpinlockAcquire(hSpinlock, &Tmp);
144 if (ASMAtomicUoReadU32(&g_iRTPowerGeneration) != iGeneration)
145 break;
146 }
147 else
148 pCur = pCur->pNext;
149 }
150 } while (pCur);
151
152 RTSpinlockRelease(hSpinlock, &Tmp);
153 return VINF_SUCCESS;
154}
155RT_EXPORT_SYMBOL(RTPowerSignalEvent);
156
157
158RTDECL(int) RTPowerNotificationRegister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
159{
160 PRTPOWERNOTIFYREG pCur;
161 PRTPOWERNOTIFYREG pNew;
162 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
163
164 /*
165 * Validation.
166 */
167 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
168 AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
169 RT_ASSERT_PREEMPTIBLE();
170
171 RTSpinlockAcquire(g_hRTPowerNotifySpinLock, &Tmp);
172 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
173 if ( pCur->pvUser == pvUser
174 && pCur->pfnCallback == pfnCallback)
175 break;
176 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
177 AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
178
179 /*
180 * Allocate a new record and attempt to insert it.
181 */
182 pNew = (PRTPOWERNOTIFYREG)RTMemAlloc(sizeof(*pNew));
183 if (!pNew)
184 return VERR_NO_MEMORY;
185
186 pNew->pNext = NULL;
187 pNew->pfnCallback = pfnCallback;
188 pNew->pvUser = pvUser;
189 memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
190
191 RTSpinlockAcquire(g_hRTPowerNotifySpinLock, &Tmp);
192
193 pCur = g_pRTPowerCallbackHead;
194 if (!pCur)
195 g_pRTPowerCallbackHead = pNew;
196 else
197 {
198 for (pCur = g_pRTPowerCallbackHead; ; pCur = pCur->pNext)
199 if ( pCur->pvUser == pvUser
200 && pCur->pfnCallback == pfnCallback)
201 break;
202 else if (!pCur->pNext)
203 {
204 pCur->pNext = pNew;
205 pCur = NULL;
206 break;
207 }
208 }
209
210 ASMAtomicIncU32(&g_iRTPowerGeneration);
211
212 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
213
214 /* duplicate? */
215 if (pCur)
216 {
217 RTMemFree(pCur);
218 AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
219 }
220
221 return VINF_SUCCESS;
222}
223RT_EXPORT_SYMBOL(RTPowerNotificationRegister);
224
225
226RTDECL(int) RTPowerNotificationDeregister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
227{
228 PRTPOWERNOTIFYREG pPrev;
229 PRTPOWERNOTIFYREG pCur;
230 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
231
232 /*
233 * Validation.
234 */
235 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
236 AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
237 RT_ASSERT_INTS_ON();
238
239 /*
240 * Find and unlink the record from the list.
241 */
242 RTSpinlockAcquire(g_hRTPowerNotifySpinLock, &Tmp);
243 pPrev = NULL;
244 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
245 {
246 if ( pCur->pvUser == pvUser
247 && pCur->pfnCallback == pfnCallback)
248 break;
249 pPrev = pCur;
250 }
251 if (pCur)
252 {
253 if (pPrev)
254 pPrev->pNext = pCur->pNext;
255 else
256 g_pRTPowerCallbackHead = pCur->pNext;
257 ASMAtomicIncU32(&g_iRTPowerGeneration);
258 }
259 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
260
261 if (!pCur)
262 return VERR_NOT_FOUND;
263
264 /*
265 * Invalidate and free the record.
266 */
267 pCur->pNext = NULL;
268 pCur->pfnCallback = NULL;
269 RTMemFree(pCur);
270
271 return VINF_SUCCESS;
272}
273RT_EXPORT_SYMBOL(RTPowerNotificationDeregister);
274
275
276DECLHIDDEN(int) rtR0PowerNotificationInit(void)
277{
278 int rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTPowerNotifySpinLock);
279 if (RT_SUCCESS(rc))
280 {
281 /** @todo OS specific init here */
282 return rc;
283#if 0
284 RTSpinlockDestroy(g_hRTPowerNotifySpinLock);
285 g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
286#endif
287 }
288 return rc;
289}
290
291
292DECLHIDDEN(void) rtR0PowerNotificationTerm(void)
293{
294 PRTPOWERNOTIFYREG pHead;
295 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
296 RTSPINLOCK hSpinlock = g_hRTPowerNotifySpinLock;
297 AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK);
298
299 /** @todo OS specific term here */
300
301 /* pick up the list and the spinlock. */
302 RTSpinlockAcquire(hSpinlock, &Tmp);
303 ASMAtomicWriteSize(&g_hRTPowerNotifySpinLock, NIL_RTSPINLOCK);
304 pHead = g_pRTPowerCallbackHead;
305 g_pRTPowerCallbackHead = NULL;
306 ASMAtomicIncU32(&g_iRTPowerGeneration);
307 RTSpinlockRelease(hSpinlock, &Tmp);
308
309 /* free the list. */
310 while (pHead)
311 {
312 PRTPOWERNOTIFYREG pFree = pHead;
313 pHead = pHead->pNext;
314
315 pFree->pNext = NULL;
316 pFree->pfnCallback = NULL;
317 RTMemFree(pFree);
318 }
319
320 RTSpinlockDestroy(hSpinlock);
321}
322
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