IOMMU Fixes for Linux v6.1-rc7
Including: - Intel VT-d fixes: - IO/TLB flush fix - Various pci_dev refcount fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEr9jSbILcajRFYWYyK/BELZcBGuMFAmOKBQkACgkQK/BELZcB GuOzPhAAtJfmbfbvGLjCz/e9DgLl1sdfFrR1FwzwpXmQ3iV1isBy8AG/PX2+uBMs Qcge0BkzX0X/8I/lbnJYHbix3Z0cjDuYl4kPyYP8V5+tqSuJRnAODw+GJK17ntn8 EfsDG4fZzEIUAgPE8PP4qXZwXI2pLfF6A4CT0ztB46976fpzcLAUicG0H2Opy9vQ DmDNOsg3R0yBB/1XaN0QSavfnoLKmaB37aHv0GeN4l5aue6tgWzxKUxBKSWnA7nF ZS+3XFe0tAhmxPH3JGmHqloxQrR52zqq9vMsbn0PTND6UKCN/pEo+3TkJQ9FLxvm qQi1lrAf9zRoIcsodXVAvgWbEgbR5LWxAffSwz+oJBv9MwMA8pfCG95HGBVX90fD WY01XcsnHmo1BqOHg5P9lSC979xGdltL71IjbKi1r31njZ2VByfDNcsa9OSBCD0L 9Y8JJ0vW8ipbpDEDoxZUuElY+UkKUyJFurNVPxpCiKQhIdWdTPUurnvCBQgi5uas zVtI6OP/I7MIZbc00C4Y7KfsLm0MqlVOYzhvG+8vGW9GLUTVtWF0MkP6sfUEiQmS OqsxqTiLjbGfIOBvhZxyVZ7sCVY2d776KS6d9LlYINmRn8UAzIQC01szyr+Jx5m4 jqs/ujTVIr2UiZ2QSrdQ2wNsrab/4vUrAN/O+uoJ5eV537ryKl8= =rgA1 -----END PGP SIGNATURE----- Merge tag 'iommu-fixes-v6.1-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu Pull iommu fixes from Joerg Roedel: "Intel VT-d fixes: - IO/TLB flush fix - Various pci_dev refcount fixes" * tag 'iommu-fixes-v6.1-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: iommu/vt-d: Fix PCI device refcount leak in dmar_dev_scope_init() iommu/vt-d: Fix PCI device refcount leak in has_external_pci() iommu/vt-d: Fix PCI device refcount leak in prq_event_thread() iommu/vt-d: Add a fix for devices need extra dtlb flush
This commit is contained in:
commit
f66f62f83d
@ -820,6 +820,7 @@ int __init dmar_dev_scope_init(void)
|
||||
info = dmar_alloc_pci_notify_info(dev,
|
||||
BUS_NOTIFY_ADD_DEVICE);
|
||||
if (!info) {
|
||||
pci_dev_put(dev);
|
||||
return dmar_dev_scope_status;
|
||||
} else {
|
||||
dmar_pci_bus_add_dev(info);
|
||||
|
@ -1396,6 +1396,24 @@ static void domain_update_iotlb(struct dmar_domain *domain)
|
||||
spin_unlock_irqrestore(&domain->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* The extra devTLB flush quirk impacts those QAT devices with PCI device
|
||||
* IDs ranging from 0x4940 to 0x4943. It is exempted from risky_device()
|
||||
* check because it applies only to the built-in QAT devices and it doesn't
|
||||
* grant additional privileges.
|
||||
*/
|
||||
#define BUGGY_QAT_DEVID_MASK 0x494c
|
||||
static bool dev_needs_extra_dtlb_flush(struct pci_dev *pdev)
|
||||
{
|
||||
if (pdev->vendor != PCI_VENDOR_ID_INTEL)
|
||||
return false;
|
||||
|
||||
if ((pdev->device & 0xfffc) != BUGGY_QAT_DEVID_MASK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void iommu_enable_pci_caps(struct device_domain_info *info)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
@ -1478,6 +1496,7 @@ static void __iommu_flush_dev_iotlb(struct device_domain_info *info,
|
||||
qdep = info->ats_qdep;
|
||||
qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
|
||||
qdep, addr, mask);
|
||||
quirk_extra_dev_tlb_flush(info, addr, mask, PASID_RID2PASID, qdep);
|
||||
}
|
||||
|
||||
static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
|
||||
@ -3854,8 +3873,10 @@ static inline bool has_external_pci(void)
|
||||
struct pci_dev *pdev = NULL;
|
||||
|
||||
for_each_pci_dev(pdev)
|
||||
if (pdev->external_facing)
|
||||
if (pdev->external_facing) {
|
||||
pci_dev_put(pdev);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -4490,9 +4511,10 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev)
|
||||
if (dev_is_pci(dev)) {
|
||||
if (ecap_dev_iotlb_support(iommu->ecap) &&
|
||||
pci_ats_supported(pdev) &&
|
||||
dmar_ats_supported(pdev, iommu))
|
||||
dmar_ats_supported(pdev, iommu)) {
|
||||
info->ats_supported = 1;
|
||||
|
||||
info->dtlb_extra_inval = dev_needs_extra_dtlb_flush(pdev);
|
||||
}
|
||||
if (sm_supported(iommu)) {
|
||||
if (pasid_supported(iommu)) {
|
||||
int features = pci_pasid_features(pdev);
|
||||
@ -4931,3 +4953,48 @@ static void __init check_tylersburg_isoch(void)
|
||||
pr_warn("Recommended TLB entries for ISOCH unit is 16; your BIOS set %d\n",
|
||||
vtisochctrl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we deal with a device TLB defect where device may inadvertently issue ATS
|
||||
* invalidation completion before posted writes initiated with translated address
|
||||
* that utilized translations matching the invalidation address range, violating
|
||||
* the invalidation completion ordering.
|
||||
* Therefore, any use cases that cannot guarantee DMA is stopped before unmap is
|
||||
* vulnerable to this defect. In other words, any dTLB invalidation initiated not
|
||||
* under the control of the trusted/privileged host device driver must use this
|
||||
* quirk.
|
||||
* Device TLBs are invalidated under the following six conditions:
|
||||
* 1. Device driver does DMA API unmap IOVA
|
||||
* 2. Device driver unbind a PASID from a process, sva_unbind_device()
|
||||
* 3. PASID is torn down, after PASID cache is flushed. e.g. process
|
||||
* exit_mmap() due to crash
|
||||
* 4. Under SVA usage, called by mmu_notifier.invalidate_range() where
|
||||
* VM has to free pages that were unmapped
|
||||
* 5. Userspace driver unmaps a DMA buffer
|
||||
* 6. Cache invalidation in vSVA usage (upcoming)
|
||||
*
|
||||
* For #1 and #2, device drivers are responsible for stopping DMA traffic
|
||||
* before unmap/unbind. For #3, iommu driver gets mmu_notifier to
|
||||
* invalidate TLB the same way as normal user unmap which will use this quirk.
|
||||
* The dTLB invalidation after PASID cache flush does not need this quirk.
|
||||
*
|
||||
* As a reminder, #6 will *NEED* this quirk as we enable nested translation.
|
||||
*/
|
||||
void quirk_extra_dev_tlb_flush(struct device_domain_info *info,
|
||||
unsigned long address, unsigned long mask,
|
||||
u32 pasid, u16 qdep)
|
||||
{
|
||||
u16 sid;
|
||||
|
||||
if (likely(!info->dtlb_extra_inval))
|
||||
return;
|
||||
|
||||
sid = PCI_DEVID(info->bus, info->devfn);
|
||||
if (pasid == PASID_RID2PASID) {
|
||||
qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
|
||||
qdep, address, mask);
|
||||
} else {
|
||||
qi_flush_dev_iotlb_pasid(info->iommu, sid, info->pfsid,
|
||||
pasid, qdep, address, mask);
|
||||
}
|
||||
}
|
||||
|
@ -623,6 +623,7 @@ struct device_domain_info {
|
||||
u8 pri_enabled:1;
|
||||
u8 ats_supported:1;
|
||||
u8 ats_enabled:1;
|
||||
u8 dtlb_extra_inval:1; /* Quirk for devices need extra flush */
|
||||
u8 ats_qdep;
|
||||
struct device *dev; /* it's NULL for PCIe-to-PCI bridge */
|
||||
struct intel_iommu *iommu; /* IOMMU used by this device */
|
||||
@ -728,6 +729,9 @@ void qi_flush_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, u64 addr,
|
||||
void qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid,
|
||||
u32 pasid, u16 qdep, u64 addr,
|
||||
unsigned int size_order);
|
||||
void quirk_extra_dev_tlb_flush(struct device_domain_info *info,
|
||||
unsigned long address, unsigned long pages,
|
||||
u32 pasid, u16 qdep);
|
||||
void qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did, u64 granu,
|
||||
u32 pasid);
|
||||
|
||||
|
@ -184,10 +184,13 @@ static void __flush_svm_range_dev(struct intel_svm *svm,
|
||||
return;
|
||||
|
||||
qi_flush_piotlb(sdev->iommu, sdev->did, svm->pasid, address, pages, ih);
|
||||
if (info->ats_enabled)
|
||||
if (info->ats_enabled) {
|
||||
qi_flush_dev_iotlb_pasid(sdev->iommu, sdev->sid, info->pfsid,
|
||||
svm->pasid, sdev->qdep, address,
|
||||
order_base_2(pages));
|
||||
quirk_extra_dev_tlb_flush(info, address, order_base_2(pages),
|
||||
svm->pasid, sdev->qdep);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_flush_svm_range_dev(struct intel_svm *svm,
|
||||
@ -745,12 +748,16 @@ bad_req:
|
||||
* If prq is to be handled outside iommu driver via receiver of
|
||||
* the fault notifiers, we skip the page response here.
|
||||
*/
|
||||
if (!pdev || intel_svm_prq_report(iommu, &pdev->dev, req))
|
||||
handle_bad_prq_event(iommu, req, QI_RESP_INVALID);
|
||||
if (!pdev)
|
||||
goto bad_req;
|
||||
|
||||
trace_prq_report(iommu, &pdev->dev, req->qw_0, req->qw_1,
|
||||
req->priv_data[0], req->priv_data[1],
|
||||
iommu->prq_seq_number++);
|
||||
if (intel_svm_prq_report(iommu, &pdev->dev, req))
|
||||
handle_bad_prq_event(iommu, req, QI_RESP_INVALID);
|
||||
else
|
||||
trace_prq_report(iommu, &pdev->dev, req->qw_0, req->qw_1,
|
||||
req->priv_data[0], req->priv_data[1],
|
||||
iommu->prq_seq_number++);
|
||||
pci_dev_put(pdev);
|
||||
prq_advance:
|
||||
head = (head + sizeof(*req)) & PRQ_RING_MASK;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user