[PATCH 11/27] iommu/arm-smmu-v3: Lift CD programming out of the SVA notifier code

Michael Shavit mshavit at google.com
Mon Oct 23 23:34:28 PDT 2023


On Thu, Oct 12, 2023 at 7:26 AM Jason Gunthorpe <jgg at nvidia.com> wrote:
> [...]
> -static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn)
> +static struct arm_smmu_ctx_desc *
> +arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn)
>  {
>         struct mm_struct *mm = smmu_mn->mn.mm;
>         struct arm_smmu_ctx_desc *cd = smmu_mn->cd;
>         struct arm_smmu_domain *smmu_domain = smmu_mn->domain;
> -       struct arm_smmu_master *master;
> -       unsigned long flags;
>
>         if (!refcount_dec_and_test(&smmu_mn->refs))
> -               return;
> +               return cd;
>
>         list_del(&smmu_mn->list);
>
> -       spin_lock_irqsave(&smmu_domain->devices_lock, flags);
> -       list_for_each_entry(master, &smmu_domain->devices, domain_head)
> -               arm_smmu_clear_cd(master, mm->pasid);
> -       spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
> -
>         /*
>          * If we went through clear(), we've already invalidated, and no
>          * new TLB entry can have been formed.

This re-orders the TLB invalidation before the CD entry is cleared.
Couldn't a misbehaving device form TLB entries in this time interval
that we'd want to avoid?

> @@ -434,11 +403,12 @@ static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn)
>
>         /* Frees smmu_mn */
>         mmu_notifier_put(&smmu_mn->mn);
> -       arm_smmu_free_shared_cd(cd);
> +       return cd;
>  }
>
> -static struct iommu_sva *
> -__arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
> +static struct iommu_sva *__arm_smmu_sva_bind(struct device *dev,
> +                                            struct mm_struct *mm,
> +                                            struct arm_smmu_cd *target)
>  {
>         int ret;
>         struct arm_smmu_bond *bond;
> @@ -456,6 +426,8 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
>         list_for_each_entry(bond, &master->bonds, list) {
>                 if (bond->mm == mm) {
>                         refcount_inc(&bond->refs);
> +                       arm_smmu_make_sva_cd(target, master, mm,
> +                                            bond->smmu_mn->cd->asid);
>                         return &bond->sva;
>                 }
>         }
> @@ -475,6 +447,7 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
>         }
>
>         list_add(&bond->list, &master->bonds);
> +       arm_smmu_make_sva_cd(target, master, mm, bond->smmu_mn->cd->asid);
>         return &bond->sva;
>
>  err_free_bond:
> @@ -646,9 +619,15 @@ void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain,
>         }
>
>         if (!WARN_ON(!bond) && refcount_dec_and_test(&bond->refs)) {
> +               struct arm_smmu_ctx_desc *cd;
> +
>                 list_del(&bond->list);
> -               arm_smmu_mmu_notifier_put(bond->smmu_mn);
> +               cd = arm_smmu_mmu_notifier_put(bond->smmu_mn);
> +               arm_smmu_remove_pasid(master, to_smmu_domain(domain), id);
> +               arm_smmu_free_shared_cd(cd);
>                 kfree(bond);

arm_smmu_mmu_notifier_put was previously only calling
free_shared_cd(cd) when smmu_mn's refcount reached 0. IIRC, the
arm_smmu_mmu_notifier refcount can be greater than 1 if an MM/SVA
domain is attached to devices with distinct SMMU instances.

> +       } else {
> +               arm_smmu_remove_pasid(master, to_smmu_domain(domain), id);
>         }

I don't think we ever expect this else condition to be hit. Patch
"iommu/arm-smmu-v3-sva: Remove bond refcount" in Joerg's next tree
removes the refcount, and bond shouldn't ever be NULL for a "valid"
pasid either.

> [...]
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> @@ -2576,6 +2576,30 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
>         return 0;
>  }
>
> +int arm_smmu_set_pasid(struct arm_smmu_master *master,
> +                      struct arm_smmu_domain *smmu_domain, ioasid_t id,
> +                      const struct arm_smmu_cd *cd)
> +{
> +       struct arm_smmu_domain *old_smmu_domain =
> +               to_smmu_domain_safe(iommu_get_domain_for_dev(master->dev));

nit: The name old_smmu_domain sounds to me like it's being replaced
with a newer domain.



More information about the linux-arm-kernel mailing list