/* $Id: PDMAsyncCompletion.cpp $ */ /** @file * PDM Async I/O - Transport data asynchronous in R3 using EMT. */ /* * Copyright (C) 2006-2022 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION #include "PDMInternal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PDMAsyncCompletionInternal.h" /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * Async I/O type. */ typedef enum PDMASYNCCOMPLETIONTEMPLATETYPE { /** Device . */ PDMASYNCCOMPLETIONTEMPLATETYPE_DEV = 1, /** Driver consumer. */ PDMASYNCCOMPLETIONTEMPLATETYPE_DRV, /** Internal consumer. */ PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL, /** Usb consumer. */ PDMASYNCCOMPLETIONTEMPLATETYPE_USB } PDMASYNCTEMPLATETYPE; /** * PDM Async I/O template. */ typedef struct PDMASYNCCOMPLETIONTEMPLATE { /** Pointer to the next template in the list. */ R3PTRTYPE(PPDMASYNCCOMPLETIONTEMPLATE) pNext; /** Pointer to the previous template in the list. */ R3PTRTYPE(PPDMASYNCCOMPLETIONTEMPLATE) pPrev; /** Type specific data. */ union { /** PDMASYNCCOMPLETIONTEMPLATETYPE_DEV */ struct { /** Pointer to consumer function. */ R3PTRTYPE(PFNPDMASYNCCOMPLETEDEV) pfnCompleted; /** Pointer to the device instance owning the template. */ R3PTRTYPE(PPDMDEVINS) pDevIns; } Dev; /** PDMASYNCCOMPLETIONTEMPLATETYPE_DRV */ struct { /** Pointer to consumer function. */ R3PTRTYPE(PFNPDMASYNCCOMPLETEDRV) pfnCompleted; /** Pointer to the driver instance owning the template. */ R3PTRTYPE(PPDMDRVINS) pDrvIns; /** User argument given during template creation. * This is only here to make things much easier * for DrVVD. */ void *pvTemplateUser; } Drv; /** PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL */ struct { /** Pointer to consumer function. */ R3PTRTYPE(PFNPDMASYNCCOMPLETEINT) pfnCompleted; /** Pointer to user data. */ R3PTRTYPE(void *) pvUser; } Int; /** PDMASYNCCOMPLETIONTEMPLATETYPE_USB */ struct { /** Pointer to consumer function. */ R3PTRTYPE(PFNPDMASYNCCOMPLETEUSB) pfnCompleted; /** Pointer to the usb instance owning the template. */ R3PTRTYPE(PPDMUSBINS) pUsbIns; } Usb; } u; /** Template type. */ PDMASYNCCOMPLETIONTEMPLATETYPE enmType; /** Pointer to the VM. */ R3PTRTYPE(PVM) pVM; /** Use count of the template. */ volatile uint32_t cUsed; } PDMASYNCCOMPLETIONTEMPLATE; /** * Bandwidth control manager instance data */ typedef struct PDMACBWMGR { /** Pointer to the next manager in the list. */ struct PDMACBWMGR *pNext; /** Pointer to the shared UVM structure. */ PPDMASYNCCOMPLETIONEPCLASS pEpClass; /** Identifier of the manager. */ char *pszId; /** Maximum number of bytes the endpoints are allowed to transfer (Max is 4GB/s currently) */ volatile uint32_t cbTransferPerSecMax; /** Number of bytes we start with */ volatile uint32_t cbTransferPerSecStart; /** Step after each update */ volatile uint32_t cbTransferPerSecStep; /** Number of bytes we are allowed to transfer till the next update. * Reset by the refresh timer. */ volatile uint32_t cbTransferAllowed; /** Timestamp of the last update */ volatile uint64_t tsUpdatedLast; /** Reference counter - How many endpoints are associated with this manager. */ volatile uint32_t cRefs; } PDMACBWMGR; /** Pointer to a bandwidth control manager pointer. */ typedef PPDMACBWMGR *PPPDMACBWMGR; /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ static void pdmR3AsyncCompletionPutTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, PPDMASYNCCOMPLETIONTASK pTask); /** * Internal worker for the creation apis * * @returns VBox status code. * @param pVM The cross context VM structure. * @param ppTemplate Where to store the template handle. * @param enmType Async completion template type (dev, drv, usb, int). */ static int pdmR3AsyncCompletionTemplateCreate(PVM pVM, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE enmType) { PUVM pUVM = pVM->pUVM; AssertPtrReturn(ppTemplate, VERR_INVALID_POINTER); PPDMASYNCCOMPLETIONTEMPLATE pTemplate; int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMASYNCCOMPLETIONTEMPLATE), (void **)&pTemplate); if (RT_FAILURE(rc)) return rc; /* * Initialize fields. */ pTemplate->pVM = pVM; pTemplate->cUsed = 0; pTemplate->enmType = enmType; /* * Add template to the global VM template list. */ RTCritSectEnter(&pUVM->pdm.s.ListCritSect); pTemplate->pNext = pUVM->pdm.s.pAsyncCompletionTemplates; if (pUVM->pdm.s.pAsyncCompletionTemplates) pUVM->pdm.s.pAsyncCompletionTemplates->pPrev = pTemplate; pUVM->pdm.s.pAsyncCompletionTemplates = pTemplate; RTCritSectLeave(&pUVM->pdm.s.ListCritSect); *ppTemplate = pTemplate; return VINF_SUCCESS; } #ifdef SOME_UNUSED_FUNCTION /** * Creates a async completion template for a device instance. * * The template is used when creating new completion tasks. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param pDevIns The device instance. * @param ppTemplate Where to store the template pointer on success. * @param pfnCompleted The completion callback routine. * @param pszDesc Description. */ int pdmR3AsyncCompletionTemplateCreateDevice(PVM pVM, PPDMDEVINS pDevIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEDEV pfnCompleted, const char *pszDesc) { LogFlow(("%s: pDevIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n", __FUNCTION__, pDevIns, ppTemplate, pfnCompleted, pszDesc)); /* * Validate input. */ VM_ASSERT_EMT(pVM); AssertPtrReturn(pfnCompleted, VERR_INVALID_POINTER); AssertPtrReturn(ppTemplate, VERR_INVALID_POINTER); /* * Create the template. */ PPDMASYNCCOMPLETIONTEMPLATE pTemplate; int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_DEV); if (RT_SUCCESS(rc)) { pTemplate->u.Dev.pDevIns = pDevIns; pTemplate->u.Dev.pfnCompleted = pfnCompleted; *ppTemplate = pTemplate; Log(("PDM: Created device template %p: pfnCompleted=%p pDevIns=%p\n", pTemplate, pfnCompleted, pDevIns)); } return rc; } #endif /* SOME_UNUSED_FUNCTION */ /** * Creates a async completion template for a driver instance. * * The template is used when creating new completion tasks. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param pDrvIns The driver instance. * @param ppTemplate Where to store the template pointer on success. * @param pfnCompleted The completion callback routine. * @param pvTemplateUser Template user argument * @param pszDesc Description. */ int pdmR3AsyncCompletionTemplateCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEDRV pfnCompleted, void *pvTemplateUser, const char *pszDesc) { LogFlow(("PDMR3AsyncCompletionTemplateCreateDriver: pDrvIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n", pDrvIns, ppTemplate, pfnCompleted, pszDesc)); RT_NOREF_PV(pszDesc); /** @todo async template description */ /* * Validate input. */ AssertPtrReturn(pfnCompleted, VERR_INVALID_POINTER); AssertPtrReturn(ppTemplate, VERR_INVALID_POINTER); /* * Create the template. */ PPDMASYNCCOMPLETIONTEMPLATE pTemplate; int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_DRV); if (RT_SUCCESS(rc)) { pTemplate->u.Drv.pDrvIns = pDrvIns; pTemplate->u.Drv.pfnCompleted = pfnCompleted; pTemplate->u.Drv.pvTemplateUser = pvTemplateUser; *ppTemplate = pTemplate; Log(("PDM: Created driver template %p: pfnCompleted=%p pDrvIns=%p\n", pTemplate, pfnCompleted, pDrvIns)); } return rc; } #ifdef SOME_UNUSED_FUNCTION /** * Creates a async completion template for a USB device instance. * * The template is used when creating new completion tasks. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param pUsbIns The USB device instance. * @param ppTemplate Where to store the template pointer on success. * @param pfnCompleted The completion callback routine. * @param pszDesc Description. */ int pdmR3AsyncCompletionTemplateCreateUsb(PVM pVM, PPDMUSBINS pUsbIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEUSB pfnCompleted, const char *pszDesc) { LogFlow(("pdmR3AsyncCompletionTemplateCreateUsb: pUsbIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n", pUsbIns, ppTemplate, pfnCompleted, pszDesc)); /* * Validate input. */ VM_ASSERT_EMT(pVM); AssertPtrReturn(pfnCompleted, VERR_INVALID_POINTER); AssertPtrReturn(ppTemplate, VERR_INVALID_POINTER); /* * Create the template. */ PPDMASYNCCOMPLETIONTEMPLATE pTemplate; int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_USB); if (RT_SUCCESS(rc)) { pTemplate->u.Usb.pUsbIns = pUsbIns; pTemplate->u.Usb.pfnCompleted = pfnCompleted; *ppTemplate = pTemplate; Log(("PDM: Created usb template %p: pfnCompleted=%p pDevIns=%p\n", pTemplate, pfnCompleted, pUsbIns)); } return rc; } #endif /** * Creates a async completion template for internally by the VMM. * * The template is used when creating new completion tasks. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param ppTemplate Where to store the template pointer on success. * @param pfnCompleted The completion callback routine. * @param pvUser2 The 2nd user argument for the callback. * @param pszDesc Description. * @internal */ VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateInternal(PVM pVM, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEINT pfnCompleted, void *pvUser2, const char *pszDesc) { LogFlow(("PDMR3AsyncCompletionTemplateCreateInternal: ppTemplate=%p pfnCompleted=%p pvUser2=%p pszDesc=%s\n", ppTemplate, pfnCompleted, pvUser2, pszDesc)); RT_NOREF_PV(pszDesc); /** @todo async template description */ /* * Validate input. */ VM_ASSERT_EMT(pVM); AssertPtrReturn(pfnCompleted, VERR_INVALID_POINTER); AssertPtrReturn(ppTemplate, VERR_INVALID_POINTER); /* * Create the template. */ PPDMASYNCCOMPLETIONTEMPLATE pTemplate; int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL); if (RT_SUCCESS(rc)) { pTemplate->u.Int.pvUser = pvUser2; pTemplate->u.Int.pfnCompleted = pfnCompleted; *ppTemplate = pTemplate; Log(("PDM: Created internal template %p: pfnCompleted=%p pvUser2=%p\n", pTemplate, pfnCompleted, pvUser2)); } return rc; } /** * Destroys the specified async completion template. * * @returns VBox status codes: * @retval VINF_SUCCESS on success. * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if the template is still in use. * * @param pTemplate The template in question. */ VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroy(PPDMASYNCCOMPLETIONTEMPLATE pTemplate) { LogFlow(("%s: pTemplate=%p\n", __FUNCTION__, pTemplate)); if (!pTemplate) { AssertMsgFailed(("pTemplate is NULL!\n")); return VERR_INVALID_PARAMETER; } /* * Check if the template is still used. */ if (pTemplate->cUsed > 0) { AssertMsgFailed(("Template is still in use\n")); return VERR_PDM_ASYNC_TEMPLATE_BUSY; } /* * Unlink the template from the list. */ PUVM pUVM = pTemplate->pVM->pUVM; RTCritSectEnter(&pUVM->pdm.s.ListCritSect); PPDMASYNCCOMPLETIONTEMPLATE pPrev = pTemplate->pPrev; PPDMASYNCCOMPLETIONTEMPLATE pNext = pTemplate->pNext; if (pPrev) pPrev->pNext = pNext; else pUVM->pdm.s.pAsyncCompletionTemplates = pNext; if (pNext) pNext->pPrev = pPrev; RTCritSectLeave(&pUVM->pdm.s.ListCritSect); /* * Free the template. */ MMR3HeapFree(pTemplate); return VINF_SUCCESS; } /** * Destroys all the specified async completion templates for the given device instance. * * @returns VBox status codes: * @retval VINF_SUCCESS on success. * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use. * * @param pVM The cross context VM structure. * @param pDevIns The device instance. */ int pdmR3AsyncCompletionTemplateDestroyDevice(PVM pVM, PPDMDEVINS pDevIns) { LogFlow(("pdmR3AsyncCompletionTemplateDestroyDevice: pDevIns=%p\n", pDevIns)); /* * Validate input. */ if (!pDevIns) return VERR_INVALID_PARAMETER; VM_ASSERT_EMT(pVM); /* * Unlink it. */ PUVM pUVM = pVM->pUVM; RTCritSectEnter(&pUVM->pdm.s.ListCritSect); PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pUVM->pdm.s.pAsyncCompletionTemplates; while (pTemplate) { if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_DEV && pTemplate->u.Dev.pDevIns == pDevIns) { PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate; pTemplate = pTemplate->pNext; int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy); if (RT_FAILURE(rc)) { RTCritSectLeave(&pUVM->pdm.s.ListCritSect); return rc; } } else pTemplate = pTemplate->pNext; } RTCritSectLeave(&pUVM->pdm.s.ListCritSect); return VINF_SUCCESS; } /** * Destroys all the specified async completion templates for the given driver instance. * * @returns VBox status codes: * @retval VINF_SUCCESS on success. * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use. * * @param pVM The cross context VM structure. * @param pDrvIns The driver instance. */ int pdmR3AsyncCompletionTemplateDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns) { LogFlow(("pdmR3AsyncCompletionTemplateDestroyDriver: pDevIns=%p\n", pDrvIns)); /* * Validate input. */ if (!pDrvIns) return VERR_INVALID_PARAMETER; VM_ASSERT_EMT(pVM); /* * Unlink it. */ PUVM pUVM = pVM->pUVM; RTCritSectEnter(&pUVM->pdm.s.ListCritSect); PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pUVM->pdm.s.pAsyncCompletionTemplates; while (pTemplate) { if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_DRV && pTemplate->u.Drv.pDrvIns == pDrvIns) { PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate; pTemplate = pTemplate->pNext; int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy); if (RT_FAILURE(rc)) { RTCritSectLeave(&pUVM->pdm.s.ListCritSect); return rc; } } else pTemplate = pTemplate->pNext; } RTCritSectLeave(&pUVM->pdm.s.ListCritSect); return VINF_SUCCESS; } /** * Destroys all the specified async completion templates for the given USB device instance. * * @returns VBox status codes: * @retval VINF_SUCCESS on success. * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use. * * @param pVM The cross context VM structure. * @param pUsbIns The USB device instance. */ int pdmR3AsyncCompletionTemplateDestroyUsb(PVM pVM, PPDMUSBINS pUsbIns) { LogFlow(("pdmR3AsyncCompletionTemplateDestroyUsb: pUsbIns=%p\n", pUsbIns)); /* * Validate input. */ if (!pUsbIns) return VERR_INVALID_PARAMETER; VM_ASSERT_EMT(pVM); /* * Unlink it. */ PUVM pUVM = pVM->pUVM; RTCritSectEnter(&pUVM->pdm.s.ListCritSect); PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pUVM->pdm.s.pAsyncCompletionTemplates; while (pTemplate) { if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_USB && pTemplate->u.Usb.pUsbIns == pUsbIns) { PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate; pTemplate = pTemplate->pNext; int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy); if (RT_FAILURE(rc)) { RTCritSectLeave(&pUVM->pdm.s.ListCritSect); return rc; } } else pTemplate = pTemplate->pNext; } RTCritSectLeave(&pUVM->pdm.s.ListCritSect); return VINF_SUCCESS; } /** Lazy coder. */ static PPDMACBWMGR pdmacBwMgrFindById(PPDMASYNCCOMPLETIONEPCLASS pEpClass, const char *pszId) { PPDMACBWMGR pBwMgr = NULL; if (pszId) { int rc = RTCritSectEnter(&pEpClass->CritSect); AssertRC(rc); pBwMgr = pEpClass->pBwMgrsHead; while ( pBwMgr && RTStrCmp(pBwMgr->pszId, pszId)) pBwMgr = pBwMgr->pNext; rc = RTCritSectLeave(&pEpClass->CritSect); AssertRC(rc); } return pBwMgr; } /** Lazy coder. */ static void pdmacBwMgrLink(PPDMACBWMGR pBwMgr) { PPDMASYNCCOMPLETIONEPCLASS pEpClass = pBwMgr->pEpClass; int rc = RTCritSectEnter(&pEpClass->CritSect); AssertRC(rc); pBwMgr->pNext = pEpClass->pBwMgrsHead; pEpClass->pBwMgrsHead = pBwMgr; rc = RTCritSectLeave(&pEpClass->CritSect); AssertRC(rc); } #ifdef SOME_UNUSED_FUNCTION /** Lazy coder. */ static void pdmacBwMgrUnlink(PPDMACBWMGR pBwMgr) { PPDMASYNCCOMPLETIONEPCLASS pEpClass = pBwMgr->pEpClass; int rc = RTCritSectEnter(&pEpClass->CritSect); AssertRC(rc); if (pBwMgr == pEpClass->pBwMgrsHead) pEpClass->pBwMgrsHead = pBwMgr->pNext; else { PPDMACBWMGR pPrev = pEpClass->pBwMgrsHead; while ( pPrev && pPrev->pNext != pBwMgr) pPrev = pPrev->pNext; AssertPtr(pPrev); pPrev->pNext = pBwMgr->pNext; } rc = RTCritSectLeave(&pEpClass->CritSect); AssertRC(rc); } #endif /* SOME_UNUSED_FUNCTION */ /** Lazy coder. */ static int pdmacAsyncCompletionBwMgrCreate(PPDMASYNCCOMPLETIONEPCLASS pEpClass, const char *pszBwMgr, uint32_t cbTransferPerSecMax, uint32_t cbTransferPerSecStart, uint32_t cbTransferPerSecStep) { LogFlowFunc(("pEpClass=%#p pszBwMgr=%#p{%s} cbTransferPerSecMax=%u cbTransferPerSecStart=%u cbTransferPerSecStep=%u\n", pEpClass, pszBwMgr, pszBwMgr, cbTransferPerSecMax, cbTransferPerSecStart, cbTransferPerSecStep)); AssertPtrReturn(pEpClass, VERR_INVALID_POINTER); AssertPtrReturn(pszBwMgr, VERR_INVALID_POINTER); AssertReturn(*pszBwMgr != '\0', VERR_INVALID_PARAMETER); int rc; PPDMACBWMGR pBwMgr = pdmacBwMgrFindById(pEpClass, pszBwMgr); if (!pBwMgr) { rc = MMR3HeapAllocZEx(pEpClass->pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMACBWMGR), (void **)&pBwMgr); if (RT_SUCCESS(rc)) { pBwMgr->pszId = RTStrDup(pszBwMgr); if (pBwMgr->pszId) { pBwMgr->pEpClass = pEpClass; pBwMgr->cRefs = 0; /* Init I/O flow control. */ pBwMgr->cbTransferPerSecMax = cbTransferPerSecMax; pBwMgr->cbTransferPerSecStart = cbTransferPerSecStart; pBwMgr->cbTransferPerSecStep = cbTransferPerSecStep; pBwMgr->cbTransferAllowed = pBwMgr->cbTransferPerSecStart; pBwMgr->tsUpdatedLast = RTTimeSystemNanoTS(); pdmacBwMgrLink(pBwMgr); rc = VINF_SUCCESS; } else { rc = VERR_NO_MEMORY; MMR3HeapFree(pBwMgr); } } } else rc = VERR_ALREADY_EXISTS; LogFlowFunc(("returns rc=%Rrc\n", rc)); return rc; } /** Lazy coder. */ DECLINLINE(void) pdmacBwMgrRetain(PPDMACBWMGR pBwMgr) { ASMAtomicIncU32(&pBwMgr->cRefs); } /** Lazy coder. */ DECLINLINE(void) pdmacBwMgrRelease(PPDMACBWMGR pBwMgr) { Assert(pBwMgr->cRefs > 0); ASMAtomicDecU32(&pBwMgr->cRefs); } /** * Checks if the endpoint is allowed to transfer the given amount of bytes. * * @returns true if the endpoint is allowed to transfer the data. * false otherwise * @param pEndpoint The endpoint. * @param cbTransfer The number of bytes to transfer. * @param pmsWhenNext Where to store the number of milliseconds * until the bandwidth is refreshed. * Only set if false is returned. */ bool pdmacEpIsTransferAllowed(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint32_t cbTransfer, RTMSINTERVAL *pmsWhenNext) { bool fAllowed = true; PPDMACBWMGR pBwMgr = ASMAtomicReadPtrT(&pEndpoint->pBwMgr, PPDMACBWMGR); LogFlowFunc(("pEndpoint=%p pBwMgr=%p cbTransfer=%u\n", pEndpoint, pBwMgr, cbTransfer)); if (pBwMgr) { uint32_t cbOld = ASMAtomicSubU32(&pBwMgr->cbTransferAllowed, cbTransfer); if (RT_LIKELY(cbOld >= cbTransfer)) fAllowed = true; else { fAllowed = false; /* We are out of resources Check if we can update again. */ uint64_t tsNow = RTTimeSystemNanoTS(); uint64_t tsUpdatedLast = ASMAtomicUoReadU64(&pBwMgr->tsUpdatedLast); if (tsNow - tsUpdatedLast >= (1000*1000*1000)) { if (ASMAtomicCmpXchgU64(&pBwMgr->tsUpdatedLast, tsNow, tsUpdatedLast)) { if (pBwMgr->cbTransferPerSecStart < pBwMgr->cbTransferPerSecMax) { pBwMgr->cbTransferPerSecStart = RT_MIN(pBwMgr->cbTransferPerSecMax, pBwMgr->cbTransferPerSecStart + pBwMgr->cbTransferPerSecStep); LogFlow(("AIOMgr: Increasing maximum bandwidth to %u bytes/sec\n", pBwMgr->cbTransferPerSecStart)); } /* Update */ uint32_t cbTransferAllowedNew = pBwMgr->cbTransferPerSecStart > cbTransfer ? pBwMgr->cbTransferPerSecStart - cbTransfer : 0; ASMAtomicWriteU32(&pBwMgr->cbTransferAllowed, cbTransferAllowedNew); fAllowed = true; LogFlow(("AIOMgr: Refreshed bandwidth\n")); } } else { ASMAtomicAddU32(&pBwMgr->cbTransferAllowed, cbTransfer); *pmsWhenNext = ((1000*1000*1000) - (tsNow - tsUpdatedLast)) / (1000*1000); } } } LogFlowFunc(("fAllowed=%RTbool\n", fAllowed)); return fAllowed; } /** * Called by the endpoint if a task has finished. * * @returns nothing * @param pTask Pointer to the finished task. * @param rc Status code of the completed request. * @param fCallCompletionHandler Flag whether the completion handler should be called to * inform the owner of the task that it has completed. */ void pdmR3AsyncCompletionCompleteTask(PPDMASYNCCOMPLETIONTASK pTask, int rc, bool fCallCompletionHandler) { LogFlow(("%s: pTask=%#p fCallCompletionHandler=%RTbool\n", __FUNCTION__, pTask, fCallCompletionHandler)); if (fCallCompletionHandler) { PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pTask->pEndpoint->pTemplate; switch (pTemplate->enmType) { case PDMASYNCCOMPLETIONTEMPLATETYPE_DEV: pTemplate->u.Dev.pfnCompleted(pTemplate->u.Dev.pDevIns, pTask->pvUser, rc); break; case PDMASYNCCOMPLETIONTEMPLATETYPE_DRV: pTemplate->u.Drv.pfnCompleted(pTemplate->u.Drv.pDrvIns, pTemplate->u.Drv.pvTemplateUser, pTask->pvUser, rc); break; case PDMASYNCCOMPLETIONTEMPLATETYPE_USB: pTemplate->u.Usb.pfnCompleted(pTemplate->u.Usb.pUsbIns, pTask->pvUser, rc); break; case PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL: pTemplate->u.Int.pfnCompleted(pTemplate->pVM, pTask->pvUser, pTemplate->u.Int.pvUser, rc); break; default: AssertMsgFailed(("Unknown template type!\n")); } } pdmR3AsyncCompletionPutTask(pTask->pEndpoint, pTask); } /** * Worker initializing a endpoint class. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param pEpClassOps Pointer to the endpoint class structure. * @param pCfgHandle Pointer to the CFGM tree. */ int pdmR3AsyncCompletionEpClassInit(PVM pVM, PCPDMASYNCCOMPLETIONEPCLASSOPS pEpClassOps, PCFGMNODE pCfgHandle) { /* Validate input. */ AssertPtrReturn(pEpClassOps, VERR_INVALID_POINTER); AssertReturn(pEpClassOps->u32Version == PDMAC_EPCLASS_OPS_VERSION, VERR_VERSION_MISMATCH); AssertReturn(pEpClassOps->u32VersionEnd == PDMAC_EPCLASS_OPS_VERSION, VERR_VERSION_MISMATCH); LogFlow(("pdmR3AsyncCompletionEpClassInit: pVM=%p pEpClassOps=%p{%s}\n", pVM, pEpClassOps, pEpClassOps->pszName)); /* Allocate global class data. */ PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = NULL; int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION, pEpClassOps->cbEndpointClassGlobal, (void **)&pEndpointClass); if (RT_SUCCESS(rc)) { /* Initialize common data. */ pEndpointClass->pVM = pVM; pEndpointClass->pEndpointOps = pEpClassOps; rc = RTCritSectInit(&pEndpointClass->CritSect); if (RT_SUCCESS(rc)) { PCFGMNODE pCfgNodeClass = CFGMR3GetChild(pCfgHandle, pEpClassOps->pszName); /* Create task cache */ rc = RTMemCacheCreate(&pEndpointClass->hMemCacheTasks, pEpClassOps->cbTask, 0, UINT32_MAX, NULL, NULL, NULL, 0); if (RT_SUCCESS(rc)) { /* Call the specific endpoint class initializer. */ rc = pEpClassOps->pfnInitialize(pEndpointClass, pCfgNodeClass); if (RT_SUCCESS(rc)) { /* Create all bandwidth groups for resource control. */ PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNodeClass, "BwGroups"); if (pCfgBwGrp) { for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur)) { size_t cbName = CFGMR3GetNameLen(pCur) + 1; char *pszBwGrpId = (char *)RTMemAllocZ(cbName); if (pszBwGrpId) { rc = CFGMR3GetName(pCur, pszBwGrpId, cbName); if (RT_SUCCESS(rc)) { uint32_t cbMax; rc = CFGMR3QueryU32(pCur, "Max", &cbMax); if (RT_SUCCESS(rc)) { uint32_t cbStart; rc = CFGMR3QueryU32Def(pCur, "Start", &cbStart, cbMax); if (RT_SUCCESS(rc)) { uint32_t cbStep; rc = CFGMR3QueryU32Def(pCur, "Step", &cbStep, 0); if (RT_SUCCESS(rc)) rc = pdmacAsyncCompletionBwMgrCreate(pEndpointClass, pszBwGrpId, cbMax, cbStart, cbStep); } } } RTMemFree(pszBwGrpId); } else rc = VERR_NO_MEMORY; if (RT_FAILURE(rc)) break; } } if (RT_SUCCESS(rc)) { PUVM pUVM = pVM->pUVM; AssertMsg(!pUVM->pdm.s.apAsyncCompletionEndpointClass[pEpClassOps->enmClassType], ("Endpoint class was already initialized\n")); #ifdef VBOX_WITH_STATISTICS CFGMR3QueryBoolDef(pCfgNodeClass, "AdvancedStatistics", &pEndpointClass->fGatherAdvancedStatistics, true); #else CFGMR3QueryBoolDef(pCfgNodeClass, "AdvancedStatistics", &pEndpointClass->fGatherAdvancedStatistics, false); #endif pUVM->pdm.s.apAsyncCompletionEndpointClass[pEpClassOps->enmClassType] = pEndpointClass; LogFlowFunc((": Initialized endpoint class \"%s\" rc=%Rrc\n", pEpClassOps->pszName, rc)); return VINF_SUCCESS; } } RTMemCacheDestroy(pEndpointClass->hMemCacheTasks); } RTCritSectDelete(&pEndpointClass->CritSect); } MMR3HeapFree(pEndpointClass); } LogFlowFunc((": Failed to initialize endpoint class rc=%Rrc\n", rc)); return rc; } /** * Worker terminating all endpoint classes. * * @returns nothing * @param pEndpointClass Pointer to the endpoint class to terminate. * * @remarks This method ensures that any still open endpoint is closed. */ static void pdmR3AsyncCompletionEpClassTerminate(PPDMASYNCCOMPLETIONEPCLASS pEndpointClass) { PVM pVM = pEndpointClass->pVM; /* Close all still open endpoints. */ while (pEndpointClass->pEndpointsHead) PDMR3AsyncCompletionEpClose(pEndpointClass->pEndpointsHead); /* Destroy the bandwidth managers. */ PPDMACBWMGR pBwMgr = pEndpointClass->pBwMgrsHead; while (pBwMgr) { PPDMACBWMGR pFree = pBwMgr; pBwMgr = pBwMgr->pNext; MMR3HeapFree(pFree); } /* Call the termination callback of the class. */ pEndpointClass->pEndpointOps->pfnTerminate(pEndpointClass); RTMemCacheDestroy(pEndpointClass->hMemCacheTasks); RTCritSectDelete(&pEndpointClass->CritSect); /* Free the memory of the class finally and clear the entry in the class array. */ pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[pEndpointClass->pEndpointOps->enmClassType] = NULL; MMR3HeapFree(pEndpointClass); } /** * Records the size of the request in the statistics. * * @returns nothing. * @param pEndpoint The endpoint to register the request size for. * @param cbReq Size of the request. */ static void pdmR3AsyncCompletionStatisticsRecordSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, size_t cbReq) { if (cbReq < 512) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSizeSmaller512); else if (cbReq < _1K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize512To1K); else if (cbReq < _2K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize1KTo2K); else if (cbReq < _4K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize2KTo4K); else if (cbReq < _8K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize4KTo8K); else if (cbReq < _16K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize8KTo16K); else if (cbReq < _32K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize16KTo32K); else if (cbReq < _64K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize32KTo64K); else if (cbReq < _128K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize64KTo128K); else if (cbReq < _256K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize128KTo256K); else if (cbReq < _512K) STAM_REL_COUNTER_INC(&pEndpoint->StatReqSize256KTo512K); else STAM_REL_COUNTER_INC(&pEndpoint->StatReqSizeOver512K); if (cbReq & ((size_t)512 - 1)) STAM_REL_COUNTER_INC(&pEndpoint->StatReqsUnaligned512); else if (cbReq & ((size_t)_4K - 1)) STAM_REL_COUNTER_INC(&pEndpoint->StatReqsUnaligned4K); else if (cbReq & ((size_t)_8K - 1)) STAM_REL_COUNTER_INC(&pEndpoint->StatReqsUnaligned8K); } /** * Records the required processing time of a request. * * @returns nothing. * @param pEndpoint The endpoint. * @param cNsRun The request time in nanoseconds. */ static void pdmR3AsyncCompletionStatisticsRecordCompletionTime(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cNsRun) { PSTAMCOUNTER pStatCounter; if (cNsRun < RT_NS_1US) pStatCounter = &pEndpoint->StatTaskRunTimesNs[cNsRun / (RT_NS_1US / 10)]; else if (cNsRun < RT_NS_1MS) pStatCounter = &pEndpoint->StatTaskRunTimesUs[cNsRun / (RT_NS_1MS / 10)]; else if (cNsRun < RT_NS_1SEC) pStatCounter = &pEndpoint->StatTaskRunTimesMs[cNsRun / (RT_NS_1SEC / 10)]; else if (cNsRun < RT_NS_1SEC_64*100) pStatCounter = &pEndpoint->StatTaskRunTimesSec[cNsRun / (RT_NS_1SEC_64*100 / 10)]; else pStatCounter = &pEndpoint->StatTaskRunOver100Sec; STAM_REL_COUNTER_INC(pStatCounter); STAM_REL_COUNTER_INC(&pEndpoint->StatIoOpsCompleted); pEndpoint->cIoOpsCompleted++; uint64_t tsMsCur = RTTimeMilliTS(); uint64_t tsInterval = tsMsCur - pEndpoint->tsIntervalStartMs; if (tsInterval >= 1000) { pEndpoint->StatIoOpsPerSec.c = pEndpoint->cIoOpsCompleted / (tsInterval / 1000); pEndpoint->tsIntervalStartMs = tsMsCur; pEndpoint->cIoOpsCompleted = 0; } } /** * Registers advanced statistics for the given endpoint. * * @returns VBox status code. * @param pEndpoint The endpoint to register the advanced statistics for. */ static int pdmR3AsyncCompletionStatisticsRegister(PPDMASYNCCOMPLETIONENDPOINT pEndpoint) { int rc = VINF_SUCCESS; PVM pVM = pEndpoint->pEpClass->pVM; pEndpoint->tsIntervalStartMs = RTTimeMilliTS(); for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesNs) && RT_SUCCESS(rc); i++) rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunTimesNs[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "Nanosecond resolution runtime statistics", "/PDM/AsyncCompletion/File/%s/%d/TaskRun1Ns-%u-%u", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId, i*100, i*100+100-1); for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesUs) && RT_SUCCESS(rc); i++) rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunTimesUs[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "Microsecond resolution runtime statistics", "/PDM/AsyncCompletion/File/%s/%d/TaskRun2MicroSec-%u-%u", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId, i*100, i*100+100-1); for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesMs) && RT_SUCCESS(rc); i++) rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunTimesMs[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "Milliseconds resolution runtime statistics", "/PDM/AsyncCompletion/File/%s/%d/TaskRun3Ms-%u-%u", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId, i*100, i*100+100-1); for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesMs) && RT_SUCCESS(rc); i++) rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunTimesSec[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "Second resolution runtime statistics", "/PDM/AsyncCompletion/File/%s/%d/TaskRun4Sec-%u-%u", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId, i*10, i*10+10-1); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunOver100Sec, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "Tasks which ran more than 100sec", "/PDM/AsyncCompletion/File/%s/%d/TaskRunSecGreater100Sec", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatIoOpsPerSec, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed I/O operations per second", "/PDM/AsyncCompletion/File/%s/%d/IoOpsPerSec", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatIoOpsStarted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Started I/O operations for this endpoint", "/PDM/AsyncCompletion/File/%s/%d/IoOpsStarted", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatIoOpsCompleted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Completed I/O operations for this endpoint", "/PDM/AsyncCompletion/File/%s/%d/IoOpsCompleted", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSizeSmaller512, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size smaller than 512 bytes", "/PDM/AsyncCompletion/File/%s/%d/ReqSizeSmaller512", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize512To1K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 512 bytes and 1KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize512To1K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize1KTo2K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 1KB and 2KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize1KTo2K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize2KTo4K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 2KB and 4KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize2KTo4K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize4KTo8K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 4KB and 8KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize4KTo8K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize8KTo16K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 8KB and 16KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize8KTo16K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize16KTo32K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 16KB and 32KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize16KTo32K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize32KTo64K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 32KB and 64KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize32KTo64K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize64KTo128K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 64KB and 128KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize64KTo128K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize128KTo256K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 128KB and 256KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize128KTo256K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSize256KTo512K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size between 256KB and 512KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSize256KTo512K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqSizeOver512K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests with a size over 512KB", "/PDM/AsyncCompletion/File/%s/%d/ReqSizeOver512K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqsUnaligned512, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests which size is not aligned to 512 bytes", "/PDM/AsyncCompletion/File/%s/%d/ReqsUnaligned512", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqsUnaligned4K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests which size is not aligned to 4KB", "/PDM/AsyncCompletion/File/%s/%d/ReqsUnaligned4K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); if (RT_SUCCESS(rc)) rc = STAMR3RegisterF(pVM, &pEndpoint->StatReqsUnaligned8K, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of requests which size is not aligned to 8KB", "/PDM/AsyncCompletion/File/%s/%d/ReqsUnaligned8K", RTPathFilename(pEndpoint->pszUri), pEndpoint->iStatId); return rc; } /** * Deregisters advanced statistics for one endpoint. * * @returns nothing. * @param pEndpoint The endpoint to deregister the advanced statistics for. */ static void pdmR3AsyncCompletionStatisticsDeregister(PPDMASYNCCOMPLETIONENDPOINT pEndpoint) { /* I hope this doesn't remove too much... */ STAMR3DeregisterF(pEndpoint->pEpClass->pVM->pUVM, "/PDM/AsyncCompletion/File/%s/*", RTPathFilename(pEndpoint->pszUri)); } /** * Initialize the async completion manager. * * @returns VBox status code * @param pVM The cross context VM structure. */ int pdmR3AsyncCompletionInit(PVM pVM) { LogFlowFunc((": pVM=%p\n", pVM)); VM_ASSERT_EMT(pVM); PCFGMNODE pCfgRoot = CFGMR3GetRoot(pVM); PCFGMNODE pCfgAsyncCompletion = CFGMR3GetChild(CFGMR3GetChild(pCfgRoot, "PDM"), "AsyncCompletion"); int rc = pdmR3AsyncCompletionEpClassInit(pVM, &g_PDMAsyncCompletionEndpointClassFile, pCfgAsyncCompletion); LogFlowFunc((": pVM=%p rc=%Rrc\n", pVM, rc)); return rc; } /** * Terminates the async completion manager. * * @returns VBox status code * @param pVM The cross context VM structure. */ int pdmR3AsyncCompletionTerm(PVM pVM) { LogFlowFunc((": pVM=%p\n", pVM)); PUVM pUVM = pVM->pUVM; for (size_t i = 0; i < RT_ELEMENTS(pUVM->pdm.s.apAsyncCompletionEndpointClass); i++) if (pUVM->pdm.s.apAsyncCompletionEndpointClass[i]) pdmR3AsyncCompletionEpClassTerminate(pUVM->pdm.s.apAsyncCompletionEndpointClass[i]); return VINF_SUCCESS; } /** * Resume worker for the async completion manager. * * @returns nothing. * @param pVM The cross context VM structure. */ void pdmR3AsyncCompletionResume(PVM pVM) { LogFlowFunc((": pVM=%p\n", pVM)); PUVM pUVM = pVM->pUVM; /* Log the bandwidth groups and all assigned endpoints. */ for (size_t i = 0; i < RT_ELEMENTS(pUVM->pdm.s.apAsyncCompletionEndpointClass); i++) if (pUVM->pdm.s.apAsyncCompletionEndpointClass[i]) { PPDMASYNCCOMPLETIONEPCLASS pEpClass = pUVM->pdm.s.apAsyncCompletionEndpointClass[i]; PPDMACBWMGR pBwMgr = pEpClass->pBwMgrsHead; PPDMASYNCCOMPLETIONENDPOINT pEp; if (pBwMgr) LogRel(("AIOMgr: Bandwidth groups for class '%s'\n", i == PDMASYNCCOMPLETIONEPCLASSTYPE_FILE ? "File" : "")); while (pBwMgr) { LogRel(("AIOMgr: Id: %s\n", pBwMgr->pszId)); LogRel(("AIOMgr: Max: %u B/s\n", pBwMgr->cbTransferPerSecMax)); LogRel(("AIOMgr: Start: %u B/s\n", pBwMgr->cbTransferPerSecStart)); LogRel(("AIOMgr: Step: %u B/s\n", pBwMgr->cbTransferPerSecStep)); LogRel(("AIOMgr: Endpoints:\n")); pEp = pEpClass->pEndpointsHead; while (pEp) { if (pEp->pBwMgr == pBwMgr) LogRel(("AIOMgr: %s\n", pEp->pszUri)); pEp = pEp->pNext; } pBwMgr = pBwMgr->pNext; } /* Print all endpoints without assigned bandwidth groups. */ pEp = pEpClass->pEndpointsHead; if (pEp) LogRel(("AIOMgr: Endpoints without assigned bandwidth groups:\n")); while (pEp) { if (!pEp->pBwMgr) LogRel(("AIOMgr: %s\n", pEp->pszUri)); pEp = pEp->pNext; } } } /** * Tries to get a free task from the endpoint or class cache * allocating the task if it fails. * * @returns Pointer to a new and initialized task or NULL * @param pEndpoint The endpoint the task is for. * @param pvUser Opaque user data for the task. */ static PPDMASYNCCOMPLETIONTASK pdmR3AsyncCompletionGetTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, void *pvUser) { PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass; PPDMASYNCCOMPLETIONTASK pTask = (PPDMASYNCCOMPLETIONTASK)RTMemCacheAlloc(pEndpointClass->hMemCacheTasks); if (RT_LIKELY(pTask)) { /* Initialize common parts. */ pTask->pvUser = pvUser; pTask->pEndpoint = pEndpoint; /* Clear list pointers for safety. */ pTask->pPrev = NULL; pTask->pNext = NULL; pTask->tsNsStart = RTTimeNanoTS(); STAM_REL_COUNTER_INC(&pEndpoint->StatIoOpsStarted); } return pTask; } /** * Puts a task in one of the caches. * * @returns nothing. * @param pEndpoint The endpoint the task belongs to. * @param pTask The task to cache. */ static void pdmR3AsyncCompletionPutTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, PPDMASYNCCOMPLETIONTASK pTask) { PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass; uint64_t cNsRun = RTTimeNanoTS() - pTask->tsNsStart; if (RT_UNLIKELY(cNsRun >= RT_NS_10SEC)) LogRel(("AsyncCompletion: Task %#p completed after %llu seconds\n", pTask, cNsRun / RT_NS_1SEC)); if (pEndpointClass->fGatherAdvancedStatistics) pdmR3AsyncCompletionStatisticsRecordCompletionTime(pEndpoint, cNsRun); RTMemCacheFree(pEndpointClass->hMemCacheTasks, pTask); } static unsigned pdmR3AsyncCompletionGetStatId(PPDMASYNCCOMPLETIONEPCLASS pEndpointClass, const char *pszUri) { PPDMASYNCCOMPLETIONENDPOINT pEndpoint = pEndpointClass->pEndpointsHead; const char *pszFilename = RTPathFilename(pszUri); unsigned iStatId = 0; while (pEndpoint) { if ( !RTStrCmp(RTPathFilename(pEndpoint->pszUri), pszFilename) && pEndpoint->iStatId >= iStatId) iStatId = pEndpoint->iStatId + 1; pEndpoint = pEndpoint->pNext; } return iStatId; } /** * Opens a file as an async completion endpoint. * * @returns VBox status code. * @param ppEndpoint Where to store the opaque endpoint handle on success. * @param pszFilename Path to the file which is to be opened. (UTF-8) * @param fFlags Open flags, see grp_pdmacep_file_flags. * @param pTemplate Handle to the completion callback template to use * for this end point. */ VMMR3DECL(int) PDMR3AsyncCompletionEpCreateForFile(PPPDMASYNCCOMPLETIONENDPOINT ppEndpoint, const char *pszFilename, uint32_t fFlags, PPDMASYNCCOMPLETIONTEMPLATE pTemplate) { LogFlowFunc((": ppEndpoint=%p pszFilename=%p{%s} fFlags=%u pTemplate=%p\n", ppEndpoint, pszFilename, pszFilename, fFlags, pTemplate)); /* Sanity checks. */ AssertPtrReturn(ppEndpoint, VERR_INVALID_POINTER); AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); AssertPtrReturn(pTemplate, VERR_INVALID_POINTER); /* Check that the flags are valid. */ AssertReturn(((~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_DONT_LOCK | PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED) & fFlags) == 0), VERR_INVALID_PARAMETER); PVM pVM = pTemplate->pVM; PUVM pUVM = pVM->pUVM; PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE]; PPDMASYNCCOMPLETIONENDPOINT pEndpoint = NULL; AssertMsg(pEndpointClass, ("File endpoint class was not initialized\n")); /* Create an endpoint. */ int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION, pEndpointClass->pEndpointOps->cbEndpoint, (void **)&pEndpoint); if (RT_SUCCESS(rc)) { /* Initialize common parts. */ pEndpoint->pNext = NULL; pEndpoint->pPrev = NULL; pEndpoint->pEpClass = pEndpointClass; pEndpoint->pTemplate = pTemplate; pEndpoint->pszUri = RTStrDup(pszFilename); pEndpoint->iStatId = pdmR3AsyncCompletionGetStatId(pEndpointClass, pszFilename); pEndpoint->pBwMgr = NULL; if ( pEndpoint->pszUri && RT_SUCCESS(rc)) { /* Call the initializer for the endpoint. */ rc = pEndpointClass->pEndpointOps->pfnEpInitialize(pEndpoint, pszFilename, fFlags); if (RT_SUCCESS(rc)) { if (pEndpointClass->fGatherAdvancedStatistics) rc = pdmR3AsyncCompletionStatisticsRegister(pEndpoint); if (RT_SUCCESS(rc)) { /* Link it into the list of endpoints. */ rc = RTCritSectEnter(&pEndpointClass->CritSect); AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc)); pEndpoint->pNext = pEndpointClass->pEndpointsHead; if (pEndpointClass->pEndpointsHead) pEndpointClass->pEndpointsHead->pPrev = pEndpoint; pEndpointClass->pEndpointsHead = pEndpoint; pEndpointClass->cEndpoints++; rc = RTCritSectLeave(&pEndpointClass->CritSect); AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc)); /* Reference the template. */ ASMAtomicIncU32(&pTemplate->cUsed); *ppEndpoint = pEndpoint; LogFlowFunc((": Created endpoint for %s\n", pszFilename)); return VINF_SUCCESS; } else pEndpointClass->pEndpointOps->pfnEpClose(pEndpoint); if (pEndpointClass->fGatherAdvancedStatistics) pdmR3AsyncCompletionStatisticsDeregister(pEndpoint); } RTStrFree(pEndpoint->pszUri); } MMR3HeapFree(pEndpoint); } LogFlowFunc((": Creation of endpoint for %s failed: rc=%Rrc\n", pszFilename, rc)); return rc; } /** * Closes a endpoint waiting for any pending tasks to finish. * * @returns nothing. * @param pEndpoint Handle of the endpoint. */ VMMR3DECL(void) PDMR3AsyncCompletionEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint) { LogFlowFunc((": pEndpoint=%p\n", pEndpoint)); /* Sanity checks. */ AssertReturnVoid(RT_VALID_PTR(pEndpoint)); PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass; pEndpointClass->pEndpointOps->pfnEpClose(pEndpoint); /* Drop reference from the template. */ ASMAtomicDecU32(&pEndpoint->pTemplate->cUsed); /* Unlink the endpoint from the list. */ int rc = RTCritSectEnter(&pEndpointClass->CritSect); AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc)); PPDMASYNCCOMPLETIONENDPOINT pEndpointNext = pEndpoint->pNext; PPDMASYNCCOMPLETIONENDPOINT pEndpointPrev = pEndpoint->pPrev; if (pEndpointPrev) pEndpointPrev->pNext = pEndpointNext; else pEndpointClass->pEndpointsHead = pEndpointNext; if (pEndpointNext) pEndpointNext->pPrev = pEndpointPrev; pEndpointClass->cEndpoints--; rc = RTCritSectLeave(&pEndpointClass->CritSect); AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc)); if (pEndpointClass->fGatherAdvancedStatistics) pdmR3AsyncCompletionStatisticsDeregister(pEndpoint); RTStrFree(pEndpoint->pszUri); MMR3HeapFree(pEndpoint); } /** * Creates a read task on the given endpoint. * * @returns VBox status code. * @param pEndpoint The file endpoint to read from. * @param off Where to start reading from. * @param paSegments Scatter gather list to store the data in. * @param cSegments Number of segments in the list. * @param cbRead The overall number of bytes to read. * @param pvUser Opaque user data returned in the completion callback * upon completion of the task. * @param ppTask Where to store the task handle on success. */ VMMR3DECL(int) PDMR3AsyncCompletionEpRead(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off, PCRTSGSEG paSegments, unsigned cSegments, size_t cbRead, void *pvUser, PPPDMASYNCCOMPLETIONTASK ppTask) { AssertPtrReturn(pEndpoint, VERR_INVALID_POINTER); AssertPtrReturn(paSegments, VERR_INVALID_POINTER); AssertPtrReturn(ppTask, VERR_INVALID_POINTER); AssertReturn(cSegments > 0, VERR_INVALID_PARAMETER); AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER); AssertReturn(off >= 0, VERR_INVALID_PARAMETER); PPDMASYNCCOMPLETIONTASK pTask; pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser); if (!pTask) return VERR_NO_MEMORY; int rc = pEndpoint->pEpClass->pEndpointOps->pfnEpRead(pTask, pEndpoint, off, paSegments, cSegments, cbRead); if (RT_SUCCESS(rc)) { if (pEndpoint->pEpClass->fGatherAdvancedStatistics) pdmR3AsyncCompletionStatisticsRecordSize(pEndpoint, cbRead); *ppTask = pTask; } else pdmR3AsyncCompletionPutTask(pEndpoint, pTask); return rc; } /** * Creates a write task on the given endpoint. * * @returns VBox status code. * @param pEndpoint The file endpoint to write to. * @param off Where to start writing at. * @param paSegments Scatter gather list of the data to write. * @param cSegments Number of segments in the list. * @param cbWrite The overall number of bytes to write. * @param pvUser Opaque user data returned in the completion callback * upon completion of the task. * @param ppTask Where to store the task handle on success. */ VMMR3DECL(int) PDMR3AsyncCompletionEpWrite(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off, PCRTSGSEG paSegments, unsigned cSegments, size_t cbWrite, void *pvUser, PPPDMASYNCCOMPLETIONTASK ppTask) { AssertPtrReturn(pEndpoint, VERR_INVALID_POINTER); AssertPtrReturn(paSegments, VERR_INVALID_POINTER); AssertPtrReturn(ppTask, VERR_INVALID_POINTER); AssertReturn(cSegments > 0, VERR_INVALID_PARAMETER); AssertReturn(cbWrite > 0, VERR_INVALID_PARAMETER); AssertReturn(off >= 0, VERR_INVALID_PARAMETER); PPDMASYNCCOMPLETIONTASK pTask; pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser); if (!pTask) return VERR_NO_MEMORY; int rc = pEndpoint->pEpClass->pEndpointOps->pfnEpWrite(pTask, pEndpoint, off, paSegments, cSegments, cbWrite); if (RT_SUCCESS(rc)) { if (pEndpoint->pEpClass->fGatherAdvancedStatistics) pdmR3AsyncCompletionStatisticsRecordSize(pEndpoint, cbWrite); *ppTask = pTask; } else pdmR3AsyncCompletionPutTask(pEndpoint, pTask); return rc; } /** * Creates a flush task on the given endpoint. * * Every read and write task initiated before the flush task is * finished upon completion of this task. * * @returns VBox status code. * @param pEndpoint The file endpoint to flush. * @param pvUser Opaque user data returned in the completion callback * upon completion of the task. * @param ppTask Where to store the task handle on success. */ VMMR3DECL(int) PDMR3AsyncCompletionEpFlush(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, void *pvUser, PPPDMASYNCCOMPLETIONTASK ppTask) { AssertPtrReturn(pEndpoint, VERR_INVALID_POINTER); AssertPtrReturn(ppTask, VERR_INVALID_POINTER); PPDMASYNCCOMPLETIONTASK pTask; pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser); if (!pTask) return VERR_NO_MEMORY; int rc = pEndpoint->pEpClass->pEndpointOps->pfnEpFlush(pTask, pEndpoint); if (RT_SUCCESS(rc)) *ppTask = pTask; else pdmR3AsyncCompletionPutTask(pEndpoint, pTask); return rc; } /** * Queries the size of an endpoint. * * Not that some endpoints may not support this and will return an error * (sockets for example). * * @returns VBox status code. * @retval VERR_NOT_SUPPORTED if the endpoint does not support this operation. * @param pEndpoint The file endpoint. * @param pcbSize Where to store the size of the endpoint. */ VMMR3DECL(int) PDMR3AsyncCompletionEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize) { AssertPtrReturn(pEndpoint, VERR_INVALID_POINTER); AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); if (pEndpoint->pEpClass->pEndpointOps->pfnEpGetSize) return pEndpoint->pEpClass->pEndpointOps->pfnEpGetSize(pEndpoint, pcbSize); return VERR_NOT_SUPPORTED; } /** * Sets the size of an endpoint. * * Not that some endpoints may not support this and will return an error * (sockets for example). * * @returns VBox status code. * @retval VERR_NOT_SUPPORTED if the endpoint does not support this operation. * @param pEndpoint The file endpoint. * @param cbSize The size to set. * * @note PDMR3AsyncCompletionEpFlush should be called before this operation is executed. */ VMMR3DECL(int) PDMR3AsyncCompletionEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize) { AssertPtrReturn(pEndpoint, VERR_INVALID_POINTER); if (pEndpoint->pEpClass->pEndpointOps->pfnEpSetSize) return pEndpoint->pEpClass->pEndpointOps->pfnEpSetSize(pEndpoint, cbSize); return VERR_NOT_SUPPORTED; } /** * Assigns or removes a bandwidth control manager to/from the endpoint. * * @returns VBox status code. * @param pEndpoint The endpoint. * @param pszBwMgr The identifer of the new bandwidth manager to assign * or NULL to remove the current one. */ VMMR3DECL(int) PDMR3AsyncCompletionEpSetBwMgr(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, const char *pszBwMgr) { AssertPtrReturn(pEndpoint, VERR_INVALID_POINTER); PPDMACBWMGR pBwMgrOld = NULL; PPDMACBWMGR pBwMgrNew = NULL; int rc = VINF_SUCCESS; if (pszBwMgr) { pBwMgrNew = pdmacBwMgrFindById(pEndpoint->pEpClass, pszBwMgr); if (pBwMgrNew) pdmacBwMgrRetain(pBwMgrNew); else rc = VERR_NOT_FOUND; } if (RT_SUCCESS(rc)) { pBwMgrOld = ASMAtomicXchgPtrT(&pEndpoint->pBwMgr, pBwMgrNew, PPDMACBWMGR); if (pBwMgrOld) pdmacBwMgrRelease(pBwMgrOld); } return rc; } /** * Cancels an async completion task. * * If you want to use this method, you have to take great create to make sure * you will never attempt cancel a task which has been completed. Since there is * no reference counting or anything on the task it self, you have to serialize * the cancelation and completion paths such that the aren't racing one another. * * @returns VBox status code * @param pTask The Task to cancel. */ VMMR3DECL(int) PDMR3AsyncCompletionTaskCancel(PPDMASYNCCOMPLETIONTASK pTask) { NOREF(pTask); return VERR_NOT_IMPLEMENTED; } /** * Changes the limit of a bandwidth manager for file endpoints to the given value. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param pszBwMgr The identifer of the bandwidth manager to change. * @param cbMaxNew The new maximum for the bandwidth manager in bytes/sec. */ VMMR3DECL(int) PDMR3AsyncCompletionBwMgrSetMaxForFile(PUVM pUVM, const char *pszBwMgr, uint32_t cbMaxNew) { UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertPtrReturn(pszBwMgr, VERR_INVALID_POINTER); int rc = VINF_SUCCESS; PPDMASYNCCOMPLETIONEPCLASS pEpClass = pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE]; PPDMACBWMGR pBwMgr = pdmacBwMgrFindById(pEpClass, pszBwMgr); if (pBwMgr) { /* * Set the new value for the start and max value to let the manager pick up * the new limit immediately. */ ASMAtomicWriteU32(&pBwMgr->cbTransferPerSecMax, cbMaxNew); ASMAtomicWriteU32(&pBwMgr->cbTransferPerSecStart, cbMaxNew); } else rc = VERR_NOT_FOUND; return rc; }