Commit cf08ca81 authored by Lu Baolu's avatar Lu Baolu Committed by Joerg Roedel
Browse files

iommu/vt-d: Draining PRQ in sva unbind path when FPD bit set



When a device uses a PASID for SVA (Shared Virtual Address), it's possible
that the PASID entry is marked as non-present and FPD bit set before the
device flushes all ongoing DMA requests and removes the SVA domain. This
can occur when an exception happens and the process terminates before the
device driver stops DMA and calls the iommu driver to unbind the PASID.

There's no need to drain the PRQ in the mm release path. Instead, the PRQ
will be drained in the SVA unbind path. But in such case,
intel_pasid_tear_down_entry() only checks the presence of the pasid entry
and returns directly.

Add the code to clear the FPD bit and drain the PRQ.

Fixes: c43e1ccd ("iommu/vt-d: Drain PRQs when domain removed from RID")
Suggested-by: default avatarKevin Tian <kevin.tian@intel.com>
Signed-off-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20241217024240.139615-1-baolu.lu@linux.intel.com


Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent c2206299
Loading
Loading
Loading
Loading
+21 −1
Original line number Diff line number Diff line
@@ -244,11 +244,31 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev,

	spin_lock(&iommu->lock);
	pte = intel_pasid_get_entry(dev, pasid);
	if (WARN_ON(!pte) || !pasid_pte_is_present(pte)) {
	if (WARN_ON(!pte)) {
		spin_unlock(&iommu->lock);
		return;
	}

	if (!pasid_pte_is_present(pte)) {
		if (!pasid_pte_is_fault_disabled(pte)) {
			WARN_ON(READ_ONCE(pte->val[0]) != 0);
			spin_unlock(&iommu->lock);
			return;
		}

		/*
		 * When a PASID is used for SVA by a device, it's possible
		 * that the pasid entry is non-present with the Fault
		 * Processing Disabled bit set. Clear the pasid entry and
		 * drain the PRQ for the PASID before return.
		 */
		pasid_clear_entry(pte);
		spin_unlock(&iommu->lock);
		intel_iommu_drain_pasid_prq(dev, pasid);

		return;
	}

	did = pasid_get_domain_id(pte);
	pgtt = pasid_pte_get_pgtt(pte);
	intel_pasid_clear_entry(dev, pasid, fault_ignore);
+6 −0
Original line number Diff line number Diff line
@@ -73,6 +73,12 @@ static inline bool pasid_pte_is_present(struct pasid_entry *pte)
	return READ_ONCE(pte->val[0]) & PASID_PTE_PRESENT;
}

/* Get FPD(Fault Processing Disable) bit of a PASID table entry */
static inline bool pasid_pte_is_fault_disabled(struct pasid_entry *pte)
{
	return READ_ONCE(pte->val[0]) & PASID_PTE_FPD;
}

/* Get PGTT field of a PASID table entry */
static inline u16 pasid_pte_get_pgtt(struct pasid_entry *pte)
{