PDMQueue.cpp revision c98fb3e16fcd571a790eab772c0c66173d225205
/* $Id$ */
/** @file
* PDM Queue - Transport data and tasks to EMT and R3.
*/
/*
* Copyright (C) 2006-2007 innotek GmbH
*
* 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 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.
*/
/*******************************************************************************
* 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 fGCEnabled Set if the queue will be used from GC 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 fGCEnabled, 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 (fGCEnabled)
else
if (VBOX_FAILURE(rc))
return rc;
/*
* Initialize the data fields.
*/
//pQueue->pTimer = NULL;
//pQueue->pPendingHC = NULL;
//pQueue->pPendingGC = NULL;
//pQueue->iFreeTail = 0;
PPDMQUEUEITEMCORE pItem = (PPDMQUEUEITEMCORE)((char *)pQueue + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16));
{
if (fGCEnabled)
}
/*
* 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 (fGCEnabled)
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.
*/
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 fGCEnabled Set if the queue must be usable from GC.
* @param ppQueue Where to store the queue handle on success.
* @thread Emulation thread only.
*/
PDMR3DECL(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 fGCEnabled=%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.
*/
PDMR3DECL(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 fGCEnabled Set if the queue must be usable from GC.
* @param ppQueue Where to store the queue handle on success.
* @thread Emulation thread only.
*/
PDMR3DECL(int) PDMR3QueueCreateInternal(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
{
LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fGCEnabled=%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.
*/
PDMR3DECL(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 GC items. */
if (pQueue->pPendingGC)
{
{
}
}
/* 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->pPendingHC
|| pCur->pPendingGC)
{
if ( pdmR3QueueFlush(pCur)
&& pCur->pPendingHC)
/* 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.
*/
{
PPDMQUEUEITEMCORE pItems = (PPDMQUEUEITEMCORE)ASMAtomicXchgPtr((void * volatile *)&pQueue->pPendingHC, NULL);
/*
* Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
*/
while (pCur)
{
}
/*
* Do the same for any pending GC items.
*/
while (pQueue->pPendingGC)
{
}
/*
* 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)
{
}
if ( pQueue->pPendingHC
{
}
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 pQueueFlushGC,
* pQueueFlushHC, or pQueueue. 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)
if (!pQueue)
if ( !pQueue
|| pdmR3QueueFlush(pQueue))
{
/*
* Recalc the FF.
*/
if ( pQueue->pPendingGC
|| pQueue->pPendingHC)
{
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->pPendingHC
|| pQueue->pPendingGC)
}