PDMQueue.cpp revision 9ad5e3912962c3dbccc1afc4e7d62890fe906814
/* $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 ppQueue Where to store the queue handle.
*/
static int pdmR3QueueCreate(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval, bool fRZEnabled, PPDMQUEUE *ppQueue)
{
/*
* Validate input.
*/
if (cbItem < sizeof(PDMQUEUEITEMCORE))
{
return VERR_INVALID_PARAMETER;
}
{
return VERR_INVALID_PARAMETER;
}
/*
* Align the item size and calculate the structure size.
*/
unsigned cb = cbItem * cItems + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16);
int rc;
if (fRZEnabled)
else
if (VBOX_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)
{
int rc = TMR3TimerCreateInternal(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, "Queue timer", &pQueue->pTimer);
if (VBOX_SUCCESS(rc))
{
if (VBOX_FAILURE(rc))
{
}
}
else
if (VBOX_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 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
{
}
}
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 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\n",
/*
* Validate input.
*/
if (!pfnCallback)
{
AssertMsgFailed(("No consumer callback!\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Create the queue.
*/
if (VBOX_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 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\n",
/*
* Validate input.
*/
if (!pfnCallback)
{
AssertMsgFailed(("No consumer callback!\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Create the queue.
*/
if (VBOX_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 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\n",
/*
* Validate input.
*/
if (!pfnCallback)
{
AssertMsgFailed(("No consumer callback!\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Create the queue.
*/
if (VBOX_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 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, PPDMQUEUE *ppQueue)
{
LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p\n", cbItem, cItems, cMilliesInterval, pfnCallback));
/*
* Validate input.
*/
if (!pfnCallback)
{
AssertMsgFailed(("No consumer callback!\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Create the queue.
*/
if (VBOX_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
}
/*
* 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"));
{
if ( pCur->pPendingR3
|| pCur->pPendingR0
|| pCur->pPendingRC)
{
if ( pdmR3QueueFlush(pCur)
&& pCur->pPendingR3)
/* new items arrived while flushing. */
}
}
}
/**
* 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);
/*
* 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)
{
/*
* Shit, no!
* 1. Insert pCur.
* 2. Reverse the list.
* 3. Insert the LIFO at the tail of the pending list.
*/
//pCur = pItems;
while (pCur)
{
}
{
}
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.
*/
{
/*
* 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)
}