VirtualBox

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

Last change on this file since 13476 was 12292, checked in by vboxsync, 16 years ago

iprt: Fixed spinlock leak in rtR0MpNotification

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