# Commit fd33987ba27607c3cc7da258cf1d86d21beeb735 # Date 2014-06-30 15:57:40 +0200 # Author Jan Beulich # Committer Jan Beulich VT-d/ATS: correct and clean up dev_invalidate_iotlb() While this was intended to only do cleanup (replace the two bogus "ret |= " constructs, and a simple formatting correction), this now also - fixes the bit manipulations for size_order > 0 a) correct an off-by-one in the use of size_order for shifting (till now double the requested size got invalidated) b) in fact setting bit 12 and up if necessary (without which too small a region might have got invalidated) c) making them capable of dealing with regions of 4Gb size and up - corrects the return value handling, such that a later iteration's success won't clear an earlier iteration's error indication - uses PCI_BDF2() instead of open coding it - bail immediately on bad passed in invalidation type, rather than repeatedly printing the same message for each ATS-capable device, at once also no longer hiding that failure from the caller Signed-off-by: Jan Beulich Reviewed-by: Andrew Cooper Acked-by: Yang Zhang --- a/xen/drivers/passthrough/vtd/x86/ats.c +++ b/xen/drivers/passthrough/vtd/x86/ats.c @@ -110,21 +110,23 @@ int dev_invalidate_iotlb(struct iommu *i u64 addr, unsigned int size_order, u64 type) { struct pci_ats_dev *pdev; - int sbit, ret = 0; - u16 sid; + int ret = 0; if ( !ecap_dev_iotlb(iommu->ecap) ) return ret; list_for_each_entry( pdev, &ats_devices, list ) { - sid = (pdev->bus << 8) | pdev->devfn; + u16 sid = PCI_BDF2(pdev->bus, pdev->devfn); + bool_t sbit; + int rc = 0; /* Only invalidate devices that belong to this IOMMU */ if ( pdev->iommu != iommu ) continue; - switch ( type ) { + switch ( type ) + { case DMA_TLB_DSI_FLUSH: if ( !device_in_domain(iommu, pdev, did) ) break; @@ -133,32 +135,37 @@ int dev_invalidate_iotlb(struct iommu *i /* invalidate all translations: sbit=1,bit_63=0,bit[62:12]=1 */ sbit = 1; addr = (~0 << PAGE_SHIFT_4K) & 0x7FFFFFFFFFFFFFFF; - ret |= qinval_device_iotlb(iommu, pdev->ats_queue_depth, - sid, sbit, addr); + rc = qinval_device_iotlb(iommu, pdev->ats_queue_depth, + sid, sbit, addr); break; case DMA_TLB_PSI_FLUSH: if ( !device_in_domain(iommu, pdev, did) ) break; - addr &= ~0 << (PAGE_SHIFT + size_order); - /* if size <= 4K, set sbit = 0, else set sbit = 1 */ sbit = size_order ? 1 : 0; /* clear lower bits */ - addr &= (~0 << (PAGE_SHIFT + size_order)); + addr &= ~0 << PAGE_SHIFT_4K; /* if sbit == 1, zero out size_order bit and set lower bits to 1 */ if ( sbit ) - addr &= (~0 & ~(1 << (PAGE_SHIFT + size_order))); + { + addr &= ~((u64)PAGE_SIZE_4K << (size_order - 1)); + addr |= (((u64)1 << (size_order - 1)) - 1) << PAGE_SHIFT_4K; + } - ret |= qinval_device_iotlb(iommu, pdev->ats_queue_depth, - sid, sbit, addr); + rc = qinval_device_iotlb(iommu, pdev->ats_queue_depth, + sid, sbit, addr); break; default: dprintk(XENLOG_WARNING VTDPREFIX, "invalid vt-d flush type\n"); - break; + return -EOPNOTSUPP; } + + if ( !ret ) + ret = rc; } + return ret; }