VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/VUSBUrbPool.cpp@ 105745

Last change on this file since 105745 was 99368, checked in by vboxsync, 20 months ago

VUSB: Better cleanup when recycling URB memory (see bugref:10140).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.1 KB
Line 
1/* $Id: VUSBUrbPool.cpp 99368 2023-04-10 18:52:44Z vboxsync $ */
2/** @file
3 * Virtual USB - URB pool.
4 */
5
6/*
7 * Copyright (C) 2016-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_VUSB
33#include <VBox/log.h>
34#include <iprt/errcore.h>
35#include <iprt/mem.h>
36#include <iprt/critsect.h>
37
38#include "VUSBInternal.h"
39
40
41/*********************************************************************************************************************************
42* Defined Constants And Macros *
43*********************************************************************************************************************************/
44
45/** Maximum age for one URB. */
46#define VUSBURB_AGE_MAX 10
47
48/** Convert from an URB to the URB header. */
49#define VUSBURBPOOL_URB_2_URBHDR(a_pUrb) RT_FROM_MEMBER(a_pUrb, VUSBURBHDR, Urb);
50
51
52/*********************************************************************************************************************************
53* Structures and Typedefs *
54*********************************************************************************************************************************/
55
56/**
57 * URB header not visible to the caller allocating an URB
58 * and only for internal tracking.
59 */
60typedef struct VUSBURBHDR
61{
62 /** List node for keeping the URB in the free list. */
63 RTLISTNODE NdFree;
64 /** Size of the data allocated for the URB (Only the variable part including the
65 * HCI and TDs). */
66 size_t cbAllocated;
67 /** Age of the URB waiting on the list, if it is waiting for too long without being used
68 * again it will be freed. */
69 uint32_t cAge;
70#if HC_ARCH_BITS == 64
71 uint32_t u32Alignment0;
72#endif
73 /** The embedded URB. */
74 VUSBURB Urb;
75} VUSBURBHDR;
76/** Pointer to a URB header. */
77typedef VUSBURBHDR *PVUSBURBHDR;
78
79AssertCompileSizeAlignment(VUSBURBHDR, 8);
80
81
82
83DECLHIDDEN(int) vusbUrbPoolInit(PVUSBURBPOOL pUrbPool)
84{
85 int rc = RTCritSectInit(&pUrbPool->CritSectPool);
86 if (RT_SUCCESS(rc))
87 {
88 pUrbPool->cUrbsInPool = 0;
89 for (unsigned i = 0; i < RT_ELEMENTS(pUrbPool->aLstFreeUrbs); i++)
90 RTListInit(&pUrbPool->aLstFreeUrbs[i]);
91 }
92
93 return rc;
94}
95
96
97DECLHIDDEN(void) vusbUrbPoolDestroy(PVUSBURBPOOL pUrbPool)
98{
99 RTCritSectEnter(&pUrbPool->CritSectPool);
100 for (unsigned i = 0; i < RT_ELEMENTS(pUrbPool->aLstFreeUrbs); i++)
101 {
102 PVUSBURBHDR pHdr, pHdrNext;
103 RTListForEachSafe(&pUrbPool->aLstFreeUrbs[i], pHdr, pHdrNext, VUSBURBHDR, NdFree)
104 {
105 RTListNodeRemove(&pHdr->NdFree);
106
107 pHdr->cbAllocated = 0;
108 pHdr->Urb.u32Magic = 0;
109 pHdr->Urb.enmState = VUSBURBSTATE_INVALID;
110 RTMemFree(pHdr);
111 }
112 }
113 RTCritSectLeave(&pUrbPool->CritSectPool);
114 RTCritSectDelete(&pUrbPool->CritSectPool);
115}
116
117
118DECLHIDDEN(PVUSBURB) vusbUrbPoolAlloc(PVUSBURBPOOL pUrbPool, VUSBXFERTYPE enmType,
119 VUSBDIRECTION enmDir, size_t cbData, size_t cbHci,
120 size_t cbHciTd, unsigned cTds)
121{
122 Assert((uint32_t)cbData == cbData);
123 Assert((uint32_t)cbHci == cbHci);
124
125 /*
126 * Reuse or allocate a new URB.
127 */
128 /** @todo The allocations should be done by the device, at least as an option, since the devices
129 * frequently wish to associate their own stuff with the in-flight URB or need special buffering
130 * (isochronous on Darwin for instance). */
131 /* Get the required amount of additional memory to allocate the whole state. */
132 size_t cbMem = cbData + sizeof(VUSBURBVUSBINT) + cbHci + cTds * cbHciTd;
133
134 AssertReturn((size_t)enmType < RT_ELEMENTS(pUrbPool->aLstFreeUrbs), NULL);
135
136 RTCritSectEnter(&pUrbPool->CritSectPool);
137 PVUSBURBHDR pHdr = NULL;
138 PVUSBURBHDR pIt, pItNext;
139 RTListForEachSafe(&pUrbPool->aLstFreeUrbs[enmType], pIt, pItNext, VUSBURBHDR, NdFree)
140 {
141 if (pIt->cbAllocated >= cbMem)
142 {
143 RTListNodeRemove(&pIt->NdFree);
144 Assert(pIt->Urb.u32Magic == VUSBURB_MAGIC);
145 Assert(pIt->Urb.enmState == VUSBURBSTATE_FREE);
146 /*
147 * If the allocation is far too big we increase the age counter too
148 * so we don't waste memory for a lot of small transfers
149 */
150 if (pIt->cbAllocated >= 2 * cbMem)
151 pIt->cAge++;
152 else
153 pIt->cAge = 0;
154 pHdr = pIt;
155 break;
156 }
157 else
158 {
159 /* Increase age and free if it reached a threshold. */
160 pIt->cAge++;
161 if (pIt->cAge == VUSBURB_AGE_MAX)
162 {
163 RTListNodeRemove(&pIt->NdFree);
164 ASMAtomicDecU32(&pUrbPool->cUrbsInPool);
165 pIt->cbAllocated = 0;
166 pIt->Urb.u32Magic = 0;
167 pIt->Urb.enmState = VUSBURBSTATE_INVALID;
168 RTMemFree(pIt);
169 }
170 }
171 }
172
173 if (!pHdr)
174 {
175 /* allocate a new one. */
176 size_t cbDataAllocated = cbMem <= _4K ? RT_ALIGN_32(cbMem, _1K)
177 : cbMem <= _32K ? RT_ALIGN_32(cbMem, _4K)
178 : RT_ALIGN_32(cbMem, 16*_1K);
179
180 pHdr = (PVUSBURBHDR)RTMemAllocZ(RT_UOFFSETOF_DYN(VUSBURBHDR, Urb.abData[cbDataAllocated]));
181 if (RT_UNLIKELY(!pHdr))
182 {
183 RTCritSectLeave(&pUrbPool->CritSectPool);
184 AssertLogRelFailedReturn(NULL);
185 }
186
187 pHdr->cbAllocated = cbDataAllocated;
188 pHdr->cAge = 0;
189 ASMAtomicIncU32(&pUrbPool->cUrbsInPool);
190 }
191 else
192 {
193 /* Paranoia: Clear memory that's part of the guest data buffer now
194 * but wasn't before. See @bugref{10410}.
195 */
196 if (cbData > pHdr->Urb.cbData)
197 {
198 memset(&pHdr->Urb.abData[pHdr->Urb.cbData], 0, cbData - pHdr->Urb.cbData);
199 }
200 }
201 RTCritSectLeave(&pUrbPool->CritSectPool);
202
203 Assert(pHdr->cbAllocated >= cbMem);
204
205 /*
206 * (Re)init the URB
207 */
208 uint32_t offAlloc = (uint32_t)cbData;
209 PVUSBURB pUrb = &pHdr->Urb;
210 pUrb->u32Magic = VUSBURB_MAGIC;
211 pUrb->enmState = VUSBURBSTATE_ALLOCATED;
212 pUrb->fCompleting = false;
213 pUrb->pszDesc = NULL;
214 pUrb->pVUsb = (PVUSBURBVUSB)&pUrb->abData[offAlloc];
215 offAlloc += sizeof(VUSBURBVUSBINT);
216 pUrb->pVUsb->pUrb = pUrb;
217 pUrb->pVUsb->pvFreeCtx = NULL;
218 pUrb->pVUsb->pfnFree = NULL;
219 pUrb->pVUsb->pCtrlUrb = NULL;
220 pUrb->pVUsb->u64SubmitTS = 0;
221 pUrb->Dev.pvPrivate = NULL;
222 pUrb->Dev.pNext = NULL;
223 pUrb->EndPt = UINT8_MAX;
224 pUrb->enmType = enmType;
225 pUrb->enmDir = enmDir;
226 pUrb->fShortNotOk = false;
227 pUrb->enmStatus = VUSBSTATUS_INVALID;
228 pUrb->cbData = (uint32_t)cbData;
229 pUrb->pHci = cbHci ? (PVUSBURBHCI)&pUrb->abData[offAlloc] : NULL;
230 offAlloc += (uint32_t)cbHci;
231 pUrb->paTds = (cbHciTd && cTds) ? (PVUSBURBHCITD)&pUrb->abData[offAlloc] : NULL;
232
233 return pUrb;
234}
235
236
237DECLHIDDEN(void) vusbUrbPoolFree(PVUSBURBPOOL pUrbPool, PVUSBURB pUrb)
238{
239 PVUSBURBHDR pHdr = VUSBURBPOOL_URB_2_URBHDR(pUrb);
240
241 /* URBs which aged too much because they are too big are freed. */
242 if (pHdr->cAge == VUSBURB_AGE_MAX)
243 {
244 ASMAtomicDecU32(&pUrbPool->cUrbsInPool);
245 pHdr->cbAllocated = 0;
246 pHdr->Urb.u32Magic = 0;
247 pHdr->Urb.enmState = VUSBURBSTATE_INVALID;
248 RTMemFree(pHdr);
249 }
250 else
251 {
252 /* Put it into the list of free URBs. */
253 VUSBXFERTYPE enmType = pUrb->enmType;
254 AssertReturnVoid((size_t)enmType < RT_ELEMENTS(pUrbPool->aLstFreeUrbs));
255 RTCritSectEnter(&pUrbPool->CritSectPool);
256 pUrb->enmState = VUSBURBSTATE_FREE;
257 RTListAppend(&pUrbPool->aLstFreeUrbs[enmType], &pHdr->NdFree);
258 RTCritSectLeave(&pUrbPool->CritSectPool);
259 }
260}
261
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette