VirtualBox

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

Last change on this file since 39137 was 39137, checked in by vboxsync, 13 years ago

build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 29.0 KB
Line 
1/* $Id: PDMQueue.cpp 39137 2011-10-28 10:16:33Z vboxsync $ */
2/** @file
3 * PDM Queue - Transport data and tasks to EMT and R3.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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/rem.h>
27#include <VBox/vmm/vm.h>
28#include <VBox/vmm/uvm.h>
29#include <VBox/err.h>
30
31#include <VBox/log.h>
32#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/thread.h>
35
36
37/*******************************************************************************
38* Internal Functions *
39*******************************************************************************/
40DECLINLINE(void) pdmR3QueueFree(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem);
41static bool pdmR3QueueFlush(PPDMQUEUE pQueue);
42static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser);
43
44
45
46/**
47 * Internal worker for the queue creation apis.
48 *
49 * @returns VBox status.
50 * @param pVM VM handle.
51 * @param cbItem Item size.
52 * @param cItems Number of items.
53 * @param cMilliesInterval Number of milliseconds between polling the queue.
54 * If 0 then the emulation thread will be notified whenever an item arrives.
55 * @param fRZEnabled Set if the queue will be used from RC/R0 and need to be allocated from the hyper heap.
56 * @param pszName The queue name. Unique. Not copied.
57 * @param ppQueue Where to store the queue handle.
58 */
59static int pdmR3QueueCreate(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval, bool fRZEnabled,
60 const char *pszName, PPDMQUEUE *ppQueue)
61{
62 PUVM pUVM = pVM->pUVM;
63
64 /*
65 * Validate input.
66 */
67 AssertMsgReturn(cbItem >= sizeof(PDMQUEUEITEMCORE) && cbItem < _1M, ("cbItem=%zu\n", cbItem), VERR_OUT_OF_RANGE);
68 AssertMsgReturn(cItems >= 1 && cItems <= _64K, ("cItems=%u\n", cItems), VERR_OUT_OF_RANGE);
69
70 /*
71 * Align the item size and calculate the structure size.
72 */
73 cbItem = RT_ALIGN(cbItem, sizeof(RTUINTPTR));
74 size_t cb = cbItem * cItems + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16);
75 PPDMQUEUE pQueue;
76 int rc;
77 if (fRZEnabled)
78 rc = MMHyperAlloc(pVM, cb, 0, MM_TAG_PDM_QUEUE, (void **)&pQueue );
79 else
80 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_QUEUE, cb, (void **)&pQueue);
81 if (RT_FAILURE(rc))
82 return rc;
83
84 /*
85 * Initialize the data fields.
86 */
87 pQueue->pVMR3 = pVM;
88 pQueue->pVMR0 = fRZEnabled ? pVM->pVMR0 : NIL_RTR0PTR;
89 pQueue->pVMRC = fRZEnabled ? pVM->pVMRC : NIL_RTRCPTR;
90 pQueue->pszName = pszName;
91 pQueue->cMilliesInterval = cMilliesInterval;
92 //pQueue->pTimer = NULL;
93 pQueue->cbItem = (uint32_t)cbItem;
94 pQueue->cItems = cItems;
95 //pQueue->pPendingR3 = NULL;
96 //pQueue->pPendingR0 = NULL;
97 //pQueue->pPendingRC = NULL;
98 pQueue->iFreeHead = cItems;
99 //pQueue->iFreeTail = 0;
100 PPDMQUEUEITEMCORE pItem = (PPDMQUEUEITEMCORE)((char *)pQueue + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16));
101 for (unsigned i = 0; i < cItems; i++, pItem = (PPDMQUEUEITEMCORE)((char *)pItem + cbItem))
102 {
103 pQueue->aFreeItems[i].pItemR3 = pItem;
104 if (fRZEnabled)
105 {
106 pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pVM, pItem);
107 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pItem);
108 }
109 }
110
111 /*
112 * Create timer?
113 */
114 if (cMilliesInterval)
115 {
116 rc = TMR3TimerCreateInternal(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, "Queue timer", &pQueue->pTimer);
117 if (RT_SUCCESS(rc))
118 {
119 rc = TMTimerSetMillies(pQueue->pTimer, cMilliesInterval);
120 if (RT_FAILURE(rc))
121 {
122 AssertMsgFailed(("TMTimerSetMillies failed rc=%Rrc\n", rc));
123 int rc2 = TMR3TimerDestroy(pQueue->pTimer); AssertRC(rc2);
124 }
125 }
126 else
127 AssertMsgFailed(("TMR3TimerCreateInternal failed rc=%Rrc\n", rc));
128 if (RT_FAILURE(rc))
129 {
130 if (fRZEnabled)
131 MMHyperFree(pVM, pQueue);
132 else
133 MMR3HeapFree(pQueue);
134 return rc;
135 }
136
137 /*
138 * Insert into the queue list for timer driven queues.
139 */
140 pdmLock(pVM);
141 pQueue->pNext = pUVM->pdm.s.pQueuesTimer;
142 pUVM->pdm.s.pQueuesTimer = pQueue;
143 pdmUnlock(pVM);
144 }
145 else
146 {
147 /*
148 * Insert into the queue list for forced action driven queues.
149 * This is a FIFO, so insert at the end.
150 */
151 /** @todo we should add a priority to the queues so we don't have to rely on
152 * the initialization order to deal with problems like #1605 (pgm/pcnet deadlock
153 * caused by the critsect queue to be last in the chain).
154 * - Update, the critical sections are no longer using queues, so this isn't a real
155 * problem any longer. The priority might be a nice feature for later though.
156 */
157 pdmLock(pVM);
158 if (!pUVM->pdm.s.pQueuesForced)
159 pUVM->pdm.s.pQueuesForced = pQueue;
160 else
161 {
162 PPDMQUEUE pPrev = pUVM->pdm.s.pQueuesForced;
163 while (pPrev->pNext)
164 pPrev = pPrev->pNext;
165 pPrev->pNext = pQueue;
166 }
167 pdmUnlock(pVM);
168 }
169
170 /*
171 * Register the statistics.
172 */
173 STAMR3RegisterF(pVM, &pQueue->cbItem, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Item size.", "/PDM/Queue/%s/cbItem", pQueue->pszName);
174 STAMR3RegisterF(pVM, &pQueue->cItems, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Queue size.", "/PDM/Queue/%s/cItems", pQueue->pszName);
175 STAMR3RegisterF(pVM, &pQueue->StatAllocFailures, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "PDMQueueAlloc failures.", "/PDM/Queue/%s/AllocFailures", pQueue->pszName);
176 STAMR3RegisterF(pVM, &pQueue->StatInsert, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Calls to PDMQueueInsert.", "/PDM/Queue/%s/Insert", pQueue->pszName);
177 STAMR3RegisterF(pVM, &pQueue->StatFlush, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Calls to pdmR3QueueFlush.", "/PDM/Queue/%s/Flush", pQueue->pszName);
178 STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Left over items after flush.", "/PDM/Queue/%s/FlushLeftovers", pQueue->pszName);
179#ifdef VBOX_WITH_STATISTICS
180 STAMR3RegisterF(pVM, &pQueue->StatFlushPrf, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Profiling pdmR3QueueFlush.", "/PDM/Queue/%s/FlushPrf", pQueue->pszName);
181 STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Pending items.", "/PDM/Queue/%s/Pending", pQueue->pszName);
182#endif
183
184 *ppQueue = pQueue;
185 return VINF_SUCCESS;
186}
187
188
189/**
190 * Create a queue with a device owner.
191 *
192 * @returns VBox status code.
193 * @param pVM VM handle.
194 * @param pDevIns Device instance.
195 * @param cbItem Size a queue item.
196 * @param cItems Number of items in the queue.
197 * @param cMilliesInterval Number of milliseconds between polling the queue.
198 * If 0 then the emulation thread will be notified whenever an item arrives.
199 * @param pfnCallback The consumer function.
200 * @param fRZEnabled Set if the queue must be usable from RC/R0.
201 * @param pszName The queue name. Unique. Not copied.
202 * @param ppQueue Where to store the queue handle on success.
203 * @thread Emulation thread only.
204 */
205VMMR3_INT_DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
206 PFNPDMQUEUEDEV pfnCallback, bool fRZEnabled, const char *pszName, PPDMQUEUE *ppQueue)
207{
208 LogFlow(("PDMR3QueueCreateDevice: pDevIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
209 pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
210
211 /*
212 * Validate input.
213 */
214 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
215 if (!pfnCallback)
216 {
217 AssertMsgFailed(("No consumer callback!\n"));
218 return VERR_INVALID_PARAMETER;
219 }
220
221 /*
222 * Create the queue.
223 */
224 PPDMQUEUE pQueue;
225 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
226 if (RT_SUCCESS(rc))
227 {
228 pQueue->enmType = PDMQUEUETYPE_DEV;
229 pQueue->u.Dev.pDevIns = pDevIns;
230 pQueue->u.Dev.pfnCallback = pfnCallback;
231
232 *ppQueue = pQueue;
233 Log(("PDM: Created device queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
234 cbItem, cItems, cMilliesInterval, pfnCallback, pDevIns));
235 }
236 return rc;
237}
238
239
240/**
241 * Create a queue with a driver owner.
242 *
243 * @returns VBox status code.
244 * @param pVM VM handle.
245 * @param pDrvIns Driver instance.
246 * @param cbItem Size a queue item.
247 * @param cItems Number of items in the queue.
248 * @param cMilliesInterval Number of milliseconds between polling the queue.
249 * If 0 then the emulation thread will be notified whenever an item arrives.
250 * @param pfnCallback The consumer function.
251 * @param pszName The queue name. Unique. Not copied.
252 * @param ppQueue Where to store the queue handle on success.
253 * @thread Emulation thread only.
254 */
255VMMR3_INT_DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
256 PFNPDMQUEUEDRV pfnCallback, const char *pszName, PPDMQUEUE *ppQueue)
257{
258 LogFlow(("PDMR3QueueCreateDriver: pDrvIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
259 pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
260
261 /*
262 * Validate input.
263 */
264 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
265 if (!pfnCallback)
266 {
267 AssertMsgFailed(("No consumer callback!\n"));
268 return VERR_INVALID_PARAMETER;
269 }
270
271 /*
272 * Create the queue.
273 */
274 PPDMQUEUE pQueue;
275 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
276 if (RT_SUCCESS(rc))
277 {
278 pQueue->enmType = PDMQUEUETYPE_DRV;
279 pQueue->u.Drv.pDrvIns = pDrvIns;
280 pQueue->u.Drv.pfnCallback = pfnCallback;
281
282 *ppQueue = pQueue;
283 Log(("PDM: Created driver queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
284 cbItem, cItems, cMilliesInterval, pfnCallback, pDrvIns));
285 }
286 return rc;
287}
288
289
290/**
291 * Create a queue with an internal owner.
292 *
293 * @returns VBox status code.
294 * @param pVM VM handle.
295 * @param cbItem Size a queue item.
296 * @param cItems Number of items in the queue.
297 * @param cMilliesInterval Number of milliseconds between polling the queue.
298 * If 0 then the emulation thread will be notified whenever an item arrives.
299 * @param pfnCallback The consumer function.
300 * @param fRZEnabled Set if the queue must be usable from RC/R0.
301 * @param pszName The queue name. Unique. Not copied.
302 * @param ppQueue Where to store the queue handle on success.
303 * @thread Emulation thread only.
304 */
305VMMR3_INT_DECL(int) PDMR3QueueCreateInternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
306 PFNPDMQUEUEINT pfnCallback, bool fRZEnabled, const char *pszName, PPDMQUEUE *ppQueue)
307{
308 LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
309 cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
310
311 /*
312 * Validate input.
313 */
314 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
315 if (!pfnCallback)
316 {
317 AssertMsgFailed(("No consumer callback!\n"));
318 return VERR_INVALID_PARAMETER;
319 }
320
321 /*
322 * Create the queue.
323 */
324 PPDMQUEUE pQueue;
325 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
326 if (RT_SUCCESS(rc))
327 {
328 pQueue->enmType = PDMQUEUETYPE_INTERNAL;
329 pQueue->u.Int.pfnCallback = pfnCallback;
330
331 *ppQueue = pQueue;
332 Log(("PDM: Created internal queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p\n",
333 cbItem, cItems, cMilliesInterval, pfnCallback));
334 }
335 return rc;
336}
337
338
339/**
340 * Create a queue with an external owner.
341 *
342 * @returns VBox status code.
343 * @param pVM VM handle.
344 * @param cbItem Size a queue item.
345 * @param cItems Number of items in the queue.
346 * @param cMilliesInterval Number of milliseconds between polling the queue.
347 * If 0 then the emulation thread will be notified whenever an item arrives.
348 * @param pfnCallback The consumer function.
349 * @param pvUser The user argument to the consumer function.
350 * @param pszName The queue name. Unique. Not copied.
351 * @param ppQueue Where to store the queue handle on success.
352 * @thread Emulation thread only.
353 */
354VMMR3_INT_DECL(int) PDMR3QueueCreateExternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
355 PFNPDMQUEUEEXT pfnCallback, void *pvUser, const char *pszName, PPDMQUEUE *ppQueue)
356{
357 LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n", cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
358
359 /*
360 * Validate input.
361 */
362 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
363 if (!pfnCallback)
364 {
365 AssertMsgFailed(("No consumer callback!\n"));
366 return VERR_INVALID_PARAMETER;
367 }
368
369 /*
370 * Create the queue.
371 */
372 PPDMQUEUE pQueue;
373 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
374 if (RT_SUCCESS(rc))
375 {
376 pQueue->enmType = PDMQUEUETYPE_EXTERNAL;
377 pQueue->u.Ext.pvUser = pvUser;
378 pQueue->u.Ext.pfnCallback = pfnCallback;
379
380 *ppQueue = pQueue;
381 Log(("PDM: Created external queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pvUser=%p\n",
382 cbItem, cItems, cMilliesInterval, pfnCallback, pvUser));
383 }
384 return rc;
385}
386
387
388/**
389 * Destroy a queue.
390 *
391 * @returns VBox status code.
392 * @param pQueue Queue to destroy.
393 * @thread Emulation thread only.
394 */
395VMMR3_INT_DECL(int) PDMR3QueueDestroy(PPDMQUEUE pQueue)
396{
397 LogFlow(("PDMR3QueueDestroy: pQueue=%p\n", pQueue));
398
399 /*
400 * Validate input.
401 */
402 if (!pQueue)
403 return VERR_INVALID_PARAMETER;
404 Assert(pQueue && pQueue->pVMR3);
405 PVM pVM = pQueue->pVMR3;
406 PUVM pUVM = pVM->pUVM;
407
408 pdmLock(pVM);
409
410 /*
411 * Unlink it.
412 */
413 if (pQueue->pTimer)
414 {
415 if (pUVM->pdm.s.pQueuesTimer != pQueue)
416 {
417 PPDMQUEUE pCur = pUVM->pdm.s.pQueuesTimer;
418 while (pCur)
419 {
420 if (pCur->pNext == pQueue)
421 {
422 pCur->pNext = pQueue->pNext;
423 break;
424 }
425 pCur = pCur->pNext;
426 }
427 AssertMsg(pCur, ("Didn't find the queue!\n"));
428 }
429 else
430 pUVM->pdm.s.pQueuesTimer = pQueue->pNext;
431 }
432 else
433 {
434 if (pUVM->pdm.s.pQueuesForced != pQueue)
435 {
436 PPDMQUEUE pCur = pUVM->pdm.s.pQueuesForced;
437 while (pCur)
438 {
439 if (pCur->pNext == pQueue)
440 {
441 pCur->pNext = pQueue->pNext;
442 break;
443 }
444 pCur = pCur->pNext;
445 }
446 AssertMsg(pCur, ("Didn't find the queue!\n"));
447 }
448 else
449 pUVM->pdm.s.pQueuesForced = pQueue->pNext;
450 }
451 pQueue->pNext = NULL;
452 pQueue->pVMR3 = NULL;
453 pdmUnlock(pVM);
454
455 /*
456 * Deregister statistics.
457 */
458 STAMR3Deregister(pVM, &pQueue->cbItem);
459 STAMR3Deregister(pVM, &pQueue->cbItem);
460 STAMR3Deregister(pVM, &pQueue->StatAllocFailures);
461 STAMR3Deregister(pVM, &pQueue->StatInsert);
462 STAMR3Deregister(pVM, &pQueue->StatFlush);
463 STAMR3Deregister(pVM, &pQueue->StatFlushLeftovers);
464#ifdef VBOX_WITH_STATISTICS
465 STAMR3Deregister(pVM, &pQueue->StatFlushPrf);
466 STAMR3Deregister(pVM, (void *)&pQueue->cStatPending);
467#endif
468
469 /*
470 * Destroy the timer and free it.
471 */
472 if (pQueue->pTimer)
473 {
474 TMR3TimerDestroy(pQueue->pTimer);
475 pQueue->pTimer = NULL;
476 }
477 if (pQueue->pVMRC)
478 {
479 pQueue->pVMRC = NIL_RTRCPTR;
480 pQueue->pVMR0 = NIL_RTR0PTR;
481 MMHyperFree(pVM, pQueue);
482 }
483 else
484 MMR3HeapFree(pQueue);
485
486 return VINF_SUCCESS;
487}
488
489
490/**
491 * Destroy a all queues owned by the specified device.
492 *
493 * @returns VBox status code.
494 * @param pVM VM handle.
495 * @param pDevIns Device instance.
496 * @thread Emulation thread only.
497 */
498VMMR3_INT_DECL(int) PDMR3QueueDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
499{
500 LogFlow(("PDMR3QueueDestroyDevice: pDevIns=%p\n", pDevIns));
501
502 /*
503 * Validate input.
504 */
505 if (!pDevIns)
506 return VERR_INVALID_PARAMETER;
507
508 PUVM pUVM = pVM->pUVM;
509 pdmLock(pVM);
510
511 /*
512 * Unlink it.
513 */
514 PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
515 PPDMQUEUE pQueue = pUVM->pdm.s.pQueuesForced;
516 do
517 {
518 while (pQueue)
519 {
520 if ( pQueue->enmType == PDMQUEUETYPE_DEV
521 && pQueue->u.Dev.pDevIns == pDevIns)
522 {
523 PPDMQUEUE pQueueDestroy = pQueue;
524 pQueue = pQueue->pNext;
525 int rc = PDMR3QueueDestroy(pQueueDestroy);
526 AssertRC(rc);
527 }
528 else
529 pQueue = pQueue->pNext;
530 }
531
532 /* next queue list */
533 pQueue = pQueueNext;
534 pQueueNext = NULL;
535 } while (pQueue);
536
537 pdmUnlock(pVM);
538 return VINF_SUCCESS;
539}
540
541
542/**
543 * Destroy a all queues owned by the specified driver.
544 *
545 * @returns VBox status code.
546 * @param pVM VM handle.
547 * @param pDrvIns Driver instance.
548 * @thread Emulation thread only.
549 */
550VMMR3_INT_DECL(int) PDMR3QueueDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
551{
552 LogFlow(("PDMR3QueueDestroyDriver: pDrvIns=%p\n", pDrvIns));
553
554 /*
555 * Validate input.
556 */
557 if (!pDrvIns)
558 return VERR_INVALID_PARAMETER;
559
560 PUVM pUVM = pVM->pUVM;
561 pdmLock(pVM);
562
563 /*
564 * Unlink it.
565 */
566 PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
567 PPDMQUEUE pQueue = pUVM->pdm.s.pQueuesForced;
568 do
569 {
570 while (pQueue)
571 {
572 if ( pQueue->enmType == PDMQUEUETYPE_DRV
573 && pQueue->u.Drv.pDrvIns == pDrvIns)
574 {
575 PPDMQUEUE pQueueDestroy = pQueue;
576 pQueue = pQueue->pNext;
577 int rc = PDMR3QueueDestroy(pQueueDestroy);
578 AssertRC(rc);
579 }
580 else
581 pQueue = pQueue->pNext;
582 }
583
584 /* next queue list */
585 pQueue = pQueueNext;
586 pQueueNext = NULL;
587 } while (pQueue);
588
589 pdmUnlock(pVM);
590 return VINF_SUCCESS;
591}
592
593
594/**
595 * Relocate the queues.
596 *
597 * @param pVM The VM handle.
598 * @param offDelta The relocation delta.
599 */
600void pdmR3QueueRelocate(PVM pVM, RTGCINTPTR offDelta)
601{
602 /*
603 * Process the queues.
604 */
605 PUVM pUVM = pVM->pUVM;
606 PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
607 PPDMQUEUE pQueue = pUVM->pdm.s.pQueuesForced;
608 do
609 {
610 while (pQueue)
611 {
612 if (pQueue->pVMRC)
613 {
614 pQueue->pVMRC = pVM->pVMRC;
615
616 /* Pending RC items. */
617 if (pQueue->pPendingRC)
618 {
619 pQueue->pPendingRC += offDelta;
620 PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pVM, pQueue->pPendingRC);
621 while (pCur->pNextRC)
622 {
623 pCur->pNextRC += offDelta;
624 pCur = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pVM, pCur->pNextRC);
625 }
626 }
627
628 /* The free items. */
629 uint32_t i = pQueue->iFreeTail;
630 while (i != pQueue->iFreeHead)
631 {
632 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pQueue->aFreeItems[i].pItemR3);
633 i = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
634 }
635 }
636
637 /* next queue */
638 pQueue = pQueue->pNext;
639 }
640
641 /* next queue list */
642 pQueue = pQueueNext;
643 pQueueNext = NULL;
644 } while (pQueue);
645}
646
647
648/**
649 * Flush pending queues.
650 * This is a forced action callback.
651 *
652 * @param pVM VM handle.
653 * @thread Emulation thread only.
654 */
655VMMR3_INT_DECL(void) PDMR3QueueFlushAll(PVM pVM)
656{
657 VM_ASSERT_EMT(pVM);
658 LogFlow(("PDMR3QueuesFlush:\n"));
659
660 /*
661 * Only let one EMT flushing queues at any one time to preserve the order
662 * and to avoid wasting time. The FF is always cleared here, because it's
663 * only used to get someones attention. Queue inserts occurring during the
664 * flush are caught using the pending bit.
665 *
666 * Note! We must check the force action and pending flags after clearing
667 * the active bit!
668 */
669 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
670 while (!ASMAtomicBitTestAndSet(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT))
671 {
672 ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT);
673
674 for (PPDMQUEUE pCur = pVM->pUVM->pdm.s.pQueuesForced; pCur; pCur = pCur->pNext)
675 if ( pCur->pPendingR3
676 || pCur->pPendingR0
677 || pCur->pPendingRC)
678 pdmR3QueueFlush(pCur);
679
680 ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT);
681
682 /* We're done if there were no inserts while we were busy. */
683 if ( !ASMBitTest(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT)
684 && !VM_FF_ISPENDING(pVM, VM_FF_PDM_QUEUES))
685 break;
686 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
687 }
688}
689
690
691/**
692 * Process pending items in one queue.
693 *
694 * @returns Success indicator.
695 * If false the item the consumer said "enough!".
696 * @param pQueue The queue.
697 */
698static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
699{
700 STAM_PROFILE_START(&pQueue->StatFlushPrf,p);
701
702 /*
703 * Get the lists.
704 */
705 PPDMQUEUEITEMCORE pItems = ASMAtomicXchgPtrT(&pQueue->pPendingR3, NULL, PPDMQUEUEITEMCORE);
706 RTRCPTR pItemsRC = ASMAtomicXchgRCPtr(&pQueue->pPendingRC, NIL_RTRCPTR);
707 RTR0PTR pItemsR0 = ASMAtomicXchgR0Ptr(&pQueue->pPendingR0, NIL_RTR0PTR);
708
709 AssertMsgReturn( pItemsR0
710 || pItemsRC
711 || pItems,
712 ("Someone is racing us? This shouldn't happen!\n"),
713 true);
714
715 /*
716 * Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
717 */
718 PPDMQUEUEITEMCORE pCur = pItems;
719 pItems = NULL;
720 while (pCur)
721 {
722 PPDMQUEUEITEMCORE pInsert = pCur;
723 pCur = pCur->pNextR3;
724 pInsert->pNextR3 = pItems;
725 pItems = pInsert;
726 }
727
728 /*
729 * Do the same for any pending RC items.
730 */
731 while (pItemsRC)
732 {
733 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pQueue->pVMR3, pItemsRC);
734 pItemsRC = pInsert->pNextRC;
735 pInsert->pNextRC = NIL_RTRCPTR;
736 pInsert->pNextR3 = pItems;
737 pItems = pInsert;
738 }
739
740 /*
741 * Do the same for any pending R0 items.
742 */
743 while (pItemsR0)
744 {
745 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperR0ToR3(pQueue->pVMR3, pItemsR0);
746 pItemsR0 = pInsert->pNextR0;
747 pInsert->pNextR0 = NIL_RTR0PTR;
748 pInsert->pNextR3 = pItems;
749 pItems = pInsert;
750 }
751
752 /*
753 * Feed the items to the consumer function.
754 */
755 Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pItems=%p\n", pQueue, pQueue->enmType, pItems));
756 switch (pQueue->enmType)
757 {
758 case PDMQUEUETYPE_DEV:
759 while (pItems)
760 {
761 if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pItems))
762 break;
763 pCur = pItems;
764 pItems = pItems->pNextR3;
765 pdmR3QueueFree(pQueue, pCur);
766 }
767 break;
768
769 case PDMQUEUETYPE_DRV:
770 while (pItems)
771 {
772 if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pItems))
773 break;
774 pCur = pItems;
775 pItems = pItems->pNextR3;
776 pdmR3QueueFree(pQueue, pCur);
777 }
778 break;
779
780 case PDMQUEUETYPE_INTERNAL:
781 while (pItems)
782 {
783 if (!pQueue->u.Int.pfnCallback(pQueue->pVMR3, pItems))
784 break;
785 pCur = pItems;
786 pItems = pItems->pNextR3;
787 pdmR3QueueFree(pQueue, pCur);
788 }
789 break;
790
791 case PDMQUEUETYPE_EXTERNAL:
792 while (pItems)
793 {
794 if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pItems))
795 break;
796 pCur = pItems;
797 pItems = pItems->pNextR3;
798 pdmR3QueueFree(pQueue, pCur);
799 }
800 break;
801
802 default:
803 AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
804 break;
805 }
806
807 /*
808 * Success?
809 */
810 if (pItems)
811 {
812 /*
813 * Reverse the list.
814 */
815 pCur = pItems;
816 pItems = NULL;
817 while (pCur)
818 {
819 PPDMQUEUEITEMCORE pInsert = pCur;
820 pCur = pInsert->pNextR3;
821 pInsert->pNextR3 = pItems;
822 pItems = pInsert;
823 }
824
825 /*
826 * Insert the list at the tail of the pending list.
827 */
828 for (;;)
829 {
830 if (ASMAtomicCmpXchgPtr(&pQueue->pPendingR3, pItems, NULL))
831 break;
832 PPDMQUEUEITEMCORE pPending = ASMAtomicXchgPtrT(&pQueue->pPendingR3, NULL, PPDMQUEUEITEMCORE);
833 if (pPending)
834 {
835 pCur = pPending;
836 while (pCur->pNextR3)
837 pCur = pCur->pNextR3;
838 pCur->pNextR3 = pItems;
839 pItems = pPending;
840 }
841 }
842
843 STAM_REL_COUNTER_INC(&pQueue->StatFlushLeftovers);
844 STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
845 return false;
846 }
847
848 STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
849 return true;
850}
851
852
853/**
854 * Free an item.
855 *
856 * @param pQueue The queue.
857 * @param pItem The item.
858 */
859DECLINLINE(void) pdmR3QueueFree(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
860{
861 VM_ASSERT_EMT(pQueue->pVMR3);
862
863 int i = pQueue->iFreeHead;
864 int iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
865
866 pQueue->aFreeItems[i].pItemR3 = pItem;
867 if (pQueue->pVMRC)
868 {
869 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pQueue->pVMR3, pItem);
870 pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pQueue->pVMR3, pItem);
871 }
872
873 if (!ASMAtomicCmpXchgU32(&pQueue->iFreeHead, iNext, i))
874 AssertMsgFailed(("huh? i=%d iNext=%d iFreeHead=%d iFreeTail=%d\n", i, iNext, pQueue->iFreeHead, pQueue->iFreeTail));
875 STAM_STATS({ ASMAtomicDecU32(&pQueue->cStatPending); });
876}
877
878
879/**
880 * Timer handler for PDM queues.
881 * This is called by for a single queue.
882 *
883 * @param pVM VM handle.
884 * @param pTimer Pointer to timer.
885 * @param pvUser Pointer to the queue.
886 */
887static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser)
888{
889 PPDMQUEUE pQueue = (PPDMQUEUE)pvUser;
890 Assert(pTimer == pQueue->pTimer); NOREF(pTimer); NOREF(pVM);
891
892 if ( pQueue->pPendingR3
893 || pQueue->pPendingR0
894 || pQueue->pPendingRC)
895 pdmR3QueueFlush(pQueue);
896 int rc = TMTimerSetMillies(pQueue->pTimer, pQueue->cMilliesInterval);
897 AssertRC(rc);
898}
899
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