VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/mpnotification-r0drv.c@ 25776

Last change on this file since 25776 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.1 KB
Line 
1/* $Id: mpnotification-r0drv.c 24180 2009-10-30 10:51:49Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, 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/mp.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
47
48/*******************************************************************************
49* Structures and Typedefs *
50*******************************************************************************/
51/**
52 * Notification registration record tracking
53 * RTMpRegisterNotification() calls.
54 */
55typedef struct RTMPNOTIFYREG
56{
57 /** Pointer to the next record. */
58 struct RTMPNOTIFYREG * volatile pNext;
59 /** The callback. */
60 PFNRTMPNOTIFICATION 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} RTMPNOTIFYREG;
66/** Pointer to a registration record. */
67typedef RTMPNOTIFYREG *PRTMPNOTIFYREG;
68
69
70/*******************************************************************************
71* Global Variables *
72*******************************************************************************/
73/** The spinlock protecting the list. */
74static RTSPINLOCK volatile g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
75/** List of callbacks, in registration order. */
76static PRTMPNOTIFYREG volatile g_pRTMpCallbackHead = NULL;
77/** The current done bit. */
78static uint32_t volatile g_iRTMpDoneBit;
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_iRTMpGeneration;
83
84
85
86
87/**
88 * This is called by the native code.
89 *
90 * @param idCpu The CPU id the event applies to.
91 * @param enmEvent The event.
92 */
93void rtMpNotificationDoCallbacks(RTMPEVENT enmEvent, RTCPUID idCpu)
94{
95 PRTMPNOTIFYREG pCur;
96 RTSPINLOCK hSpinlock;
97 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
98
99 /*
100 * This is a little bit tricky as we cannot be holding the spinlock
101 * while calling the callback. This means that the list might change
102 * while we're walking it, and that multiple events might be running
103 * concurrently (depending on the OS).
104 *
105 * So, the first measure is to employ a 32-bitmask for each
106 * record where we'll use a bit that rotates for each call to
107 * this function to indicate which records that has been
108 * processed. This will take care of both changes to the list
109 * and a reasonable amount of concurrent events.
110 *
111 * In order to avoid having to restart the list walks for every
112 * callback we make, we'll make use a list generation number that is
113 * incremented everytime the list is changed. So, if it remains
114 * unchanged over a callback we can safely continue the iteration.
115 */
116 uint32_t iDone = ASMAtomicIncU32(&g_iRTMpDoneBit);
117 iDone %= RT_SIZEOFMEMB(RTMPNOTIFYREG, bmDone) * 8;
118
119 hSpinlock = g_hRTMpNotifySpinLock;
120 if (hSpinlock == NIL_RTSPINLOCK)
121 return;
122 RTSpinlockAcquire(hSpinlock, &Tmp);
123
124 /* Clear the bit. */
125 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
126 ASMAtomicBitClear(&pCur->bmDone[0], iDone);
127
128 /* Iterate the records and perform the callbacks. */
129 do
130 {
131 uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTMpGeneration);
132
133 pCur = g_pRTMpCallbackHead;
134 while (pCur)
135 {
136 if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone))
137 {
138 PFNRTMPNOTIFICATION pfnCallback = pCur->pfnCallback;
139 void *pvUser = pCur->pvUser;
140 pCur = pCur->pNext;
141 RTSpinlockRelease(g_hRTMpNotifySpinLock, &Tmp);
142
143 pfnCallback(enmEvent, idCpu, pvUser);
144
145 /* carefully require the lock here, see RTR0MpNotificationTerm(). */
146 hSpinlock = g_hRTMpNotifySpinLock;
147 if (hSpinlock == NIL_RTSPINLOCK)
148 return;
149 RTSpinlockAcquire(hSpinlock, &Tmp);
150 if (ASMAtomicUoReadU32(&g_iRTMpGeneration) != iGeneration)
151 break;
152 }
153 else
154 pCur = pCur->pNext;
155 }
156 } while (pCur);
157
158 RTSpinlockRelease(hSpinlock, &Tmp);
159}
160
161
162
163RTDECL(int) RTMpNotificationRegister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
164{
165 PRTMPNOTIFYREG pCur;
166 PRTMPNOTIFYREG pNew;
167 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
168
169 /*
170 * Validation.
171 */
172 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
173 AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
174 RT_ASSERT_PREEMPTIBLE();
175
176 RTSpinlockAcquire(g_hRTMpNotifySpinLock, &Tmp);
177 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
178 if ( pCur->pvUser == pvUser
179 && pCur->pfnCallback == pfnCallback)
180 break;
181 RTSpinlockRelease(g_hRTMpNotifySpinLock, &Tmp);
182 AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
183
184 /*
185 * Allocate a new record and attempt to insert it.
186 */
187 pNew = (PRTMPNOTIFYREG)RTMemAlloc(sizeof(*pNew));
188 if (!pNew)
189 return VERR_NO_MEMORY;
190
191 pNew->pNext = NULL;
192 pNew->pfnCallback = pfnCallback;
193 pNew->pvUser = pvUser;
194 memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
195
196 RTSpinlockAcquire(g_hRTMpNotifySpinLock, &Tmp);
197
198 pCur = g_pRTMpCallbackHead;
199 if (!pCur)
200 g_pRTMpCallbackHead = pNew;
201 else
202 {
203 for (pCur = g_pRTMpCallbackHead; ; pCur = pCur->pNext)
204 if ( pCur->pvUser == pvUser
205 && pCur->pfnCallback == pfnCallback)
206 break;
207 else if (!pCur->pNext)
208 {
209 pCur->pNext = pNew;
210 pCur = NULL;
211 break;
212 }
213 }
214
215 ASMAtomicIncU32(&g_iRTMpGeneration);
216
217 RTSpinlockRelease(g_hRTMpNotifySpinLock, &Tmp);
218
219 /* duplicate? */
220 if (pCur)
221 {
222 RTMemFree(pCur);
223 AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
224 }
225
226 return VINF_SUCCESS;
227}
228RT_EXPORT_SYMBOL(RTMpNotificationRegister);
229
230
231RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
232{
233 PRTMPNOTIFYREG pPrev;
234 PRTMPNOTIFYREG pCur;
235 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
236
237 /*
238 * Validation.
239 */
240 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
241 AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
242 RT_ASSERT_INTS_ON();
243
244 /*
245 * Find and unlink the record from the list.
246 */
247 RTSpinlockAcquire(g_hRTMpNotifySpinLock, &Tmp);
248 pPrev = NULL;
249 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
250 {
251 if ( pCur->pvUser == pvUser
252 && pCur->pfnCallback == pfnCallback)
253 break;
254 pPrev = pCur;
255 }
256 if (pCur)
257 {
258 if (pPrev)
259 pPrev->pNext = pCur->pNext;
260 else
261 g_pRTMpCallbackHead = pCur->pNext;
262 ASMAtomicIncU32(&g_iRTMpGeneration);
263 }
264 RTSpinlockRelease(g_hRTMpNotifySpinLock, &Tmp);
265
266 if (!pCur)
267 return VERR_NOT_FOUND;
268
269 /*
270 * Invalidate and free the record.
271 */
272 pCur->pNext = NULL;
273 pCur->pfnCallback = NULL;
274 RTMemFree(pCur);
275
276 return VINF_SUCCESS;
277}
278RT_EXPORT_SYMBOL(RTMpNotificationDeregister);
279
280
281int rtR0MpNotificationInit(void)
282{
283 int rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTMpNotifySpinLock);
284 if (RT_SUCCESS(rc))
285 {
286 rc = rtR0MpNotificationNativeInit();
287 if (RT_SUCCESS(rc))
288 return rc;
289
290 RTSpinlockDestroy(g_hRTMpNotifySpinLock);
291 g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
292 }
293 return rc;
294}
295
296
297void rtR0MpNotificationTerm(void)
298{
299 PRTMPNOTIFYREG pHead;
300 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
301 RTSPINLOCK hSpinlock = g_hRTMpNotifySpinLock;
302 AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK);
303
304 rtR0MpNotificationNativeTerm();
305
306 /* pick up the list and the spinlock. */
307 RTSpinlockAcquire(hSpinlock, &Tmp);
308 ASMAtomicWriteSize(&g_hRTMpNotifySpinLock, NIL_RTSPINLOCK);
309 pHead = g_pRTMpCallbackHead;
310 g_pRTMpCallbackHead = NULL;
311 ASMAtomicIncU32(&g_iRTMpGeneration);
312 RTSpinlockRelease(hSpinlock, &Tmp);
313
314 /* free the list. */
315 while (pHead)
316 {
317 PRTMPNOTIFYREG pFree = pHead;
318 pHead = pHead->pNext;
319
320 pFree->pNext = NULL;
321 pFree->pfnCallback = NULL;
322 RTMemFree(pFree);
323 }
324
325 RTSpinlockDestroy(hSpinlock);
326}
327
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