[PATCH] iommu/arm-smmu-v3: Handle duplicated Stream IDs from other masters

From: Ajay Kumar
Date: Thu Jan 07 2021 - 05:32:09 EST


When PCI function drivers(ex:pci-endpoint-test) are probed for already
initialized PCIe-RC(Root Complex), and PCIe-RC is already bound to SMMU,
then we encounter a situation where the function driver tries to attach
itself to the smmu with the same stream-id as PCIe-RC and re-initialize
an already initialized STE. This causes ste_live BUG_ON() in the driver.

There is an already existing check in the driver to manage duplicated ids
if duplicated ids are added in same master device, but there can be
scenarios like above where we need to extend the check for other masters
using the same stream-id.

Signed-off-by: Ajay Kumar <ajaykumar.rs@xxxxxxxxxxx>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 33 +++++++++++++++++++++
1 file changed, 33 insertions(+)

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 e634bbe60573..a91c3c0e9ee8 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2022,10 +2022,26 @@ static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
return step;
}

+static bool arm_smmu_check_duplicated_sid(struct arm_smmu_master *master,
+ int sid)
+{
+ int i;
+
+ for (i = 0; i < master->num_sids; ++i)
+ if (master->sids[i] == sid)
+ return true;
+
+ return false;
+}
+
static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master)
{
+ bool sid_in_other_masters;
int i, j;
struct arm_smmu_device *smmu = master->smmu;
+ unsigned long flags;
+ struct arm_smmu_domain *smmu_domain = master->domain;
+ struct arm_smmu_master *other_masters;

for (i = 0; i < master->num_sids; ++i) {
u32 sid = master->sids[i];
@@ -2038,6 +2054,23 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master)
if (j < i)
continue;

+ /* Check for stream-ID duplication in masters in given domain */
+ sid_in_other_masters = false;
+ spin_lock_irqsave(&smmu_domain->devices_lock, flags);
+ list_for_each_entry(other_masters, &smmu_domain->devices,
+ domain_head) {
+ sid_in_other_masters =
+ arm_smmu_check_duplicated_sid(other_masters,
+ sid);
+ if (sid_in_other_masters)
+ break;
+ }
+ spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
+
+ /* Skip STE re-init if stream-id found in other masters */
+ if (sid_in_other_masters)
+ continue;
+
arm_smmu_write_strtab_ent(master, sid, step);
}
}
--
2.17.1