VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use