iommu/tegra-smmu: Prune IOMMU group when it is released
authorThierry Reding <treding@nvidia.com>
Thu, 6 Aug 2020 15:54:04 +0000 (17:54 +0200)
committerJoerg Roedel <jroedel@suse.de>
Fri, 4 Sep 2020 09:00:14 +0000 (11:00 +0200)
In order to share groups between multiple devices we keep track of them
in a per-SMMU list. When an IOMMU group is released, a dangling pointer
to it stays around in that list. Fix this by implementing an IOMMU data
release callback for groups where the dangling pointer can be removed.

Signed-off-by: Thierry Reding <treding@nvidia.com>
Link: https://lore.kernel.org/r/20200806155404.3936074-4-thierry.reding@gmail.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/tegra-smmu.c

index c439c09..2574e71 100644 (file)
@@ -19,6 +19,7 @@
 
 struct tegra_smmu_group {
        struct list_head list;
+       struct tegra_smmu *smmu;
        const struct tegra_smmu_group_soc *soc;
        struct iommu_group *group;
 };
@@ -813,6 +814,16 @@ tegra_smmu_find_group(struct tegra_smmu *smmu, unsigned int swgroup)
        return NULL;
 }
 
+static void tegra_smmu_group_release(void *iommu_data)
+{
+       struct tegra_smmu_group *group = iommu_data;
+       struct tegra_smmu *smmu = group->smmu;
+
+       mutex_lock(&smmu->lock);
+       list_del(&group->list);
+       mutex_unlock(&smmu->lock);
+}
+
 static struct iommu_group *tegra_smmu_group_get(struct tegra_smmu *smmu,
                                                unsigned int swgroup)
 {
@@ -840,6 +851,7 @@ static struct iommu_group *tegra_smmu_group_get(struct tegra_smmu *smmu,
        }
 
        INIT_LIST_HEAD(&group->list);
+       group->smmu = smmu;
        group->soc = soc;
 
        group->group = iommu_group_alloc();
@@ -849,6 +861,7 @@ static struct iommu_group *tegra_smmu_group_get(struct tegra_smmu *smmu,
                return NULL;
        }
 
+       iommu_group_set_iommudata(group->group, group, tegra_smmu_group_release);
        iommu_group_set_name(group->group, soc->name);
        list_add_tail(&group->list, &smmu->groups);
        mutex_unlock(&smmu->lock);