VirtualBox

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

Last change on this file since 21694 was 21367, checked in by vboxsync, 15 years ago

PDMQueue: More stats.

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