VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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