VirtualBox

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

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

IOBufMgmt: Defrag the I/O memory buffer if everything is free and an allocation couldn't be satisfied from one of the larger bins

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.0 KB
Line 
1/* $Id: IOBufMgmt.cpp 64674 2016-11-15 14:09:23Z 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 uint32_t cBins;
80 /** Flag whether allocation is on hold waiting for everything to be free
81 * to be able to defragment the memory. */
82 bool fAllocSuspended;
83 /** Array of bins. */
84 PIOBUFMGRBIN paBins;
85 /** Array of pointer entries for the various bins - variable in size. */
86 void *apvObj[1];
87} IOBUFMGRINT;
88
89/* Must be included after IOBUFDESCINT was defined. */
90#define IOBUFDESCINT_DECLARED
91#include "IOBufMgmt.h"
92
93/**
94 * Gets the number of bins required between the given minimum and maximum size
95 * to have a bin for every power of two size inbetween.
96 *
97 * @returns The number of bins required.
98 * @param cbMin The size of the smallest bin.
99 * @param cbMax The size of the largest bin.
100 */
101DECLINLINE(uint32_t) iobufMgrGetBinCount(uint32_t cbMin, uint32_t cbMax)
102{
103 uint32_t u32Max = ASMBitLastSetU32(cbMax);
104 uint32_t u32Min = ASMBitLastSetU32(cbMin);
105
106 Assert(cbMax >= cbMin && cbMax != 0 && cbMin != 0);
107 return u32Max - u32Min + 1;
108}
109
110/**
111 * Returns the number of entries required in the object array to cover all bins.
112 *
113 * @returns Number of entries required in the object array.
114 * @param cbMem Size of the memory buffer.
115 * @param cBins Number of bins available.
116 * @param cbBinMin Minimum object size.
117 */
118DECLINLINE(uint32_t) iobufMgrGetObjCount(size_t cbMem, unsigned cBins, size_t cbMinBin)
119{
120 size_t cObjs = 0;
121 size_t cbBin = cbMinBin;
122
123 while (cBins-- > 0)
124 {
125 cObjs += cbMem / cbBin;
126 cbBin <<= 1; /* The size doubles for every bin. */
127 }
128
129 Assert((uint32_t)cObjs == cObjs);
130 return (uint32_t)cObjs;
131}
132
133/**
134 * Resets the bins to factory default (memory resigin in the largest bin).
135 *
136 * @returns nothing.
137 * @param pThis The I/O buffer manager instance.
138 */
139static void iobufMgrResetBins(PIOBUFMGRINT pThis)
140{
141 /* Init the bins. */
142 size_t cbMax = pThis->cbMax;
143 size_t iObj = 0;
144 uint32_t cbBin = IOBUFMGR_BIN_SIZE_MIN;
145 for (unsigned i = 0; i < pThis->cBins; i++)
146 {
147 PIOBUFMGRBIN pBin = &pThis->paBins[i];
148 pBin->iFree = 0;
149 pBin->papvFree = &pThis->apvObj[iObj];
150 iObj += cbMax / cbBin;
151
152 /* Init the biggest possible bin with the free objects. */
153 if ( (cbBin << 1) > cbMax
154 || i == pThis->cBins - 1)
155 {
156 uint8_t *pbMem = (uint8_t *)pThis->pvMem;
157 while (cbMax)
158 {
159 pBin->papvFree[pBin->iFree] = pbMem;
160 cbMax -= cbBin;
161 pbMem += cbBin;
162 pBin->iFree++;
163
164 if (cbMax < cbBin) /** @todo Populate smaller bins and don't waste memory. */
165 break;
166 }
167
168 /* Limit the number of available bins. */
169 pThis->cBins = i + 1;
170 break;
171 }
172
173 cbBin <<= 1;
174 }
175}
176
177/**
178 * Allocate one segment from the manager.
179 *
180 * @returns Number of bytes allocated, 0 if there is no free memory.
181 * @param pThis The I/O buffer manager instance.
182 * @param pSeg The segment to fill in on success.
183 * @param cb Maximum number of bytes to allocate.
184 */
185static size_t iobufMgrAllocSegment(PIOBUFMGRINT pThis, PRTSGSEG pSeg, size_t cb)
186{
187 size_t cbAlloc = 0;
188
189 /* Round to the next power of two and get the bin to try first. */
190 uint32_t u32Order = ASMBitLastSetU32((uint32_t)cb) - 1;
191 if (cbAlloc & ~RT_BIT_32(u32Order))
192 u32Order <<= 1;
193
194 u32Order = RT_CLAMP(u32Order, pThis->u32OrderMin, pThis->u32OrderMax);
195 unsigned iBin = u32Order - pThis->u32OrderMin;
196
197 /*
198 * Check whether the bin can satisfy the request. If not try the next bigger
199 * bin and so on. If there is nothing to find try the smaller bins.
200 */
201 Assert(iBin < pThis->cBins);
202
203 PIOBUFMGRBIN pBin = &pThis->paBins[iBin];
204 if (pBin->iFree == 0)
205 {
206 unsigned iBinCur = iBin;
207 PIOBUFMGRBIN pBinCur = &pThis->paBins[iBinCur];
208
209 while (iBinCur < pThis->cBins)
210 {
211 if (pBinCur->iFree != 0)
212 {
213 pBinCur->iFree--;
214 uint8_t *pbMem = (uint8_t *)pBinCur->papvFree[pBinCur->iFree];
215 AssertPtr(pbMem);
216
217 /* Always split into half. */
218 while (iBinCur > iBin)
219 {
220 iBinCur--;
221 pBinCur = &pThis->paBins[iBinCur];
222 pBinCur->papvFree[pBinCur->iFree] = pbMem + (size_t)RT_BIT(iBinCur + pThis->u32OrderMin); /* (RT_BIT causes weird MSC warning without cast) */
223 pBinCur->iFree++;
224 }
225
226 /* For the last bin we will get two new memory blocks. */
227 pBinCur->papvFree[pBinCur->iFree] = pbMem;
228 pBinCur->iFree++;
229 Assert(pBin == pBinCur);
230 break;
231 }
232
233 pBinCur++;
234 iBinCur++;
235 }
236 }
237
238 /*
239 * If we didn't find something in the higher bins try to accumulate as much as possible from the smaller bins.
240 */
241 if ( pBin->iFree == 0
242 && iBin > 0)
243 {
244#if 1
245 pThis->fAllocSuspended = true;
246#else
247 do
248 {
249 iBin--;
250 pBin = &pThis->paBins[iBin];
251
252 if (pBin->iFree != 0)
253 {
254 pBin->iFree--;
255 pSeg->pvSeg = pBin->papvFree[pBin->iFree];
256 pSeg->cbSeg = (size_t)RT_BIT_32(iBin + pThis->u32OrderMin);
257 AssertPtr(pSeg->pvSeg);
258 cbAlloc = pSeg->cbSeg;
259 break;
260 }
261 }
262 while (iBin > 0);
263#endif
264 }
265 else if (pBin->iFree != 0)
266 {
267 pBin->iFree--;
268 pSeg->pvSeg = pBin->papvFree[pBin->iFree];
269 pSeg->cbSeg = (size_t)RT_BIT_32(u32Order);
270 cbAlloc = pSeg->cbSeg;
271 AssertPtr(pSeg->pvSeg);
272 }
273
274 return cbAlloc;
275}
276
277DECLHIDDEN(int) IOBUFMgrCreate(PIOBUFMGR phIoBufMgr, size_t cbMax, uint32_t fFlags)
278{
279 int rc = VINF_SUCCESS;
280
281 AssertPtrReturn(phIoBufMgr, VERR_INVALID_POINTER);
282 AssertReturn(cbMax, VERR_NOT_IMPLEMENTED);
283
284 /* Allocate the basic structure in one go. */
285 unsigned cBins = iobufMgrGetBinCount(IOBUFMGR_BIN_SIZE_MIN, IOBUFMGR_BIN_SIZE_MAX);
286 uint32_t cObjs = iobufMgrGetObjCount(cbMax, cBins, IOBUFMGR_BIN_SIZE_MIN);
287 PIOBUFMGRINT pThis = (PIOBUFMGRINT)RTMemAllocZ(RT_OFFSETOF(IOBUFMGRINT, apvObj[cObjs]) + cBins * sizeof(IOBUFMGRBIN));
288 if (RT_LIKELY(pThis))
289 {
290 pThis->fFlags = fFlags;
291 pThis->cbMax = cbMax;
292 pThis->cbFree = cbMax;
293 pThis->cBins = cBins;
294 pThis->fAllocSuspended = false;
295 pThis->u32OrderMin = ASMBitLastSetU32(IOBUFMGR_BIN_SIZE_MIN) - 1;
296 pThis->u32OrderMax = ASMBitLastSetU32(IOBUFMGR_BIN_SIZE_MAX) - 1;
297 pThis->paBins = (PIOBUFMGRBIN)((uint8_t *)pThis + RT_OFFSETOF(IOBUFMGRINT, apvObj[cObjs]));
298
299 rc = RTCritSectInit(&pThis->CritSectAlloc);
300 if (RT_SUCCESS(rc))
301 {
302 if (pThis->fFlags & IOBUFMGR_F_REQUIRE_NOT_PAGABLE)
303 rc = RTMemSaferAllocZEx(&pThis->pvMem, RT_ALIGN_Z(pThis->cbMax, _4K),
304 RTMEMSAFER_F_REQUIRE_NOT_PAGABLE);
305 else
306 pThis->pvMem = RTMemPageAllocZ(RT_ALIGN_Z(pThis->cbMax, _4K));
307
308 if ( RT_LIKELY(pThis->pvMem)
309 && RT_SUCCESS(rc))
310 {
311 iobufMgrResetBins(pThis);
312
313 *phIoBufMgr = pThis;
314 return VINF_SUCCESS;
315 }
316 else
317 rc = VERR_NO_MEMORY;
318
319 RTCritSectDelete(&pThis->CritSectAlloc);
320 }
321
322 RTMemFree(pThis);
323 }
324 else
325 rc = VERR_NO_MEMORY;
326
327 return rc;
328}
329
330DECLHIDDEN(int) IOBUFMgrDestroy(IOBUFMGR hIoBufMgr)
331{
332 PIOBUFMGRINT pThis = hIoBufMgr;
333
334 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
335
336 int rc = RTCritSectEnter(&pThis->CritSectAlloc);
337 if (RT_SUCCESS(rc))
338 {
339 if (pThis->cbFree == pThis->cbMax)
340 {
341 if (pThis->fFlags & IOBUFMGR_F_REQUIRE_NOT_PAGABLE)
342 RTMemSaferFree(pThis->pvMem, RT_ALIGN_Z(pThis->cbMax, _4K));
343 else
344 RTMemPageFree(pThis->pvMem, RT_ALIGN_Z(pThis->cbMax, _4K));
345
346 RTCritSectLeave(&pThis->CritSectAlloc);
347 RTCritSectDelete(&pThis->CritSectAlloc);
348 RTMemFree(pThis);
349 }
350 else
351 {
352 rc = VERR_INVALID_STATE;
353 RTCritSectLeave(&pThis->CritSectAlloc);
354 }
355 }
356
357 return rc;
358}
359
360DECLHIDDEN(int) IOBUFMgrAllocBuf(IOBUFMGR hIoBufMgr, PIOBUFDESC pIoBufDesc, size_t cbIoBuf,
361 size_t *pcbIoBufAllocated)
362{
363 PIOBUFMGRINT pThis = hIoBufMgr;
364
365 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
366 AssertReturn(cbIoBuf > 0, VERR_INVALID_PARAMETER);
367
368 if (!pThis->cbFree)
369 return VERR_NO_MEMORY;
370
371 int rc = RTCritSectEnter(&pThis->CritSectAlloc);
372 if (RT_SUCCESS(rc))
373 {
374 unsigned iSeg = 0;
375 size_t cbLeft = cbIoBuf;
376 PRTSGSEG pSeg = &pIoBufDesc->Int.aSegs[0];
377
378 while ( iSeg < RT_ELEMENTS(pIoBufDesc->Int.aSegs)
379 && cbLeft)
380 {
381 size_t cbAlloc = iobufMgrAllocSegment(pThis, pSeg, cbLeft);
382 if (!cbAlloc)
383 break;
384
385 iSeg++;
386 pSeg++;
387 cbLeft -= RT_MIN(cbAlloc, cbLeft);
388 }
389
390 if (iSeg)
391 RTSgBufInit(&pIoBufDesc->SgBuf, &pIoBufDesc->Int.aSegs[0], iSeg);
392 else
393 rc = VERR_NO_MEMORY;
394
395 pIoBufDesc->Int.cSegsUsed = iSeg;
396 pIoBufDesc->Int.pIoBufMgr = pThis;
397 *pcbIoBufAllocated = cbIoBuf - cbLeft;
398 Assert( (RT_SUCCESS(rc) && *pcbIoBufAllocated > 0)
399 || RT_FAILURE(rc));
400
401 pThis->cbFree -= cbIoBuf - cbLeft;
402
403 RTCritSectLeave(&pThis->CritSectAlloc);
404 }
405
406 return rc;
407}
408
409DECLHIDDEN(void) IOBUFMgrFreeBuf(PIOBUFDESC pIoBufDesc)
410{
411 PIOBUFMGRINT pThis = pIoBufDesc->Int.pIoBufMgr;
412
413 AssertPtr(pThis);
414
415 int rc = RTCritSectEnter(&pThis->CritSectAlloc);
416 AssertRC(rc);
417
418 if (RT_SUCCESS(rc))
419 {
420 for (unsigned i = 0; i < pIoBufDesc->Int.cSegsUsed; i++)
421 {
422 PRTSGSEG pSeg = &pIoBufDesc->Int.aSegs[i];
423
424 uint32_t u32Order = ASMBitLastSetU32((uint32_t)pSeg->cbSeg) - 1;
425 unsigned iBin = u32Order - pThis->u32OrderMin;
426
427 Assert(iBin < pThis->cBins);
428 PIOBUFMGRBIN pBin = &pThis->paBins[iBin];
429 pBin->papvFree[pBin->iFree] = pSeg->pvSeg;
430 pBin->iFree++;
431 pThis->cbFree += (size_t)RT_BIT_32(u32Order);
432 }
433
434 if ( pThis->cbFree == pThis->cbMax
435 && pThis->fAllocSuspended)
436 {
437 iobufMgrResetBins(pThis);
438 pThis->fAllocSuspended = false;
439 }
440
441 RTCritSectLeave(&pThis->CritSectAlloc);
442 }
443
444 pIoBufDesc->Int.cSegsUsed = 0;
445}
446
Note: See TracBrowser for help on using the repository browser.

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