]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iommu/arm-smmu-v3: Do not bother impl_ops if IOMMU_VIOMMU_TYPE_ARM_SMMUV3
authorNicolin Chen <nicolinc@nvidia.com>
Thu, 24 Jul 2025 22:10:01 +0000 (15:10 -0700)
committerJason Gunthorpe <jgg@nvidia.com>
Mon, 28 Jul 2025 15:07:50 +0000 (12:07 -0300)
When viommu type is IOMMU_VIOMMU_TYPE_ARM_SMMUV3, always return or init the
standard struct arm_vsmmu, instead of going through impl_ops that must have
its own viommu type than the standard IOMMU_VIOMMU_TYPE_ARM_SMMUV3.

Given that arm_vsmmu_init() is called after arm_smmu_get_viommu_size(), any
unsupported viommu->type must be a corruption. And it must be a driver bug
that its vsmmu_size and vsmmu_init ops aren't paired. Warn these two cases.

Link: https://patch.msgid.link/r/20250724221002.1883034-2-nicolinc@nvidia.com
Suggested-by: Will Deacon <will@kernel.org>
Acked-by: Will Deacon <will@kernel.org>
Reviewed-by: Pranjal Shrivastava <praan@google.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c

index d9bea8f1f636d015d04dfc62dcd8267ad3cec263..b963b9b3de54232eade9bfd463f70952186ddc24 100644 (file)
@@ -420,14 +420,13 @@ size_t arm_smmu_get_viommu_size(struct device *dev,
            !(smmu->features & ARM_SMMU_FEAT_S2FWB))
                return 0;
 
-       if (smmu->impl_ops && smmu->impl_ops->vsmmu_size &&
-           viommu_type == smmu->impl_ops->vsmmu_type)
-               return smmu->impl_ops->vsmmu_size;
+       if (viommu_type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3)
+               return VIOMMU_STRUCT_SIZE(struct arm_vsmmu, core);
 
-       if (viommu_type != IOMMU_VIOMMU_TYPE_ARM_SMMUV3)
+       if (!smmu->impl_ops || !smmu->impl_ops->vsmmu_size ||
+           viommu_type != smmu->impl_ops->vsmmu_type)
                return 0;
-
-       return VIOMMU_STRUCT_SIZE(struct arm_vsmmu, core);
+       return smmu->impl_ops->vsmmu_size;
 }
 
 int arm_vsmmu_init(struct iommufd_viommu *viommu,
@@ -447,12 +446,18 @@ int arm_vsmmu_init(struct iommufd_viommu *viommu,
        /* FIXME Move VMID allocation from the S2 domain allocation to here */
        vsmmu->vmid = s2_parent->s2_cfg.vmid;
 
-       if (smmu->impl_ops && smmu->impl_ops->vsmmu_init &&
-           viommu->type == smmu->impl_ops->vsmmu_type)
-               return smmu->impl_ops->vsmmu_init(vsmmu, user_data);
+       if (viommu->type == IOMMU_VIOMMU_TYPE_ARM_SMMUV3) {
+               viommu->ops = &arm_vsmmu_ops;
+               return 0;
+       }
 
-       viommu->ops = &arm_vsmmu_ops;
-       return 0;
+       /*
+        * Unsupported type should be rejected by arm_smmu_get_viommu_size.
+        * Seeing one here indicates a kernel bug or some data corruption.
+        */
+       if (WARN_ON(viommu->type != smmu->impl_ops->vsmmu_type))
+               return -EOPNOTSUPP;
+       return smmu->impl_ops->vsmmu_init(vsmmu, user_data);
 }
 
 int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster, u64 *evt)
index 181d07bc1a9d4faaa465ea998aa21033613f6725..9f4ad370580104b6f6155e9d179acc58ad44a79d 100644 (file)
@@ -4703,6 +4703,7 @@ static void arm_smmu_impl_remove(void *data)
 static struct arm_smmu_device *arm_smmu_impl_probe(struct arm_smmu_device *smmu)
 {
        struct arm_smmu_device *new_smmu = ERR_PTR(-ENODEV);
+       const struct arm_smmu_impl_ops *ops;
        int ret;
 
        if (smmu->impl_dev && (smmu->options & ARM_SMMU_OPT_TEGRA241_CMDQV))
@@ -4713,11 +4714,24 @@ static struct arm_smmu_device *arm_smmu_impl_probe(struct arm_smmu_device *smmu)
        if (IS_ERR(new_smmu))
                return new_smmu;
 
+       ops = new_smmu->impl_ops;
+       if (ops) {
+               /* vsmmu_size and vsmmu_init ops must be paired */
+               if (WARN_ON(!ops->vsmmu_size != !ops->vsmmu_init)) {
+                       ret = -EINVAL;
+                       goto err_remove;
+               }
+       }
+
        ret = devm_add_action_or_reset(new_smmu->dev, arm_smmu_impl_remove,
                                       new_smmu);
        if (ret)
                return ERR_PTR(ret);
        return new_smmu;
+
+err_remove:
+       arm_smmu_impl_remove(new_smmu);
+       return ERR_PTR(ret);
 }
 
 static int arm_smmu_device_probe(struct platform_device *pdev)