VirtualBox

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

Last change on this file since 21543 was 21337, checked in by vboxsync, 15 years ago

IPRT,HostDrv,AddDrv: Export public IPRT symbols for the linux kernel (pain).

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