iommu/vt-d: Report prq to io-pgfault framework
authorLu Baolu <baolu.lu@linux.intel.com>
Thu, 10 Jun 2021 02:01:03 +0000 (10:01 +0800)
committerJoerg Roedel <jroedel@suse.de>
Thu, 10 Jun 2021 07:06:13 +0000 (09:06 +0200)
Let the IO page fault requests get handled through the io-pgfault
framework.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Link: https://lore.kernel.org/r/20210520031531.712333-1-baolu.lu@linux.intel.com
Link: https://lore.kernel.org/r/20210610020115.1637656-12-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/intel/iommu.c
drivers/iommu/intel/svm.c

index e78773d..c45d494 100644 (file)
@@ -5343,6 +5343,7 @@ static int intel_iommu_enable_sva(struct device *dev)
 {
        struct device_domain_info *info = get_domain_info(dev);
        struct intel_iommu *iommu = info->iommu;
+       int ret;
 
        if (!info || !iommu || dmar_disabled)
                return -EINVAL;
@@ -5356,15 +5357,24 @@ static int intel_iommu_enable_sva(struct device *dev)
        if (!info->pasid_enabled || !info->pri_enabled || !info->ats_enabled)
                return -EINVAL;
 
-       return iopf_queue_add_device(iommu->iopf_queue, dev);
+       ret = iopf_queue_add_device(iommu->iopf_queue, dev);
+       if (!ret)
+               ret = iommu_register_device_fault_handler(dev, iommu_queue_iopf, dev);
+
+       return ret;
 }
 
 static int intel_iommu_disable_sva(struct device *dev)
 {
        struct device_domain_info *info = get_domain_info(dev);
        struct intel_iommu *iommu = info->iommu;
+       int ret;
+
+       ret = iommu_unregister_device_fault_handler(dev);
+       if (!ret)
+               ret = iopf_queue_remove_device(iommu->iopf_queue, dev);
 
-       return iopf_queue_remove_device(iommu->iopf_queue, dev);
+       return ret;
 }
 
 /*
index 4dc3ab3..ade157b 100644 (file)
@@ -724,22 +724,6 @@ struct page_req_dsc {
 
 #define PRQ_RING_MASK  ((0x1000 << PRQ_ORDER) - 0x20)
 
-static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req)
-{
-       unsigned long requested = 0;
-
-       if (req->exe_req)
-               requested |= VM_EXEC;
-
-       if (req->rd_req)
-               requested |= VM_READ;
-
-       if (req->wr_req)
-               requested |= VM_WRITE;
-
-       return (requested & ~vma->vm_flags) != 0;
-}
-
 static bool is_canonical_address(u64 addr)
 {
        int shift = 64 - (__VIRTUAL_MASK_SHIFT + 1);
@@ -809,6 +793,8 @@ prq_retry:
                goto prq_retry;
        }
 
+       iopf_queue_flush_dev(dev);
+
        /*
         * Perform steps described in VT-d spec CH7.10 to drain page
         * requests and responses in hardware.
@@ -924,61 +910,6 @@ static void handle_bad_prq_event(struct intel_iommu *iommu,
        qi_submit_sync(iommu, &desc, 1, 0);
 }
 
-static void handle_single_prq_event(struct intel_iommu *iommu,
-                                   struct mm_struct *mm,
-                                   struct page_req_dsc *req)
-{
-       u64 address = (u64)req->addr << VTD_PAGE_SHIFT;
-       int result = QI_RESP_INVALID;
-       struct vm_area_struct *vma;
-       struct qi_desc desc;
-       unsigned int flags;
-       vm_fault_t ret;
-
-       /* If the mm is already defunct, don't handle faults. */
-       if (!mmget_not_zero(mm))
-               goto response;
-
-       mmap_read_lock(mm);
-       vma = find_extend_vma(mm, address);
-       if (!vma || address < vma->vm_start)
-               goto invalid;
-
-       if (access_error(vma, req))
-               goto invalid;
-
-       flags = FAULT_FLAG_USER | FAULT_FLAG_REMOTE;
-       if (req->wr_req)
-               flags |= FAULT_FLAG_WRITE;
-
-       ret = handle_mm_fault(vma, address, flags, NULL);
-       if (!(ret & VM_FAULT_ERROR))
-               result = QI_RESP_SUCCESS;
-invalid:
-       mmap_read_unlock(mm);
-       mmput(mm);
-
-response:
-       if (!(req->lpig || req->priv_data_present))
-               return;
-
-       desc.qw0 = QI_PGRP_PASID(req->pasid) |
-                       QI_PGRP_DID(req->rid) |
-                       QI_PGRP_PASID_P(req->pasid_present) |
-                       QI_PGRP_PDP(req->priv_data_present) |
-                       QI_PGRP_RESP_CODE(result) |
-                       QI_PGRP_RESP_TYPE;
-       desc.qw1 = QI_PGRP_IDX(req->prg_index) |
-                       QI_PGRP_LPIG(req->lpig);
-       desc.qw2 = 0;
-       desc.qw3 = 0;
-
-       if (req->priv_data_present)
-               memcpy(&desc.qw2, req->priv_data, sizeof(req->priv_data));
-
-       qi_submit_sync(iommu, &desc, 1, 0);
-}
-
 static irqreturn_t prq_event_thread(int irq, void *d)
 {
        struct intel_svm_dev *sdev = NULL;
@@ -1049,14 +980,8 @@ bad_req:
                 * If prq is to be handled outside iommu driver via receiver of
                 * the fault notifiers, we skip the page response here.
                 */
-               if (svm->flags & SVM_FLAG_GUEST_MODE) {
-                       if (!intel_svm_prq_report(sdev->dev, req))
-                               goto prq_advance;
-                       else
-                               goto bad_req;
-               }
-
-               handle_single_prq_event(iommu, svm->mm, req);
+               if (intel_svm_prq_report(sdev->dev, req))
+                       handle_bad_prq_event(iommu, req, QI_RESP_INVALID);
 prq_advance:
                head = (head + sizeof(*req)) & PRQ_RING_MASK;
        }
@@ -1073,6 +998,7 @@ prq_advance:
                head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK;
                tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK;
                if (head == tail) {
+                       iopf_queue_discard_partial(iommu->iopf_queue);
                        writel(DMA_PRS_PRO, iommu->reg + DMAR_PRS_REG);
                        pr_info_ratelimited("IOMMU: %s: PRQ overflow cleared",
                                            iommu->name);