VirtualBox

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

Last change on this file since 37211 was 37211, checked in by vboxsync, 14 years ago

Some ASMAtomic*Size elimiation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.0 KB
Line 
1/* $Id: mpnotification-r0drv.c 37211 2011-05-25 11:37:52Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Ring-0 Driver, Event Notifications.
4 */
5
6/*
7 * Copyright (C) 2008-2010 Oracle Corporation
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
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/mp.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
36# include <iprt/asm-amd64-x86.h>
37#endif
38#include <iprt/assert.h>
39#include <iprt/err.h>
40#include <iprt/mem.h>
41#include <iprt/spinlock.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include "r0drv/mp-r0drv.h"
45
46
47/*******************************************************************************
48* Structures and Typedefs *
49*******************************************************************************/
50/**
51 * Notification registration record tracking
52 * RTMpRegisterNotification() calls.
53 */
54typedef struct RTMPNOTIFYREG
55{
56 /** Pointer to the next record. */
57 struct RTMPNOTIFYREG * volatile pNext;
58 /** The callback. */
59 PFNRTMPNOTIFICATION pfnCallback;
60 /** The user argument. */
61 void *pvUser;
62 /** Bit mask indicating whether we've done this callback or not. */
63 uint8_t bmDone[sizeof(void *)];
64} RTMPNOTIFYREG;
65/** Pointer to a registration record. */
66typedef RTMPNOTIFYREG *PRTMPNOTIFYREG;
67
68
69/*******************************************************************************
70* Global Variables *
71*******************************************************************************/
72/** The spinlock protecting the list. */
73static RTSPINLOCK volatile g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
74/** List of callbacks, in registration order. */
75static PRTMPNOTIFYREG volatile g_pRTMpCallbackHead = NULL;
76/** The current done bit. */
77static uint32_t volatile g_iRTMpDoneBit;
78/** The list generation.
79 * This is increased whenever the list has been modified. The callback routine
80 * make use of this to avoid having restart at the list head after each callback. */
81static uint32_t volatile g_iRTMpGeneration;
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 */
92DECLHIDDEN(void) rtMpNotificationDoCallbacks(RTMPEVENT enmEvent, RTCPUID idCpu)
93{
94 PRTMPNOTIFYREG pCur;
95 RTSPINLOCK hSpinlock;
96 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
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 = RTSPINLOCKTMP_INITIALIZER;
167
168 /*
169 * Validation.
170 */
171 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
172 AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
173 RT_ASSERT_PREEMPTIBLE();
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}
227RT_EXPORT_SYMBOL(RTMpNotificationRegister);
228
229
230RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
231{
232 PRTMPNOTIFYREG pPrev;
233 PRTMPNOTIFYREG pCur;
234 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
235
236 /*
237 * Validation.
238 */
239 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
240 AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
241 RT_ASSERT_INTS_ON();
242
243 /*
244 * Find and unlink the record from the list.
245 */
246 RTSpinlockAcquire(g_hRTMpNotifySpinLock, &Tmp);
247 pPrev = NULL;
248 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
249 {
250 if ( pCur->pvUser == pvUser
251 && pCur->pfnCallback == pfnCallback)
252 break;
253 pPrev = pCur;
254 }
255 if (pCur)
256 {
257 if (pPrev)
258 pPrev->pNext = pCur->pNext;
259 else
260 g_pRTMpCallbackHead = pCur->pNext;
261 ASMAtomicIncU32(&g_iRTMpGeneration);
262 }
263 RTSpinlockRelease(g_hRTMpNotifySpinLock, &Tmp);
264
265 if (!pCur)
266 return VERR_NOT_FOUND;
267
268 /*
269 * Invalidate and free the record.
270 */
271 pCur->pNext = NULL;
272 pCur->pfnCallback = NULL;
273 RTMemFree(pCur);
274
275 return VINF_SUCCESS;
276}
277RT_EXPORT_SYMBOL(RTMpNotificationDeregister);
278
279
280DECLHIDDEN(int) rtR0MpNotificationInit(void)
281{
282 int rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTMpNotifySpinLock);
283 if (RT_SUCCESS(rc))
284 {
285 rc = rtR0MpNotificationNativeInit();
286 if (RT_SUCCESS(rc))
287 return rc;
288
289 RTSpinlockDestroy(g_hRTMpNotifySpinLock);
290 g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
291 }
292 return rc;
293}
294
295
296DECLHIDDEN(void) rtR0MpNotificationTerm(void)
297{
298 PRTMPNOTIFYREG pHead;
299 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
300 RTSPINLOCK hSpinlock = g_hRTMpNotifySpinLock;
301 AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK);
302
303 rtR0MpNotificationNativeTerm();
304
305 /* pick up the list and the spinlock. */
306 RTSpinlockAcquire(hSpinlock, &Tmp);
307 ASMAtomicWriteHandle(&g_hRTMpNotifySpinLock, NIL_RTSPINLOCK);
308 pHead = g_pRTMpCallbackHead;
309 g_pRTMpCallbackHead = NULL;
310 ASMAtomicIncU32(&g_iRTMpGeneration);
311 RTSpinlockRelease(hSpinlock, &Tmp);
312
313 /* free the list. */
314 while (pHead)
315 {
316 PRTMPNOTIFYREG pFree = pHead;
317 pHead = pHead->pNext;
318
319 pFree->pNext = NULL;
320 pFree->pfnCallback = NULL;
321 RTMemFree(pFree);
322 }
323
324 RTSpinlockDestroy(hSpinlock);
325}
326
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