VirtualBox

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

Last change on this file since 9700 was 9602, checked in by vboxsync, 17 years ago

Changed RTR0MpNotificationInit/Term to rtR0MpNotificationInit/Term and dropped the unused pvOS argument. They are now IPRT internal and called by RTR0Init/Term.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1/* $Id: mpnotification-r0drv.c 9602 2008-06-11 12:09:31Z 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 <iprt/asm.h>
37#include <iprt/assert.h>
38#include <iprt/err.h>
39#include <iprt/mem.h>
40#include <iprt/spinlock.h>
41#include <iprt/string.h>
42#include "r0drv/mp-r0drv.h"
43
44
45/*******************************************************************************
46* Structures and Typedefs *
47*******************************************************************************/
48/**
49 * Notification registration record tracking
50 * RTMpRegisterNotification() calls.
51 */
52typedef struct RTMPNOTIFYREG
53{
54 /** Pointer to the next record. */
55 struct RTMPNOTIFYREG * volatile pNext;
56 /** The callback. */
57 PFNRTMPNOTIFICATION pfnCallback;
58 /** The user argument. */
59 void *pvUser;
60 /** Bit mask indicating whether we've done this callback or not. */
61 uint8_t bmDone[sizeof(void *)];
62} RTMPNOTIFYREG;
63/** Pointer to a registration record. */
64typedef RTMPNOTIFYREG *PRTMPNOTIFYREG;
65
66
67/*******************************************************************************
68* Global Variables *
69*******************************************************************************/
70/** The spinlock protecting the list. */
71static RTSPINLOCK volatile g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
72/** List of callbacks, in registration order. */
73static PRTMPNOTIFYREG volatile g_pRTMpCallbackHead = NULL;
74/** The current done bit. */
75static uint32_t volatile g_iRTMpDoneBit;
76/** The list generation.
77 * This is increased whenever the list has been modified. The callback routine
78 * make use of this to avoid having restart at the list head after each callback. */
79static uint32_t volatile g_iRTMpGeneration;
80/** The number of RTMpNotification users.
81 * This is incremented on init and decremented on termination. */
82static uint32_t volatile g_cRTMpUsers = 0;
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 RTSPINLOCKTMP Tmp;
97 RTSPINLOCK hSpinlock;
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;
168
169 /*
170 * Validation.
171 */
172 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
173 AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
174
175 RTSpinlockAcquire(g_hRTMpNotifySpinLock, &Tmp);
176 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
177 if ( pCur->pvUser == pvUser
178 && pCur->pfnCallback == pfnCallback)
179 break;
180 RTSpinlockRelease(g_hRTMpNotifySpinLock, &Tmp);
181 AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
182
183 /*
184 * Allocate a new record and attempt to insert it.
185 */
186 pNew = (PRTMPNOTIFYREG)RTMemAlloc(sizeof(*pNew));
187 if (!pNew)
188 return VERR_NO_MEMORY;
189
190 pNew->pNext = NULL;
191 pNew->pfnCallback = pfnCallback;
192 pNew->pvUser = pvUser;
193 memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
194
195 RTSpinlockAcquire(g_hRTMpNotifySpinLock, &Tmp);
196
197 pCur = g_pRTMpCallbackHead;
198 if (!pCur)
199 g_pRTMpCallbackHead = pNew;
200 else
201 {
202 for (pCur = g_pRTMpCallbackHead; ; pCur = pCur->pNext)
203 if ( pCur->pvUser == pvUser
204 && pCur->pfnCallback == pfnCallback)
205 break;
206 else if (!pCur->pNext)
207 {
208 pCur->pNext = pNew;
209 pCur = NULL;
210 break;
211 }
212 }
213
214 ASMAtomicIncU32(&g_iRTMpGeneration);
215
216 RTSpinlockRelease(g_hRTMpNotifySpinLock, &Tmp);
217
218 /* duplicate? */
219 if (pCur)
220 {
221 RTMemFree(pCur);
222 AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
223 }
224
225 return VINF_SUCCESS;
226}
227
228
229
230RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
231{
232 PRTMPNOTIFYREG pPrev;
233 PRTMPNOTIFYREG pCur;
234 RTSPINLOCKTMP Tmp;
235
236 /*
237 * Validation.
238 */
239 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
240 AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
241
242 /*
243 * Find and unlink the record from the list.
244 */
245 RTSpinlockAcquire(g_hRTMpNotifySpinLock, &Tmp);
246 pPrev = NULL;
247 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
248 {
249 if ( pCur->pvUser == pvUser
250 && pCur->pfnCallback == pfnCallback)
251 break;
252 pPrev = pCur;
253 }
254 if (pCur)
255 {
256 if (pPrev)
257 pPrev->pNext = pCur->pNext;
258 else
259 g_pRTMpCallbackHead = pCur->pNext;
260 ASMAtomicIncU32(&g_iRTMpGeneration);
261 }
262 RTSpinlockRelease(g_hRTMpNotifySpinLock, &Tmp);
263
264 if (!pCur)
265 return VERR_NOT_FOUND;
266
267 /*
268 * Invalidate and free the record.
269 */
270 pCur->pNext = NULL;
271 pCur->pfnCallback = NULL;
272 RTMemFree(pCur);
273
274 return VINF_SUCCESS;
275}
276
277
278int rtR0MpNotificationInit(void)
279{
280 int rc = VINF_SUCCESS;
281
282 if (ASMAtomicIncS32(&g_cRTMpUsers) == 1)
283 {
284 rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTMpNotifySpinLock);
285 if (RT_SUCCESS(rc))
286 {
287 rc = rtR0MpNotificationNativeInit();
288 if (RT_SUCCESS(rc))
289 return rc;
290
291 RTSpinlockDestroy(g_hRTMpNotifySpinLock);
292 g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
293 }
294 ASMAtomicDecS32(&g_cRTMpUsers);
295 }
296 return rc;
297}
298
299
300void rtR0MpNotificationTerm(void)
301{
302 RTSPINLOCK hSpinlock = g_hRTMpNotifySpinLock;
303 if (hSpinlock != NIL_RTSPINLOCK)
304 {
305 AssertMsg(g_cRTMpUsers > 0, ("%d\n", g_cRTMpUsers));
306 if (ASMAtomicDecS32(&g_cRTMpUsers) == 0)
307 {
308
309 PRTMPNOTIFYREG pHead;
310 RTSPINLOCKTMP Tmp;
311
312 rtR0MpNotificationNativeTerm();
313
314 /* pick up the list and the spinlock. */
315 RTSpinlockAcquire(hSpinlock, &Tmp);
316 ASMAtomicWriteSize(&g_hRTMpNotifySpinLock, NIL_RTSPINLOCK);
317 pHead = g_pRTMpCallbackHead;
318 g_pRTMpCallbackHead = NULL;
319 ASMAtomicIncU32(&g_iRTMpGeneration);
320 RTSpinlockRelease(hSpinlock, &Tmp);
321
322 /* free the list. */
323 while (pHead)
324 {
325 PRTMPNOTIFYREG pFree = pHead;
326 pHead = pHead->pNext;
327
328 pFree->pNext = NULL;
329 pFree->pfnCallback = NULL;
330 RTMemFree(pFree);
331 }
332
333 RTSpinlockDestroy(g_hRTMpNotifySpinLock);
334 g_hRTMpNotifySpinLock = NULL;
335 }
336 }
337}
338
339
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