|
23 | 23 | #include "intel-pasid.h"
|
24 | 24 |
|
25 | 25 | static irqreturn_t prq_event_thread(int irq, void *d);
|
| 26 | +static void intel_svm_drain_prq(struct device *dev, int pasid); |
26 | 27 |
|
27 | 28 | #define PRQ_ORDER 0
|
28 | 29 |
|
@@ -66,6 +67,8 @@ int intel_svm_enable_prq(struct intel_iommu *iommu)
|
66 | 67 | dmar_writeq(iommu->reg + DMAR_PQT_REG, 0ULL);
|
67 | 68 | dmar_writeq(iommu->reg + DMAR_PQA_REG, virt_to_phys(iommu->prq) | PRQ_ORDER);
|
68 | 69 |
|
| 70 | + init_completion(&iommu->prq_complete); |
| 71 | + |
69 | 72 | return 0;
|
70 | 73 | }
|
71 | 74 |
|
@@ -399,12 +402,8 @@ int intel_svm_unbind_gpasid(struct device *dev, int pasid)
|
399 | 402 | list_del_rcu(&sdev->list);
|
400 | 403 | intel_pasid_tear_down_entry(iommu, dev,
|
401 | 404 | svm->pasid, false);
|
| 405 | + intel_svm_drain_prq(dev, svm->pasid); |
402 | 406 | intel_flush_svm_range_dev(svm, sdev, 0, -1, 0);
|
403 |
| - /* TODO: Drain in flight PRQ for the PASID since it |
404 |
| - * may get reused soon, we don't want to |
405 |
| - * confuse with its previous life. |
406 |
| - * intel_svm_drain_prq(dev, pasid); |
407 |
| - */ |
408 | 407 | kfree_rcu(sdev, rcu);
|
409 | 408 |
|
410 | 409 | if (list_empty(&svm->devs)) {
|
@@ -643,6 +642,7 @@ int intel_svm_unbind_mm(struct device *dev, int pasid)
|
643 | 642 | * hard to be as defensive as we might like. */
|
644 | 643 | intel_pasid_tear_down_entry(iommu, dev,
|
645 | 644 | svm->pasid, false);
|
| 645 | + intel_svm_drain_prq(dev, svm->pasid); |
646 | 646 | intel_flush_svm_range_dev(svm, sdev, 0, -1, 0);
|
647 | 647 | kfree_rcu(sdev, rcu);
|
648 | 648 |
|
@@ -721,6 +721,93 @@ static bool is_canonical_address(u64 addr)
|
721 | 721 | return (((saddr << shift) >> shift) == saddr);
|
722 | 722 | }
|
723 | 723 |
|
| 724 | +/** |
| 725 | + * intel_svm_drain_prq - Drain page requests and responses for a pasid |
| 726 | + * @dev: target device |
| 727 | + * @pasid: pasid for draining |
| 728 | + * |
| 729 | + * Drain all pending page requests and responses related to @pasid in both |
| 730 | + * software and hardware. This is supposed to be called after the device |
| 731 | + * driver has stopped DMA, the pasid entry has been cleared, and both IOTLB |
| 732 | + * and DevTLB have been invalidated. |
| 733 | + * |
| 734 | + * It waits until all pending page requests for @pasid in the page fault |
| 735 | + * queue are completed by the prq handling thread. Then follow the steps |
| 736 | + * described in VT-d spec CH7.10 to drain all page requests and page |
| 737 | + * responses pending in the hardware. |
| 738 | + */ |
| 739 | +static void intel_svm_drain_prq(struct device *dev, int pasid) |
| 740 | +{ |
| 741 | + struct device_domain_info *info; |
| 742 | + struct dmar_domain *domain; |
| 743 | + struct intel_iommu *iommu; |
| 744 | + struct qi_desc desc[3]; |
| 745 | + struct pci_dev *pdev; |
| 746 | + int head, tail; |
| 747 | + u16 sid, did; |
| 748 | + int qdep; |
| 749 | + |
| 750 | + info = get_domain_info(dev); |
| 751 | + if (WARN_ON(!info || !dev_is_pci(dev))) |
| 752 | + return; |
| 753 | + |
| 754 | + if (!info->pri_enabled) |
| 755 | + return; |
| 756 | + |
| 757 | + iommu = info->iommu; |
| 758 | + domain = info->domain; |
| 759 | + pdev = to_pci_dev(dev); |
| 760 | + sid = PCI_DEVID(info->bus, info->devfn); |
| 761 | + did = domain->iommu_did[iommu->seq_id]; |
| 762 | + qdep = pci_ats_queue_depth(pdev); |
| 763 | + |
| 764 | + /* |
| 765 | + * Check and wait until all pending page requests in the queue are |
| 766 | + * handled by the prq handling thread. |
| 767 | + */ |
| 768 | +prq_retry: |
| 769 | + reinit_completion(&iommu->prq_complete); |
| 770 | + tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK; |
| 771 | + head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK; |
| 772 | + while (head != tail) { |
| 773 | + struct page_req_dsc *req; |
| 774 | + |
| 775 | + req = &iommu->prq[head / sizeof(*req)]; |
| 776 | + if (!req->pasid_present || req->pasid != pasid) { |
| 777 | + head = (head + sizeof(*req)) & PRQ_RING_MASK; |
| 778 | + continue; |
| 779 | + } |
| 780 | + |
| 781 | + wait_for_completion(&iommu->prq_complete); |
| 782 | + goto prq_retry; |
| 783 | + } |
| 784 | + |
| 785 | + /* |
| 786 | + * Perform steps described in VT-d spec CH7.10 to drain page |
| 787 | + * requests and responses in hardware. |
| 788 | + */ |
| 789 | + memset(desc, 0, sizeof(desc)); |
| 790 | + desc[0].qw0 = QI_IWD_STATUS_DATA(QI_DONE) | |
| 791 | + QI_IWD_FENCE | |
| 792 | + QI_IWD_TYPE; |
| 793 | + desc[1].qw0 = QI_EIOTLB_PASID(pasid) | |
| 794 | + QI_EIOTLB_DID(did) | |
| 795 | + QI_EIOTLB_GRAN(QI_GRAN_NONG_PASID) | |
| 796 | + QI_EIOTLB_TYPE; |
| 797 | + desc[2].qw0 = QI_DEV_EIOTLB_PASID(pasid) | |
| 798 | + QI_DEV_EIOTLB_SID(sid) | |
| 799 | + QI_DEV_EIOTLB_QDEP(qdep) | |
| 800 | + QI_DEIOTLB_TYPE | |
| 801 | + QI_DEV_IOTLB_PFSID(info->pfsid); |
| 802 | +qi_retry: |
| 803 | + reinit_completion(&iommu->prq_complete); |
| 804 | + qi_submit_sync(iommu, desc, 3, QI_OPT_WAIT_DRAIN); |
| 805 | + if (readl(iommu->reg + DMAR_PRS_REG) & DMA_PRS_PRO) { |
| 806 | + wait_for_completion(&iommu->prq_complete); |
| 807 | + goto qi_retry; |
| 808 | + } |
| 809 | +} |
| 810 | + |
724 | 811 | static irqreturn_t prq_event_thread(int irq, void *d)
|
725 | 812 | {
|
726 | 813 | struct intel_iommu *iommu = d;
|
@@ -856,6 +943,16 @@ static irqreturn_t prq_event_thread(int irq, void *d)
|
856 | 943 |
|
857 | 944 | dmar_writeq(iommu->reg + DMAR_PQH_REG, tail);
|
858 | 945 |
|
| 946 | + /* |
| 947 | + * Clear the page request overflow bit and wake up all threads that |
| 948 | + * are waiting for the completion of this handling. |
| 949 | + */ |
| 950 | + if (readl(iommu->reg + DMAR_PRS_REG) & DMA_PRS_PRO) |
| 951 | + writel(DMA_PRS_PRO, iommu->reg + DMAR_PRS_REG); |
| 952 | + |
| 953 | + if (!completion_done(&iommu->prq_complete)) |
| 954 | + complete(&iommu->prq_complete); |
| 955 | + |
859 | 956 | return IRQ_RETVAL(handled);
|
860 | 957 | }
|
861 | 958 |
|
|
0 commit comments