[PATCH V1 3/3] iommu/arm-smmu-v3: Honor IORT Root Complex PASID descriptors

Vidya Sagar <[email protected]>
Newsgroups dev.linux.lists.acpica-devel,dev.linux.lists.iommu,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
The SMMUv3 driver currently calls pci_enable_pasid() for any PCI
master that exposes a PASID capability, regardless of whether the
upstream Root Complex actually supports PASID and regardless of the
RC's declared Max PASID Width. With IORT spec E.c (RC node revision
>= 4) firmware reports both, so we can do better:

  - If the IORT Root Complex node says PASID is not supported
    (Flags bit 0 == 0 at byte offset 36), enabling PASID on the
    endpoint is futile - the RC will not forward the PASID prefix to
    the SMMU - so skip pci_enable_pasid() silently.

  - If the IORT Root Complex node reports a Max PASID Width (bits[4:0]
    of PASID Capabilities at offset 33), clamp the endpoint's
    pci_max_pasids() result by 1 << width before computing the SMMU
    SSID width. This prevents master->ssid_bits from exceeding what
    the RC can actually carry.

Both behaviours are gated on iort_pci_rc_pasid_max_width_known(), i.e.
RC node revision >= 4, so platforms with older IORT firmware see no
behavioural change and continue to enable PASID purely on the basis
of the endpoint capability.

Use the new IOMMU_FWSPEC_PCI_RC_PASID fwspec flag (set by IORT) for
the support check, and call iort_pci_rc_pasid_max_width_for_dev() for
the width clamp; both pieces are wired up in
iort_iommu_configure_id() by the previous patch.

Signed-off-by: Vidya Sagar <[email protected]>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 26 ++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index e8d7dbe495f0..2b269307fd33 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3071,16 +3071,28 @@ static void arm_smmu_enable_ats(struct arm_smmu_master *master)
 
 static int arm_smmu_enable_pasid(struct arm_smmu_master *master)
 {
-	int ret;
-	int features;
-	int num_pasids;
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
 	struct pci_dev *pdev;
+	int features, num_pasids, ret, rc_width;
 
 	if (!dev_is_pci(master->dev))
 		return -ENODEV;
 
 	pdev = to_pci_dev(master->dev);
 
+	/*
+	 * IORT E.c (RC node revision >= 4) reports whether the root
+	 * complex actually supports PASID. If it does not, enabling
+	 * PASID on the endpoint is futile - the RC will not forward
+	 * the PASID prefix - so skip silently. Older firmware is
+	 * treated as "unknown / assume supported" to preserve the
+	 * pre-E.c behaviour.
+	 */
+	if (fwspec &&
+	    !(fwspec->flags & IOMMU_FWSPEC_PCI_RC_PASID) &&
+	    iort_pci_rc_pasid_max_width_known(master->dev))
+		return 0;
+
 	features = pci_pasid_features(pdev);
 	if (features < 0)
 		return features;
@@ -3089,6 +3101,14 @@ static int arm_smmu_enable_pasid(struct arm_smmu_master *master)
 	if (num_pasids <= 0)
 		return num_pasids;
 
+	/* Clamp by what the root complex can carry, when known. */
+	rc_width = iort_pci_rc_pasid_max_width_for_dev(master->dev);
+	if (rc_width >= 0)
+		num_pasids = min_t(int, num_pasids, 1 << rc_width);
+
+	if (num_pasids <= 1)
+		return 0;
+
 	ret = pci_enable_pasid(pdev, features);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to enable PASID\n");
-- 
2.25.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.