VirtualBox

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

Last change on this file since 98102 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.5 KB
Line 
1/* $Id: mpnotification-r0drv.c 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Ring-0 Driver, Event Notifications.
4 */
5
6/*
7 * Copyright (C) 2008-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/mp.h>
42#include "internal/iprt.h"
43
44#include <iprt/asm.h>
45#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
46# include <iprt/asm-amd64-x86.h>
47#endif
48#include <iprt/assert.h>
49#include <iprt/err.h>
50#include <iprt/mem.h>
51#include <iprt/spinlock.h>
52#include <iprt/string.h>
53#include <iprt/thread.h>
54#include "r0drv/mp-r0drv.h"
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * Notification registration record tracking
62 * RTMpRegisterNotification() calls.
63 */
64typedef struct RTMPNOTIFYREG
65{
66 /** Pointer to the next record. */
67 struct RTMPNOTIFYREG * volatile pNext;
68 /** The callback. */
69 PFNRTMPNOTIFICATION pfnCallback;
70 /** The user argument. */
71 void *pvUser;
72 /** Bit mask indicating whether we've done this callback or not. */
73 uint8_t bmDone[sizeof(void *)];
74} RTMPNOTIFYREG;
75/** Pointer to a registration record. */
76typedef RTMPNOTIFYREG *PRTMPNOTIFYREG;
77
78
79/*********************************************************************************************************************************
80* Global Variables *
81*********************************************************************************************************************************/
82/** The spinlock protecting the list. */
83static RTSPINLOCK volatile g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
84/** List of callbacks, in registration order. */
85static PRTMPNOTIFYREG volatile g_pRTMpCallbackHead = NULL;
86/** The current done bit. */
87static uint32_t volatile g_iRTMpDoneBit;
88/** The list generation.
89 * This is increased whenever the list has been modified. The callback routine
90 * make use of this to avoid having restart at the list head after each callback. */
91static uint32_t volatile g_iRTMpGeneration;
92
93
94
95
96/**
97 * This is called by the native code.
98 *
99 * @param idCpu The CPU id the event applies to.
100 * @param enmEvent The event.
101 */
102DECLHIDDEN(void) rtMpNotificationDoCallbacks(RTMPEVENT enmEvent, RTCPUID idCpu)
103{
104 PRTMPNOTIFYREG pCur;
105 RTSPINLOCK hSpinlock;
106
107 /*
108 * This is a little bit tricky as we cannot be holding the spinlock
109 * while calling the callback. This means that the list might change
110 * while we're walking it, and that multiple events might be running
111 * concurrently (depending on the OS).
112 *
113 * So, the first measure is to employ a 32-bitmask for each
114 * record where we'll use a bit that rotates for each call to
115 * this function to indicate which records that has been
116 * processed. This will take care of both changes to the list
117 * and a reasonable amount of concurrent events.
118 *
119 * In order to avoid having to restart the list walks for every
120 * callback we make, we'll make use a list generation number that is
121 * incremented everytime the list is changed. So, if it remains
122 * unchanged over a callback we can safely continue the iteration.
123 */
124 uint32_t iDone = ASMAtomicIncU32(&g_iRTMpDoneBit);
125 iDone %= RT_SIZEOFMEMB(RTMPNOTIFYREG, bmDone) * 8;
126
127 hSpinlock = g_hRTMpNotifySpinLock;
128 if (hSpinlock == NIL_RTSPINLOCK)
129 return;
130 RTSpinlockAcquire(hSpinlock);
131
132 /* Clear the bit. */
133 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
134 ASMAtomicBitClear(&pCur->bmDone[0], iDone);
135
136 /* Iterate the records and perform the callbacks. */
137 do
138 {
139 uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTMpGeneration);
140
141 pCur = g_pRTMpCallbackHead;
142 while (pCur)
143 {
144 if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone))
145 {
146 PFNRTMPNOTIFICATION pfnCallback = pCur->pfnCallback;
147 void *pvUser = pCur->pvUser;
148 pCur = pCur->pNext;
149 RTSpinlockRelease(g_hRTMpNotifySpinLock);
150
151 pfnCallback(enmEvent, idCpu, pvUser);
152
153 /* carefully require the lock here, see RTR0MpNotificationTerm(). */
154 hSpinlock = g_hRTMpNotifySpinLock;
155 if (hSpinlock == NIL_RTSPINLOCK)
156 return;
157 RTSpinlockAcquire(hSpinlock);
158 if (ASMAtomicUoReadU32(&g_iRTMpGeneration) != iGeneration)
159 break;
160 }
161 else
162 pCur = pCur->pNext;
163 }
164 } while (pCur);
165
166 RTSpinlockRelease(hSpinlock);
167}
168
169
170
171RTDECL(int) RTMpNotificationRegister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
172{
173 PRTMPNOTIFYREG pCur;
174 PRTMPNOTIFYREG pNew;
175
176 /*
177 * Validation.
178 */
179 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
180 AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
181 RT_ASSERT_PREEMPTIBLE();
182
183 RTSpinlockAcquire(g_hRTMpNotifySpinLock);
184 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
185 if ( pCur->pvUser == pvUser
186 && pCur->pfnCallback == pfnCallback)
187 break;
188 RTSpinlockRelease(g_hRTMpNotifySpinLock);
189 AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
190
191 /*
192 * Allocate a new record and attempt to insert it.
193 */
194 pNew = (PRTMPNOTIFYREG)RTMemAlloc(sizeof(*pNew));
195 if (!pNew)
196 return VERR_NO_MEMORY;
197
198 pNew->pNext = NULL;
199 pNew->pfnCallback = pfnCallback;
200 pNew->pvUser = pvUser;
201 memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
202
203 RTSpinlockAcquire(g_hRTMpNotifySpinLock);
204
205 pCur = g_pRTMpCallbackHead;
206 if (!pCur)
207 g_pRTMpCallbackHead = pNew;
208 else
209 {
210 for (pCur = g_pRTMpCallbackHead; ; pCur = pCur->pNext)
211 if ( pCur->pvUser == pvUser
212 && pCur->pfnCallback == pfnCallback)
213 break;
214 else if (!pCur->pNext)
215 {
216 pCur->pNext = pNew;
217 pCur = NULL;
218 break;
219 }
220 }
221
222 ASMAtomicIncU32(&g_iRTMpGeneration);
223
224 RTSpinlockRelease(g_hRTMpNotifySpinLock);
225
226 /* duplicate? */
227 if (pCur)
228 {
229 RTMemFree(pCur);
230 AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
231 }
232
233 return VINF_SUCCESS;
234}
235RT_EXPORT_SYMBOL(RTMpNotificationRegister);
236
237
238RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
239{
240 PRTMPNOTIFYREG pPrev;
241 PRTMPNOTIFYREG pCur;
242
243 /*
244 * Validation.
245 */
246 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
247 AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
248 RT_ASSERT_INTS_ON();
249
250 /*
251 * Find and unlink the record from the list.
252 */
253 RTSpinlockAcquire(g_hRTMpNotifySpinLock);
254 pPrev = NULL;
255 for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
256 {
257 if ( pCur->pvUser == pvUser
258 && pCur->pfnCallback == pfnCallback)
259 break;
260 pPrev = pCur;
261 }
262 if (pCur)
263 {
264 if (pPrev)
265 pPrev->pNext = pCur->pNext;
266 else
267 g_pRTMpCallbackHead = pCur->pNext;
268 ASMAtomicIncU32(&g_iRTMpGeneration);
269 }
270 RTSpinlockRelease(g_hRTMpNotifySpinLock);
271
272 if (!pCur)
273 return VERR_NOT_FOUND;
274
275 /*
276 * Invalidate and free the record.
277 */
278 pCur->pNext = NULL;
279 pCur->pfnCallback = NULL;
280 RTMemFree(pCur);
281
282 return VINF_SUCCESS;
283}
284RT_EXPORT_SYMBOL(RTMpNotificationDeregister);
285
286
287DECLHIDDEN(int) rtR0MpNotificationInit(void)
288{
289 int rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTMpNotifySpinLock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "RTR0Mp");
290 if (RT_SUCCESS(rc))
291 {
292 rc = rtR0MpNotificationNativeInit();
293 if (RT_SUCCESS(rc))
294 return rc;
295
296 RTSpinlockDestroy(g_hRTMpNotifySpinLock);
297 g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
298 }
299 return rc;
300}
301
302
303DECLHIDDEN(void) rtR0MpNotificationTerm(void)
304{
305 PRTMPNOTIFYREG pHead;
306 RTSPINLOCK hSpinlock = g_hRTMpNotifySpinLock;
307 AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK);
308
309 rtR0MpNotificationNativeTerm();
310
311 /* pick up the list and the spinlock. */
312 RTSpinlockAcquire(hSpinlock);
313 ASMAtomicWriteHandle(&g_hRTMpNotifySpinLock, NIL_RTSPINLOCK);
314 pHead = g_pRTMpCallbackHead;
315 g_pRTMpCallbackHead = NULL;
316 ASMAtomicIncU32(&g_iRTMpGeneration);
317 RTSpinlockRelease(hSpinlock);
318
319 /* free the list. */
320 while (pHead)
321 {
322 PRTMPNOTIFYREG pFree = pHead;
323 pHead = pHead->pNext;
324
325 pFree->pNext = NULL;
326 pFree->pfnCallback = NULL;
327 RTMemFree(pFree);
328 }
329
330 RTSpinlockDestroy(hSpinlock);
331}
332
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