VirtualBox

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

Last change on this file since 25046 was 24180, checked in by vboxsync, 15 years ago

IPRT: Drop the extra usage counting in powernotification-r0drv.c and mpnotification-r0drv.c. Initialize RTSPINLOCKTMP variables.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.2 KB
Line 
1/* $Id: powernotification-r0drv.c 24180 2009-10-30 10:51:49Z 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 <iprt/thread.h>
45#include "r0drv/mp-r0drv.h"
46#include "r0drv/power-r0drv.h"
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/**
53 * Notification registration record tracking
54 * RTPowerRegisterNotification() calls.
55 */
56typedef struct RTPOWERNOTIFYREG
57{
58 /** Pointer to the next record. */
59 struct RTPOWERNOTIFYREG * volatile pNext;
60 /** The callback. */
61 PFNRTPOWERNOTIFICATION pfnCallback;
62 /** The user argument. */
63 void *pvUser;
64 /** Bit mask indicating whether we've done this callback or not. */
65 uint8_t bmDone[sizeof(void *)];
66} RTPOWERNOTIFYREG;
67/** Pointer to a registration record. */
68typedef RTPOWERNOTIFYREG *PRTPOWERNOTIFYREG;
69
70
71/*******************************************************************************
72* Global Variables *
73*******************************************************************************/
74/** The spinlock protecting the list. */
75static RTSPINLOCK volatile g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
76/** List of callbacks, in registration order. */
77static PRTPOWERNOTIFYREG volatile g_pRTPowerCallbackHead = NULL;
78/** The current done bit. */
79static uint32_t volatile g_iRTPowerDoneBit;
80/** The list generation.
81 * This is increased whenever the list has been modified. The callback routine
82 * make use of this to avoid having restart at the list head after each callback. */
83static uint32_t volatile g_iRTPowerGeneration;
84
85
86
87
88RTDECL(int) RTPowerSignalEvent(RTPOWEREVENT enmEvent)
89{
90 PRTPOWERNOTIFYREG pCur;
91 RTSPINLOCK hSpinlock;
92 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
93
94 /*
95 * This is a little bit tricky as we cannot be holding the spinlock
96 * while calling the callback. This means that the list might change
97 * while we're walking it, and that multiple events might be running
98 * concurrently (depending on the OS).
99 *
100 * So, the first measure is to employ a 32-bitmask for each
101 * record where we'll use a bit that rotates for each call to
102 * this function to indicate which records that has been
103 * processed. This will take care of both changes to the list
104 * and a reasonable amount of concurrent events.
105 *
106 * In order to avoid having to restart the list walks for every
107 * callback we make, we'll make use a list generation number that is
108 * incremented everytime the list is changed. So, if it remains
109 * unchanged over a callback we can safely continue the iteration.
110 */
111 uint32_t iDone = ASMAtomicIncU32(&g_iRTPowerDoneBit);
112 iDone %= RT_SIZEOFMEMB(RTPOWERNOTIFYREG, bmDone) * 8;
113
114 hSpinlock = g_hRTPowerNotifySpinLock;
115 if (hSpinlock == NIL_RTSPINLOCK)
116 return VERR_ACCESS_DENIED;
117 RTSpinlockAcquire(hSpinlock, &Tmp);
118
119 /* Clear the bit. */
120 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
121 ASMAtomicBitClear(&pCur->bmDone[0], iDone);
122
123 /* Iterate the records and perform the callbacks. */
124 do
125 {
126 uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTPowerGeneration);
127
128 pCur = g_pRTPowerCallbackHead;
129 while (pCur)
130 {
131 if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone))
132 {
133 PFNRTPOWERNOTIFICATION pfnCallback = pCur->pfnCallback;
134 void *pvUser = pCur->pvUser;
135 pCur = pCur->pNext;
136 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
137
138 pfnCallback(enmEvent, pvUser);
139
140 /* carefully require the lock here, see RTR0MpNotificationTerm(). */
141 hSpinlock = g_hRTPowerNotifySpinLock;
142 if (hSpinlock == NIL_RTSPINLOCK)
143 return VERR_ACCESS_DENIED;
144 RTSpinlockAcquire(hSpinlock, &Tmp);
145 if (ASMAtomicUoReadU32(&g_iRTPowerGeneration) != iGeneration)
146 break;
147 }
148 else
149 pCur = pCur->pNext;
150 }
151 } while (pCur);
152
153 RTSpinlockRelease(hSpinlock, &Tmp);
154 return VINF_SUCCESS;
155}
156RT_EXPORT_SYMBOL(RTPowerSignalEvent);
157
158
159RTDECL(int) RTPowerNotificationRegister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
160{
161 PRTPOWERNOTIFYREG pCur;
162 PRTPOWERNOTIFYREG pNew;
163 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
164
165 /*
166 * Validation.
167 */
168 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
169 AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
170 RT_ASSERT_PREEMPTIBLE();
171
172 RTSpinlockAcquire(g_hRTPowerNotifySpinLock, &Tmp);
173 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
174 if ( pCur->pvUser == pvUser
175 && pCur->pfnCallback == pfnCallback)
176 break;
177 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
178 AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
179
180 /*
181 * Allocate a new record and attempt to insert it.
182 */
183 pNew = (PRTPOWERNOTIFYREG)RTMemAlloc(sizeof(*pNew));
184 if (!pNew)
185 return VERR_NO_MEMORY;
186
187 pNew->pNext = NULL;
188 pNew->pfnCallback = pfnCallback;
189 pNew->pvUser = pvUser;
190 memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
191
192 RTSpinlockAcquire(g_hRTPowerNotifySpinLock, &Tmp);
193
194 pCur = g_pRTPowerCallbackHead;
195 if (!pCur)
196 g_pRTPowerCallbackHead = pNew;
197 else
198 {
199 for (pCur = g_pRTPowerCallbackHead; ; pCur = pCur->pNext)
200 if ( pCur->pvUser == pvUser
201 && pCur->pfnCallback == pfnCallback)
202 break;
203 else if (!pCur->pNext)
204 {
205 pCur->pNext = pNew;
206 pCur = NULL;
207 break;
208 }
209 }
210
211 ASMAtomicIncU32(&g_iRTPowerGeneration);
212
213 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
214
215 /* duplicate? */
216 if (pCur)
217 {
218 RTMemFree(pCur);
219 AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
220 }
221
222 return VINF_SUCCESS;
223}
224RT_EXPORT_SYMBOL(RTPowerNotificationRegister);
225
226
227RTDECL(int) RTPowerNotificationDeregister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
228{
229 PRTPOWERNOTIFYREG pPrev;
230 PRTPOWERNOTIFYREG pCur;
231 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
232
233 /*
234 * Validation.
235 */
236 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
237 AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
238 RT_ASSERT_INTS_ON();
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 = RTSpinlockCreate((PRTSPINLOCK)&g_hRTPowerNotifySpinLock);
280 if (RT_SUCCESS(rc))
281 {
282 /** @todo OS specific init here */
283 return rc;
284#if 0
285 RTSpinlockDestroy(g_hRTPowerNotifySpinLock);
286 g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
287#endif
288 }
289 return rc;
290}
291
292
293void rtR0PowerNotificationTerm(void)
294{
295 PRTPOWERNOTIFYREG pHead;
296 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
297 RTSPINLOCK hSpinlock = g_hRTPowerNotifySpinLock;
298 AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK);
299
300 /** @todo OS specific term here */
301
302 /* pick up the list and the spinlock. */
303 RTSpinlockAcquire(hSpinlock, &Tmp);
304 ASMAtomicWriteSize(&g_hRTPowerNotifySpinLock, NIL_RTSPINLOCK);
305 pHead = g_pRTPowerCallbackHead;
306 g_pRTPowerCallbackHead = NULL;
307 ASMAtomicIncU32(&g_iRTPowerGeneration);
308 RTSpinlockRelease(hSpinlock, &Tmp);
309
310 /* free the list. */
311 while (pHead)
312 {
313 PRTPOWERNOTIFYREG pFree = pHead;
314 pHead = pHead->pNext;
315
316 pFree->pNext = NULL;
317 pFree->pfnCallback = NULL;
318 RTMemFree(pFree);
319 }
320
321 RTSpinlockDestroy(hSpinlock);
322}
323
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