VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMQueue.cpp@ 107262

Last change on this file since 107262 was 107194, checked in by vboxsync, 2 months ago

VMM: More adjustments for VBOX_WITH_ONLY_PGM_NEM_MODE, VBOX_WITH_MINIMAL_R0, VBOX_WITH_HWVIRT and such. jiraref:VBP-1466

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 34.1 KB
Line 
1/* $Id: PDMQueue.cpp 107194 2024-11-29 14:47:06Z vboxsync $ */
2/** @file
3 * PDM Queue - Transport data and tasks to EMT and R3.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_PDM_QUEUE
33#include "PDMInternal.h"
34#include <VBox/vmm/pdm.h>
35#include <VBox/vmm/mm.h>
36#include <VBox/vmm/vm.h>
37#include <VBox/vmm/uvm.h>
38#include <iprt/errcore.h>
39
40#include <VBox/log.h>
41#include <iprt/asm.h>
42#include <iprt/assert.h>
43#include <iprt/mem.h>
44#include <iprt/thread.h>
45
46
47/*********************************************************************************************************************************
48* Internal Functions *
49*********************************************************************************************************************************/
50static int pdmR3QueueDestroyLocked(PVM pVM, PDMQUEUEHANDLE hQueue, void *pvOwner);
51static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, TMTIMERHANDLE hTimer, void *pvUser);
52
53
54
55/**
56 * Internal worker for the queue creation apis.
57 *
58 * @returns VBox status code.
59 * @param pVM The cross context VM structure.
60 * @param cbItem Item size.
61 * @param cItems Number of items.
62 * @param cMilliesInterval Number of milliseconds between polling the queue.
63 * If 0 then the emulation thread will be notified whenever an item arrives.
64 * @param fRZEnabled Set if the queue will be used from RC/R0,
65 * these can only be created from EMT0.
66 * @param pszName The queue name. Unique. Not copied.
67 * @param enmType Owner type.
68 * @param pvOwner The queue owner pointer.
69 * @param uCallback Callback function.
70 * @param phQueue Where to store the queue handle.
71 *
72 * @thread Emulation thread only. When @a fRZEnables is true only EMT0.
73 * @note Caller owns ListCritSect.
74 */
75static int pdmR3QueueCreateLocked(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval, bool fRZEnabled,
76 const char *pszName, PDMQUEUETYPE enmType, void *pvOwner, uintptr_t uCallback,
77 PDMQUEUEHANDLE *phQueue)
78{
79 /*
80 * Validate and adjust the input.
81 */
82 if (fRZEnabled)
83 VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
84 else
85 VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
86
87 cbItem = RT_ALIGN(cbItem, sizeof(uint64_t));
88 AssertMsgReturn(cbItem >= sizeof(PDMQUEUEITEMCORE) && cbItem < PDMQUEUE_MAX_ITEM_SIZE, ("cbItem=%zu\n", cbItem),
89 VERR_OUT_OF_RANGE);
90 AssertMsgReturn(cItems >= 1 && cItems <= PDMQUEUE_MAX_ITEMS, ("cItems=%u\n", cItems), VERR_OUT_OF_RANGE);
91 AssertMsgReturn((uint64_t)cbItem * cItems <= (fRZEnabled ? PDMQUEUE_MAX_TOTAL_SIZE_R0 : PDMQUEUE_MAX_TOTAL_SIZE_R3),
92 ("cItems=%u cbItem=%#x -> %#RX64, max %'u\n", cItems, cbItem, (uint64_t)cbItem * cItems,
93 fRZEnabled ? PDMQUEUE_MAX_TOTAL_SIZE_R0 : PDMQUEUE_MAX_TOTAL_SIZE_R3),
94 VERR_OUT_OF_RANGE);
95 AssertReturn(!fRZEnabled || enmType == PDMQUEUETYPE_INTERNAL || enmType == PDMQUEUETYPE_DEV, VERR_INVALID_PARAMETER);
96#if defined(VBOX_WITH_R0_MODULES) && !defined(VBOX_WITH_MINIMAL_R0)
97 if (SUPR3IsDriverless())
98#endif
99 fRZEnabled = false;
100
101 /* Unqiue name that fits within the szName field: */
102 size_t cchName = strlen(pszName);
103 AssertReturn(cchName > 0, VERR_INVALID_NAME);
104 AssertMsgReturn(cchName < RT_SIZEOFMEMB(PDMQUEUE, szName), ("'%s' is too long\n", pszName), VERR_INVALID_NAME);
105 size_t i = pVM->pdm.s.cRing3Queues;
106 while (i-- > 0 )
107 AssertMsgReturn(strcmp(pVM->pdm.s.papRing3Queues[i]->szName, pszName) != 0, ("%s\n", pszName), VERR_DUPLICATE);
108 i = pVM->pdm.s.cRing0Queues;
109 while (i-- > 0 )
110 AssertMsgReturn(strcmp(pVM->pdm.s.apRing0Queues[i]->szName, pszName) != 0, ("%s\n", pszName), VERR_DUPLICATE);
111
112 /*
113 * Align the item size and calculate the structure size.
114 */
115 PPDMQUEUE pQueue;
116 PDMQUEUEHANDLE hQueue;
117#if defined(VBOX_WITH_R0_MODULES) && !defined(VBOX_WITH_MINIMAL_R0)
118 if (fRZEnabled)
119 {
120 /* Call ring-0 to allocate and create the queue: */
121 PDMQUEUECREATEREQ Req;
122 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
123 Req.Hdr.cbReq = sizeof(Req);
124 Req.cItems = cItems;
125 Req.cbItem = (uint32_t)cbItem;
126 Req.enmType = enmType;
127 Req.pvOwner = pvOwner;
128 Req.pfnCallback = (RTR3PTR)uCallback;
129 RTStrCopy(Req.szName, sizeof(Req.szName), pszName);
130 AssertCompileMembersSameSize(PDMQUEUECREATEREQ, szName, PDMQUEUE, szName);
131 Req.hQueue = NIL_PDMQUEUEHANDLE;
132
133 int rc = VMMR3CallR0(pVM, VMMR0_DO_PDM_QUEUE_CREATE, 0, &Req.Hdr);
134 if (RT_FAILURE(rc))
135 return rc;
136 hQueue = Req.hQueue;
137 AssertReturn(hQueue < RT_ELEMENTS(pVM->pdm.s.apRing0Queues), VERR_INTERNAL_ERROR_2);
138 pQueue = pVM->pdm.s.apRing0Queues[hQueue];
139 AssertPtrReturn(pQueue, VERR_INTERNAL_ERROR_3);
140 AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INTERNAL_ERROR_4);
141 AssertReturn(pQueue->cbItem == cbItem, VERR_INTERNAL_ERROR_4);
142 AssertReturn(pQueue->cItems == cItems, VERR_INTERNAL_ERROR_4);
143 AssertReturn(pQueue->enmType == enmType, VERR_INTERNAL_ERROR_4);
144 AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INTERNAL_ERROR_4);
145 AssertReturn(pQueue->u.Gen.pfnCallback == (RTR3PTR)uCallback, VERR_INTERNAL_ERROR_4);
146 }
147 else
148#endif
149 {
150 /* Do it here using the paged heap: */
151 uint32_t const cbBitmap = RT_ALIGN_32(RT_ALIGN_32(cItems, 64) / 8, 64); /* keep bitmap in it's own cacheline */
152 uint32_t const cbQueue = RT_OFFSETOF(PDMQUEUE, bmAlloc)
153 + cbBitmap
154 + (uint32_t)cbItem * cItems;
155 pQueue = (PPDMQUEUE)RTMemPageAllocZ(cbQueue);
156 if (!pQueue)
157 return VERR_NO_PAGE_MEMORY;
158 pdmQueueInit(pQueue, cbBitmap, (uint32_t)cbItem, cItems, pszName, enmType, (RTR3PTR)uCallback, pvOwner);
159
160 uint32_t iQueue = pVM->pdm.s.cRing3Queues;
161 if (iQueue >= pVM->pdm.s.cRing3QueuesAlloc)
162 {
163 AssertLogRelMsgReturnStmt(iQueue < _16K, ("%#x\n", iQueue), RTMemPageFree(pQueue, cbQueue), VERR_TOO_MANY_OPENS);
164
165 uint32_t const cNewAlloc = RT_ALIGN_32(iQueue, 64) + 64;
166 PPDMQUEUE *papQueuesNew = (PPDMQUEUE *)RTMemAllocZ(cNewAlloc * sizeof(papQueuesNew[0]));
167 AssertLogRelMsgReturnStmt(papQueuesNew, ("cNewAlloc=%u\n", cNewAlloc), RTMemPageFree(pQueue, cbQueue), VERR_NO_MEMORY);
168
169 if (iQueue)
170 memcpy(papQueuesNew, pVM->pdm.s.papRing3Queues, iQueue * sizeof(papQueuesNew[0]));
171 PPDMQUEUE *papQueuesOld = ASMAtomicXchgPtrT(&pVM->pdm.s.papRing3Queues, papQueuesNew, PPDMQUEUE *);
172 pVM->pdm.s.cRing3QueuesAlloc = cNewAlloc;
173 RTMemFree(papQueuesOld);
174 }
175
176 pVM->pdm.s.papRing3Queues[iQueue] = pQueue;
177 pVM->pdm.s.cRing3Queues = iQueue + 1;
178 hQueue = iQueue + RT_ELEMENTS(pVM->pdm.s.apRing0Queues);
179 }
180
181 /*
182 * Create timer?
183 */
184 if (cMilliesInterval)
185 {
186 char szName[48+6];
187 RTStrPrintf(szName, sizeof(szName), "Que/%s", pQueue->szName);
188 int rc = TMR3TimerCreate(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, TMTIMER_FLAGS_NO_RING0, szName, &pQueue->hTimer);
189 if (RT_SUCCESS(rc))
190 {
191 rc = TMTimerSetMillies(pVM, pQueue->hTimer, cMilliesInterval);
192 if (RT_SUCCESS(rc))
193 pQueue->cMilliesInterval = cMilliesInterval;
194 else
195 {
196 AssertMsgFailed(("TMTimerSetMillies failed rc=%Rrc\n", rc));
197 int rc2 = TMR3TimerDestroy(pVM, pQueue->hTimer); AssertRC(rc2);
198 pQueue->hTimer = NIL_TMTIMERHANDLE;
199 }
200 }
201 else
202 AssertMsgFailed(("TMR3TimerCreateInternal failed rc=%Rrc\n", rc));
203 if (RT_FAILURE(rc))
204 {
205 if (!fRZEnabled)
206 pdmR3QueueDestroyLocked(pVM, hQueue, pvOwner);
207 /* else: will clean up queue when VM is destroyed */
208 return rc;
209 }
210 }
211
212 /*
213 * Register the statistics.
214 */
215 STAMR3RegisterF(pVM, &pQueue->cbItem, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
216 "Item size.", "/PDM/Queue/%s/cbItem", pQueue->szName);
217 STAMR3RegisterF(pVM, &pQueue->cItems, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
218 "Queue size.", "/PDM/Queue/%s/cItems", pQueue->szName);
219 STAMR3RegisterF(pVM, &pQueue->rcOkay, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
220 "Non-zero means queue is busted.", "/PDM/Queue/%s/rcOkay", pQueue->szName);
221 STAMR3RegisterF(pVM, &pQueue->StatAllocFailures, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
222 "PDMQueueAlloc failures.", "/PDM/Queue/%s/AllocFailures", pQueue->szName);
223 STAMR3RegisterF(pVM, &pQueue->StatInsert, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,
224 "Calls to PDMQueueInsert.", "/PDM/Queue/%s/Insert", pQueue->szName);
225 STAMR3RegisterF(pVM, &pQueue->StatFlush, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,
226 "Calls to pdmR3QueueFlush.", "/PDM/Queue/%s/Flush", pQueue->szName);
227 STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
228 "Left over items after flush.", "/PDM/Queue/%s/FlushLeftovers", pQueue->szName);
229#ifdef VBOX_WITH_STATISTICS
230 STAMR3RegisterF(pVM, &pQueue->StatFlushPrf, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL,
231 "Profiling pdmR3QueueFlush.", "/PDM/Queue/%s/FlushPrf", pQueue->szName);
232 STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
233 "Pending items.", "/PDM/Queue/%s/Pending", pQueue->szName);
234#endif
235
236 *phQueue = hQueue;
237 return VINF_SUCCESS;
238}
239
240
241/**
242 * Create a queue with a device owner.
243 *
244 * @returns VBox status code.
245 * @param pVM The cross context VM structure.
246 * @param pDevIns Device instance.
247 * @param cbItem Size a queue item.
248 * @param cItems Number of items in the queue.
249 * @param cMilliesInterval Number of milliseconds between polling the queue.
250 * If 0 then the emulation thread will be notified whenever an item arrives.
251 * @param pfnCallback The consumer function.
252 * @param fRZEnabled Set if the queue must be usable from RC/R0.
253 * @param pszName The queue name. Unique. Copied.
254 * @param phQueue Where to store the queue handle on success.
255 * @thread Emulation thread only. Only EMT0 when @a fRZEnables is true.
256 */
257VMMR3_INT_DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, size_t cbItem, uint32_t cItems,
258 uint32_t cMilliesInterval, PFNPDMQUEUEDEV pfnCallback,
259 bool fRZEnabled, const char *pszName, PDMQUEUEHANDLE *phQueue)
260{
261 LogFlow(("PDMR3QueueCreateDevice: pDevIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
262 pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
263
264 /*
265 * Validate input.
266 */
267 VM_ASSERT_EMT0(pVM);
268 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
269 AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
270
271 if (!(pDevIns->Internal.s.fIntFlags & PDMDEVINSINT_FLAGS_R0_ENABLED))
272 fRZEnabled = false;
273
274 /*
275 * Create the queue.
276 */
277 int rc = RTCritSectEnter(&pVM->pUVM->pdm.s.ListCritSect);
278 AssertRCReturn(rc, rc);
279
280 rc = pdmR3QueueCreateLocked(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName,
281 PDMQUEUETYPE_DEV, pDevIns, (uintptr_t)pfnCallback, phQueue);
282
283 RTCritSectLeave(&pVM->pUVM->pdm.s.ListCritSect);
284 if (RT_SUCCESS(rc))
285 Log(("PDM: Created device queue %#RX64; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
286 *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDevIns));
287 return rc;
288}
289
290
291/**
292 * Create a queue with a driver owner.
293 *
294 * @returns VBox status code.
295 * @param pVM The cross context VM structure.
296 * @param pDrvIns Driver instance.
297 * @param cbItem Size a queue item.
298 * @param cItems Number of items in the queue.
299 * @param cMilliesInterval Number of milliseconds between polling the queue.
300 * If 0 then the emulation thread will be notified whenever an item arrives.
301 * @param pfnCallback The consumer function.
302 * @param pszName The queue name. Unique. Copied.
303 * @param phQueue Where to store the queue handle on success.
304 * @thread Emulation thread only.
305 */
306VMMR3_INT_DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
307 PFNPDMQUEUEDRV pfnCallback, const char *pszName, PDMQUEUEHANDLE *phQueue)
308{
309 LogFlow(("PDMR3QueueCreateDriver: pDrvIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
310 pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
311
312 /*
313 * Validate input.
314 */
315 VM_ASSERT_EMT0(pVM);
316 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
317 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
318
319 /*
320 * Create the queue.
321 */
322 int rc = RTCritSectEnter(&pVM->pUVM->pdm.s.ListCritSect);
323 AssertRCReturn(rc, rc);
324
325 rc = pdmR3QueueCreateLocked(pVM, cbItem, cItems, cMilliesInterval, false /*fRZEnabled*/, pszName,
326 PDMQUEUETYPE_DRV, pDrvIns, (uintptr_t)pfnCallback, phQueue);
327
328 RTCritSectLeave(&pVM->pUVM->pdm.s.ListCritSect);
329 if (RT_SUCCESS(rc))
330 Log(("PDM: Created driver queue %#RX64; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
331 *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDrvIns));
332 return rc;
333}
334
335
336/**
337 * Create a queue with an internal owner.
338 *
339 * @returns VBox status code.
340 * @param pVM The cross context VM structure.
341 * @param cbItem Size a queue item.
342 * @param cItems Number of items in the queue.
343 * @param cMilliesInterval Number of milliseconds between polling the queue.
344 * If 0 then the emulation thread will be notified whenever an item arrives.
345 * @param pfnCallback The consumer function.
346 * @param fRZEnabled Set if the queue must be usable from RC/R0.
347 * @param pszName The queue name. Unique. Copied.
348 * @param phQueue Where to store the queue handle on success.
349 * @thread Emulation thread only. When @a fRZEnables is true only EMT0.
350 */
351VMMR3_INT_DECL(int) PDMR3QueueCreateInternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
352 PFNPDMQUEUEINT pfnCallback, bool fRZEnabled,
353 const char *pszName, PDMQUEUEHANDLE *phQueue)
354{
355 LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
356 cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
357
358 /*
359 * Validate input.
360 */
361 VM_ASSERT_EMT0(pVM);
362 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
363
364 /*
365 * Create the queue.
366 */
367 int rc = RTCritSectEnter(&pVM->pUVM->pdm.s.ListCritSect);
368 AssertRCReturn(rc, rc);
369
370 rc = pdmR3QueueCreateLocked(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName,
371 PDMQUEUETYPE_INTERNAL, pVM, (uintptr_t)pfnCallback, phQueue);
372
373 RTCritSectLeave(&pVM->pUVM->pdm.s.ListCritSect);
374 if (RT_SUCCESS(rc))
375 Log(("PDM: Created internal queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p\n",
376 *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback));
377 return rc;
378}
379
380
381/**
382 * Create a queue with an external owner.
383 *
384 * @returns VBox status code.
385 * @param pVM The cross context VM structure.
386 * @param cbItem Size a queue item.
387 * @param cItems Number of items in the queue.
388 * @param cMilliesInterval Number of milliseconds between polling the queue.
389 * If 0 then the emulation thread will be notified whenever an item arrives.
390 * @param pfnCallback The consumer function.
391 * @param pvUser The user argument to the consumer function.
392 * @param pszName The queue name. Unique. Not copied.
393 * @param phQueue Where to store the queue handle on success.
394 * @thread Emulation thread only.
395 */
396VMMR3DECL(int) PDMR3QueueCreateExternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
397 PFNPDMQUEUEEXT pfnCallback, void *pvUser,
398 const char *pszName, PDMQUEUEHANDLE *phQueue)
399{
400 LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
401 cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
402
403 /*
404 * Validate input.
405 */
406 VM_ASSERT_EMT0(pVM);
407 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
408
409 /*
410 * Create the queue.
411 */
412 int rc = RTCritSectEnter(&pVM->pUVM->pdm.s.ListCritSect);
413 AssertRCReturn(rc, rc);
414
415 rc = pdmR3QueueCreateLocked(pVM, cbItem, cItems, cMilliesInterval, false /*fRZEnabled*/, pszName,
416 PDMQUEUETYPE_EXTERNAL, pvUser, (uintptr_t)pfnCallback, phQueue);
417
418 RTCritSectLeave(&pVM->pUVM->pdm.s.ListCritSect);
419 if (RT_SUCCESS(rc))
420 Log(("PDM: Created external queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pvUser=%p\n",
421 *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pvUser));
422 return rc;
423}
424
425
426/**
427 * Destroy a queue.
428 *
429 * @returns VBox status code.
430 * @param pVM Pointer to the cross context VM structure.
431 * @param hQueue Handle to the queue that should be destroyed.
432 * @param pvOwner The owner address.
433 * @thread EMT
434 */
435static int pdmR3QueueDestroyLocked(PVM pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
436{
437 LogFlow(("pdmR3QueueDestroyLocked: hQueue=%p pvOwner=%p\n", hQueue, pvOwner));
438 Assert(RTCritSectIsOwner(&pVM->pUVM->pdm.s.ListCritSect));
439
440 /*
441 * Validate input.
442 */
443 VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
444 if (hQueue == NIL_PDMQUEUEHANDLE)
445 return VINF_SUCCESS;
446
447 PPDMQUEUE pQueue;
448 bool fRZEnabled = false;
449 if (hQueue < RT_ELEMENTS(pVM->pdm.s.apRing0Queues))
450 {
451 AssertReturn(hQueue < pVM->pdm.s.cRing0Queues, VERR_INVALID_HANDLE);
452 pQueue = pVM->pdm.s.apRing0Queues[hQueue];
453 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
454 AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE);
455 AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INVALID_HANDLE);
456
457 /* Lazy bird: Cannot dynamically delete ring-0 capable queues. */
458 AssertFailedReturn(VERR_NOT_SUPPORTED);
459 }
460 else
461 {
462 hQueue -= RT_ELEMENTS(pVM->pdm.s.apRing0Queues);
463 AssertReturn(hQueue < pVM->pdm.s.cRing3Queues, VERR_INVALID_HANDLE);
464 pQueue = pVM->pdm.s.papRing3Queues[hQueue];
465 AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
466 AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE);
467 AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INVALID_HANDLE);
468
469 /* Enter the lock here to serialize with other EMTs traversing the handles. */
470 pdmLock(pVM);
471 pVM->pdm.s.papRing3Queues[hQueue] = NULL;
472 if (hQueue + 1 == pVM->pdm.s.cRing3Queues)
473 {
474 while (hQueue > 0 && pVM->pdm.s.papRing3Queues[hQueue - 1] == NULL)
475 hQueue--;
476 pVM->pdm.s.cRing3Queues = hQueue;
477 }
478 pQueue->u32Magic = PDMQUEUE_MAGIC_DEAD;
479 pdmUnlock(pVM);
480 }
481
482 /*
483 * Deregister statistics.
484 */
485 STAMR3DeregisterF(pVM->pUVM, "/PDM/Queue/%s/*", pQueue->szName);
486
487 /*
488 * Destroy the timer and free it.
489 */
490 if (pQueue->hTimer != NIL_TMTIMERHANDLE)
491 {
492 TMR3TimerDestroy(pVM, pQueue->hTimer);
493 pQueue->hTimer = NIL_TMTIMERHANDLE;
494 }
495 if (!fRZEnabled)
496 RTMemPageFree(pQueue, pQueue->offItems + pQueue->cbItem * pQueue->cItems);
497
498 return VINF_SUCCESS;
499}
500
501
502/**
503 * Destroy a queue.
504 *
505 * @returns VBox status code.
506 * @param pVM Pointer to the cross context VM structure.
507 * @param hQueue Handle to the queue that should be destroyed.
508 * @param pvOwner The owner address.
509 * @thread EMT
510 * @note Externally visible mainly for testing purposes.
511 */
512VMMR3DECL(int) PDMR3QueueDestroy(PVM pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
513{
514 PUVM const pUVM = pVM->pUVM;
515 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
516
517 int rc = pdmR3QueueDestroyLocked(pVM, hQueue, pvOwner);
518
519 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
520 return rc;
521}
522
523
524/**
525 * Destroy a all queues with a given owner.
526 *
527 * @returns VBox status code.
528 * @param pVM The cross context VM structure.
529 * @param pvOwner The owner pointer.
530 * @param enmType Owner type.
531 * @thread EMT
532 */
533static int pdmR3QueueDestroyByOwner(PVM pVM, void *pvOwner, PDMQUEUETYPE enmType)
534{
535 LogFlow(("pdmR3QueueDestroyByOwner: pvOwner=%p enmType=%d\n", pvOwner, enmType));
536
537 /*
538 * Validate input.
539 */
540 AssertPtrReturn(pvOwner, VERR_INVALID_PARAMETER);
541 AssertReturn(pvOwner != pVM, VERR_INVALID_PARAMETER);
542 VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* Not requiring EMT0 here as we cannot destroy RZ capable ones here. */
543
544 /*
545 * Scan and destroy.
546 */
547 PUVM const pUVM = pVM->pUVM;
548 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
549
550 uint32_t i = pVM->pdm.s.cRing0Queues;
551 while (i-- > 0)
552 {
553 PPDMQUEUE pQueue = pVM->pdm.s.apRing0Queues[i];
554 if ( pQueue
555 && pQueue->u.Gen.pvOwner == pvOwner
556 && pQueue->enmType == enmType)
557 {
558 /* Not supported at runtime. */
559 VM_ASSERT_STATE_RETURN(pVM, VMSTATE_DESTROYING, VERR_WRONG_ORDER);
560 }
561 }
562
563 i = pVM->pdm.s.cRing3Queues;
564 while (i-- > 0)
565 {
566 PPDMQUEUE pQueue = pVM->pdm.s.papRing3Queues[i];
567 if ( pQueue
568 && pQueue->u.Gen.pvOwner == pvOwner
569 && pQueue->enmType == enmType)
570 pdmR3QueueDestroyLocked(pVM, i + RT_ELEMENTS(pVM->pdm.s.apRing0Queues), pvOwner);
571 }
572
573 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
574 return VINF_SUCCESS;
575}
576
577
578/**
579 * Destroy a all queues owned by the specified device.
580 *
581 * @returns VBox status code.
582 * @param pVM The cross context VM structure.
583 * @param pDevIns Device instance.
584 * @thread EMT(0)
585 */
586VMMR3_INT_DECL(int) PDMR3QueueDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
587{
588 LogFlow(("PDMR3QueueDestroyDevice: pDevIns=%p\n", pDevIns));
589 return pdmR3QueueDestroyByOwner(pVM, pDevIns, PDMQUEUETYPE_DEV);
590}
591
592
593/**
594 * Destroy a all queues owned by the specified driver.
595 *
596 * @returns VBox status code.
597 * @param pVM The cross context VM structure.
598 * @param pDrvIns Driver instance.
599 * @thread EMT(0)
600 */
601VMMR3_INT_DECL(int) PDMR3QueueDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
602{
603 LogFlow(("PDMR3QueueDestroyDriver: pDrvIns=%p\n", pDrvIns));
604 return pdmR3QueueDestroyByOwner(pVM, pDrvIns, PDMQUEUETYPE_DRV);
605}
606
607
608/**
609 * Free an item.
610 *
611 * @param pQueue The queue.
612 * @param pbItems Where the items area starts.
613 * @param cbItem Item size.
614 * @param pItem The item to free.
615 */
616DECLINLINE(void) pdmR3QueueFreeItem(PPDMQUEUE pQueue, uint8_t *pbItems, uint32_t cbItem, PPDMQUEUEITEMCORE pItem)
617{
618 pItem->u64View = UINT64_C(0xfeedfeedfeedfeed);
619
620 uintptr_t const offItem = (uintptr_t)pItem - (uintptr_t)pbItems;
621 uintptr_t const iItem = offItem / cbItem;
622 Assert(!(offItem % cbItem));
623 Assert(iItem < pQueue->cItems);
624 AssertReturnVoidStmt(ASMAtomicBitTestAndSet(pQueue->bmAlloc, iItem) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_4);
625 STAM_STATS({ ASMAtomicDecU32(&pQueue->cStatPending); });
626}
627
628
629
630/**
631 * Process pending items in one queue.
632 *
633 * @returns VBox status code.
634 * @param pVM The cross context VM structure.
635 * @param pQueue The queue needing flushing.
636 */
637static int pdmR3QueueFlush(PVM pVM, PPDMQUEUE pQueue)
638{
639 STAM_PROFILE_START(&pQueue->StatFlushPrf,p);
640
641 uint32_t const cbItem = pQueue->cbItem;
642 uint32_t const cItems = pQueue->cItems;
643 uint8_t * const pbItems = (uint8_t *)pQueue + pQueue->offItems;
644
645 /*
646 * Get the list and reverse it into a pointer list (inserted in LIFO order to avoid locking).
647 */
648 uint32_t cPending = 0;
649 PPDMQUEUEITEMCORE pHead = NULL;
650 {
651 uint32_t iCur = ASMAtomicXchgU32(&pQueue->iPending, UINT32_MAX);
652 do
653 {
654 AssertMsgReturn(iCur < cItems, ("%#x vs %#x\n", iCur, cItems), pQueue->rcOkay = VERR_INTERNAL_ERROR_5);
655 AssertReturn(ASMBitTest(pQueue->bmAlloc, iCur) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_3);
656 PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)&pbItems[iCur * cbItem];
657
658 iCur = pCur->iNext;
659 ASMCompilerBarrier(); /* paranoia */
660 pCur->pNext = pHead;
661 pHead = pCur;
662 cPending++;
663 } while (iCur != UINT32_MAX);
664 }
665 RT_NOREF(cPending);
666
667 /*
668 * Feed the items to the consumer function.
669 */
670 Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pHead=%p cItems=%u\n", pQueue, pQueue->enmType, pHead, cPending));
671 switch (pQueue->enmType)
672 {
673 case PDMQUEUETYPE_DEV:
674 while (pHead)
675 {
676 if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pHead))
677 break;
678 PPDMQUEUEITEMCORE pFree = pHead;
679 pHead = pHead->pNext;
680 ASMCompilerBarrier(); /* paranoia */
681 pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
682 }
683 break;
684
685 case PDMQUEUETYPE_DRV:
686 while (pHead)
687 {
688 if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pHead))
689 break;
690 PPDMQUEUEITEMCORE pFree = pHead;
691 pHead = pHead->pNext;
692 ASMCompilerBarrier(); /* paranoia */
693 pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
694 }
695 break;
696
697 case PDMQUEUETYPE_INTERNAL:
698 while (pHead)
699 {
700 if (!pQueue->u.Int.pfnCallback(pVM, pHead))
701 break;
702 PPDMQUEUEITEMCORE pFree = pHead;
703 pHead = pHead->pNext;
704 ASMCompilerBarrier(); /* paranoia */
705 pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
706 }
707 break;
708
709 case PDMQUEUETYPE_EXTERNAL:
710 while (pHead)
711 {
712 if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pHead))
713 break;
714 PPDMQUEUEITEMCORE pFree = pHead;
715 pHead = pHead->pNext;
716 ASMCompilerBarrier(); /* paranoia */
717 pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
718 }
719 break;
720
721 default:
722 AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
723 break;
724 }
725
726 /*
727 * Success?
728 */
729 if (!pHead)
730 { /* likely */ }
731 else
732 {
733 /*
734 * Reverse the list and turn it back into index chain.
735 */
736 uint32_t iPendingHead = UINT32_MAX;
737 do
738 {
739 PPDMQUEUEITEMCORE pInsert = pHead;
740 pHead = pHead->pNext;
741 ASMCompilerBarrier(); /* paranoia */
742 pInsert->iNext = iPendingHead;
743 iPendingHead = ((uintptr_t)pInsert - (uintptr_t)pbItems) / cbItem;
744 } while (pHead);
745
746 /*
747 * Insert the list at the tail of the pending list. If someone races
748 * us there, we have to join the new LIFO with the old.
749 */
750 for (;;)
751 {
752 if (ASMAtomicCmpXchgU32(&pQueue->iPending, iPendingHead, UINT32_MAX))
753 break;
754
755 uint32_t const iNewPending = ASMAtomicXchgU32(&pQueue->iPending, UINT32_MAX);
756 if (iNewPending != UINT32_MAX)
757 {
758 /* Find the last entry and chain iPendingHead onto it. */
759 uint32_t iCur = iNewPending;
760 for (;;)
761 {
762 AssertReturn(iCur < cItems, pQueue->rcOkay = VERR_INTERNAL_ERROR_2);
763 AssertReturn(ASMBitTest(pQueue->bmAlloc, iCur) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_3);
764 PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)&pbItems[iCur * cbItem];
765 iCur = pCur->iNext;
766 if (iCur == UINT32_MAX)
767 {
768 pCur->iNext = iPendingHead;
769 break;
770 }
771 }
772
773 iPendingHead = iNewPending;
774 }
775 }
776
777 STAM_REL_COUNTER_INC(&pQueue->StatFlushLeftovers);
778 }
779
780 STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
781 return VINF_SUCCESS;
782}
783
784
785/**
786 * Flush pending queues.
787 * This is a forced action callback.
788 *
789 * @param pVM The cross context VM structure.
790 * @thread Emulation thread only.
791 * @note Internal, but exported for use in the testcase.
792 */
793VMMR3DECL(void) PDMR3QueueFlushAll(PVM pVM)
794{
795 VM_ASSERT_EMT(pVM);
796 LogFlow(("PDMR3QueuesFlush:\n"));
797
798 /*
799 * Only let one EMT flushing queues at any one time to preserve the order
800 * and to avoid wasting time. The FF is always cleared here, because it's
801 * only used to get someones attention. Queue inserts occurring during the
802 * flush are caught using the pending bit.
803 *
804 * Note! We must check the force action and pending flags after clearing
805 * the active bit!
806 */
807 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
808 while (!ASMAtomicBitTestAndSet(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT))
809 {
810 ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT);
811
812 /* Scan the ring-0 queues: */
813 size_t i = pVM->pdm.s.cRing0Queues;
814 while (i-- > 0)
815 {
816 PPDMQUEUE pQueue = pVM->pdm.s.apRing0Queues[i];
817 if ( pQueue
818 && pQueue->iPending != UINT32_MAX
819 && pQueue->hTimer == NIL_TMTIMERHANDLE
820 && pQueue->rcOkay == VINF_SUCCESS)
821 pdmR3QueueFlush(pVM, pQueue);
822 }
823
824 /* Scan the ring-3 queues: */
825/** @todo Deal with destroy concurrency issues. */
826 i = pVM->pdm.s.cRing3Queues;
827 while (i-- > 0)
828 {
829 PPDMQUEUE pQueue = pVM->pdm.s.papRing3Queues[i];
830 if ( pQueue
831 && pQueue->iPending != UINT32_MAX
832 && pQueue->hTimer == NIL_TMTIMERHANDLE
833 && pQueue->rcOkay == VINF_SUCCESS)
834 pdmR3QueueFlush(pVM, pQueue);
835 }
836
837 ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT);
838
839 /* We're done if there were no inserts while we were busy. */
840 if ( !ASMBitTest(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT)
841 && !VM_FF_IS_SET(pVM, VM_FF_PDM_QUEUES))
842 break;
843 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
844 }
845}
846
847
848
849/**
850 * @callback_method_impl{FNTMTIMERINT, Timer handler for one PDM queue.}
851 */
852static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, TMTIMERHANDLE hTimer, void *pvUser)
853{
854 PPDMQUEUE pQueue = (PPDMQUEUE)pvUser;
855 Assert(hTimer == pQueue->hTimer);
856
857 if (pQueue->iPending != UINT32_MAX)
858 pdmR3QueueFlush(pVM, pQueue);
859
860 int rc = TMTimerSetMillies(pVM, hTimer, pQueue->cMilliesInterval);
861 AssertRC(rc);
862}
863
864
865/**
866 * Terminate the queues, freeing any resources still allocated.
867 *
868 * @param pVM The cross-context VM structure.
869 */
870DECLHIDDEN(void) pdmR3QueueTerm(PVM pVM)
871{
872 PUVM const pUVM = pVM->pUVM;
873 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
874
875 if (pVM->pdm.s.papRing3Queues)
876 {
877 /*
878 * Free the R3 queue handle array.
879 */
880 PDMQUEUEHANDLE cQueues = pVM->pdm.s.cRing3Queues;
881 for (PDMQUEUEHANDLE i = 0; i < cQueues; i++)
882 if (pVM->pdm.s.papRing3Queues[i])
883 {
884 PPDMQUEUE pQueue = pVM->pdm.s.papRing3Queues[i];
885
886 pdmR3QueueDestroyLocked(pVM, RT_ELEMENTS(pVM->pdm.s.apRing0Queues) + i, pQueue->u.Gen.pvOwner);
887 Assert(!pVM->pdm.s.papRing3Queues[i]);
888 }
889
890 RTMemFree(pVM->pdm.s.papRing3Queues);
891 pVM->pdm.s.cRing3QueuesAlloc = 0;
892 pVM->pdm.s.papRing3Queues = NULL;
893 }
894
895 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
896}
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