VirtualBox

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

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

PDM: Moving more stuff to PDMUSERPERVM. Protect the loader list using ListCritSect.

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