VirtualBox

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

Last change on this file was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use