VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/IOBufMgmt.cpp@ 59783

Last change on this file since 59783 was 59452, checked in by vboxsync, 9 years ago

Devices/IOBufMgmt: fixes properties

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.5 KB
Line 
1/* $Id: IOBufMgmt.cpp 59452 2016-01-25 09:30:48Z vboxsync $ */
2/** @file
3 * VBox storage devices: I/O buffer management API.
4 */
5
6/*
7 * Copyright (C) 2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include <VBox/cdefs.h>
18#include <VBox/err.h>
19#include <iprt/assert.h>
20#include <iprt/critsect.h>
21#include <iprt/mem.h>
22#include <iprt/memsafer.h>
23#include <iprt/sg.h>
24#include <iprt/asm.h>
25
26/** The minimum bin size to create - power of two!. */
27#define IOBUFMGR_BIN_SIZE_MIN _4K
28/** The maximum bin size to create - power of two!. */
29#define IOBUFMGR_BIN_SIZE_MAX _1M
30
31/** Pointer to the internal I/O buffer manager data. */
32typedef struct IOBUFMGRINT *PIOBUFMGRINT;
33
34/**
35 * Internal I/O buffer descriptor data.
36 */
37typedef struct IOBUFDESCINT
38{
39 /** Data segments. */
40 RTSGSEG aSegs[10];
41 /** Data segments used for the current allocation. */
42 unsigned cSegsUsed;
43 /** Pointer to I/O buffer manager. */
44 PIOBUFMGRINT pIoBufMgr;
45} IOBUFDESCINT;
46
47/**
48 * A
49 */
50typedef struct IOBUFMGRBIN
51{
52 /** Index of the next free entry. */
53 unsigned iFree;
54 /** Pointer to the array of free objects for this bin. */
55 void **papvFree;
56} IOBUFMGRBIN;
57typedef IOBUFMGRBIN *PIOBUFMGRBIN;
58
59/**
60 * Internal I/O buffer manager data.
61 */
62typedef struct IOBUFMGRINT
63{
64 /** Critical section protecting the allocation path. */
65 RTCRITSECT CritSectAlloc;
66 /** Flags the manager was created with. */
67 uint32_t fFlags;
68 /** Maximum size of I/O memory to allocate. */
69 size_t cbMax;
70 /** Amount of free memory. */
71 size_t cbFree;
72 /** The order of smallest bin. */
73 uint32_t u32OrderMin;
74 /** The order of largest bin. */
75 uint32_t u32OrderMax;
76 /** Pointer to the base memory of the allocation. */
77 void *pvMem;
78 /** Number of bins for free objects. */
79 unsigned cBins;
80 /** Array of bins. */
81 PIOBUFMGRBIN paBins;
82 /** Array of pointer entries for the various bins - variable in size. */
83 void *apvObj[1];
84} IOBUFMGRINT;
85
86/* Must be included after IOBUFDESCINT was defined. */
87#define IOBUFDESCINT_DECLARED
88#include "IOBufMgmt.h"
89
90DECLINLINE(unsigned) iobufMgrGetBinCount(size_t cbMin, size_t cbMax)
91{
92 unsigned u32Max = ASMBitLastSetU32((uint32_t)cbMax);
93 unsigned u32Min = ASMBitLastSetU32((uint32_t)cbMin);
94
95 Assert(cbMax >= cbMin && cbMax != 0 && cbMin != 0);
96 return u32Max - u32Min + 1;
97}
98
99DECLINLINE(uint32_t) iobufMgrGetObjCount(size_t cbMem, unsigned cBins, size_t cbMin)
100{
101 uint32_t cObjCnt = 0;
102 uint32_t cbBin = cbMin;
103
104 while (cBins-- > 0)
105 {
106 cObjCnt += cbMem / cbBin;
107 cbBin <<= 1; /* The size doubles for every bin. */
108 }
109
110 return cObjCnt;
111}
112
113DECLHIDDEN(int) IOBUFMgrCreate(PIOBUFMGR phIoBufMgr, size_t cbMax, uint32_t fFlags)
114{
115 int rc = VINF_SUCCESS;
116
117 AssertPtrReturn(phIoBufMgr, VERR_INVALID_POINTER);
118 AssertReturn(cbMax, VERR_NOT_IMPLEMENTED);
119
120 /* Allocate the basic structure in one go. */
121 unsigned cBins = iobufMgrGetBinCount(IOBUFMGR_BIN_SIZE_MIN, IOBUFMGR_BIN_SIZE_MAX);
122 uint32_t cObjs = iobufMgrGetObjCount(cbMax, cBins, IOBUFMGR_BIN_SIZE_MIN);
123 PIOBUFMGRINT pThis = (PIOBUFMGRINT)RTMemAllocZ(RT_OFFSETOF(IOBUFMGRINT, apvObj[cObjs]) + cBins * sizeof(IOBUFMGRBIN));
124 if (RT_LIKELY(pThis))
125 {
126 pThis->fFlags = fFlags;
127 pThis->cbMax = cbMax;
128 pThis->cbFree = cbMax;
129 pThis->cBins = cBins;
130 pThis->u32OrderMin = ASMBitLastSetU32(IOBUFMGR_BIN_SIZE_MIN) - 1;
131 pThis->u32OrderMax = ASMBitLastSetU32(IOBUFMGR_BIN_SIZE_MAX) - 1;
132 pThis->paBins = (PIOBUFMGRBIN)((uint8_t *)pThis + RT_OFFSETOF(IOBUFMGRINT, apvObj[cObjs]));
133
134 rc = RTCritSectInit(&pThis->CritSectAlloc);
135 if (RT_SUCCESS(rc))
136 {
137 if (pThis->fFlags & IOBUFMGR_F_REQUIRE_NOT_PAGABLE)
138 rc = RTMemSaferAllocZEx(&pThis->pvMem, RT_ALIGN_Z(pThis->cbMax, _4K),
139 RTMEMSAFER_F_REQUIRE_NOT_PAGABLE);
140 else
141 pThis->pvMem = RTMemPageAllocZ(RT_ALIGN_Z(pThis->cbMax, _4K));
142
143 if ( RT_LIKELY(pThis->pvMem)
144 && RT_SUCCESS(rc))
145 {
146 /* Init the bins. */
147 uint32_t iObj = 0;
148 uint32_t cbBin = IOBUFMGR_BIN_SIZE_MIN;
149 for (unsigned i = 0; i < cBins; i++)
150 {
151 PIOBUFMGRBIN pBin = &pThis->paBins[i];
152 pBin->iFree = 0;
153 pBin->papvFree = &pThis->apvObj[iObj];
154 iObj += cbMax / cbBin;
155
156 /* Init the biggest possible bin with the free objects. */
157 if ( (cbBin << 1) > cbMax
158 || i == cBins - 1)
159 {
160 uint8_t *pbMem = (uint8_t *)pThis->pvMem;
161 while (cbMax)
162 {
163 pBin->papvFree[pBin->iFree] = pbMem;
164 cbMax -= cbBin;
165 pbMem += cbBin;
166 pBin->iFree++;
167
168 if (cbMax < cbBin) /** @todo: Populate smaller bins and don't waste memory. */
169 break;
170 }
171
172 /* Limit the number of available bins. */
173 pThis->cBins = i + 1;
174 break;
175 }
176
177 cbBin <<= 1;
178 }
179
180 *phIoBufMgr = pThis;
181 return VINF_SUCCESS;
182 }
183 else
184 rc = VERR_NO_MEMORY;
185
186 RTCritSectDelete(&pThis->CritSectAlloc);
187 }
188
189 RTMemFree(pThis);
190 }
191 else
192 rc = VERR_NO_MEMORY;
193
194 return rc;
195}
196
197DECLHIDDEN(int) IOBUFMgrDestroy(IOBUFMGR hIoBufMgr)
198{
199 PIOBUFMGRINT pThis = hIoBufMgr;
200
201 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
202
203 int rc = RTCritSectEnter(&pThis->CritSectAlloc);
204 if (RT_SUCCESS(rc))
205 {
206 if (pThis->cbFree == pThis->cbMax)
207 {
208 if (pThis->fFlags & IOBUFMGR_F_REQUIRE_NOT_PAGABLE)
209 RTMemSaferFree(pThis->pvMem, RT_ALIGN_Z(pThis->cbMax, _4K));
210 else
211 RTMemPageFree(pThis->pvMem, RT_ALIGN_Z(pThis->cbMax, _4K));
212
213 RTCritSectLeave(&pThis->CritSectAlloc);
214 RTCritSectDelete(&pThis->CritSectAlloc);
215 RTMemFree(pThis);
216 }
217 else
218 {
219 rc = VERR_INVALID_STATE;
220 RTCritSectLeave(&pThis->CritSectAlloc);
221 }
222 }
223
224 return rc;
225}
226
227static size_t iobufMgrAllocSegment(PIOBUFMGRINT pThis, PRTSGSEG pSeg, size_t cb)
228{
229 size_t cbAlloc = 0;
230
231 /* Round to the next power of two and get the bin to try first. */
232 uint32_t u32Order = ASMBitLastSetU32((uint32_t)cb) - 1;
233 if (cbAlloc & ~RT_BIT_32(u32Order))
234 u32Order <<= 1;
235
236 u32Order = RT_CLAMP(u32Order, pThis->u32OrderMin, pThis->u32OrderMax);
237 unsigned iBin = u32Order - pThis->u32OrderMin;
238
239 /*
240 * Check whether the bin can satisfy the request. If not try the next bigger
241 * bin and so on. If there is nothing to find try the smaller bins.
242 */
243 Assert(iBin < pThis->cBins);
244
245 PIOBUFMGRBIN pBin = &pThis->paBins[iBin];
246 if (pBin->iFree == 0)
247 {
248 unsigned iBinCur = iBin;
249 PIOBUFMGRBIN pBinCur = &pThis->paBins[iBinCur];
250
251 while (iBinCur < pThis->cBins)
252 {
253 if (pBinCur->iFree != 0)
254 {
255 pBinCur->iFree--;
256 uint8_t *pbMem = (uint8_t *)pBinCur->papvFree[pBinCur->iFree];
257 AssertPtr(pbMem);
258
259 /* Always split into half. */
260 while (iBinCur > iBin)
261 {
262 iBinCur--;
263 pBinCur = &pThis->paBins[iBinCur];
264 pBinCur->papvFree[pBinCur->iFree] = pbMem + (RT_BIT(iBinCur + pThis->u32OrderMin));
265 pBinCur->iFree++;
266 }
267
268 /* For the last bin we will get two new memory blocks. */
269 pBinCur->papvFree[pBinCur->iFree] = pbMem;
270 pBinCur->iFree++;
271 Assert(pBin == pBinCur);
272 break;
273 }
274
275 pBinCur++;
276 iBinCur++;
277 }
278 }
279
280 /*
281 * If we didn't find something in the higher bins try to accumulate as much as possible from the smaller bins.
282 * @todo: Defragment in case there is enough memory free but we can't find something in our bin.
283 */
284 if ( pBin->iFree == 0
285 && iBin > 0)
286 {
287 do
288 {
289 iBin--;
290 pBin = &pThis->paBins[iBin];
291
292 if (pBin->iFree != 0)
293 {
294 pBin->iFree--;
295 pSeg->pvSeg = pBin->papvFree[pBin->iFree];
296 pSeg->cbSeg = RT_BIT_32(iBin + pThis->u32OrderMin);
297 AssertPtr(pSeg->pvSeg);
298 cbAlloc = pSeg->cbSeg;
299 break;
300 }
301 }
302 while (iBin > 0);
303 }
304 else if (pBin->iFree != 0)
305 {
306 pBin->iFree--;
307 pSeg->pvSeg = pBin->papvFree[pBin->iFree];
308 pSeg->cbSeg = RT_BIT_32(u32Order);
309 cbAlloc = pSeg->cbSeg;
310 AssertPtr(pSeg->pvSeg);
311 }
312
313 return cbAlloc;
314}
315
316DECLHIDDEN(int) IOBUFMgrAllocBuf(IOBUFMGR hIoBufMgr, PIOBUFDESC pIoBufDesc, size_t cbIoBuf,
317 size_t *pcbIoBufAllocated)
318{
319 PIOBUFMGRINT pThis = hIoBufMgr;
320
321 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
322
323 if (!pThis->cbFree)
324 return VERR_NO_MEMORY;
325
326 int rc = RTCritSectEnter(&pThis->CritSectAlloc);
327 if (RT_SUCCESS(rc))
328 {
329 unsigned iSeg = 0;
330 unsigned cbLeft = cbIoBuf;
331 PRTSGSEG pSeg = &pIoBufDesc->Int.aSegs[0];
332
333 while ( iSeg < RT_ELEMENTS(pIoBufDesc->Int.aSegs)
334 && cbLeft)
335 {
336 size_t cbAlloc = iobufMgrAllocSegment(pThis, pSeg, cbLeft);
337 if (!cbAlloc)
338 break;
339
340 iSeg++;
341 pSeg++;
342 cbLeft -= RT_MIN(cbAlloc, cbLeft);
343 }
344
345 if (iSeg)
346 RTSgBufInit(&pIoBufDesc->SgBuf, &pIoBufDesc->Int.aSegs[0], iSeg);
347 else
348 rc = VERR_NO_MEMORY;
349
350 pIoBufDesc->Int.cSegsUsed = iSeg;
351 pIoBufDesc->Int.pIoBufMgr = pThis;
352 *pcbIoBufAllocated = cbIoBuf - cbLeft;
353
354 pThis->cbFree -= cbIoBuf - cbLeft;
355
356 RTCritSectLeave(&pThis->CritSectAlloc);
357 }
358
359 return rc;
360}
361
362DECLHIDDEN(void) IOBUFMgrFreeBuf(PIOBUFDESC pIoBufDesc)
363{
364 PIOBUFMGRINT pThis = pIoBufDesc->Int.pIoBufMgr;
365
366 AssertPtr(pThis);
367
368 int rc = RTCritSectEnter(&pThis->CritSectAlloc);
369 AssertRC(rc);
370
371 if (RT_SUCCESS(rc))
372 {
373 for (unsigned i = 0; i < pIoBufDesc->Int.cSegsUsed; i++)
374 {
375 PRTSGSEG pSeg = &pIoBufDesc->Int.aSegs[i];
376
377 uint32_t u32Order = ASMBitLastSetU32((uint32_t)pSeg->cbSeg) - 1;
378 unsigned iBin = u32Order - pThis->u32OrderMin;
379
380 Assert(iBin < pThis->cBins);
381 PIOBUFMGRBIN pBin = &pThis->paBins[iBin];
382 pBin->papvFree[pBin->iFree] = pSeg->pvSeg;
383 pBin->iFree++;
384 pThis->cbFree += RT_BIT_32(u32Order);
385 }
386 RTCritSectLeave(&pThis->CritSectAlloc);
387 }
388
389 pIoBufDesc->Int.cSegsUsed = 0;
390}
391
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