PDMQueue.cpp revision ec8d8f1b8459c8db1da05787727d7cac048ef11a
/* $Id$ */
/** @file
* PDM Queue - Transport data and tasks to EMT and R3.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PDM_QUEUE
#include "PDMInternal.h"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Internal worker for the queue creation apis.
*
* @returns VBox status.
* @param pVM VM handle.
* @param cbItem Item size.
* @param cItems Number of items.
* @param cMilliesInterval Number of milliseconds between polling the queue.
* If 0 then the emulation thread will be notified whenever an item arrives.
* @param fRZEnabled Set if the queue will be used from RC/R0 and need to be allocated from the hyper heap.
* @param pszName The queue name. Unique. Not copied.
* @param ppQueue Where to store the queue handle.
*/
static int pdmR3QueueCreate(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval, bool fRZEnabled,
{
/*
* Validate input.
*/
if (cbItem < sizeof(PDMQUEUEITEMCORE))
{
return VERR_INVALID_PARAMETER;
}
{
return VERR_INVALID_PARAMETER;
}
/*
* Align the item size and calculate the structure size.
*/
size_t cb = cbItem * cItems + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16);
int rc;
if (fRZEnabled)
else
if (RT_FAILURE(rc))
return rc;
/*
* Initialize the data fields.
*/
//pQueue->pTimer = NULL;
//pQueue->pPendingR3 = NULL;
//pQueue->pPendingR0 = NULL;
//pQueue->pPendingRC = NULL;
//pQueue->iFreeTail = 0;
PPDMQUEUEITEMCORE pItem = (PPDMQUEUEITEMCORE)((char *)pQueue + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16));
{
if (fRZEnabled)
{
}
}
/*
* Create timer?
*/
if (cMilliesInterval)
{
rc = TMR3TimerCreateInternal(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, "Queue timer", &pQueue->pTimer);
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
{
}
}
else
if (RT_FAILURE(rc))
{
if (fRZEnabled)
else
return rc;
}
/*
* Insert into the queue list for timer driven queues.
*/
}
else
{
/*
* Insert into the queue list for forced action driven queues.
* This is a FIFO, so insert at the end.
*/
/** @todo we should add a priority to the queues so we don't have to rely on
* caused by the critsect queue to be last in the chain).
* - Update, the critical sections are no longer using queues, so this isn't a real
* problem any longer. The priority might be a nice feature for later though.
*/
else
{
}
}
/*
* Register the statistics.
*/
STAMR3RegisterF(pVM, &pQueue->cbItem, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Item size.", "/PDM/Queue/%s/cbItem", pQueue->pszName);
STAMR3RegisterF(pVM, &pQueue->cItems, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Queue size.", "/PDM/Queue/%s/cItems", pQueue->pszName);
STAMR3RegisterF(pVM, &pQueue->StatAllocFailures, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "PDMQueueAlloc failures.", "/PDM/Queue/%s/AllocFailures", pQueue->pszName);
STAMR3RegisterF(pVM, &pQueue->StatInsert, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Calls to PDMQueueInsert.", "/PDM/Queue/%s/Insert", pQueue->pszName);
STAMR3RegisterF(pVM, &pQueue->StatFlush, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Calls to pdmR3QueueFlush.", "/PDM/Queue/%s/Flush", pQueue->pszName);
STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Left over items after flush.", "/PDM/Queue/%s/FlushLeftovers", pQueue->pszName);
#ifdef VBOX_WITH_STATISTICS
STAMR3RegisterF(pVM, &pQueue->StatFlushPrf, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Profiling pdmR3QueueFlush.", "/PDM/Queue/%s/FlushPrf", pQueue->pszName);
STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Pending items.", "/PDM/Queue/%s/Pending", pQueue->pszName);
#endif
return VINF_SUCCESS;
}
/**
* Create a queue with a device owner.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param pDevIns Device instance.
* @param cbItem Size a queue item.
* @param cItems Number of items in the queue.
* @param cMilliesInterval Number of milliseconds between polling the queue.
* If 0 then the emulation thread will be notified whenever an item arrives.
* @param pfnCallback The consumer function.
* @param pszName The queue name. Unique. Not copied.
* @param ppQueue Where to store the queue handle on success.
* @thread Emulation thread only.
*/
VMMR3DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
{
LogFlow(("PDMR3QueueCreateDevice: pDevIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
/*
* Validate input.
*/
if (!pfnCallback)
{
AssertMsgFailed(("No consumer callback!\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Create the queue.
*/
if (RT_SUCCESS(rc))
{
Log(("PDM: Created device queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
}
return rc;
}
/**
* Create a queue with a driver owner.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param pDrvIns Driver instance.
* @param cbItem Size a queue item.
* @param cItems Number of items in the queue.
* @param cMilliesInterval Number of milliseconds between polling the queue.
* If 0 then the emulation thread will be notified whenever an item arrives.
* @param pfnCallback The consumer function.
* @param pszName The queue name. Unique. Not copied.
* @param ppQueue Where to store the queue handle on success.
* @thread Emulation thread only.
*/
VMMR3DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
{
LogFlow(("PDMR3QueueCreateDriver: pDrvIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
/*
* Validate input.
*/
if (!pfnCallback)
{
AssertMsgFailed(("No consumer callback!\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Create the queue.
*/
if (RT_SUCCESS(rc))
{
Log(("PDM: Created driver queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
}
return rc;
}
/**
* Create a queue with an internal owner.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param cbItem Size a queue item.
* @param cItems Number of items in the queue.
* @param cMilliesInterval Number of milliseconds between polling the queue.
* If 0 then the emulation thread will be notified whenever an item arrives.
* @param pfnCallback The consumer function.
* @param pszName The queue name. Unique. Not copied.
* @param ppQueue Where to store the queue handle on success.
* @thread Emulation thread only.
*/
VMMR3DECL(int) PDMR3QueueCreateInternal(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
{
LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
/*
* Validate input.
*/
if (!pfnCallback)
{
AssertMsgFailed(("No consumer callback!\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Create the queue.
*/
if (RT_SUCCESS(rc))
{
Log(("PDM: Created internal queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p\n",
}
return rc;
}
/**
* Create a queue with an external owner.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param cbItem Size a queue item.
* @param cItems Number of items in the queue.
* @param cMilliesInterval Number of milliseconds between polling the queue.
* If 0 then the emulation thread will be notified whenever an item arrives.
* @param pfnCallback The consumer function.
* @param pvUser The user argument to the consumer function.
* @param pszName The queue name. Unique. Not copied.
* @param ppQueue Where to store the queue handle on success.
* @thread Emulation thread only.
*/
VMMR3DECL(int) PDMR3QueueCreateExternal(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval, PFNPDMQUEUEEXT pfnCallback, void *pvUser, const char *pszName, PPDMQUEUE *ppQueue)
{
LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n", cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
/*
* Validate input.
*/
if (!pfnCallback)
{
AssertMsgFailed(("No consumer callback!\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Create the queue.
*/
if (RT_SUCCESS(rc))
{
Log(("PDM: Created external queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pvUser=%p\n",
}
return rc;
}
/**
* Destroy a queue.
*
* @returns VBox status code.
* @param pQueue Queue to destroy.
* @thread Emulation thread only.
*/
{
/*
* Validate input.
*/
if (!pQueue)
return VERR_INVALID_PARAMETER;
/*
* Unlink it.
*/
{
{
while (pCur)
{
{
break;
}
}
}
else
}
else
{
{
while (pCur)
{
{
break;
}
}
}
else
}
/*
* Deregister statistics.
*/
#ifdef VBOX_WITH_STATISTICS
#endif
/*
* Destroy the timer and free it.
*/
{
}
{
}
else
return VINF_SUCCESS;
}
/**
* Destroy a all queues owned by the specified device.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param pDevIns Device instance.
* @thread Emulation thread only.
*/
{
/*
* Validate input.
*/
if (!pDevIns)
return VERR_INVALID_PARAMETER;
/*
* Unlink it.
*/
do
{
while (pQueue)
{
{
}
else
}
/* next queue list */
pQueue = pQueueNext;
pQueueNext = NULL;
} while (pQueue);
return VINF_SUCCESS;
}
/**
* Destroy a all queues owned by the specified driver.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param pDrvIns Driver instance.
* @thread Emulation thread only.
*/
{
/*
* Validate input.
*/
if (!pDrvIns)
return VERR_INVALID_PARAMETER;
/*
* Unlink it.
*/
do
{
while (pQueue)
{
{
}
else
}
/* next queue list */
pQueue = pQueueNext;
pQueueNext = NULL;
} while (pQueue);
return VINF_SUCCESS;
}
/**
* Relocate the queues.
*
* @param pVM The VM handle.
* @param offDelta The relocation delta.
*/
{
/*
* Process the queues.
*/
do
{
while (pQueue)
{
{
/* Pending RC items. */
if (pQueue->pPendingRC)
{
{
}
}
/* The free items. */
{
}
}
/* next queue */
}
/* next queue list */
pQueue = pQueueNext;
pQueueNext = NULL;
} while (pQueue);
}
/**
* Flush pending queues.
* This is a forced action callback.
*
* @param pVM VM handle.
* @thread Emulation thread only.
*/
{
LogFlow(("PDMR3QueuesFlush:\n"));
/*
* Only let one EMT flushing queues at any one time to queue preserve order
* and to avoid wasting time. The FF is always cleared here, because it's
* only used to get someones attention. Queue inserts occuring during the
* flush are caught using the pending bit.
*
* Note. The order in which the FF and pending bit are set and cleared is important.
*/
{
do
{
if ( pCur->pPendingR3
|| pCur->pPendingR0
|| pCur->pPendingRC)
}
}
/**
* Process pending items in one queue.
*
* @returns Success indicator.
* If false the item the consumer said "enough!".
* @param pQueue The queue.
*/
{
/*
* Get the lists.
*/
PPDMQUEUEITEMCORE pItems = (PPDMQUEUEITEMCORE)ASMAtomicXchgPtr((void * volatile *)&pQueue->pPendingR3, NULL);
if ( !pItems
&& !pItemsRC
&& !pItemsR0)
/* Somebody may be racing us ... never mind. */
return true;
/*
* Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
*/
while (pCur)
{
}
/*
* Do the same for any pending RC items.
*/
while (pItemsRC)
{
}
/*
* Do the same for any pending R0 items.
*/
while (pItemsR0)
{
}
/*
* Feed the items to the consumer function.
*/
{
case PDMQUEUETYPE_DEV:
while (pItems)
{
break;
}
break;
case PDMQUEUETYPE_DRV:
while (pItems)
{
break;
}
break;
case PDMQUEUETYPE_INTERNAL:
while (pItems)
{
break;
}
break;
case PDMQUEUETYPE_EXTERNAL:
while (pItems)
{
break;
}
break;
default:
break;
}
/*
* Success?
*/
if (pItems)
{
/*
* Reverse the list.
*/
while (pCur)
{
}
/*
* Insert the list at the tail of the pending list.
*/
for (;;)
{
break;
PPDMQUEUEITEMCORE pPending = (PPDMQUEUEITEMCORE)ASMAtomicXchgPtr((void * volatile *)&pQueue->pPendingR3, NULL);
if (pPending)
{
}
}
return false;
}
return true;
}
/**
* This is a worker function used by PDMQueueFlush to perform the
* flush in ring-3.
*
* The queue which should be flushed is pointed to by either pQueueFlushRC,
* pQueueFlushR0, or pQueue. This function will flush that queue and recalc
* the queue FF.
*
* @param pVM The VM handle.
* @param pQueue The queue to flush. Only used in Ring-3.
*/
{
/** @todo This will clash with PDMR3QueueFlushAll (guest SMP)! */
/*
* Flush the queue.
*/
{
}
{
}
if ( !pQueue
|| pdmR3QueueFlush(pQueue))
{
/*
* Recalc the FF (for the queues using force action).
*/
if ( pQueue->pPendingRC
|| pQueue->pPendingR0
|| pQueue->pPendingR3)
{
break;
}
}
}
/**
* Free an item.
*
* @param pQueue The queue.
* @param pItem The item.
*/
{
{
}
AssertMsgFailed(("huh? i=%d iNext=%d iFreeHead=%d iFreeTail=%d\n", i, iNext, pQueue->iFreeHead, pQueue->iFreeTail));
}
/**
* Timer handler for PDM queues.
* This is called by for a single queue.
*
* @param pVM VM handle.
* @param pTimer Pointer to timer.
* @param pvUser Pointer to the queue.
*/
{
if ( pQueue->pPendingR3
|| pQueue->pPendingR0
|| pQueue->pPendingRC)
}