VirtualBox

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

Last change on this file since 93991 was 93991, checked in by vboxsync, 3 years ago

VMM: Fix a possible PDM queue leak when destroying a VM, bugref:10093

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