diff options
Diffstat (limited to 'src/VBox/Devices/USB/VUSBUrbPool.cpp')
-rw-r--r-- | src/VBox/Devices/USB/VUSBUrbPool.cpp | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/src/VBox/Devices/USB/VUSBUrbPool.cpp b/src/VBox/Devices/USB/VUSBUrbPool.cpp new file mode 100644 index 00000000..3353c86d --- /dev/null +++ b/src/VBox/Devices/USB/VUSBUrbPool.cpp @@ -0,0 +1,245 @@ +/* $Id: VUSBUrbPool.cpp $ */ +/** @file + * Virtual USB - URB pool. + */ + +/* + * Copyright (C) 2016-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 <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DRV_VUSB +#include <VBox/log.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/critsect.h> + +#include "VUSBInternal.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ + +/** Maximum age for one URB. */ +#define VUSBURB_AGE_MAX 10 + +/** Convert from an URB to the URB header. */ +#define VUSBURBPOOL_URB_2_URBHDR(a_pUrb) RT_FROM_MEMBER(a_pUrb, VUSBURBHDR, Urb); + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * URB header not visible to the caller allocating an URB + * and only for internal tracking. + */ +typedef struct VUSBURBHDR +{ + /** List node for keeping the URB in the free list. */ + RTLISTNODE NdFree; + /** Size of the data allocated for the URB (Only the variable part including the + * HCI and TDs). */ + size_t cbAllocated; + /** Age of the URB waiting on the list, if it is waiting for too long without being used + * again it will be freed. */ + uint32_t cAge; +#if HC_ARCH_BITS == 64 + uint32_t u32Alignment0; +#endif + /** The embedded URB. */ + VUSBURB Urb; +} VUSBURBHDR; +/** Pointer to a URB header. */ +typedef VUSBURBHDR *PVUSBURBHDR; + +AssertCompileSizeAlignment(VUSBURBHDR, 8); + + + +DECLHIDDEN(int) vusbUrbPoolInit(PVUSBURBPOOL pUrbPool) +{ + int rc = RTCritSectInit(&pUrbPool->CritSectPool); + if (RT_SUCCESS(rc)) + { + pUrbPool->cUrbsInPool = 0; + for (unsigned i = 0; i < RT_ELEMENTS(pUrbPool->aLstFreeUrbs); i++) + RTListInit(&pUrbPool->aLstFreeUrbs[i]); + } + + return rc; +} + + +DECLHIDDEN(void) vusbUrbPoolDestroy(PVUSBURBPOOL pUrbPool) +{ + RTCritSectEnter(&pUrbPool->CritSectPool); + for (unsigned i = 0; i < RT_ELEMENTS(pUrbPool->aLstFreeUrbs); i++) + { + PVUSBURBHDR pHdr, pHdrNext; + RTListForEachSafe(&pUrbPool->aLstFreeUrbs[i], pHdr, pHdrNext, VUSBURBHDR, NdFree) + { + RTListNodeRemove(&pHdr->NdFree); + + pHdr->cbAllocated = 0; + pHdr->Urb.u32Magic = 0; + pHdr->Urb.enmState = VUSBURBSTATE_INVALID; + RTMemFree(pHdr); + } + } + RTCritSectLeave(&pUrbPool->CritSectPool); + RTCritSectDelete(&pUrbPool->CritSectPool); +} + + +DECLHIDDEN(PVUSBURB) vusbUrbPoolAlloc(PVUSBURBPOOL pUrbPool, VUSBXFERTYPE enmType, + VUSBDIRECTION enmDir, size_t cbData, size_t cbHci, + size_t cbHciTd, unsigned cTds) +{ + Assert((uint32_t)cbData == cbData); + Assert((uint32_t)cbHci == cbHci); + + /* + * Reuse or allocate a new URB. + */ + /** @todo The allocations should be done by the device, at least as an option, since the devices + * frequently wish to associate their own stuff with the in-flight URB or need special buffering + * (isochronous on Darwin for instance). */ + /* Get the required amount of additional memory to allocate the whole state. */ + size_t cbMem = cbData + sizeof(VUSBURBVUSBINT) + cbHci + cTds * cbHciTd; + + AssertReturn((size_t)enmType < RT_ELEMENTS(pUrbPool->aLstFreeUrbs), NULL); + + RTCritSectEnter(&pUrbPool->CritSectPool); + PVUSBURBHDR pHdr = NULL; + PVUSBURBHDR pIt, pItNext; + RTListForEachSafe(&pUrbPool->aLstFreeUrbs[enmType], pIt, pItNext, VUSBURBHDR, NdFree) + { + if (pIt->cbAllocated >= cbMem) + { + RTListNodeRemove(&pIt->NdFree); + Assert(pIt->Urb.u32Magic == VUSBURB_MAGIC); + Assert(pIt->Urb.enmState == VUSBURBSTATE_FREE); + /* + * If the allocation is far too big we increase the age counter too + * so we don't waste memory for a lot of small transfers + */ + if (pIt->cbAllocated >= 2 * cbMem) + pIt->cAge++; + else + pIt->cAge = 0; + pHdr = pIt; + break; + } + else + { + /* Increase age and free if it reached a threshold. */ + pIt->cAge++; + if (pIt->cAge == VUSBURB_AGE_MAX) + { + RTListNodeRemove(&pIt->NdFree); + ASMAtomicDecU32(&pUrbPool->cUrbsInPool); + RTMemFree(pIt); + } + } + } + + if (!pHdr) + { + /* allocate a new one. */ + size_t cbDataAllocated = cbMem <= _4K ? RT_ALIGN_32(cbMem, _1K) + : cbMem <= _32K ? RT_ALIGN_32(cbMem, _4K) + : RT_ALIGN_32(cbMem, 16*_1K); + + pHdr = (PVUSBURBHDR)RTMemAllocZ(RT_UOFFSETOF_DYN(VUSBURBHDR, Urb.abData[cbDataAllocated])); + if (RT_UNLIKELY(!pHdr)) + { + RTCritSectLeave(&pUrbPool->CritSectPool); + AssertLogRelFailedReturn(NULL); + } + + pHdr->cbAllocated = cbDataAllocated; + pHdr->cAge = 0; + ASMAtomicIncU32(&pUrbPool->cUrbsInPool); + } + RTCritSectLeave(&pUrbPool->CritSectPool); + + Assert(pHdr->cbAllocated >= cbMem); + + /* + * (Re)init the URB + */ + uint32_t offAlloc = (uint32_t)cbData; + PVUSBURB pUrb = &pHdr->Urb; + pUrb->u32Magic = VUSBURB_MAGIC; + pUrb->enmState = VUSBURBSTATE_ALLOCATED; + pUrb->fCompleting = false; + pUrb->pszDesc = NULL; + pUrb->pVUsb = (PVUSBURBVUSB)&pUrb->abData[offAlloc]; + offAlloc += sizeof(VUSBURBVUSBINT); + pUrb->pVUsb->pUrb = pUrb; + pUrb->pVUsb->pvFreeCtx = NULL; + pUrb->pVUsb->pfnFree = NULL; + pUrb->pVUsb->pCtrlUrb = NULL; + pUrb->pVUsb->u64SubmitTS = 0; + pUrb->Dev.pvPrivate = NULL; + pUrb->Dev.pNext = NULL; + pUrb->EndPt = UINT8_MAX; + pUrb->enmType = enmType; + pUrb->enmDir = enmDir; + pUrb->fShortNotOk = false; + pUrb->enmStatus = VUSBSTATUS_INVALID; + pUrb->cbData = (uint32_t)cbData; + pUrb->pHci = cbHci ? (PVUSBURBHCI)&pUrb->abData[offAlloc] : NULL; + offAlloc += (uint32_t)cbHci; + pUrb->paTds = (cbHciTd && cTds) ? (PVUSBURBHCITD)&pUrb->abData[offAlloc] : NULL; + + return pUrb; +} + + +DECLHIDDEN(void) vusbUrbPoolFree(PVUSBURBPOOL pUrbPool, PVUSBURB pUrb) +{ + PVUSBURBHDR pHdr = VUSBURBPOOL_URB_2_URBHDR(pUrb); + + /* URBs which aged too much because they are too big are freed. */ + if (pHdr->cAge == VUSBURB_AGE_MAX) + { + ASMAtomicDecU32(&pUrbPool->cUrbsInPool); + RTMemFree(pHdr); + } + else + { + /* Put it into the list of free URBs. */ + VUSBXFERTYPE enmType = pUrb->enmType; + AssertReturnVoid((size_t)enmType < RT_ELEMENTS(pUrbPool->aLstFreeUrbs)); + RTCritSectEnter(&pUrbPool->CritSectPool); + pUrb->enmState = VUSBURBSTATE_FREE; + RTListAppend(&pUrbPool->aLstFreeUrbs[enmType], &pHdr->NdFree); + RTCritSectLeave(&pUrbPool->CritSectPool); + } +} + |