]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iommu/arm-smmu-v3: Make domain_alloc_paging_flags() directly determine the S1/S2
authorJason Gunthorpe <jgg@nvidia.com>
Thu, 5 Dec 2024 15:43:28 +0000 (11:43 -0400)
committerWill Deacon <will@kernel.org>
Mon, 9 Dec 2024 23:08:06 +0000 (23:08 +0000)
The selection of S1/S2 is a bit indirect today, make
domain_alloc_paging_flags() directly decode the flags and select the
correct S1/S2 type.

Directly reject flag combinations the HW doesn't support when processing
the flags.

Fix missing rejection of some flag combinations that are not supported
today (ie NEST_PARENT | DIRTY_TRACKING) by using a switch statement to
list out exactly the combinations that are currently supported.

Move the determination of the stage out of arm_smmu_domain_finalise() and
into both callers. As today the default stage is S1 if supported in HW.

This makes arm_smmu_domain_alloc_paging_flags() self contained and no
longer calling arm_smmu_domain_alloc_paging().

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/2-v1-0bb8d5313a27+27b-smmuv3_paging_flags_jgg@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c

index dc5fab268f7d72b304e20b78a779700b25273a47..ef0d644ad34d6bb76da3dcbea91be84896ec56c6 100644 (file)
@@ -2481,6 +2481,11 @@ static struct iommu_domain *arm_smmu_domain_alloc_paging(struct device *dev)
        if (IS_ERR(smmu_domain))
                return ERR_CAST(smmu_domain);
 
+       if (master->smmu->features & ARM_SMMU_FEAT_TRANS_S1)
+               smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+       else
+               smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+
        ret = arm_smmu_domain_finalise(smmu_domain, master->smmu, 0);
        if (ret) {
                kfree(smmu_domain);
@@ -2554,12 +2559,6 @@ static int arm_smmu_domain_finalise(struct arm_smmu_domain *smmu_domain,
                                 struct arm_smmu_domain *smmu_domain);
        bool enable_dirty = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
 
-       /* Restrict the stage to what we can actually support */
-       if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
-               smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
-       if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
-               smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
-
        pgtbl_cfg = (struct io_pgtable_cfg) {
                .pgsize_bitmap  = smmu->pgsize_bitmap,
                .coherent_walk  = smmu->features & ARM_SMMU_FEAT_COHERENCY,
@@ -3231,6 +3230,7 @@ arm_smmu_domain_alloc_paging_flags(struct device *dev, u32 flags,
                                   const struct iommu_user_data *user_data)
 {
        struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+       struct arm_smmu_device *smmu = master->smmu;
        const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
                                 IOMMU_HWPT_ALLOC_PASID |
                                 IOMMU_HWPT_ALLOC_NEST_PARENT;
@@ -3242,25 +3242,43 @@ arm_smmu_domain_alloc_paging_flags(struct device *dev, u32 flags,
        if (user_data)
                return ERR_PTR(-EOPNOTSUPP);
 
-       if (flags & IOMMU_HWPT_ALLOC_PASID)
-               return arm_smmu_domain_alloc_paging(dev);
-
        smmu_domain = arm_smmu_domain_alloc();
        if (IS_ERR(smmu_domain))
                return ERR_CAST(smmu_domain);
 
-       if (flags & IOMMU_HWPT_ALLOC_NEST_PARENT) {
-               if (!(master->smmu->features & ARM_SMMU_FEAT_NESTING)) {
+       switch (flags) {
+       case 0:
+               /* Prefer S1 if available */
+               if (smmu->features & ARM_SMMU_FEAT_TRANS_S1)
+                       smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+               else
+                       smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+               break;
+       case IOMMU_HWPT_ALLOC_NEST_PARENT:
+               if (!(smmu->features & ARM_SMMU_FEAT_NESTING)) {
                        ret = -EOPNOTSUPP;
                        goto err_free;
                }
                smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
                smmu_domain->nest_parent = true;
+               break;
+       case IOMMU_HWPT_ALLOC_DIRTY_TRACKING:
+       case IOMMU_HWPT_ALLOC_DIRTY_TRACKING | IOMMU_HWPT_ALLOC_PASID:
+       case IOMMU_HWPT_ALLOC_PASID:
+               if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) {
+                       ret = -EOPNOTSUPP;
+                       goto err_free;
+               }
+               smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               goto err_free;
        }
 
        smmu_domain->domain.type = IOMMU_DOMAIN_UNMANAGED;
        smmu_domain->domain.ops = arm_smmu_ops.default_domain_ops;
-       ret = arm_smmu_domain_finalise(smmu_domain, master->smmu, flags);
+       ret = arm_smmu_domain_finalise(smmu_domain, smmu, flags);
        if (ret)
                goto err_free;
        return &smmu_domain->domain;