VirtualBox

Changeset 87326 in vbox for trunk/src


Ignore:
Timestamp:
Jan 20, 2021 4:56:18 PM (4 years ago)
Author:
vboxsync
Message:

AMD IOMMU: bugref:9654 IOTLB cache bits.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp

    r87315 r87326  
    4242/** The IOMMU device instance magic. */
    4343#define IOMMU_MAGIC                                 0x10acce55
    44 /** The maximum number of IOTLB entries in our cache implementation. */
    45 #define IOMMU_IOTLBE_MAX                            64
    4644/** Enable the IOTLBE cache. */
    4745#define IOMMU_WITH_IOTLBE_CACHE
     46
    4847#ifdef IOMMU_WITH_IOTLBE_CACHE
     48# define IOMMU_IOTLBE_PAGE_SIZE_MAX                 _4M
     49/** The maximum number of IOTLB entries in our cache implementation. */
     50# define IOMMU_IOTLBE_MAX                           (IOMMU_IOTLBE_PAGE_SIZE_MAX / _4K)
    4951/** The mask of bits for the domain ID of the IOTLBE key. */
    5052# define IOMMU_IOTLB_DOMAIN_ID_MASK                 UINT64_C(0xffffff0000000000)
     
    133135    uint8_t         fIoPerm;
    134136    /** Padding. */
    135     uint8_t         abPadding[2];
     137    uint16_t        abPadding[2];
    136138} IOWALKRESULT;
    137139/** Pointer to an I/O walk result struct. */
     
    148150    /** The domain ID assigned by software. */
    149151    uint16_t        uDomainId;
    150     /** Whether the domain ID is valid (since all bits of domain ID are usable). */
     152    /** Whether the device ID is valid (if not, lookups must re-read DTE). */
    151153    bool            fValid;
    152154    bool            afAlignment[1];
     
    544546#ifdef IOMMU_WITH_IOTLBE_CACHE
    545547/**
     548 * @callback_method_impl{AVLU64CALLBACK}
     549 */
     550static DECLCALLBACK(int) iommuAmdR3DestroyIotlbe(PAVLU64NODECORE pCore, void *pvUser)
     551{
     552    RT_NOREF2(pCore, pvUser);
     553    /* Nothing to do as we will destroy IOTLB entries wholesale later. */
     554    return VINF_SUCCESS;
     555}
     556
     557
     558/**
    546559 * Constructs the key for an IOTLB entry suitable for using as part of the IOTLB
    547560 * cache.
     
    607620static void iommuAmdIotlbAdd(PIOMMU pThis, PCIOWALKRESULT pWalkResult)
    608621{
     622    /** @todo Make sure somewhere we don't cache translations whose permissions are
     623     *        IOMMU_IO_PERM_NONE. */
    609624    /*
    610625     * If the cache is full, evict the last recently used entry.
     
    618633        RTAvlU64Remove(&pThis->TreeIotlbe, pIotlbe->Core.Key);
    619634        --pThis->cCachedIotlbes;
    620         /* Zero out IOTLB entry before reuse. */
    621         RT_BZERO(pIotlbe, sizeof(IOTLBE));
    622635    }
    623636    else
     
    628641    }
    629642
     643    /* Zero out IOTLB entry before reuse. */
     644    Assert(pIotlbe);
     645    RT_ZERO(*pIotlbe);
     646
    630647    /* Update the entry with the results of the page walk. */
    631     Assert(pIotlbe);
    632648    pIotlbe->WalkResult = *pWalkResult;
    633649
     
    640656     */
    641657    RTListAppend(&pThis->LstLruIotlbe, &pIotlbe->NdLru);
     658}
     659
     660
     661/**
     662 * Removes the IOTLB entry corresponding to the given domain ID and I/O virtual
     663 * address.
     664 *
     665 * @param   pThis           The IOMMU device state.
     666 * @param   uDomainId       The domain ID.
     667 * @param   uIova           The I/O virtual address.
     668 */
     669static void iommuAmdIotlbRemove(PIOMMU pThis, uint16_t uDomainId, uint64_t uIova)
     670{
     671    uint64_t const uKey = iommuAmdIotlbConstructKey(uDomainId, uIova);
     672    PIOTLBE pIotlbe = (PIOTLBE)RTAvlU64Remove(&pThis->TreeIotlbe, uKey);
     673    if (pIotlbe)
     674    {
     675        RTListNodeRemove(&pIotlbe->NdLru);
     676        --pThis->cCachedIotlbes;
     677        RT_BZERO(pIotlbe, sizeof(IOTLBE));
     678    }
     679}
     680
     681
     682/**
     683 * Removes all IOTLB entries from the cache.
     684 *
     685 * @param   pThis   The IOMMU device state.
     686 */
     687static void iommuAmdIotlbRemoveAll(PIOMMU pThis)
     688{
     689    RTAvlU64Destroy(&pThis->TreeIotlbe, iommuAmdR3DestroyIotlbe, NULL /* pvParam */);
     690    RTListInit(&pThis->LstLruIotlbe);
     691    pThis->cCachedIotlbes = 0;
     692}
     693
     694
     695/**
     696 * Removes a set of IOTLB entries from the cache given the domain ID, I/O virtual
     697 * address and size.
     698 *
     699 * @param   pThis       The IOMMU device state.
     700 * @param   uDomainId   The domain ID.
     701 * @param   uIova       The I/O virtual address.
     702 * @param   cShift      The number of bits to shift to get the size of the range
     703 *                      being removed.
     704 */
     705static void iommuAmdIotlbRemoveRange(PIOMMU pThis, uint16_t uDomainId, uint64_t uIova, uint8_t cShift)
     706{
     707    /*
     708     * Our cache is currently based on 4K pages. Pages larger than this are split into 4K pages
     709     * before storing in the cache. So an 8K page will have 2 IOTLB entries, 16K will have 4 etc.
     710     * However, our cache capacity is limited (see IOMMU_IOTLBE_MAX). Thus, if the guest uses pages
     711     * larger than what our cache can split and hold, we will simply flush the entire cache when
     712     * requested to flush a partial range since it exceeds the capacity anyway.
     713     */
     714    bool fFitsInCache;
     715    /** @todo Find a more efficient way of checking split sizes? */
     716    if (   cShift == 12 /* 4K  */
     717        || cShift == 13 /* 8K  */
     718        || cShift == 14 /* 16K */
     719        || cShift == 20 /* 1M */)
     720        fFitsInCache = true;
     721    else
     722        fFitsInCache = false;
     723
     724    if (fFitsInCache)
     725    {
     726        /* Mask off 4K page offset from the address. */
     727        uIova &= X86_PAGE_4K_OFFSET_MASK;
     728
     729        /* Remove pages in the given range. */
     730        uint16_t const cPages = (1 << cShift) / X86_PAGE_4K_SHIFT;
     731        Assert(cPages <= IOMMU_IOTLBE_MAX);
     732        for (uint32_t i = 0; i < cPages; i++)
     733        {
     734            iommuAmdIotlbRemove(pThis, uDomainId, uIova);
     735            uIova += _4K;
     736        }
     737    }
     738    else
     739        iommuAmdIotlbRemoveAll(pThis);
    642740}
    643741#endif  /* IOMMU_WITH_IOTLBE_CACHE */
     
    45704668    return VERR_NOT_IMPLEMENTED;
    45714669}
    4572 
    4573 
    4574 #ifdef IOMMU_WITH_IOTLBE_CACHE
    4575 /**
    4576  * @callback_method_impl{AVLU64CALLBACK}
    4577  */
    4578 static DECLCALLBACK(int) iommuAmdR3DestroyIotlbe(PAVLU64NODECORE pCore, void *pvUser)
    4579 {
    4580     RT_NOREF2(pCore, pvUser);
    4581     /* Nothing to do as we will destroy IOTLB entries wholesale later. */
    4582     return VINF_SUCCESS;
    4583 }
    4584 #endif
    45854670
    45864671
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