VirtualBox

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

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

VMM: ran scm. Mostly svn:keywords changes (adding Revision).

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