VirtualBox

Changeset 93609 in vbox for trunk/src/VBox/VMM/VMMR3


Ignore:
Timestamp:
Feb 5, 2022 7:03:08 PM (3 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
149754
Message:

VMM/PDMQueue: Rewrote the queue code to not use the hyper heap and be a bit safer. Added a testcase (driverless). bugref:10093

Location:
trunk/src/VBox/VMM/VMMR3
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMR3/PDMDevHlp.cpp

    r93554 r93609  
    27472747    }
    27482748
    2749     PPDMQUEUE pQueue = NULL;
    2750     int rc = PDMR3QueueCreateDevice(pVM, pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName, &pQueue);
    2751     *phQueue = (uintptr_t)pQueue;
    2752 
    2753     LogFlow(("pdmR3DevHlp_QueueCreate: caller='%s'/%d: returns %Rrc *ppQueue=%p\n", pDevIns->pReg->szName, pDevIns->iInstance, rc, *phQueue));
     2749    int rc = PDMR3QueueCreateDevice(pVM, pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName, phQueue);
     2750
     2751    LogFlow(("pdmR3DevHlp_QueueCreate: caller='%s'/%d: returns %Rrc *phQueue=%p\n",
     2752             pDevIns->pReg->szName, pDevIns->iInstance, rc, *phQueue));
    27542753    return rc;
    27552754}
     
    27702769static DECLCALLBACK(PPDMQUEUEITEMCORE) pdmR3DevHlp_QueueAlloc(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue)
    27712770{
    2772     return PDMQueueAlloc(pdmR3DevHlp_QueueToPtr(pDevIns, hQueue));
     2771    PDMDEV_ASSERT_DEVINS(pDevIns);
     2772    return PDMQueueAlloc(pDevIns->Internal.s.pVMR3, hQueue, pDevIns);
    27732773}
    27742774
    27752775
    27762776/** @interface_method_impl{PDMDEVHLPR3,pfnQueueInsert} */
    2777 static DECLCALLBACK(void) pdmR3DevHlp_QueueInsert(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
    2778 {
    2779     return PDMQueueInsert(pdmR3DevHlp_QueueToPtr(pDevIns, hQueue), pItem);
     2777static DECLCALLBACK(int) pdmR3DevHlp_QueueInsert(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
     2778{
     2779    return PDMQueueInsert(pDevIns->Internal.s.pVMR3, hQueue, pDevIns, pItem);
    27802780}
    27812781
     
    27842784static DECLCALLBACK(bool) pdmR3DevHlp_QueueFlushIfNecessary(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue)
    27852785{
    2786     return PDMQueueFlushIfNecessary(pdmR3DevHlp_QueueToPtr(pDevIns, hQueue));
     2786    return PDMQueueFlushIfNecessary(pDevIns->Internal.s.pVMR3, hQueue, pDevIns) == VINF_SUCCESS;
    27872787}
    27882788
     
    63606360        {
    63616361            /* Same as pdmR3DevHlp_PCISetIrq, except we've got a tag already. */
    6362             PPDMPCIDEV pPciDev = pTask->u.PciSetIrq.pPciDevR3;
     6362            PPDMDEVINSR3 pDevIns = pTask->pDevInsR3;
     6363            PPDMPCIDEV   pPciDev = pTask->u.PciSetIrq.idxPciDev < RT_ELEMENTS(pDevIns->apPciDevs)
     6364                                 ? pDevIns->apPciDevs[pTask->u.PciSetIrq.idxPciDev] : NULL;
    63636365            if (pPciDev)
    63646366            {
     
    63736375            }
    63746376            else
    6375                 AssertReleaseMsgFailed(("No PCI device registered!\n"));
     6377                AssertReleaseMsgFailed(("No PCI device given! (%#x)\n", pPciDev->Int.s.idxSubDev));
    63766378            break;
    63776379        }
  • trunk/src/VBox/VMM/VMMR3/PDMDevice.cpp

    r93115 r93609  
    131131     * Get the RC & R0 devhlps and create the devhlp R3 task queue.
    132132     */
    133     rc = PDMR3QueueCreateInternal(pVM, sizeof(PDMDEVHLPTASK), 8, 0, pdmR3DevHlpQueueConsumer, true, "DevHlp",
    134                                   &pVM->pdm.s.pDevHlpQueueR3);
     133    rc = PDMR3QueueCreateInternal(pVM, sizeof(PDMDEVHLPTASK), pVM->cCpus * 8, 0, pdmR3DevHlpQueueConsumer, true, "DevHlp",
     134                                  &pVM->pdm.s.hDevHlpQueue);
    135135    AssertRCReturn(rc, rc);
    136     pVM->pdm.s.pDevHlpQueueR0 = PDMQueueR0Ptr(pVM->pdm.s.pDevHlpQueueR3);
    137136
    138137    /*
  • trunk/src/VBox/VMM/VMMR3/PDMDriver.cpp

    r93115 r93609  
    12551255
    12561256
    1257 /**
    1258  * Conversion from handle to queue pointer (temporary).
    1259  */
    1260 DECLINLINE(PPDMQUEUE) pdmR3DrvHlp_QueueToPtr(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue)
    1261 {
    1262     PDMDRV_ASSERT_DRVINS(pDrvIns);
    1263     RT_NOREF(pDrvIns);
    1264     return (PPDMQUEUE)hQueue;
    1265 }
    1266 
    1267 
    12681257/** @interface_method_impl{PDMDRVHLPR3,pfnQueueCreate} */
    12691258static DECLCALLBACK(int) pdmR3DrvHlp_QueueCreate(PPDMDRVINS pDrvIns, uint32_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
     
    12821271    }
    12831272
    1284     PPDMQUEUE pQueue = NULL;
    1285     int rc = PDMR3QueueCreateDriver(pVM, pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName, &pQueue);
    1286     *phQueue = (PDMQUEUEHANDLE)pQueue;
     1273    int rc = PDMR3QueueCreateDriver(pVM, pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName, phQueue);
    12871274
    12881275    LogFlow(("pdmR3DrvHlp_PDMQueueCreate: caller='%s'/%d: returns %Rrc *phQueue=%p\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc, *phQueue));
     
    12941281static DECLCALLBACK(PPDMQUEUEITEMCORE) pdmR3DrvHlp_QueueAlloc(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue)
    12951282{
    1296     return PDMQueueAlloc(pdmR3DrvHlp_QueueToPtr(pDrvIns, hQueue));
     1283    return PDMQueueAlloc(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns);
    12971284}
    12981285
    12991286
    13001287/** @interface_method_impl{PDMDRVHLPR3,pfnQueueInsert} */
    1301 static DECLCALLBACK(void) pdmR3DrvHlp_QueueInsert(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
    1302 {
    1303     return PDMQueueInsert(pdmR3DrvHlp_QueueToPtr(pDrvIns, hQueue), pItem);
     1288static DECLCALLBACK(int) pdmR3DrvHlp_QueueInsert(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
     1289{
     1290    return PDMQueueInsert(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns, pItem);
    13041291}
    13051292
     
    13081295static DECLCALLBACK(bool) pdmR3DrvHlp_QueueFlushIfNecessary(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue)
    13091296{
    1310     return PDMQueueFlushIfNecessary(pdmR3DrvHlp_QueueToPtr(pDrvIns, hQueue));
    1311 }
    1312 
     1297    return PDMQueueFlushIfNecessary(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns) == VINF_SUCCESS;
     1298}
    13131299
    13141300
  • trunk/src/VBox/VMM/VMMR3/PDMQueue.cpp

    r93115 r93609  
    3131#include <iprt/asm.h>
    3232#include <iprt/assert.h>
     33#include <iprt/mem.h>
    3334#include <iprt/thread.h>
    3435
     
    3738*   Internal Functions                                                                                                           *
    3839*********************************************************************************************************************************/
    39 DECLINLINE(void)            pdmR3QueueFreeItem(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem);
    40 static bool                 pdmR3QueueFlush(PPDMQUEUE pQueue);
    4140static DECLCALLBACK(void)   pdmR3QueueTimer(PVM pVM, TMTIMERHANDLE hTimer, void *pvUser);
    4241
     
    5453 * @param   fRZEnabled          Set if the queue will be used from RC/R0 and need to be allocated from the hyper heap.
    5554 * @param   pszName             The queue name. Unique. Not copied.
    56  * @param   ppQueue             Where to store the queue handle.
     55 * @param   enmType             Owner type.
     56 * @param   pvOwner             The queue owner pointer.
     57 * @param   uCallback           Callback function.
     58 * @param   phQueue             Where to store the queue handle.
    5759 */
    5860static int pdmR3QueueCreate(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval, bool fRZEnabled,
    59                             const char *pszName, PPDMQUEUE *ppQueue)
    60 {
    61     PUVM pUVM = pVM->pUVM;
    62 
    63     /*
    64      * Validate input.
    65      */
    66     AssertMsgReturn(cbItem >= sizeof(PDMQUEUEITEMCORE) && cbItem < _1M, ("cbItem=%zu\n", cbItem), VERR_OUT_OF_RANGE);
    67     AssertMsgReturn(cItems >= 1 && cItems <= _64K, ("cItems=%u\n", cItems), VERR_OUT_OF_RANGE);
     61                            const char *pszName, PDMQUEUETYPE enmType, void *pvOwner, uintptr_t uCallback,
     62                            PDMQUEUEHANDLE *phQueue)
     63{
     64    /*
     65     * Validate and adjust the input.
     66     */
     67    VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* serialization by exclusivity */
     68
     69    cbItem = RT_ALIGN(cbItem, sizeof(uint64_t));
     70    AssertMsgReturn(cbItem >= sizeof(PDMQUEUEITEMCORE) && cbItem < PDMQUEUE_MAX_ITEM_SIZE, ("cbItem=%zu\n", cbItem),
     71                    VERR_OUT_OF_RANGE);
     72    AssertMsgReturn(cItems >= 1 && cItems <= PDMQUEUE_MAX_ITEMS, ("cItems=%u\n", cItems), VERR_OUT_OF_RANGE);
     73    AssertMsgReturn((uint64_t)cbItem * cItems <= (fRZEnabled ? PDMQUEUE_MAX_TOTAL_SIZE_R0 : PDMQUEUE_MAX_TOTAL_SIZE_R3),
     74                    ("cItems=%u cbItem=%#x -> %#RX64, max %'u\n", cItems, cbItem, (uint64_t)cbItem * cItems,
     75                     fRZEnabled ? PDMQUEUE_MAX_TOTAL_SIZE_R0 : PDMQUEUE_MAX_TOTAL_SIZE_R3),
     76                    VERR_OUT_OF_RANGE);
     77    AssertReturn(!fRZEnabled || enmType == PDMQUEUETYPE_INTERNAL || enmType == PDMQUEUETYPE_DEV, VERR_INVALID_PARAMETER);
     78    if (SUPR3IsDriverless())
     79        fRZEnabled = false;
     80
     81    /* Unqiue name that fits within the szName field: */
     82    size_t cchName = strlen(pszName);
     83    AssertReturn(cchName > 0, VERR_INVALID_NAME);
     84    AssertMsgReturn(cchName < RT_SIZEOFMEMB(PDMQUEUE, szName), ("'%s' is too long\n", pszName), VERR_INVALID_NAME);
     85    size_t i = pVM->pdm.s.cRing3Queues;
     86    while (i-- > 0 )
     87        AssertMsgReturn(strcmp(pVM->pdm.s.papRing3Queues[i]->szName, pszName) != 0, ("%s\n", pszName), VERR_DUPLICATE);
     88    i = pVM->pdm.s.cRing0Queues;
     89    while (i-- > 0 )
     90        AssertMsgReturn(strcmp(pVM->pdm.s.apRing0Queues[i]->szName, pszName) != 0, ("%s\n", pszName), VERR_DUPLICATE);
    6891
    6992    /*
    7093     * Align the item size and calculate the structure size.
    7194     */
    72     cbItem = RT_ALIGN(cbItem, sizeof(RTUINTPTR));
    73     size_t cb = cbItem * cItems + RT_ALIGN_Z(RT_UOFFSETOF_DYN(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16);
    74     PPDMQUEUE pQueue;
    75     int rc;
     95    PPDMQUEUE      pQueue;
     96    PDMQUEUEHANDLE hQueue;
    7697    if (fRZEnabled)
    77         rc = MMHyperAlloc(pVM, cb, 0, MM_TAG_PDM_QUEUE, (void **)&pQueue );
     98    {
     99        /* Call ring-0 to allocate and create the queue: */
     100        PDMQUEUECREATEREQ Req;
     101        Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
     102        Req.Hdr.cbReq    = sizeof(Req);
     103        Req.cItems       = cItems;
     104        Req.cbItem       = (uint32_t)cbItem;
     105        Req.enmType      = enmType;
     106        Req.pvOwner      = pvOwner;
     107        Req.pfnCallback  = (RTR3PTR)uCallback;
     108        RTStrCopy(Req.szName, sizeof(Req.szName), pszName);
     109        AssertCompileMembersSameSize(PDMQUEUECREATEREQ, szName, PDMQUEUE, szName);
     110        Req.hQueue       = NIL_PDMQUEUEHANDLE;
     111
     112        int rc = VMMR3CallR0(pVM, VMMR0_DO_PDM_QUEUE_CREATE, 0, &Req.Hdr);
     113        if (RT_FAILURE(rc))
     114            return rc;
     115        hQueue = Req.hQueue;
     116        AssertReturn(hQueue < RT_ELEMENTS(pVM->pdm.s.apRing0Queues), VERR_INTERNAL_ERROR_2);
     117        pQueue = pVM->pdm.s.apRing0Queues[hQueue];
     118        AssertPtrReturn(pQueue, VERR_INTERNAL_ERROR_3);
     119        AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INTERNAL_ERROR_4);
     120        AssertReturn(pQueue->cbItem == cbItem, VERR_INTERNAL_ERROR_4);
     121        AssertReturn(pQueue->cItems == cItems, VERR_INTERNAL_ERROR_4);
     122        AssertReturn(pQueue->enmType == enmType, VERR_INTERNAL_ERROR_4);
     123        AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INTERNAL_ERROR_4);
     124        AssertReturn(pQueue->u.Gen.pfnCallback == (RTR3PTR)uCallback, VERR_INTERNAL_ERROR_4);
     125    }
    78126    else
    79         rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_QUEUE, cb, (void **)&pQueue);
    80     if (RT_FAILURE(rc))
    81         return rc;
    82 
    83     /*
    84      * Initialize the data fields.
    85      */
    86     pQueue->pVMR3 = pVM;
    87     pQueue->pVMR0 = fRZEnabled ? pVM->pVMR0ForCall : NIL_RTR0PTR;
    88     pQueue->pszName = pszName;
    89     pQueue->cMilliesInterval = cMilliesInterval;
    90     pQueue->hTimer = NIL_TMTIMERHANDLE;
    91     pQueue->cbItem = (uint32_t)cbItem;
    92     pQueue->cItems = cItems;
    93     //pQueue->pPendingR3 = NULL;
    94     //pQueue->pPendingR0 = NULL;
    95     //pQueue->pPendingRC = NULL;
    96     pQueue->iFreeHead = cItems;
    97     //pQueue->iFreeTail = 0;
    98     PPDMQUEUEITEMCORE pItem = (PPDMQUEUEITEMCORE)((char *)pQueue + RT_ALIGN_Z(RT_UOFFSETOF_DYN(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16));
    99     for (unsigned i = 0; i < cItems; i++, pItem = (PPDMQUEUEITEMCORE)((char *)pItem + cbItem))
    100     {
    101         pQueue->aFreeItems[i].pItemR3 = pItem;
    102         if (fRZEnabled)
    103             pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pVM, pItem);
     127    {
     128        /* Do it here using the paged heap: */
     129        uint32_t const cbBitmap = RT_ALIGN_32(RT_ALIGN_32(cItems, 64) / 8, 64); /* keep bitmap in it's own cacheline  */
     130        uint32_t const cbQueue  = RT_OFFSETOF(PDMQUEUE, bmAlloc)
     131                                + cbBitmap
     132                                + (uint32_t)cbItem * cItems;
     133        pQueue = (PPDMQUEUE)RTMemPageAllocZ(cbQueue);
     134        if (!pQueue)
     135            return VERR_NO_PAGE_MEMORY;
     136        pdmQueueInit(pQueue, cbBitmap, (uint32_t)cbItem, cItems, pszName, enmType, (RTR3PTR)uCallback, pvOwner);
     137
     138        uint32_t iQueue = pVM->pdm.s.cRing3Queues;
     139        if (iQueue >= pVM->pdm.s.cRing3QueuesAlloc)
     140        {
     141            AssertLogRelMsgReturnStmt(iQueue < _16K, ("%#x\n", iQueue), RTMemPageFree(pQueue, cbQueue), VERR_TOO_MANY_OPENS);
     142
     143            uint32_t const cNewAlloc = RT_ALIGN_32(iQueue, 64) + 64;
     144            PPDMQUEUE *papQueuesNew = (PPDMQUEUE *)RTMemAllocZ(cNewAlloc * sizeof(papQueuesNew[0]));
     145            AssertLogRelMsgReturnStmt(papQueuesNew, ("cNewAlloc=%u\n", cNewAlloc), RTMemPageFree(pQueue, cbQueue), VERR_NO_MEMORY);
     146
     147            if (iQueue)
     148                memcpy(papQueuesNew, pVM->pdm.s.papRing3Queues, iQueue * sizeof(papQueuesNew[0]));
     149            PPDMQUEUE *papQueuesOld = ASMAtomicXchgPtrT(&pVM->pdm.s.papRing3Queues, papQueuesNew, PPDMQUEUE *);
     150            pVM->pdm.s.cRing3QueuesAlloc = cNewAlloc;
     151            RTMemFree(papQueuesOld);
     152        }
     153
     154        pVM->pdm.s.papRing3Queues[iQueue] = pQueue;
     155        pVM->pdm.s.cRing3Queues           = iQueue + 1;
     156        hQueue = iQueue + RT_ELEMENTS(pVM->pdm.s.apRing0Queues);
    104157    }
    105158
     
    109162    if (cMilliesInterval)
    110163    {
    111         char szName[32];
    112         RTStrPrintf(szName, sizeof(szName), "Queue %s", pQueue->pszName);
    113         rc = TMR3TimerCreate(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, TMTIMER_FLAGS_NO_RING0, szName, &pQueue->hTimer);
     164        char szName[48+6];
     165        RTStrPrintf(szName, sizeof(szName), "Que/%s", pQueue->szName);
     166        int rc = TMR3TimerCreate(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, TMTIMER_FLAGS_NO_RING0, szName, &pQueue->hTimer);
    114167        if (RT_SUCCESS(rc))
    115168        {
    116169            rc = TMTimerSetMillies(pVM, pQueue->hTimer, cMilliesInterval);
    117             if (RT_FAILURE(rc))
     170            if (RT_SUCCESS(rc))
     171                pQueue->cMilliesInterval = cMilliesInterval;
     172            else
    118173            {
    119174                AssertMsgFailed(("TMTimerSetMillies failed rc=%Rrc\n", rc));
    120175                int rc2 = TMR3TimerDestroy(pVM, pQueue->hTimer); AssertRC(rc2);
     176                pQueue->hTimer = NIL_TMTIMERHANDLE;
    121177            }
    122178        }
     
    125181        if (RT_FAILURE(rc))
    126182        {
    127             if (fRZEnabled)
    128                 MMHyperFree(pVM, pQueue);
    129             else
    130                 MMR3HeapFree(pQueue);
     183            if (!fRZEnabled)
     184                PDMR3QueueDestroy(pVM, hQueue, pvOwner);
     185            /* else: will clean up queue when VM is destroyed */
    131186            return rc;
    132187        }
    133 
    134         /*
    135          * Insert into the queue list for timer driven queues.
    136          */
    137         pdmLock(pVM);
    138         pQueue->pNext = pUVM->pdm.s.pQueuesTimer;
    139         pUVM->pdm.s.pQueuesTimer = pQueue;
    140         pdmUnlock(pVM);
    141     }
    142     else
    143     {
    144         /*
    145          * Insert into the queue list for forced action driven queues.
    146          * This is a FIFO, so insert at the end.
    147          */
    148         /** @todo we should add a priority to the queues so we don't have to rely on
    149          * the initialization order to deal with problems like @bugref{1605} (pgm/pcnet
    150          * deadlock caused by the critsect queue to be last in the chain).
    151          * - Update, the critical sections are no longer using queues, so this isn't a real
    152          *   problem any longer. The priority might be a nice feature for later though.
    153          */
    154         pdmLock(pVM);
    155         if (!pUVM->pdm.s.pQueuesForced)
    156             pUVM->pdm.s.pQueuesForced = pQueue;
    157         else
    158         {
    159             PPDMQUEUE pPrev = pUVM->pdm.s.pQueuesForced;
    160             while (pPrev->pNext)
    161                 pPrev = pPrev->pNext;
    162             pPrev->pNext = pQueue;
    163         }
    164         pdmUnlock(pVM);
    165188    }
    166189
     
    168191     * Register the statistics.
    169192     */
    170     STAMR3RegisterF(pVM, &pQueue->cbItem,               STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,        "Item size.",                       "/PDM/Queue/%s/cbItem",         pQueue->pszName);
    171     STAMR3RegisterF(pVM, &pQueue->cItems,               STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,        "Queue size.",                      "/PDM/Queue/%s/cItems",         pQueue->pszName);
    172     STAMR3RegisterF(pVM, &pQueue->StatAllocFailures,    STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,   "PDMQueueAlloc failures.",          "/PDM/Queue/%s/AllocFailures",  pQueue->pszName);
    173     STAMR3RegisterF(pVM, &pQueue->StatInsert,           STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,        "Calls to PDMQueueInsert.",         "/PDM/Queue/%s/Insert",         pQueue->pszName);
    174     STAMR3RegisterF(pVM, &pQueue->StatFlush,            STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,        "Calls to pdmR3QueueFlush.",        "/PDM/Queue/%s/Flush",          pQueue->pszName);
    175     STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers,   STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,   "Left over items after flush.",     "/PDM/Queue/%s/FlushLeftovers", pQueue->pszName);
     193    STAMR3RegisterF(pVM, &pQueue->cbItem,               STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
     194                    "Item size.",                       "/PDM/Queue/%s/cbItem",         pQueue->szName);
     195    STAMR3RegisterF(pVM, &pQueue->cItems,               STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
     196                    "Queue size.",                      "/PDM/Queue/%s/cItems",         pQueue->szName);
     197    STAMR3RegisterF(pVM, &pQueue->rcOkay,               STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
     198                    "Non-zero means queue is busted.",  "/PDM/Queue/%s/rcOkay",         pQueue->szName);
     199    STAMR3RegisterF(pVM, &pQueue->StatAllocFailures,    STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
     200                    "PDMQueueAlloc failures.",          "/PDM/Queue/%s/AllocFailures",  pQueue->szName);
     201    STAMR3RegisterF(pVM, &pQueue->StatInsert,           STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,
     202                    "Calls to PDMQueueInsert.",         "/PDM/Queue/%s/Insert",         pQueue->szName);
     203    STAMR3RegisterF(pVM, &pQueue->StatFlush,            STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,
     204                    "Calls to pdmR3QueueFlush.",        "/PDM/Queue/%s/Flush",          pQueue->szName);
     205    STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers,   STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
     206                    "Left over items after flush.",     "/PDM/Queue/%s/FlushLeftovers", pQueue->szName);
    176207#ifdef VBOX_WITH_STATISTICS
    177     STAMR3RegisterF(pVM, &pQueue->StatFlushPrf,         STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling pdmR3QueueFlush.",     "/PDM/Queue/%s/FlushPrf",       pQueue->pszName);
    178     STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,        "Pending items.",                   "/PDM/Queue/%s/Pending",        pQueue->pszName);
     208    STAMR3RegisterF(pVM, &pQueue->StatFlushPrf,         STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL,
     209                    "Profiling pdmR3QueueFlush.",       "/PDM/Queue/%s/FlushPrf",       pQueue->szName);
     210    STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32,     STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
     211                    "Pending items.",                   "/PDM/Queue/%s/Pending",        pQueue->szName);
    179212#endif
    180213
    181     *ppQueue = pQueue;
     214    *phQueue = hQueue;
    182215    return VINF_SUCCESS;
    183216}
     
    196229 * @param   pfnCallback         The consumer function.
    197230 * @param   fRZEnabled          Set if the queue must be usable from RC/R0.
    198  * @param   pszName             The queue name. Unique. Not copied.
    199  * @param   ppQueue             Where to store the queue handle on success.
     231 * @param   pszName             The queue name. Unique. Copied.
     232 * @param   phQueue             Where to store the queue handle on success.
    200233 * @thread  Emulation thread only.
    201234 */
    202 VMMR3_INT_DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
    203                                            PFNPDMQUEUEDEV pfnCallback, bool fRZEnabled, const char *pszName, PPDMQUEUE *ppQueue)
     235VMMR3_INT_DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, size_t cbItem, uint32_t cItems,
     236                                           uint32_t cMilliesInterval, PFNPDMQUEUEDEV pfnCallback,
     237                                           bool fRZEnabled, const char *pszName, PDMQUEUEHANDLE *phQueue)
    204238{
    205239    LogFlow(("PDMR3QueueCreateDevice: pDevIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
     
    210244     */
    211245    VM_ASSERT_EMT0(pVM);
    212     if (!pfnCallback)
    213     {
    214         AssertMsgFailed(("No consumer callback!\n"));
    215         return VERR_INVALID_PARAMETER;
    216     }
     246    AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
     247    AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
     248
     249    if (!(pDevIns->Internal.s.fIntFlags & PDMDEVINSINT_FLAGS_R0_ENABLED))
     250        fRZEnabled = false;
    217251
    218252    /*
    219253     * Create the queue.
    220254     */
    221     PPDMQUEUE pQueue;
    222     int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
     255    int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName,
     256                              PDMQUEUETYPE_DEV, pDevIns, (uintptr_t)pfnCallback, phQueue);
    223257    if (RT_SUCCESS(rc))
    224     {
    225         pQueue->enmType = PDMQUEUETYPE_DEV;
    226         pQueue->u.Dev.pDevIns = pDevIns;
    227         pQueue->u.Dev.pfnCallback = pfnCallback;
    228 
    229         *ppQueue = pQueue;
    230         Log(("PDM: Created device queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
    231              pQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDevIns));
    232     }
     258        Log(("PDM: Created device queue %#RX64; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
     259             *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDevIns));
    233260    return rc;
    234261}
     
    246273 *                              If 0 then the emulation thread will be notified whenever an item arrives.
    247274 * @param   pfnCallback         The consumer function.
    248  * @param   pszName             The queue name. Unique. Not copied.
    249  * @param   ppQueue             Where to store the queue handle on success.
     275 * @param   pszName             The queue name. Unique. Copied.
     276 * @param   phQueue             Where to store the queue handle on success.
    250277 * @thread  Emulation thread only.
    251278 */
    252279VMMR3_INT_DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
    253                                            PFNPDMQUEUEDRV pfnCallback, const char *pszName, PPDMQUEUE *ppQueue)
     280                                           PFNPDMQUEUEDRV pfnCallback, const char *pszName, PDMQUEUEHANDLE *phQueue)
    254281{
    255282    LogFlow(("PDMR3QueueCreateDriver: pDrvIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
     
    261288    VM_ASSERT_EMT0(pVM);
    262289    AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
     290    AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
    263291
    264292    /*
    265293     * Create the queue.
    266294     */
    267     PPDMQUEUE pQueue;
    268     int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
     295    int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false /*fRZEnabled*/, pszName,
     296                              PDMQUEUETYPE_DRV, pDrvIns, (uintptr_t)pfnCallback, phQueue);
    269297    if (RT_SUCCESS(rc))
    270     {
    271         pQueue->enmType = PDMQUEUETYPE_DRV;
    272         pQueue->u.Drv.pDrvIns = pDrvIns;
    273         pQueue->u.Drv.pfnCallback = pfnCallback;
    274 
    275         *ppQueue = pQueue;
    276         Log(("PDM: Created driver queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
    277              pQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDrvIns));
    278     }
     298        Log(("PDM: Created driver queue %#RX64; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
     299             *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDrvIns));
    279300    return rc;
    280301}
     
    292313 * @param   pfnCallback         The consumer function.
    293314 * @param   fRZEnabled          Set if the queue must be usable from RC/R0.
    294  * @param   pszName             The queue name. Unique. Not copied.
    295  * @param   ppQueue             Where to store the queue handle on success.
     315 * @param   pszName             The queue name. Unique. Copied.
     316 * @param   phQueue             Where to store the queue handle on success.
    296317 * @thread  Emulation thread only.
    297318 */
    298319VMMR3_INT_DECL(int) PDMR3QueueCreateInternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
    299                                              PFNPDMQUEUEINT pfnCallback, bool fRZEnabled, const char *pszName, PPDMQUEUE *ppQueue)
     320                                             PFNPDMQUEUEINT pfnCallback, bool fRZEnabled,
     321                                             const char *pszName, PDMQUEUEHANDLE *phQueue)
    300322{
    301323    LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
     
    311333     * Create the queue.
    312334     */
    313     PPDMQUEUE pQueue;
    314     int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
     335    int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName,
     336                              PDMQUEUETYPE_INTERNAL, pVM, (uintptr_t)pfnCallback, phQueue);
    315337    if (RT_SUCCESS(rc))
    316     {
    317         pQueue->enmType = PDMQUEUETYPE_INTERNAL;
    318         pQueue->u.Int.pfnCallback = pfnCallback;
    319 
    320         *ppQueue = pQueue;
    321338        Log(("PDM: Created internal queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p\n",
    322              pQueue, cbItem, cItems, cMilliesInterval, pfnCallback));
    323     }
     339             *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback));
    324340    return rc;
    325341}
     
    338354 * @param   pvUser              The user argument to the consumer function.
    339355 * @param   pszName             The queue name. Unique. Not copied.
    340  * @param   ppQueue             Where to store the queue handle on success.
     356 * @param   phQueue             Where to store the queue handle on success.
    341357 * @thread  Emulation thread only.
    342358 */
    343 VMMR3_INT_DECL(int) PDMR3QueueCreateExternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
    344                                              PFNPDMQUEUEEXT pfnCallback, void *pvUser, const char *pszName, PPDMQUEUE *ppQueue)
    345 {
    346     LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n", cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
     359VMMR3DECL(int) PDMR3QueueCreateExternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
     360                                        PFNPDMQUEUEEXT pfnCallback, void *pvUser,
     361                                        const char *pszName, PDMQUEUEHANDLE *phQueue)
     362{
     363    LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
     364             cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
    347365
    348366    /*
     
    355373     * Create the queue.
    356374     */
     375    int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false /*fRZEnabled*/, pszName,
     376                              PDMQUEUETYPE_EXTERNAL, pvUser, (uintptr_t)pfnCallback, phQueue);
     377    if (RT_SUCCESS(rc))
     378        Log(("PDM: Created external queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pvUser=%p\n",
     379             *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pvUser));
     380    return rc;
     381}
     382
     383
     384/**
     385 * Destroy a queue.
     386 *
     387 * @returns VBox status code.
     388 * @param   pVM         Pointer to the cross context VM structure.
     389 * @param   hQueue      Handle to the queue that should be destroyed.
     390 * @param   pvOwner     The owner address.
     391 * @thread  EMT(0)
     392 * @note    Externally visible mainly for testing purposes.
     393 */
     394VMMR3DECL(int) PDMR3QueueDestroy(PVM pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
     395{
     396    LogFlow(("PDMR3QueueDestroy: hQueue=%p pvOwner=%p\n", hQueue, pvOwner));
     397
     398    /*
     399     * Validate input.
     400     */
     401    VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* serialization */
     402    if (hQueue == NIL_PDMQUEUEHANDLE)
     403        return VINF_SUCCESS;
     404
    357405    PPDMQUEUE pQueue;
    358     int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
    359     if (RT_SUCCESS(rc))
    360     {
    361         pQueue->enmType = PDMQUEUETYPE_EXTERNAL;
    362         pQueue->u.Ext.pvUser = pvUser;
    363         pQueue->u.Ext.pfnCallback = pfnCallback;
    364 
    365         *ppQueue = pQueue;
    366         Log(("PDM: Created external queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pvUser=%p\n",
    367              pQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pvUser));
    368     }
    369     return rc;
    370 }
    371 
    372 
    373 /**
    374  * Destroy a queue.
    375  *
    376  * @returns VBox status code.
    377  * @param   pQueue      Queue to destroy.
    378  * @thread  Emulation thread only.
    379  */
    380 VMMR3_INT_DECL(int) PDMR3QueueDestroy(PPDMQUEUE pQueue)
    381 {
    382     LogFlow(("PDMR3QueueDestroy: pQueue=%p\n", pQueue));
    383 
    384     /*
    385      * Validate input.
    386      */
    387     if (!pQueue)
    388         return VERR_INVALID_PARAMETER;
    389     Assert(pQueue && pQueue->pVMR3);
    390     PVM     pVM  = pQueue->pVMR3;
    391     PUVM    pUVM = pVM->pUVM;
    392 
    393     pdmLock(pVM);
    394 
    395     /*
    396      * Unlink it.
    397      */
    398     if (pQueue->hTimer != NIL_TMTIMERHANDLE)
    399     {
    400         if (pUVM->pdm.s.pQueuesTimer != pQueue)
    401         {
    402             PPDMQUEUE pCur = pUVM->pdm.s.pQueuesTimer;
    403             while (pCur)
    404             {
    405                 if (pCur->pNext == pQueue)
    406                 {
    407                     pCur->pNext = pQueue->pNext;
    408                     break;
    409                 }
    410                 pCur = pCur->pNext;
    411             }
    412             AssertMsg(pCur, ("Didn't find the queue!\n"));
     406    bool      fRZEnabled = false;
     407    if (hQueue < RT_ELEMENTS(pVM->pdm.s.apRing0Queues))
     408    {
     409        AssertReturn(hQueue < pVM->pdm.s.cRing0Queues, VERR_INVALID_HANDLE);
     410        pQueue = pVM->pdm.s.apRing0Queues[hQueue];
     411        AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
     412        AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE);
     413        AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INVALID_HANDLE);
     414
     415        /* Lazy bird: Cannot dynamically delete ring-0 capable queues. */
     416        AssertFailedReturn(VERR_NOT_SUPPORTED);
     417    }
     418    else
     419    {
     420        hQueue -= RT_ELEMENTS(pVM->pdm.s.apRing0Queues);
     421        AssertReturn(hQueue < pVM->pdm.s.cRing3Queues, VERR_INVALID_HANDLE);
     422        pQueue = pVM->pdm.s.papRing3Queues[hQueue];
     423        AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
     424        AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE);
     425        AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INVALID_HANDLE);
     426
     427        /* Enter the lock here to serialize with other EMTs traversing the handles. */
     428        pdmLock(pVM);
     429        pVM->pdm.s.papRing3Queues[hQueue] = NULL;
     430        if (hQueue + 1 == pVM->pdm.s.cRing3Queues)
     431        {
     432            while (hQueue > 0 && pVM->pdm.s.papRing3Queues[hQueue - 1] == NULL)
     433                hQueue--;
     434            pVM->pdm.s.cRing3Queues = hQueue;
    413435        }
    414         else
    415             pUVM->pdm.s.pQueuesTimer = pQueue->pNext;
    416     }
    417     else
    418     {
    419         if (pUVM->pdm.s.pQueuesForced != pQueue)
    420         {
    421             PPDMQUEUE pCur = pUVM->pdm.s.pQueuesForced;
    422             while (pCur)
    423             {
    424                 if (pCur->pNext == pQueue)
    425                 {
    426                     pCur->pNext = pQueue->pNext;
    427                     break;
    428                 }
    429                 pCur = pCur->pNext;
    430             }
    431             AssertMsg(pCur, ("Didn't find the queue!\n"));
    432         }
    433         else
    434             pUVM->pdm.s.pQueuesForced = pQueue->pNext;
    435     }
    436     pQueue->pNext = NULL;
    437     pQueue->pVMR3 = NULL;
    438     pdmUnlock(pVM);
     436        pQueue->u32Magic = PDMQUEUE_MAGIC_DEAD;
     437        pdmUnlock(pVM);
     438    }
    439439
    440440    /*
    441441     * Deregister statistics.
    442442     */
    443     STAMR3DeregisterF(pVM->pUVM, "/PDM/Queue/%s/cbItem", pQueue->pszName);
     443    STAMR3DeregisterF(pVM->pUVM, "/PDM/Queue/%s/*", pQueue->szName);
    444444
    445445    /*
     
    451451        pQueue->hTimer = NIL_TMTIMERHANDLE;
    452452    }
    453     if (pQueue->pVMR0)
    454     {
    455         pQueue->pVMR0 = NIL_RTR0PTR;
    456         MMHyperFree(pVM, pQueue);
    457     }
    458     else
    459         MMR3HeapFree(pQueue);
     453    if (!fRZEnabled)
     454        RTMemPageFree(pQueue, pQueue->offItems + pQueue->cbItem * pQueue->cItems);
     455
     456    return VINF_SUCCESS;
     457}
     458
     459
     460/**
     461 * Destroy a all queues with a given owner.
     462 *
     463 * @returns VBox status code.
     464 * @param   pVM         The cross context VM structure.
     465 * @param   pvOwner     The owner pointer.
     466 * @param   enmType     Owner type.
     467 * @thread  EMT(0)
     468 */
     469static int pdmR3QueueDestroyByOwner(PVM pVM, void *pvOwner, PDMQUEUETYPE enmType)
     470{
     471    LogFlow(("pdmR3QueueDestroyByOwner: pvOwner=%p enmType=%d\n", pvOwner, enmType));
     472
     473    /*
     474     * Validate input.
     475     */
     476    AssertPtrReturn(pvOwner, VERR_INVALID_PARAMETER);
     477    AssertReturn(pvOwner != pVM, VERR_INVALID_PARAMETER);
     478    VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* serialization */
     479
     480    /*
     481     * Scan and destroy.
     482     */
     483    uint32_t i = pVM->pdm.s.cRing0Queues;
     484    while (i-- > 0)
     485    {
     486        PPDMQUEUE pQueue = pVM->pdm.s.apRing0Queues[i];
     487        if (   pQueue
     488            && pQueue->u.Gen.pvOwner == pvOwner
     489            && pQueue->enmType == enmType)
     490        {
     491            /* Not supported at runtime. */
     492            VM_ASSERT_STATE_RETURN(pVM, VMSTATE_DESTROYING, VERR_WRONG_ORDER);
     493        }
     494    }
     495
     496    i = pVM->pdm.s.cRing3Queues;
     497    while (i-- > 0)
     498    {
     499        PPDMQUEUE pQueue = pVM->pdm.s.papRing3Queues[i];
     500        if (   pQueue
     501            && pQueue->u.Gen.pvOwner == pvOwner
     502            && pQueue->enmType       == enmType)
     503            PDMR3QueueDestroy(pVM, i + RT_ELEMENTS(pVM->pdm.s.apRing0Queues), pvOwner);
     504    }
    460505
    461506    return VINF_SUCCESS;
     
    469514 * @param   pVM         The cross context VM structure.
    470515 * @param   pDevIns     Device instance.
    471  * @thread  Emulation thread only.
     516 * @thread  EMT(0)
    472517 */
    473518VMMR3_INT_DECL(int) PDMR3QueueDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
    474519{
    475520    LogFlow(("PDMR3QueueDestroyDevice: pDevIns=%p\n", pDevIns));
    476 
    477     /*
    478      * Validate input.
    479      */
    480     if (!pDevIns)
    481         return VERR_INVALID_PARAMETER;
    482 
    483     PUVM pUVM = pVM->pUVM;
    484     pdmLock(pVM);
    485 
    486     /*
    487      * Unlink it.
    488      */
    489     PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
    490     PPDMQUEUE pQueue     = pUVM->pdm.s.pQueuesForced;
    491     do
    492     {
    493         while (pQueue)
    494         {
    495             if (    pQueue->enmType == PDMQUEUETYPE_DEV
    496                 &&  pQueue->u.Dev.pDevIns == pDevIns)
    497             {
    498                 PPDMQUEUE pQueueDestroy = pQueue;
    499                 pQueue = pQueue->pNext;
    500                 int rc = PDMR3QueueDestroy(pQueueDestroy);
    501                 AssertRC(rc);
    502             }
    503             else
    504                 pQueue = pQueue->pNext;
    505         }
    506 
    507         /* next queue list */
    508         pQueue = pQueueNext;
    509         pQueueNext = NULL;
    510     } while (pQueue);
    511 
    512     pdmUnlock(pVM);
    513     return VINF_SUCCESS;
     521    return pdmR3QueueDestroyByOwner(pVM, pDevIns, PDMQUEUETYPE_DEV);
    514522}
    515523
     
    521529 * @param   pVM         The cross context VM structure.
    522530 * @param   pDrvIns     Driver instance.
    523  * @thread  Emulation thread only.
     531 * @thread  EMT(0)
    524532 */
    525533VMMR3_INT_DECL(int) PDMR3QueueDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
    526534{
    527535    LogFlow(("PDMR3QueueDestroyDriver: pDrvIns=%p\n", pDrvIns));
    528 
    529     /*
    530      * Validate input.
    531      */
    532     if (!pDrvIns)
    533         return VERR_INVALID_PARAMETER;
    534 
    535     PUVM pUVM = pVM->pUVM;
    536     pdmLock(pVM);
    537 
    538     /*
    539      * Unlink it.
    540      */
    541     PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
    542     PPDMQUEUE pQueue     = pUVM->pdm.s.pQueuesForced;
    543     do
    544     {
    545         while (pQueue)
    546         {
    547             if (    pQueue->enmType == PDMQUEUETYPE_DRV
    548                 &&  pQueue->u.Drv.pDrvIns == pDrvIns)
     536    return pdmR3QueueDestroyByOwner(pVM, pDrvIns, PDMQUEUETYPE_DRV);
     537}
     538
     539
     540/**
     541 * Free an item.
     542 *
     543 * @param   pQueue  The queue.
     544 * @param   pItem   The item.
     545 */
     546DECLINLINE(void) pdmR3QueueFreeItem(PPDMQUEUE pQueue, uint8_t *pbItems, uint32_t cbItem, PPDMQUEUEITEMCORE pItem)
     547{
     548    pItem->u64View = UINT64_C(0xfeedfeedfeedfeed);
     549
     550    uintptr_t const offItem = (uintptr_t)pItem - (uintptr_t)pbItems;
     551    uintptr_t const iItem   = offItem / cbItem;
     552    Assert(!(offItem % cbItem));
     553    Assert(iItem < pQueue->cItems);
     554    AssertReturnVoidStmt(ASMAtomicBitTestAndSet(pQueue->bmAlloc, iItem) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_4);
     555    STAM_STATS({ ASMAtomicDecU32(&pQueue->cStatPending); });
     556}
     557
     558
     559
     560/**
     561 * Process pending items in one queue.
     562 *
     563 * @returns VBox status code.
     564 * @param   pVM     The cross context VM structure.
     565 * @param   pQueue  The queue needing flushing.
     566 */
     567static int pdmR3QueueFlush(PVM pVM, PPDMQUEUE pQueue)
     568{
     569    STAM_PROFILE_START(&pQueue->StatFlushPrf,p);
     570
     571    uint32_t const  cbItem  = pQueue->cbItem;
     572    uint32_t const  cItems  = pQueue->cItems;
     573    uint8_t * const pbItems = (uint8_t *)pQueue + pQueue->offItems;
     574
     575    /*
     576     * Get the list and reverse it into a pointer list (inserted in LIFO order to avoid locking).
     577     */
     578    uint32_t          cPending = 0;
     579    PPDMQUEUEITEMCORE pHead    = NULL;
     580    {
     581        uint32_t iCur = ASMAtomicXchgU32(&pQueue->iPending, UINT32_MAX);
     582        do
     583        {
     584            AssertMsgReturn(iCur < cItems, ("%#x vs %#x\n", iCur, cItems), pQueue->rcOkay = VERR_INTERNAL_ERROR_5);
     585            AssertReturn(ASMBitTest(pQueue->bmAlloc, iCur) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_3);
     586            PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)&pbItems[iCur * cbItem];
     587
     588            iCur = pCur->iNext;
     589            ASMCompilerBarrier(); /* paranoia */
     590            pCur->pNext = pHead;
     591            pHead = pCur;
     592            cPending++;
     593        } while (iCur != UINT32_MAX);
     594    }
     595    RT_NOREF(cPending);
     596
     597    /*
     598     * Feed the items to the consumer function.
     599     */
     600    Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pHead=%p cItems=%u\n", pQueue, pQueue->enmType, pHead, cPending));
     601    switch (pQueue->enmType)
     602    {
     603        case PDMQUEUETYPE_DEV:
     604            while (pHead)
    549605            {
    550                 PPDMQUEUE pQueueDestroy = pQueue;
    551                 pQueue = pQueue->pNext;
    552                 int rc = PDMR3QueueDestroy(pQueueDestroy);
    553                 AssertRC(rc);
     606                if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pHead))
     607                    break;
     608                PPDMQUEUEITEMCORE pFree = pHead;
     609                pHead = pHead->pNext;
     610                ASMCompilerBarrier(); /* paranoia */
     611                pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
    554612            }
    555             else
    556                 pQueue = pQueue->pNext;
     613            break;
     614
     615        case PDMQUEUETYPE_DRV:
     616            while (pHead)
     617            {
     618                if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pHead))
     619                    break;
     620                PPDMQUEUEITEMCORE pFree = pHead;
     621                pHead = pHead->pNext;
     622                ASMCompilerBarrier(); /* paranoia */
     623                pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
     624            }
     625            break;
     626
     627        case PDMQUEUETYPE_INTERNAL:
     628            while (pHead)
     629            {
     630                if (!pQueue->u.Int.pfnCallback(pVM, pHead))
     631                    break;
     632                PPDMQUEUEITEMCORE pFree = pHead;
     633                pHead = pHead->pNext;
     634                ASMCompilerBarrier(); /* paranoia */
     635                pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
     636            }
     637            break;
     638
     639        case PDMQUEUETYPE_EXTERNAL:
     640            while (pHead)
     641            {
     642                if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pHead))
     643                    break;
     644                PPDMQUEUEITEMCORE pFree = pHead;
     645                pHead = pHead->pNext;
     646                ASMCompilerBarrier(); /* paranoia */
     647                pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
     648            }
     649            break;
     650
     651        default:
     652            AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
     653            break;
     654    }
     655
     656    /*
     657     * Success?
     658     */
     659    if (!pHead)
     660    { /* likely */ }
     661    else
     662    {
     663        /*
     664         * Reverse the list and turn it back into index chain.
     665         */
     666        uint32_t iPendingHead = UINT32_MAX;
     667        do
     668        {
     669            PPDMQUEUEITEMCORE pInsert = pHead;
     670            pHead = pHead->pNext;
     671            ASMCompilerBarrier(); /* paranoia */
     672            pInsert->iNext = iPendingHead;
     673            iPendingHead = ((uintptr_t)pInsert - (uintptr_t)pbItems) / cbItem;
     674        } while (pHead);
     675
     676        /*
     677         * Insert the list at the tail of the pending list.  If someone races
     678         * us there, we have to join the new LIFO with the old.
     679         */
     680        for (;;)
     681        {
     682            if (ASMAtomicCmpXchgU32(&pQueue->iPending, iPendingHead, UINT32_MAX))
     683                break;
     684
     685            uint32_t const iNewPending = ASMAtomicXchgU32(&pQueue->iPending, UINT32_MAX);
     686            if (iNewPending != UINT32_MAX)
     687            {
     688                /* Find the last entry and chain iPendingHead onto it. */
     689                uint32_t iCur = iNewPending;
     690                for (;;)
     691                {
     692                    AssertReturn(iCur < cItems, pQueue->rcOkay = VERR_INTERNAL_ERROR_2);
     693                    AssertReturn(ASMBitTest(pQueue->bmAlloc, iCur) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_3);
     694                    PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)&pbItems[iCur * cbItem];
     695                    iCur = pCur->iNext;
     696                    if (iCur == UINT32_MAX)
     697                    {
     698                        pCur->iNext = iPendingHead;
     699                        break;
     700                    }
     701                }
     702
     703                iPendingHead = iNewPending;
     704            }
    557705        }
    558706
    559         /* next queue list */
    560         pQueue = pQueueNext;
    561         pQueueNext = NULL;
    562     } while (pQueue);
    563 
    564     pdmUnlock(pVM);
     707        STAM_REL_COUNTER_INC(&pQueue->StatFlushLeftovers);
     708    }
     709
     710    STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
    565711    return VINF_SUCCESS;
    566712}
     
    573719 * @param   pVM     The cross context VM structure.
    574720 * @thread  Emulation thread only.
    575  */
    576 VMMR3_INT_DECL(void) PDMR3QueueFlushAll(PVM pVM)
     721 * @note    Internal, but exported for use in the testcase.
     722 */
     723VMMR3DECL(void) PDMR3QueueFlushAll(PVM pVM)
    577724{
    578725    VM_ASSERT_EMT(pVM);
     
    593740        ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT);
    594741
    595         for (PPDMQUEUE pCur = pVM->pUVM->pdm.s.pQueuesForced; pCur; pCur = pCur->pNext)
    596             if (    pCur->pPendingR3
    597                 ||  pCur->pPendingR0)
    598                 pdmR3QueueFlush(pCur);
     742        /* Scan the ring-0 queues: */
     743        size_t i = pVM->pdm.s.cRing0Queues;
     744        while (i-- > 0)
     745        {
     746            PPDMQUEUE pQueue = pVM->pdm.s.apRing0Queues[i];
     747            if (   pQueue
     748                && pQueue->iPending != UINT32_MAX
     749                && pQueue->hTimer == NIL_TMTIMERHANDLE
     750                && pQueue->rcOkay == VINF_SUCCESS)
     751                pdmR3QueueFlush(pVM, pQueue);
     752        }
     753
     754        /* Scan the ring-3 queues: */
     755/** @todo Deal with destroy concurrency issues. */
     756        i = pVM->pdm.s.cRing3Queues;
     757        while (i-- > 0)
     758        {
     759            PPDMQUEUE pQueue = pVM->pdm.s.papRing3Queues[i];
     760            if (   pQueue
     761                && pQueue->iPending != UINT32_MAX
     762                && pQueue->hTimer == NIL_TMTIMERHANDLE
     763                && pQueue->rcOkay == VINF_SUCCESS)
     764                pdmR3QueueFlush(pVM, pQueue);
     765        }
    599766
    600767        ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT);
     
    609776
    610777
    611 /**
    612  * Process pending items in one queue.
    613  *
    614  * @returns Success indicator.
    615  *          If false the item the consumer said "enough!".
    616  * @param   pQueue  The queue.
    617  */
    618 static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
    619 {
    620     STAM_PROFILE_START(&pQueue->StatFlushPrf,p);
    621 
    622     /*
    623      * Get the lists.
    624      */
    625     PPDMQUEUEITEMCORE pItems   = ASMAtomicXchgPtrT(&pQueue->pPendingR3, NULL, PPDMQUEUEITEMCORE);
    626     RTR0PTR           pItemsR0 = ASMAtomicXchgR0Ptr(&pQueue->pPendingR0, NIL_RTR0PTR);
    627 
    628     AssertMsgReturn(   pItemsR0
    629                     || pItems,
    630                     ("Someone is racing us? This shouldn't happen!\n"),
    631                     true);
    632 
    633     /*
    634      * Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
    635      */
    636     PPDMQUEUEITEMCORE pCur = pItems;
    637     pItems = NULL;
    638     while (pCur)
    639     {
    640         PPDMQUEUEITEMCORE pInsert = pCur;
    641         pCur = pCur->pNextR3;
    642         pInsert->pNextR3 = pItems;
    643         pItems = pInsert;
    644     }
    645 
    646     /*
    647      * Do the same for any pending R0 items.
    648      */
    649     while (pItemsR0)
    650     {
    651         PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperR0ToR3(pQueue->pVMR3, pItemsR0);
    652         pItemsR0 = pInsert->pNextR0;
    653         pInsert->pNextR0 = NIL_RTR0PTR;
    654         pInsert->pNextR3 = pItems;
    655         pItems = pInsert;
    656     }
    657 
    658     /*
    659      * Feed the items to the consumer function.
    660      */
    661     Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pItems=%p\n", pQueue, pQueue->enmType, pItems));
    662     switch (pQueue->enmType)
    663     {
    664         case PDMQUEUETYPE_DEV:
    665             while (pItems)
    666             {
    667                 if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pItems))
    668                     break;
    669                 pCur = pItems;
    670                 pItems = pItems->pNextR3;
    671                 pdmR3QueueFreeItem(pQueue, pCur);
    672             }
    673             break;
    674 
    675         case PDMQUEUETYPE_DRV:
    676             while (pItems)
    677             {
    678                 if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pItems))
    679                     break;
    680                 pCur = pItems;
    681                 pItems = pItems->pNextR3;
    682                 pdmR3QueueFreeItem(pQueue, pCur);
    683             }
    684             break;
    685 
    686         case PDMQUEUETYPE_INTERNAL:
    687             while (pItems)
    688             {
    689                 if (!pQueue->u.Int.pfnCallback(pQueue->pVMR3, pItems))
    690                     break;
    691                 pCur = pItems;
    692                 pItems = pItems->pNextR3;
    693                 pdmR3QueueFreeItem(pQueue, pCur);
    694             }
    695             break;
    696 
    697         case PDMQUEUETYPE_EXTERNAL:
    698             while (pItems)
    699             {
    700                 if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pItems))
    701                     break;
    702                 pCur = pItems;
    703                 pItems = pItems->pNextR3;
    704                 pdmR3QueueFreeItem(pQueue, pCur);
    705             }
    706             break;
    707 
    708         default:
    709             AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
    710             break;
    711     }
    712 
    713     /*
    714      * Success?
    715      */
    716     if (pItems)
    717     {
    718         /*
    719          * Reverse the list.
    720          */
    721         pCur = pItems;
    722         pItems = NULL;
    723         while (pCur)
    724         {
    725             PPDMQUEUEITEMCORE pInsert = pCur;
    726             pCur = pInsert->pNextR3;
    727             pInsert->pNextR3 = pItems;
    728             pItems = pInsert;
    729         }
    730 
    731         /*
    732          * Insert the list at the tail of the pending list.
    733          */
    734         for (;;)
    735         {
    736             if (ASMAtomicCmpXchgPtr(&pQueue->pPendingR3, pItems, NULL))
    737                 break;
    738             PPDMQUEUEITEMCORE pPending = ASMAtomicXchgPtrT(&pQueue->pPendingR3, NULL, PPDMQUEUEITEMCORE);
    739             if (pPending)
    740             {
    741                 pCur = pPending;
    742                 while (pCur->pNextR3)
    743                     pCur = pCur->pNextR3;
    744                 pCur->pNextR3 = pItems;
    745                 pItems = pPending;
    746             }
    747         }
    748 
    749         STAM_REL_COUNTER_INC(&pQueue->StatFlushLeftovers);
    750         STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
    751         return false;
    752     }
    753 
    754     STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
    755     return true;
    756 }
    757 
    758 
    759 /**
    760  * Free an item.
    761  *
    762  * @param   pQueue  The queue.
    763  * @param   pItem   The item.
    764  */
    765 DECLINLINE(void) pdmR3QueueFreeItem(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
    766 {
    767     VM_ASSERT_EMT(pQueue->pVMR3);
    768 
    769     int i = pQueue->iFreeHead;
    770     int iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
    771 
    772     pQueue->aFreeItems[i].pItemR3 = pItem;
    773     if (pQueue->pVMR0)
    774         pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pQueue->pVMR3, pItem);
    775 
    776     if (!ASMAtomicCmpXchgU32(&pQueue->iFreeHead, iNext, i))
    777         AssertMsgFailed(("huh? i=%d iNext=%d iFreeHead=%d iFreeTail=%d\n", i, iNext, pQueue->iFreeHead, pQueue->iFreeTail));
    778     STAM_STATS({ ASMAtomicDecU32(&pQueue->cStatPending); });
    779 }
    780 
    781778
    782779/**
     
    788785    Assert(hTimer == pQueue->hTimer);
    789786
    790     if (   pQueue->pPendingR3
    791         || pQueue->pPendingR0)
    792         pdmR3QueueFlush(pQueue);
     787    if (pQueue->iPending != UINT32_MAX)
     788        pdmR3QueueFlush(pVM, pQueue);
     789
    793790    int rc = TMTimerSetMillies(pVM, hTimer, pQueue->cMilliesInterval);
    794791    AssertRC(rc);
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette