~ubuntu-branches/ubuntu/raring/virtualbox-ose/raring

« back to all changes in this revision

Viewing changes to src/VBox/VMM/MMUkHeap.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-30 23:27:25 UTC
  • mfrom: (0.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20110130232725-2ouajjd2ggdet0zd
Tags: 4.0.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Drop *-source packages.
* Drop ubuntu-01-fix-build-gcc45.patch, fixed upstream.
* Drop ubuntu-02-as-needed.patch, added to the Debian package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: MMUkHeap.cpp $ */
2
 
/** @file
3
 
 * MM - Memory Manager - Ring-3 Heap with kernel accessible mapping.
4
 
 */
5
 
 
6
 
/*
7
 
 * Copyright (C) 2006-2009 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
 
 
18
 
 
19
 
/*******************************************************************************
20
 
*   Header Files                                                               *
21
 
*******************************************************************************/
22
 
#define LOG_GROUP LOG_GROUP_MM_HEAP
23
 
#include <VBox/mm.h>
24
 
#include <VBox/stam.h>
25
 
#include "MMInternal.h"
26
 
#include <VBox/vm.h>
27
 
#include <VBox/uvm.h>
28
 
#include <VBox/err.h>
29
 
#include <VBox/param.h>
30
 
#include <VBox/log.h>
31
 
 
32
 
#include <iprt/assert.h>
33
 
#include <iprt/string.h>
34
 
#include <iprt/heap.h>
35
 
 
36
 
 
37
 
/*******************************************************************************
38
 
*   Internal Functions                                                         *
39
 
*******************************************************************************/
40
 
static void *mmR3UkHeapAlloc(PMMUKHEAP pHeap, MMTAG enmTag, size_t cb, bool fZero, PRTR0PTR pR0Ptr);
41
 
 
42
 
 
43
 
 
44
 
/**
45
 
 * Create a User-kernel heap.
46
 
 *
47
 
 * This does not require SUPLib to be initialized as we'll lazily allocate the
48
 
 * kernel accessible memory on the first alloc call.
49
 
 *
50
 
 * @returns VBox status.
51
 
 * @param   pVM     The handle to the VM the heap should be associated with.
52
 
 * @param   ppHeap  Where to store the heap pointer.
53
 
 */
54
 
int mmR3UkHeapCreateU(PUVM pUVM, PMMUKHEAP *ppHeap)
55
 
{
56
 
    PMMUKHEAP pHeap = (PMMUKHEAP)MMR3HeapAllocZU(pUVM, MM_TAG_MM, sizeof(MMUKHEAP));
57
 
    if (pHeap)
58
 
    {
59
 
        int rc = RTCritSectInit(&pHeap->Lock);
60
 
        if (RT_SUCCESS(rc))
61
 
        {
62
 
            /*
63
 
             * Initialize the global stat record.
64
 
             */
65
 
            pHeap->pUVM = pUVM;
66
 
#ifdef MMUKHEAP_WITH_STATISTICS
67
 
            PMMUKHEAPSTAT pStat = &pHeap->Stat;
68
 
            STAMR3RegisterU(pUVM, &pStat->cAllocations,   STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cAllocations",     STAMUNIT_CALLS, "Number or MMR3UkHeapAlloc() calls.");
69
 
            STAMR3RegisterU(pUVM, &pStat->cReallocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cReallocations",   STAMUNIT_CALLS, "Number of MMR3UkHeapRealloc() calls.");
70
 
            STAMR3RegisterU(pUVM, &pStat->cFrees,         STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cFrees",           STAMUNIT_CALLS, "Number of MMR3UkHeapFree() calls.");
71
 
            STAMR3RegisterU(pUVM, &pStat->cFailures,      STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cFailures",        STAMUNIT_COUNT, "Number of failures.");
72
 
            STAMR3RegisterU(pUVM, &pStat->cbCurAllocated, sizeof(pStat->cbCurAllocated) == sizeof(uint32_t) ? STAMTYPE_U32 : STAMTYPE_U64,
73
 
                                                                        STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbCurAllocated",   STAMUNIT_BYTES, "Number of bytes currently allocated.");
74
 
            STAMR3RegisterU(pUVM, &pStat->cbAllocated,    STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbAllocated",      STAMUNIT_BYTES, "Total number of bytes allocated.");
75
 
            STAMR3RegisterU(pUVM, &pStat->cbFreed,        STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbFreed",          STAMUNIT_BYTES, "Total number of bytes freed.");
76
 
#endif
77
 
            *ppHeap = pHeap;
78
 
            return VINF_SUCCESS;
79
 
        }
80
 
        AssertRC(rc);
81
 
        MMR3HeapFree(pHeap);
82
 
    }
83
 
    AssertMsgFailed(("failed to allocate heap structure\n"));
84
 
    return VERR_NO_MEMORY;
85
 
}
86
 
 
87
 
 
88
 
/**
89
 
 * Destroy a User-kernel heap.
90
 
 *
91
 
 * @param   pHeap   Heap handle.
92
 
 */
93
 
void mmR3UkHeapDestroy(PMMUKHEAP pHeap)
94
 
{
95
 
    /*
96
 
     * Start by deleting the lock, that'll trap anyone
97
 
     * attempting to use the heap.
98
 
     */
99
 
    RTCritSectDelete(&pHeap->Lock);
100
 
 
101
 
    /*
102
 
     * Walk the sub-heaps and free them.
103
 
     */
104
 
    while (pHeap->pSubHeapHead)
105
 
    {
106
 
        PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
107
 
        pHeap->pSubHeapHead = pSubHeap->pNext;
108
 
        SUPR3PageFreeEx(pSubHeap->pv, pSubHeap->cb >> PAGE_SHIFT);
109
 
        //MMR3HeapFree(pSubHeap); - rely on the automatic cleanup.
110
 
    }
111
 
    //MMR3HeapFree(pHeap->stats);
112
 
    //MMR3HeapFree(pHeap);
113
 
}
114
 
 
115
 
 
116
 
/**
117
 
 * Allocate memory associating it with the VM for collective cleanup.
118
 
 *
119
 
 * The memory will be allocated from the default heap but a header
120
 
 * is added in which we keep track of which VM it belongs to and chain
121
 
 * all the allocations together so they can be freed in one go.
122
 
 *
123
 
 * This interface is typically used for memory block which will not be
124
 
 * freed during the life of the VM.
125
 
 *
126
 
 * @returns Pointer to allocated memory.
127
 
 * @param   pVM         VM handle.
128
 
 * @param   enmTag      Statistics tag. Statistics are collected on a per tag
129
 
 *                      basis in addition to a global one. Thus we can easily
130
 
 *                      identify how memory is used by the VM.
131
 
 * @param   cbSize      Size of the block.
132
 
 * @param   pR0Ptr      Where to return the ring-0 address of the memory.
133
 
 */
134
 
VMMR3DECL(void *) MMR3UkHeapAlloc(PVM pVM, MMTAG enmTag, size_t cbSize, PRTR0PTR pR0Ptr)
135
 
{
136
 
    return mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, false, pR0Ptr);
137
 
}
138
 
 
139
 
 
140
 
/**
141
 
 * Same as MMR3UkHeapAlloc().
142
 
 *
143
 
 * @returns Pointer to allocated memory.
144
 
 * @param   pVM         VM handle.
145
 
 * @param   enmTag      Statistics tag. Statistics are collected on a per tag
146
 
 *                      basis in addition to a global one. Thus we can easily
147
 
 *                      identify how memory is used by the VM.
148
 
 * @param   cbSize      Size of the block.
149
 
 * @param   ppv         Where to store the pointer to the allocated memory on success.
150
 
 * @param   pR0Ptr      Where to return the ring-0 address of the memory.
151
 
 */
152
 
VMMR3DECL(int) MMR3UkHeapAllocEx(PVM pVM, MMTAG enmTag, size_t cbSize, void **ppv, PRTR0PTR pR0Ptr)
153
 
{
154
 
    void *pv = mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, false, pR0Ptr);
155
 
    if (pv)
156
 
    {
157
 
        *ppv = pv;
158
 
        return VINF_SUCCESS;
159
 
    }
160
 
    return VERR_NO_MEMORY;
161
 
}
162
 
 
163
 
 
164
 
/**
165
 
 * Same as MMR3UkHeapAlloc() only the memory is zeroed.
166
 
 *
167
 
 * @returns Pointer to allocated memory.
168
 
 * @param   pVM         VM handle.
169
 
 * @param   enmTag      Statistics tag. Statistics are collected on a per tag
170
 
 *                      basis in addition to a global one. Thus we can easily
171
 
 *                      identify how memory is used by the VM.
172
 
 * @param   cbSize      Size of the block.
173
 
 * @param   pR0Ptr      Where to return the ring-0 address of the memory.
174
 
 */
175
 
VMMR3DECL(void *) MMR3UkHeapAllocZ(PVM pVM, MMTAG enmTag, size_t cbSize, PRTR0PTR pR0Ptr)
176
 
{
177
 
    return mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, true, pR0Ptr);
178
 
}
179
 
 
180
 
 
181
 
/**
182
 
 * Same as MMR3UkHeapAllocZ().
183
 
 *
184
 
 * @returns Pointer to allocated memory.
185
 
 * @param   pVM         VM handle.
186
 
 * @param   enmTag      Statistics tag. Statistics are collected on a per tag
187
 
 *                      basis in addition to a global one. Thus we can easily
188
 
 *                      identify how memory is used by the VM.
189
 
 * @param   cbSize      Size of the block.
190
 
 * @param   ppv         Where to store the pointer to the allocated memory on success.
191
 
 * @param   pR0Ptr      Where to return the ring-0 address of the memory.
192
 
 */
193
 
VMMR3DECL(int) MMR3UkHeapAllocZEx(PVM pVM, MMTAG enmTag, size_t cbSize, void **ppv, PRTR0PTR pR0Ptr)
194
 
{
195
 
    void *pv = mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, true, pR0Ptr);
196
 
    if (pv)
197
 
    {
198
 
        *ppv = pv;
199
 
        return VINF_SUCCESS;
200
 
    }
201
 
    return VERR_NO_MEMORY;
202
 
}
203
 
 
204
 
 
205
 
/***
206
 
 * Worker for mmR3UkHeapAlloc that creates and adds a new sub-heap.
207
 
 *
208
 
 * @returns Pointer to the new sub-heap.
209
 
 * @param   pHeap       The heap
210
 
 * @param   cbSubHeap   The size of the sub-heap.
211
 
 */
212
 
static PMMUKHEAPSUB mmR3UkHeapAddSubHeap(PMMUKHEAP pHeap, size_t cbSubHeap)
213
 
{
214
 
    PMMUKHEAPSUB pSubHeap = (PMMUKHEAPSUB)MMR3HeapAllocU(pHeap->pUVM, MM_TAG_MM/*_UK_HEAP*/, sizeof(*pSubHeap));
215
 
    if (pSubHeap)
216
 
    {
217
 
        pSubHeap->cb = cbSubHeap;
218
 
        int rc = SUPR3PageAllocEx(pSubHeap->cb >> PAGE_SHIFT, 0, &pSubHeap->pv, &pSubHeap->pvR0, NULL);
219
 
        if (RT_SUCCESS(rc))
220
 
        {
221
 
            rc = RTHeapSimpleInit(&pSubHeap->hSimple, pSubHeap->pv, pSubHeap->cb);
222
 
            if (RT_SUCCESS(rc))
223
 
            {
224
 
                pSubHeap->pNext = pHeap->pSubHeapHead;
225
 
                pHeap->pSubHeapHead = pSubHeap;
226
 
                return pSubHeap;
227
 
            }
228
 
 
229
 
            /* bail out */
230
 
            SUPR3PageFreeEx(pSubHeap->pv, pSubHeap->cb >> PAGE_SHIFT);
231
 
        }
232
 
        MMR3HeapFree(pSubHeap);
233
 
    }
234
 
    return NULL;
235
 
}
236
 
 
237
 
 
238
 
/**
239
 
 * Allocate memory from the heap.
240
 
 *
241
 
 * @returns Pointer to allocated memory.
242
 
 * @param   pHeap       Heap handle.
243
 
 * @param   enmTag      Statistics tag. Statistics are collected on a per tag
244
 
 *                      basis in addition to a global one. Thus we can easily
245
 
 *                      identify how memory is used by the VM.
246
 
 * @param   cb          Size of the block.
247
 
 * @param   fZero       Whether or not to zero the memory block.
248
 
 * @param   pR0Ptr      Where to return the ring-0 pointer.
249
 
 */
250
 
static void *mmR3UkHeapAlloc(PMMUKHEAP pHeap, MMTAG enmTag, size_t cb, bool fZero, PRTR0PTR pR0Ptr)
251
 
{
252
 
    if (pR0Ptr)
253
 
        *pR0Ptr = NIL_RTR0PTR;
254
 
    RTCritSectEnter(&pHeap->Lock);
255
 
 
256
 
#ifdef MMUKHEAP_WITH_STATISTICS
257
 
    /*
258
 
     * Find/alloc statistics nodes.
259
 
     */
260
 
    pHeap->Stat.cAllocations++;
261
 
    PMMUKHEAPSTAT pStat = (PMMUKHEAPSTAT)RTAvlULGet(&pHeap->pStatTree, (AVLULKEY)enmTag);
262
 
    if (pStat)
263
 
        pStat->cAllocations++;
264
 
    else
265
 
    {
266
 
        pStat = (PMMUKHEAPSTAT)MMR3HeapAllocZU(pHeap->pUVM, MM_TAG_MM, sizeof(MMUKHEAPSTAT));
267
 
        if (!pStat)
268
 
        {
269
 
            pHeap->Stat.cFailures++;
270
 
            AssertMsgFailed(("Failed to allocate heap stat record.\n"));
271
 
            RTCritSectLeave(&pHeap->Lock);
272
 
            return NULL;
273
 
        }
274
 
        pStat->Core.Key = (AVLULKEY)enmTag;
275
 
        RTAvlULInsert(&pHeap->pStatTree, &pStat->Core);
276
 
 
277
 
        pStat->cAllocations++;
278
 
 
279
 
        /* register the statistics */
280
 
        PUVM pUVM = pHeap->pUVM;
281
 
        const char *pszTag = mmGetTagName(enmTag);
282
 
        STAMR3RegisterFU(pUVM, &pStat->cbCurAllocated, STAMTYPE_U32, STAMVISIBILITY_ALWAYS,  STAMUNIT_BYTES, "Number of bytes currently allocated.",    "/MM/UkHeap/%s", pszTag);
283
 
        STAMR3RegisterFU(pUVM, &pStat->cAllocations,   STAMTYPE_U64, STAMVISIBILITY_ALWAYS,  STAMUNIT_CALLS, "Number or MMR3UkHeapAlloc() calls.",      "/MM/UkHeap/%s/cAllocations", pszTag);
284
 
        STAMR3RegisterFU(pUVM, &pStat->cReallocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS,  STAMUNIT_CALLS, "Number of MMR3UkHeapRealloc() calls.",    "/MM/UkHeap/%s/cReallocations", pszTag);
285
 
        STAMR3RegisterFU(pUVM, &pStat->cFrees,         STAMTYPE_U64, STAMVISIBILITY_ALWAYS,  STAMUNIT_CALLS, "Number of MMR3UkHeapFree() calls.",       "/MM/UkHeap/%s/cFrees", pszTag);
286
 
        STAMR3RegisterFU(pUVM, &pStat->cFailures,      STAMTYPE_U64, STAMVISIBILITY_ALWAYS,  STAMUNIT_COUNT, "Number of failures.",                     "/MM/UkHeap/%s/cFailures", pszTag);
287
 
        STAMR3RegisterFU(pUVM, &pStat->cbAllocated,    STAMTYPE_U64, STAMVISIBILITY_ALWAYS,  STAMUNIT_BYTES, "Total number of bytes allocated.",        "/MM/UkHeap/%s/cbAllocated", pszTag);
288
 
        STAMR3RegisterFU(pUVM, &pStat->cbFreed,        STAMTYPE_U64, STAMVISIBILITY_ALWAYS,  STAMUNIT_BYTES, "Total number of bytes freed.",            "/MM/UkHeap/%s/cbFreed", pszTag);
289
 
    }
290
 
#endif
291
 
 
292
 
    /*
293
 
     * Validate input.
294
 
     */
295
 
    if (cb == 0)
296
 
    {
297
 
#ifdef MMUKHEAP_WITH_STATISTICS
298
 
        pStat->cFailures++;
299
 
        pHeap->Stat.cFailures++;
300
 
#endif
301
 
        RTCritSectLeave(&pHeap->Lock);
302
 
        return NULL;
303
 
    }
304
 
 
305
 
    /*
306
 
     * Allocate heap block.
307
 
     */
308
 
    cb = RT_ALIGN_Z(cb, MMUKHEAP_SIZE_ALIGNMENT);
309
 
    void *pv = NULL;
310
 
    PMMUKHEAPSUB pSubHeapPrev = NULL;
311
 
    PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
312
 
    while (pSubHeap)
313
 
    {
314
 
        if (fZero)
315
 
            pv = RTHeapSimpleAllocZ(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
316
 
        else
317
 
            pv = RTHeapSimpleAlloc(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
318
 
        if (pv)
319
 
        {
320
 
            /* Move the sub-heap with free memory to the head. */
321
 
            if (pSubHeapPrev)
322
 
            {
323
 
                pSubHeapPrev->pNext = pSubHeap->pNext;
324
 
                pSubHeap->pNext = pHeap->pSubHeapHead;
325
 
                pHeap->pSubHeapHead = pSubHeap;
326
 
            }
327
 
            break;
328
 
        }
329
 
        pSubHeapPrev = pSubHeap;
330
 
        pSubHeap = pSubHeap->pNext;
331
 
    }
332
 
    if (RT_UNLIKELY(!pv))
333
 
    {
334
 
        /*
335
 
         * Add another sub-heap.
336
 
         */
337
 
        pSubHeap = mmR3UkHeapAddSubHeap(pHeap, RT_MAX(RT_ALIGN_Z(cb, PAGE_SIZE) + PAGE_SIZE * 16, _256K));
338
 
        if (pSubHeap)
339
 
        {
340
 
            if (fZero)
341
 
                pv = RTHeapSimpleAllocZ(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
342
 
            else
343
 
                pv = RTHeapSimpleAlloc(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
344
 
        }
345
 
        if (RT_UNLIKELY(!pv))
346
 
        {
347
 
            AssertMsgFailed(("Failed to allocate heap block %d, enmTag=%x(%.4s).\n", cb, enmTag, &enmTag));
348
 
#ifdef MMUKHEAP_WITH_STATISTICS
349
 
            pStat->cFailures++;
350
 
            pHeap->Stat.cFailures++;
351
 
#endif
352
 
            RTCritSectLeave(&pHeap->Lock);
353
 
            return NULL;
354
 
        }
355
 
    }
356
 
 
357
 
    /*
358
 
     * Update statistics
359
 
     */
360
 
#ifdef MMUKHEAP_WITH_STATISTICS
361
 
    size_t cbActual = RTHeapSimpleSize(pSubHeap->hSimple, pv);
362
 
    pStat->cbAllocated          += cbActual;
363
 
    pStat->cbCurAllocated       += cbActual;
364
 
    pHeap->Stat.cbAllocated     += cbActual;
365
 
    pHeap->Stat.cbCurAllocated  += cbActual;
366
 
#endif
367
 
 
368
 
    if (pR0Ptr)
369
 
        *pR0Ptr = (uintptr_t)pv - (uintptr_t)pSubHeap->pv + pSubHeap->pvR0;
370
 
    RTCritSectLeave(&pHeap->Lock);
371
 
    return pv;
372
 
}
373
 
 
374
 
 
375
 
/**
376
 
 * Releases memory allocated with MMR3UkHeapAlloc() and MMR3UkHeapAllocZ()
377
 
 *
378
 
 * @param   pVM         The VM handle.
379
 
 * @param   pv          Pointer to the memory block to free.
380
 
 */
381
 
VMMR3DECL(void) MMR3UkHeapFree(PVM pVM, void *pv, MMTAG enmTag)
382
 
{
383
 
    /* Ignore NULL pointers. */
384
 
    if (!pv)
385
 
        return;
386
 
 
387
 
    PMMUKHEAP pHeap = pVM->pUVM->mm.s.pUkHeap;
388
 
    RTCritSectEnter(&pHeap->Lock);
389
 
 
390
 
    /*
391
 
     * Find the sub-heap and block
392
 
     */
393
 
#ifdef MMUKHEAP_WITH_STATISTICS
394
 
    size_t cbActual = 0;
395
 
#endif
396
 
    PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
397
 
    while (pSubHeap)
398
 
    {
399
 
        if ((uintptr_t)pv - (uintptr_t)pSubHeap->pv < pSubHeap->cb)
400
 
        {
401
 
#ifdef MMUKHEAP_WITH_STATISTICS
402
 
            cbActual = RTHeapSimpleSize(pSubHeap->hSimple, pv);
403
 
            PMMUKHEAPSTAT pStat = (PMMUKHEAPSTAT)RTAvlULGet(&pHeap->pStatTree, (AVLULKEY)enmTag);
404
 
            if (pStat)
405
 
            {
406
 
                pStat->cFrees++;
407
 
                pStat->cbCurAllocated     -= cbActual;
408
 
                pStat->cbFreed            += cbActual;
409
 
            }
410
 
            pHeap->Stat.cFrees++;
411
 
            pHeap->Stat.cbFreed           += cbActual;
412
 
            pHeap->Stat.cbCurAllocated    -= cbActual;
413
 
#endif
414
 
            RTHeapSimpleFree(pSubHeap->hSimple, pv);
415
 
 
416
 
            RTCritSectLeave(&pHeap->Lock);
417
 
            return;
418
 
        }
419
 
    }
420
 
    AssertMsgFailed(("pv=%p\n", pv));
421
 
}
422