VirtualBox

Changeset 104114 in vbox for trunk/src


Ignore:
Timestamp:
Mar 29, 2024 1:57:23 AM (13 months ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
162521
Message:

VMM/IEM: Implemented a very simple alternative to iemTbAllocatorFreeupNativeSpace that frees up to 4MB of memory from a chunk in a linear fashion w/o considering TB recently use and such. Fixed a heap alloc overrun issue in iemExecMemAllocatorInit (dependant on the IEMEXECMEMALLOCATOR structure size). Fixed a use after free problem with threaded TBs and iemThreadedTbObsolete. bugref:10370

Location:
trunk/src/VBox/VMM
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMAll/IEMAllN8veRecompiler.cpp

    r104112 r104114  
    148148 * This is useful for freeing up executable memory among other things.  */
    149149#define IEMEXECMEM_ALT_SUB_WITH_ALLOC_HEADER
     150/** Use alternative pruning. */
     151#define IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING
    150152
    151153
     
    328330     * portion corresponding to an chunk). */
    329331    uint32_t                cBitmapElementsPerChunk;
     332
     333#ifdef IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING
     334    /** The next chunk to prune in. */
     335    uint32_t                idxChunkPrune;
     336    /** Where in chunk offset to start pruning at. */
     337    uint32_t                offChunkPrune;
     338    /** Profiling the pruning code. */
     339    STAMPROFILE             StatPruneProf;
     340    /** Number of bytes recovered by the pruning. */
     341    STAMPROFILE             StatPruneRecovered;
     342#endif
     343
    330344#ifdef VBOX_WITH_STATISTICS
    331345    STAMPROFILE             StatAlloc;
    332346#endif
     347
    333348
    334349#if defined(IN_RING3) && !defined(RT_OS_WINDOWS)
     
    373388
    374389static int iemExecMemAllocatorGrow(PVMCPUCC pVCpu, PIEMEXECMEMALLOCATOR pExecMemAllocator);
     390
     391#ifdef IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING
     392/**
     393 * Frees up executable memory when we're out space.
     394 *
     395 * This is an alternative to iemTbAllocatorFreeupNativeSpace() that frees up
     396 * space in a more linear fashion from the allocator's point of view.  It may
     397 * also defragment if implemented & enabled
     398 */
     399static void iemExecMemAllocatorPrune(PVMCPU pVCpu, PIEMEXECMEMALLOCATOR pExecMemAllocator)
     400{
     401# ifndef IEMEXECMEM_ALT_SUB_WITH_ALLOC_HEADER
     402#  error "IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING requires IEMEXECMEM_ALT_SUB_WITH_ALLOC_HEADER"
     403# endif
     404    STAM_REL_PROFILE_START(&pExecMemAllocator->StatPruneProf, a);
     405
     406    /*
     407     * Before we can start, we must process delayed frees.
     408     */
     409    iemTbAllocatorProcessDelayedFrees(pVCpu, pVCpu->iem.s.pTbAllocatorR3);
     410
     411    AssertCompile(RT_IS_POWER_OF_TWO(IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE));
     412
     413    uint32_t const cbChunk = pExecMemAllocator->cbChunk;
     414    AssertReturnVoid(RT_IS_POWER_OF_TWO(cbChunk));
     415    AssertReturnVoid(cbChunk >= _1M && cbChunk <= _256M); /* see iemExecMemAllocatorInit */
     416
     417    uint32_t const cChunks = pExecMemAllocator->cChunks;
     418    AssertReturnVoid(cChunks == pExecMemAllocator->cMaxChunks);
     419    AssertReturnVoid(cChunks >= 1);
     420
     421    /*
     422     * Decide how much to prune.  The chunk is is a multiple of two, so we'll be
     423     * scanning a multiple of two here as well.
     424     */
     425    uint32_t cbToPrune = cbChunk;
     426
     427    /* Never more than 25%. */
     428    if (cChunks < 4)
     429        cbToPrune /= cChunks == 1 ? 4 : 2;
     430
     431    /* Upper limit. In a debug build a 4MB limit averages out at ~0.6ms per call. */
     432    if (cbToPrune > _4M)
     433        cbToPrune = _4M;
     434
     435    /*
     436     * Adjust the pruning chunk and offset accordingly.
     437     */
     438    uint32_t idxChunk = pExecMemAllocator->idxChunkPrune;
     439    uint32_t offChunk = pExecMemAllocator->offChunkPrune;
     440    offChunk &= ~(uint32_t)(IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE - 1U);
     441    if (offChunk >= cbChunk)
     442    {
     443        offChunk = 0;
     444        idxChunk += 1;
     445    }
     446    if (idxChunk >= cChunks)
     447    {
     448        offChunk = 0;
     449        idxChunk = 0;
     450    }
     451
     452    uint32_t const offPruneEnd = RT_MIN(offChunk + cbToPrune, cbChunk);
     453
     454    /*
     455     * Do the pruning.  The current approach is the sever kind.
     456     */
     457    uint64_t            cbPruned = 0;
     458    uint8_t * const     pbChunk  = (uint8_t *)pExecMemAllocator->aChunks[idxChunk].pvChunk;
     459    while (offChunk < offPruneEnd)
     460    {
     461        PIEMEXECMEMALLOCHDR pHdr = (PIEMEXECMEMALLOCHDR)&pbChunk[offChunk];
     462
     463        /* Is this the start of an allocation block for TB? (We typically have
     464           one allocation at the start of each chunk for the unwind info where
     465           pTb is NULL.)  */
     466        if (   pHdr->uMagic   == IEMEXECMEMALLOCHDR_MAGIC
     467            && pHdr->pTb      != NULL
     468            && pHdr->idxChunk == idxChunk)
     469        {
     470            PIEMTB const pTb = pHdr->pTb;
     471            AssertPtr(pTb);
     472            Assert((pTb->fFlags & IEMTB_F_TYPE_MASK) == IEMTB_F_TYPE_NATIVE);
     473
     474            uint32_t const cbBlock = RT_ALIGN_32(pTb->Native.cInstructions * sizeof(IEMNATIVEINSTR) + sizeof(*pHdr),
     475                                                 IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE);
     476            AssertBreakStmt(offChunk + cbBlock <= cbChunk, offChunk += IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE); /* paranoia */
     477
     478            iemTbAllocatorFree(pVCpu, pTb);
     479
     480            cbPruned += cbBlock;
     481            offChunk += cbBlock;
     482        }
     483        else
     484            offChunk += IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE;
     485    }
     486    STAM_REL_PROFILE_ADD_PERIOD(&pExecMemAllocator->StatPruneRecovered, cbPruned);
     487
     488    /*
     489     * Save the current pruning point.
     490     */
     491    pExecMemAllocator->offChunkPrune = offChunk;
     492    pExecMemAllocator->idxChunkPrune = idxChunk;
     493
     494    STAM_REL_PROFILE_STOP(&pExecMemAllocator->StatPruneProf, a);
     495}
     496#endif /* IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING */
    375497
    376498
     
    558680        if (iIteration == 0)
    559681        {
     682#ifdef IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING
     683            iemExecMemAllocatorPrune(pVCpu, pExecMemAllocator);
     684#else
    560685            /* No header included in the instruction count here. */
    561686            uint32_t const cNeededInstrs = RT_ALIGN_32(cbReq, IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE) / sizeof(IEMNATIVEINSTR);
    562687            iemTbAllocatorFreeupNativeSpace(pVCpu, cNeededInstrs);
     688#endif
    563689        }
    564690        else
     
    14021528     * Allocate and initialize the allocatore instance.
    14031529     */
    1404     size_t       cbNeeded   = RT_UOFFSETOF_DYN(IEMEXECMEMALLOCATOR, aChunks[cMaxChunks]);
    1405     size_t const offBitmaps = RT_ALIGN_Z(cbNeeded, RT_CACHELINE_SIZE);
    1406     size_t const cbBitmap   = cbChunk >> (IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT + 3);
    1407     cbNeeded += cbBitmap * cMaxChunks;
     1530    size_t const offBitmaps = RT_ALIGN_Z(RT_UOFFSETOF_DYN(IEMEXECMEMALLOCATOR, aChunks[cMaxChunks]), RT_CACHELINE_SIZE);
     1531    size_t const cbBitmaps  = (size_t)(cbChunk >> (IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT + 3)) * cMaxChunks;
     1532    size_t       cbNeeded   = offBitmaps + cbBitmaps;
    14081533    AssertCompile(IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT <= 10);
    14091534    Assert(cbChunk > RT_BIT_32(IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT + 3));
     
    14271552    pExecMemAllocator->cUnitsPerChunk           = cbChunk >> IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT;
    14281553    pExecMemAllocator->cBitmapElementsPerChunk  = cbChunk >> (IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT + 6);
    1429     memset(pExecMemAllocator->pbmAlloc, 0xff, cbBitmap); /* Mark everything as allocated. Clear when chunks are added. */
     1554    memset(pExecMemAllocator->pbmAlloc, 0xff, cbBitmaps); /* Mark everything as allocated. Clear when chunks are added. */
    14301555#if defined(IN_RING3) && !defined(RT_OS_WINDOWS)
    14311556    pExecMemAllocator->paEhFrames = (PIEMEXECMEMCHUNKEHFRAME)((uintptr_t)pExecMemAllocator + offEhFrames);
     
    14761601    STAMR3RegisterFU(pUVM, &pExecMemAllocator->StatAlloc,       STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL,
    14771602                     "Profiling the allocator",                 "/IEM/CPU%u/re/ExecMem/ProfAlloc", pVCpu->idCpu);
     1603#endif
     1604#ifdef IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING
     1605    STAMR3RegisterFU(pUVM, &pExecMemAllocator->StatPruneProf,   STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL,
     1606                     "Pruning executable memory (alt)",         "/IEM/CPU%u/re/ExecMem/Pruning", pVCpu->idCpu);
     1607    STAMR3RegisterFU(pUVM, &pExecMemAllocator->StatPruneRecovered, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES_PER_CALL,
     1608                     "Bytes recovered while pruning",           "/IEM/CPU%u/re/ExecMem/PruningRecovered", pVCpu->idCpu);
    14781609#endif
    14791610
  • trunk/src/VBox/VMM/VMMAll/IEMAllThrdRecompiler.cpp

    r104064 r104114  
    114114#endif
    115115
    116 
    117 /*********************************************************************************************************************************
    118 *   Internal Functions                                                                                                           *
    119 *********************************************************************************************************************************/
    120 static void         iemTbAllocatorFree(PVMCPUCC pVCpu, PIEMTB pTb);
    121116
    122117
     
    921916 * @thread  EMT(pVCpu)
    922917 */
    923 static void iemTbAllocatorFree(PVMCPUCC pVCpu, PIEMTB pTb)
     918DECLHIDDEN(void) iemTbAllocatorFree(PVMCPUCC pVCpu, PIEMTB pTb)
    924919{
    925920    /*
     
    941936
    942937/**
    943  * Schedules a native TB for freeing when it's not longer being executed and
    944  * part of the caller's call stack.
     938 * Schedules a TB for freeing when it's not longer being executed and/or part of
     939 * the caller's call stack.
    945940 *
    946941 * The TB will be removed from the translation block cache, though, so it isn't
     
    964959                      IEMTBALLOC_IDX_MAKE(pTbAllocator, pTb->idxAllocChunk,
    965960                                          (uintptr_t)(pTb - pTbAllocator->aChunks[pTb->idxAllocChunk].paTbs))));
    966     Assert((pTb->fFlags & IEMTB_F_TYPE_MASK) == IEMTB_F_TYPE_NATIVE);
     961    Assert(   (pTb->fFlags & IEMTB_F_TYPE_MASK) == IEMTB_F_TYPE_NATIVE
     962           || (pTb->fFlags & IEMTB_F_TYPE_MASK) == IEMTB_F_TYPE_THREADED);
    967963
    968964    /*
     
    16671663void iemThreadedTbObsolete(PVMCPUCC pVCpu, PIEMTB pTb, bool fSafeToFree)
    16681664{
    1669     /* Unless it's safe, we can only immediately free threaded TB, as we will
    1670        have more code left to execute in native TBs when fSafeToFree == false.  */
    1671     if (fSafeToFree || (pTb->fFlags & IEMTB_F_TYPE_THREADED))
     1665    /* We cannot free the current TB (indicated by fSafeToFree) because:
     1666            - A threaded TB will have its current call entry accessed
     1667              to update pVCpu->iem.s.cInstructions.
     1668            - A native TB will have code left to execute. */
     1669    if (fSafeToFree)
    16721670        iemTbAllocatorFree(pVCpu, pTb);
    16731671    else
  • trunk/src/VBox/VMM/include/IEMInternal.h

    r104103 r104114  
    60716071                              uint64_t cbInitialExec, uint64_t cbMaxExec, uint32_t cbChunkExec);
    60726072void                iemThreadedTbObsolete(PVMCPUCC pVCpu, PIEMTB pTb, bool fSafeToFree);
     6073DECLHIDDEN(void)    iemTbAllocatorFree(PVMCPUCC pVCpu, PIEMTB pTb);
    60736074void                iemTbAllocatorProcessDelayedFrees(PVMCPUCC pVCpu, PIEMTBALLOCATOR pTbAllocator);
    60746075void                iemTbAllocatorFreeupNativeSpace(PVMCPUCC pVCpu, uint32_t cNeededInstrs);
Note: See TracChangeset for help on using the changeset viewer.

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