VirtualBox

Changeset 76328 in vbox for trunk/src/VBox/Runtime/common/fs


Ignore:
Timestamp:
Dec 20, 2018 7:43:52 PM (6 years ago)
Author:
vboxsync
Message:

Runtime/fs/vfsext: Implement a small block cache for caching block maps or extents to speed up I/O

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/fs/extvfs.cpp

    r76318 r76328  
    5959#else
    6060# define RTFSEXT_MAX_INODE_CACHE_SIZE       _128K
     61#endif
     62/** The maximum extent/block map cache size (in bytes). */
     63#if ARCH_BITS >= 64
     64# define RTFSEXT_MAX_BLOCK_CACHE_SIZE       _512K
     65#else
     66# define RTFSEXT_MAX_BLOCK_CACHE_SIZE       _128K
    6167#endif
    6268
     
    118124/** Pointer to an in-memory inode. */
    119125typedef RTFSEXTINODE *PRTFSEXTINODE;
     126
     127
     128/**
     129 * Block cache entry.
     130 */
     131typedef struct RTFSEXTBLOCKENTRY
     132{
     133    /** AVL tree node, indexed by the filesystem block number. */
     134    AVLU64NODECORE    Core;
     135    /** List node for the inode LRU list used for eviction. */
     136    RTLISTNODE        NdLru;
     137    /** Reference counter. */
     138    volatile uint32_t cRefs;
     139    /** The block data. */
     140    uint8_t           abData[1];
     141} RTFSEXTBLOCKENTRY;
     142/** Pointer to a block cache entry. */
     143typedef RTFSEXTBLOCKENTRY *PRTFSEXTBLOCKENTRY;
    120144
    121145
     
    197221    uint32_t            fFeaturesIncompat;
    198222
    199 
    200223    /** @name Block group cache.
    201224     * @{ */
     
    216239    /** Size of the cached inodes. */
    217240    size_t              cbInodes;
     241    /** @} */
     242
     243    /** @name Block cache.
     244     * @{ */
     245    /** LRU list anchor for the block cache. */
     246    RTLISTANCHOR        LstBlockLru;
     247    /** Root of the cached block tree. */
     248    AVLU64TREE          BlockRoot;
     249    /** Size of cached blocks. */
     250    size_t              cbBlocks;
    218251    /** @} */
    219252} RTFSEXTVOL;
     
    628661 * @param   iBlockGroup         Block group number.
    629662 */
     663static PRTFSEXTBLOCKENTRY rtFsExtVol_BlockAlloc(PRTFSEXTVOL pThis, size_t cbAlloc, uint64_t iBlock)
     664{
     665    PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)RTMemAllocZ(cbAlloc);
     666    if (RT_LIKELY(pBlock))
     667    {
     668        pBlock->Core.Key = iBlock;
     669        pBlock->cRefs    = 0;
     670        pThis->cbBlocks  += cbAlloc;
     671    }
     672
     673    return pBlock;
     674}
     675
     676
     677/**
     678 * Returns a new block entry utilizing the cache if possible.
     679 *
     680 * @returns Pointer to the new block entry or NULL if out of memory.
     681 * @param   pThis               The ext volume instance.
     682 * @param   iBlock              Block number.
     683 */
     684static PRTFSEXTBLOCKENTRY rtFsExtVol_BlockGetNew(PRTFSEXTVOL pThis, uint64_t iBlock)
     685{
     686    PRTFSEXTBLOCKENTRY pBlock = NULL;
     687    size_t cbAlloc = RT_UOFFSETOF_DYN(RTFSEXTBLOCKENTRY, abData[pThis->cbBlock]);
     688    if (pThis->cbBlocks + cbAlloc <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
     689        pBlock = rtFsExtVol_BlockAlloc(pThis, cbAlloc, iBlock);
     690    else
     691    {
     692        pBlock = RTListRemoveLast(&pThis->LstBlockLru, RTFSEXTBLOCKENTRY, NdLru);
     693        if (!pBlock)
     694            pBlock = rtFsExtVol_BlockAlloc(pThis, cbAlloc, iBlock);
     695        else
     696        {
     697            /* Remove the block group from the tree because it gets a new key. */
     698            PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
     699            Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
     700        }
     701    }
     702
     703    Assert(!pBlock->cRefs);
     704    pBlock->Core.Key = iBlock;
     705    pBlock->cRefs    = 1;
     706
     707    return pBlock;
     708}
     709
     710
     711/**
     712 * Frees the given block.
     713 *
     714 * @returns nothing.
     715 * @param   pThis               The ext volume instance.
     716 * @param   pBlock              The block to free.
     717 */
     718static void rtFsExtVol_BlockFree(PRTFSEXTVOL pThis, PRTFSEXTBLOCKENTRY pBlock)
     719{
     720    Assert(!pBlock->cRefs);
     721
     722    /*
     723     * Put it into the cache if the limit wasn't exceeded, otherwise the block group
     724     * is freed right away.
     725     */
     726    if (pThis->cbBlocks <= RTFSEXT_MAX_BLOCK_CACHE_SIZE)
     727    {
     728        /* Put onto the LRU list. */
     729        RTListPrepend(&pThis->LstBlockLru, &pBlock->NdLru);
     730    }
     731    else
     732    {
     733        /* Remove from the tree and free memory. */
     734        PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
     735        Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
     736        RTMemFree(pBlock);
     737        pThis->cbBlocks -= RT_UOFFSETOF_DYN(RTFSEXTBLOCKENTRY, abData[pThis->cbBlock]);
     738    }
     739}
     740
     741
     742/**
     743 * Gets the specified block data from the volume.
     744 *
     745 * @returns IPRT status code.
     746 * @param   pThis               The ext volume instance.
     747 * @param   iBlock              The filesystem block to load.
     748 * @param   ppBlock             Where to return the pointer to the block entry on success.
     749 * @param   ppvData             Where to return the pointer to the block data on success.
     750 */
     751static int rtFsExtVol_BlockLoad(PRTFSEXTVOL pThis, uint64_t iBlock, PRTFSEXTBLOCKENTRY *ppBlock, void **ppvData)
     752{
     753    int rc = VINF_SUCCESS;
     754
     755    /* Try to fetch the block group from the cache first. */
     756    PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)RTAvlU64Get(&pThis->BlockRoot, iBlock);
     757    if (!pBlock)
     758    {
     759        /* Slow path, load from disk. */
     760        pBlock = rtFsExtVol_BlockGetNew(pThis, iBlock);
     761        if (RT_LIKELY(pBlock))
     762        {
     763            uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, iBlock);
     764            rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlock->abData[0], pThis->cbBlock, NULL);
     765            if (RT_SUCCESS(rc))
     766            {
     767                bool fIns = RTAvlU64Insert(&pThis->BlockRoot, &pBlock->Core);
     768                Assert(fIns); RT_NOREF(fIns);
     769            }
     770        }
     771        else
     772            rc = VERR_NO_MEMORY;
     773    }
     774    else
     775    {
     776        /* Remove from current LRU list position and add to the beginning. */
     777        uint32_t cRefs = ASMAtomicIncU32(&pBlock->cRefs);
     778        if (cRefs == 1) /* Blocks get removed from the LRU list if they are referenced. */
     779            RTListNodeRemove(&pBlock->NdLru);
     780    }
     781
     782    if (RT_SUCCESS(rc))
     783    {
     784        *ppBlock = pBlock;
     785        *ppvData = &pBlock->abData[0];
     786    }
     787    else if (pBlock)
     788    {
     789        ASMAtomicDecU32(&pBlock->cRefs);
     790        rtFsExtVol_BlockFree(pThis, pBlock); /* Free the block. */
     791    }
     792
     793    return rc;
     794}
     795
     796
     797/**
     798 * Releases a reference of the given block.
     799 *
     800 * @returns nothing.
     801 * @param   pThis               The ext volume instance.
     802 * @param   pBlock              The block to release.
     803 */
     804static void rtFsExtVol_BlockRelease(PRTFSEXTVOL pThis, PRTFSEXTBLOCKENTRY pBlock)
     805{
     806    uint32_t cRefs = ASMAtomicDecU32(&pBlock->cRefs);
     807    if (!cRefs)
     808        rtFsExtVol_BlockFree(pThis, pBlock);
     809}
     810
     811
     812/**
     813 * Allocates a new block group.
     814 *
     815 * @returns Pointer to the new block group descriptor or NULL if out of memory.
     816 * @param   pThis               The ext volume instance.
     817 * @param   cbAlloc             How much to allocate.
     818 * @param   iBlockGroup         Block group number.
     819 */
    630820static PRTFSEXTBLKGRP rtFsExtBlockGroupAlloc(PRTFSEXTVOL pThis, size_t cbAlloc, uint32_t iBlockGroup)
    631821{
     
    12291419        else
    12301420        {
    1231             uint8_t *pbExtent = (uint8_t *)RTMemTmpAllocZ(pThis->cbBlock);
    1232             if (RT_LIKELY(pbExtent))
     1421            uint8_t *pbExtent = NULL;
     1422            PRTFSEXTBLOCKENTRY pBlock = NULL;
     1423            uint64_t iBlockNext = 0;
     1424            PCEXTEXTENTIDX paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
     1425            uint16_t cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
     1426
     1427            /* Descend the tree until we reached the leaf nodes. */
     1428            do
    12331429            {
    1234                 uint64_t iBlockNext = 0;
    1235                 PCEXTEXTENTIDX paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
    1236                 uint16_t cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
    1237 
    1238                 /* Descend the tree until we reached the leaf nodes. */
    1239                 do
    1240                 {
    1241                     iBlockNext = rtFsExtInode_ExtentIndexLocateNextLvl(paExtentIdx, cEntries, iBlock);
    1242                     /* Read in the full block. */
    1243                     uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, iBlockNext);
    1244                     rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, pbExtent, pThis->cbBlock, NULL);
    1245                     if (RT_SUCCESS(rc))
    1246                     {
    1247                         pExtentHdr = (PCEXTEXTENTHDR)pbExtent;
    1248 
    1249 #ifdef LOG_ENABLED
    1250                         rtFsExtExtentHdr_Log(pExtentHdr);
    1251 #endif
    1252 
    1253                         if (   rtFsExtInode_ExtentHdrValidate(pExtentHdr)
    1254                             && RT_LE2H_U16(pExtentHdr->cMax) <= (pThis->cbBlock - sizeof(EXTEXTENTHDR)) / sizeof(EXTEXTENTIDX)
    1255                             && RT_LE2H_U16(pExtentHdr->uDepth) == uDepthCur - 1)
    1256                         {
    1257                             uDepthCur--;
    1258                             cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
    1259                             paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
    1260                         }
    1261                         else
    1262                             rc = VERR_VFS_BOGUS_FORMAT;
    1263                     }
    1264                 }
    1265                 while (   uDepthCur > 0
    1266                        && RT_SUCCESS(rc));
    1267 
     1430                iBlockNext = rtFsExtInode_ExtentIndexLocateNextLvl(paExtentIdx, cEntries, iBlock);
     1431                /* Read in the full block. */
     1432                rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&pbExtent);
    12681433                if (RT_SUCCESS(rc))
    12691434                {
    1270                     Assert(!uDepthCur);
    1271 
    1272                     /* We reached the leaf nodes. */
    1273                     PCEXTEXTENT pExtent = (PCEXTEXTENT)(pExtentHdr + 1);
    1274                     for (uint32_t i = 0; i < RT_LE2H_U16(pExtentHdr->cEntries); i++)
     1435                    pExtentHdr = (PCEXTEXTENTHDR)pbExtent;
     1436
     1437#ifdef LOG_ENABLED
     1438                    rtFsExtExtentHdr_Log(pExtentHdr);
     1439#endif
     1440
     1441                    if (   rtFsExtInode_ExtentHdrValidate(pExtentHdr)
     1442                        && RT_LE2H_U16(pExtentHdr->cMax) <= (pThis->cbBlock - sizeof(EXTEXTENTHDR)) / sizeof(EXTEXTENTIDX)
     1443                        && RT_LE2H_U16(pExtentHdr->uDepth) == uDepthCur - 1)
    12751444                    {
    1276                         /* Check whether the extent intersects with the block. */
    1277                         if (rtFsExtInode_ExtentParse(pExtent, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse))
    1278                         {
    1279                             rc = VINF_SUCCESS;
    1280                             break;
    1281                         }
    1282                         pExtent++;
     1445                        uDepthCur--;
     1446                        cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
     1447                        paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
     1448                        if (uDepthCur)
     1449                            rtFsExtVol_BlockRelease(pThis, pBlock);
    12831450                    }
     1451                    else
     1452                        rc = VERR_VFS_BOGUS_FORMAT;
    12841453                }
    1285 
    1286                 RTMemTmpFree(pbExtent);
    12871454            }
    1288             else
    1289                 rc = VERR_NO_MEMORY;
     1455            while (   uDepthCur > 0
     1456                   && RT_SUCCESS(rc));
     1457
     1458            if (RT_SUCCESS(rc))
     1459            {
     1460                Assert(!uDepthCur);
     1461
     1462                /* We reached the leaf nodes. */
     1463                PCEXTEXTENT pExtent = (PCEXTEXTENT)(pExtentHdr + 1);
     1464                for (uint32_t i = 0; i < RT_LE2H_U16(pExtentHdr->cEntries); i++)
     1465                {
     1466                    /* Check whether the extent intersects with the block. */
     1467                    if (rtFsExtInode_ExtentParse(pExtent, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse))
     1468                    {
     1469                        rc = VINF_SUCCESS;
     1470                        break;
     1471                    }
     1472                    pExtent++;
     1473                }
     1474            }
     1475
     1476            if (pBlock)
     1477                rtFsExtVol_BlockRelease(pThis, pBlock);
    12901478        }
    12911479    }
     
    13261514    {
    13271515        uint32_t cEntriesPerBlockMap = (uint32_t)(pThis->cbBlock >> sizeof(uint32_t));
    1328         uint32_t *paBlockMap = (uint32_t *)RTMemTmpAllocZ(pThis->cbBlock);
    1329 
    1330         if (!paBlockMap)
    1331             return VERR_NO_MEMORY;
    13321516
    13331517        if (iBlock <= cEntriesPerBlockMap + 11)
    13341518        {
    13351519            /* Indirect block. */
    1336             uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pInode->aiBlocks[12]);
    1337             rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, paBlockMap, pThis->cbBlock, NULL);
     1520            PRTFSEXTBLOCKENTRY pBlock = NULL;
     1521            uint32_t *paBlockMap = NULL;
     1522            rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[12], &pBlock, (void **)&paBlockMap);
    13381523            if (RT_SUCCESS(rc))
     1524            {
    13391525                *piBlockFs = RT_LE2H_U32(paBlockMap[iBlock - 12]);
     1526                rtFsExtVol_BlockRelease(pThis, pBlock);
     1527            }
    13401528        }
    13411529        else if (iBlock <= cEntriesPerBlockMap * cEntriesPerBlockMap + cEntriesPerBlockMap + 11)
    13421530        {
    13431531            /* Double indirect block. */
    1344             iBlock -= 12;
    1345             uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pInode->aiBlocks[13]);
    1346             rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, paBlockMap, pThis->cbBlock, NULL);
     1532            PRTFSEXTBLOCKENTRY pBlock = NULL;
     1533            uint32_t *paBlockMap = NULL;
     1534
     1535            iBlock -= 12 + cEntriesPerBlockMap;
     1536            rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[13], &pBlock, (void **)&paBlockMap);
    13471537            if (RT_SUCCESS(rc))
    13481538            {
    13491539                uint32_t idxBlockL2 = iBlock / cEntriesPerBlockMap;
    13501540                uint32_t idxBlockL1 = iBlock % cEntriesPerBlockMap;
    1351                 offRead = rtFsExtBlockIdxToDiskOffset(pThis, RT_LE2H_U32(paBlockMap[idxBlockL2]));
    1352                 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, paBlockMap, pThis->cbBlock, NULL);
     1541                uint32_t iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL2]);
     1542
     1543                rtFsExtVol_BlockRelease(pThis, pBlock);
     1544                rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
    13531545                if (RT_SUCCESS(rc))
     1546                {
    13541547                    *piBlockFs = RT_LE2H_U32(paBlockMap[idxBlockL1]);
     1548                    rtFsExtVol_BlockRelease(pThis, pBlock);
     1549                }
    13551550            }
    13561551        }
     
    13581553        {
    13591554            /* Triple indirect block. */
    1360             rc = VERR_NOT_IMPLEMENTED;
    1361         }
    1362 
    1363         RTMemTmpFree(paBlockMap);
     1555            PRTFSEXTBLOCKENTRY pBlock = NULL;
     1556            uint32_t *paBlockMap = NULL;
     1557
     1558            iBlock -= 12 + cEntriesPerBlockMap * cEntriesPerBlockMap + cEntriesPerBlockMap;
     1559            rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[14], &pBlock, (void **)&paBlockMap);
     1560            if (RT_SUCCESS(rc))
     1561            {
     1562                uint32_t idxBlockL3 = iBlock / (cEntriesPerBlockMap * cEntriesPerBlockMap);
     1563                uint32_t iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL3]);
     1564
     1565                rtFsExtVol_BlockRelease(pThis, pBlock);
     1566                rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
     1567                if (RT_SUCCESS(rc))
     1568                {
     1569                    uint32_t idxBlockL2 = (iBlock % (cEntriesPerBlockMap * cEntriesPerBlockMap)) / cEntriesPerBlockMap;
     1570                    uint32_t idxBlockL1 = iBlock % cEntriesPerBlockMap;
     1571                    iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL2]);
     1572
     1573                    rtFsExtVol_BlockRelease(pThis, pBlock);
     1574                    rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
     1575                    if (RT_SUCCESS(rc))
     1576                    {
     1577                        *piBlockFs = RT_LE2H_U32(paBlockMap[idxBlockL1]);
     1578                        rtFsExtVol_BlockRelease(pThis, pBlock);
     1579                    }
     1580                }
     1581            }
     1582        }
    13641583    }
    13651584
     
    22362455
    22372456
     2457static DECLCALLBACK(int) rtFsExtVolBlockTreeDestroy(PAVLU64NODECORE pCore, void *pvUser)
     2458{
     2459    RT_NOREF(pvUser);
     2460
     2461    PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)pCore;
     2462    Assert(!pBlock->cRefs);
     2463    RTMemFree(pBlock);
     2464    return VINF_SUCCESS;
     2465}
     2466
     2467
    22382468/**
    22392469 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
     
    22522482    pThis->InodeRoot = NULL;
    22532483    RTListInit(&pThis->LstInodeLru);
     2484
     2485    /* Destroy the block cache tree. */
     2486    RTAvlU64Destroy(&pThis->BlockRoot, rtFsExtVolBlockTreeDestroy, pThis);
     2487    pThis->BlockRoot = NULL;
     2488    RTListInit(&pThis->LstBlockLru);
    22542489
    22552490    /*
     
    24792714        pThis->BlockGroupRoot = NULL;
    24802715        pThis->InodeRoot      = NULL;
     2716        pThis->BlockRoot      = NULL;
    24812717        pThis->cbBlockGroups  = 0;
    24822718        pThis->cbInodes       = 0;
     2719        pThis->cbBlocks       = 0;
    24832720        RTListInit(&pThis->LstBlockGroupLru);
    24842721        RTListInit(&pThis->LstInodeLru);
     2722        RTListInit(&pThis->LstBlockLru);
    24852723
    24862724        rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
Note: See TracChangeset for help on using the changeset viewer.

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