[PATCH v5 12/27] iommu/arm-smmu-v3: Start building a generic PASID layer
Michael Shavit
mshavit at google.com
Tue Mar 19 09:11:36 PDT 2024
On Tue, Mar 5, 2024 at 7:44 AM Jason Gunthorpe <jgg at nvidia.com> wrote:
>
> Add arm_smmu_set_pasid()/arm_smmu_remove_pasid() which are to be used by
> callers that already constructed the arm_smmu_cd they wish to program.
>
> These functions will encapsulate the shared logic to setup a CD entry that
> will be shared by SVA and S1 domain cases.
>
> Prior fixes had already moved this logic most up into
> __arm_smmu_sva_bind(), move it to it's final home.
>
> This does not yet relieve the SVA restrictions:
> - The RID domain is a S1 domain
> - The programmed PASID is the mm_get_enqcmd_pasid()
> - Nothing changes while SVA is running (sva_enable)
>
> Also, SVA invalidation will still iterate over the S1 domain's master
> list.
>
> Tested-by: Nicolin Chen <nicolinc at nvidia.com>
> Signed-off-by: Jason Gunthorpe <jgg at nvidia.com>
> ---
> .../iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c | 39 +++++++++----------
> drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 29 ++++++++++++++
> drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 6 +++
> 3 files changed, 53 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
> index 159350a78d4cf9..e1fa2074c0b37b 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
> @@ -416,12 +416,10 @@ static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn)
> arm_smmu_free_shared_cd(cd);
> }
>
> -static int __arm_smmu_sva_bind(struct device *dev, ioasid_t pasid,
> - struct mm_struct *mm)
> +static int __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm,
> + struct arm_smmu_cd *target)
> {
> int ret;
> - struct arm_smmu_cd target;
> - struct arm_smmu_cd *cdptr;
> struct arm_smmu_bond *bond;
> struct arm_smmu_master *master = dev_iommu_priv_get(dev);
> struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> @@ -448,19 +446,10 @@ static int __arm_smmu_sva_bind(struct device *dev, ioasid_t pasid,
> goto err_free_bond;
> }
>
> - cdptr = arm_smmu_get_cd_ptr(master, mm_get_enqcmd_pasid(mm));
> - if (!cdptr) {
> - ret = -ENOMEM;
> - goto err_put_notifier;
> - }
> - arm_smmu_make_sva_cd(&target, master, mm, bond->smmu_mn->cd->asid);
> - arm_smmu_write_cd_entry(master, pasid, cdptr, &target);
> -
> + arm_smmu_make_sva_cd(target, master, mm, bond->smmu_mn->cd->asid);
This can probably already move out to arm_smmu_sva_set_dev_pasid in
this commit. Removing the arm_smmu_get_cd_ptr pre-alloc might also be
possible if we're careful with failure of arm_smmu_set_pasid.
This is eventually addressed in "iommu/arm-smmu-v3: Put the SVA mmu
notifier in the smmu_domain", but that patch is already big enough as
it is and the change fits nicely here.
> list_add(&bond->list, &master->bonds);
> return 0;
>
> -err_put_notifier:
> - arm_smmu_mmu_notifier_put(bond->smmu_mn);
> err_free_bond:
> kfree(bond);
> return ret;
> @@ -621,10 +610,9 @@ void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain,
> struct arm_smmu_bond *bond = NULL, *t;
> struct arm_smmu_master *master = dev_iommu_priv_get(dev);
>
> + arm_smmu_remove_pasid(master, to_smmu_domain(domain), id);
> +
> mutex_lock(&sva_lock);
> -
> - arm_smmu_clear_cd(master, id);
> -
> list_for_each_entry(t, &master->bonds, list) {
> if (t->mm == mm) {
> bond = t;
> @@ -643,17 +631,26 @@ void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain,
> static int arm_smmu_sva_set_dev_pasid(struct iommu_domain *domain,
> struct device *dev, ioasid_t id)
> {
> + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
> int ret = 0;
> struct mm_struct *mm = domain->mm;
> + struct arm_smmu_cd target;
>
> if (mm_get_enqcmd_pasid(mm) != id)
> return -EINVAL;
>
> - mutex_lock(&sva_lock);
> - ret = __arm_smmu_sva_bind(dev, id, mm);
> - mutex_unlock(&sva_lock);
> + if (!arm_smmu_get_cd_ptr(master, id))
> + return -ENOMEM;
>
> - return ret;
> + mutex_lock(&sva_lock);
> + ret = __arm_smmu_sva_bind(dev, mm, &target);
> + mutex_unlock(&sva_lock);
> + if (ret)
> + return ret;
> +
> + /* This cannot fail since we preallocated the cdptr */
> + arm_smmu_set_pasid(master, to_smmu_domain(domain), id, &target);
> + return 0;
> }
>
> static void arm_smmu_sva_domain_free(struct iommu_domain *domain)
> 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 dfdd48cf217c4e..7a0be7dd719793 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> @@ -2656,6 +2656,35 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
> return 0;
> }
>
> +static bool arm_smmu_is_s1_domain(struct iommu_domain *domain)
> +{
> + if (!domain || !(domain->type & __IOMMU_DOMAIN_PAGING))
> + return false;
> + return to_smmu_domain(domain)->stage == ARM_SMMU_DOMAIN_S1;
> +}
> +
> +int arm_smmu_set_pasid(struct arm_smmu_master *master,
> + struct arm_smmu_domain *smmu_domain, ioasid_t pasid,
> + const struct arm_smmu_cd *cd)
> +{
> + struct arm_smmu_cd *cdptr;
> +
> + if (!arm_smmu_is_s1_domain(iommu_get_domain_for_dev(master->dev)))
> + return -ENODEV;
> +
> + cdptr = arm_smmu_get_cd_ptr(master, pasid);
> + if (!cdptr)
> + return -ENOMEM;
> + arm_smmu_write_cd_entry(master, pasid, cdptr, cd);
> + return 0;
> +}
> +
> +void arm_smmu_remove_pasid(struct arm_smmu_master *master,
> + struct arm_smmu_domain *smmu_domain, ioasid_t pasid)
> +{
> + arm_smmu_clear_cd(master, pasid);
> +}
> +
> static int arm_smmu_attach_dev_ste(struct device *dev,
> struct arm_smmu_ste *ste)
> {
> diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
> index 468cd33b80ac35..ab0a6587e07ec8 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
> @@ -753,6 +753,12 @@ void arm_smmu_write_cd_entry(struct arm_smmu_master *master, int ssid,
> struct arm_smmu_cd *cdptr,
> const struct arm_smmu_cd *target);
>
> +int arm_smmu_set_pasid(struct arm_smmu_master *master,
> + struct arm_smmu_domain *smmu_domain, ioasid_t pasid,
> + const struct arm_smmu_cd *cd);
> +void arm_smmu_remove_pasid(struct arm_smmu_master *master,
> + struct arm_smmu_domain *smmu_domain, ioasid_t pasid);
> +
> void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid);
> void arm_smmu_tlb_inv_range_asid(unsigned long iova, size_t size, int asid,
> size_t granule, bool leaf,
> --
> 2.43.2
>
More information about the linux-arm-kernel
mailing list