]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iommu/arm-smmu-v3: Start building a generic PASID layer
authorJason Gunthorpe <jgg@nvidia.com>
Tue, 25 Jun 2024 12:37:33 +0000 (09:37 -0300)
committerWill Deacon <will@kernel.org>
Tue, 2 Jul 2024 14:39:47 +0000 (15:39 +0100)
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 most of this logic up into
__arm_smmu_sva_bind(), move it to it's final home.

Following patches will relieve some of the remaining SVA restrictions:

 - The RID domain is a S1 domain and has already setup the STE to point to
   the CD table
 - The programmed PASID is the mm_get_enqcmd_pasid()
 - Nothing changes while SVA is running (sva_enable)

SVA invalidation will still iterate over the S1 domain's master list,
later patches will resolve that.

Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Tested-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-by: Jerry Snitselaar <jsnitsel@redhat.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/2-v9-5cd718286059+79186-smmuv3_newapi_p2b_jgg@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h

index 28f8bf4327f69a97f32be1eab1cfbcc8f54b5ea5..71ca87c2c5c3b6e0f8da3c96be33b0cd0f425a1c 100644 (file)
@@ -417,29 +417,27 @@ 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 struct arm_smmu_bond *__arm_smmu_sva_bind(struct device *dev,
+                                                struct mm_struct *mm)
 {
        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);
        struct arm_smmu_domain *smmu_domain;
 
        if (!(domain->type & __IOMMU_DOMAIN_PAGING))
-               return -ENODEV;
+               return ERR_PTR(-ENODEV);
        smmu_domain = to_smmu_domain(domain);
        if (smmu_domain->stage != ARM_SMMU_DOMAIN_S1)
-               return -ENODEV;
+               return ERR_PTR(-ENODEV);
 
        if (!master || !master->sva_enabled)
-               return -ENODEV;
+               return ERR_PTR(-ENODEV);
 
        bond = kzalloc(sizeof(*bond), GFP_KERNEL);
        if (!bond)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        bond->mm = mm;
 
@@ -449,22 +447,12 @@ static int __arm_smmu_sva_bind(struct device *dev, ioasid_t pasid,
                goto err_free_bond;
        }
 
-       cdptr = arm_smmu_alloc_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);
-
        list_add(&bond->list, &master->bonds);
-       return 0;
+       return bond;
 
-err_put_notifier:
-       arm_smmu_mmu_notifier_put(bond->smmu_mn);
 err_free_bond:
        kfree(bond);
-       return ret;
+       return ERR_PTR(ret);
 }
 
 bool arm_smmu_sva_supported(struct arm_smmu_device *smmu)
@@ -611,10 +599,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);
 
-       mutex_lock(&sva_lock);
-
-       arm_smmu_clear_cd(master, id);
+       arm_smmu_remove_pasid(master, to_smmu_domain(domain), id);
 
+       mutex_lock(&sva_lock);
        list_for_each_entry(t, &master->bonds, list) {
                if (t->mm == mm) {
                        bond = t;
@@ -633,17 +620,33 @@ 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)
 {
-       int ret = 0;
+       struct arm_smmu_master *master = dev_iommu_priv_get(dev);
        struct mm_struct *mm = domain->mm;
+       struct arm_smmu_bond *bond;
+       struct arm_smmu_cd target;
+       int ret;
 
        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);
+       bond = __arm_smmu_sva_bind(dev, mm);
+       if (IS_ERR(bond)) {
+               mutex_unlock(&sva_lock);
+               return PTR_ERR(bond);
+       }
 
-       return ret;
+       arm_smmu_make_sva_cd(&target, master, mm, bond->smmu_mn->cd->asid);
+       ret = arm_smmu_set_pasid(master, NULL, id, &target);
+       if (ret) {
+               list_del(&bond->list);
+               arm_smmu_mmu_notifier_put(bond->smmu_mn);
+               kfree(bond);
+               mutex_unlock(&sva_lock);
+               return ret;
+       }
+       mutex_unlock(&sva_lock);
+       return 0;
 }
 
 static void arm_smmu_sva_domain_free(struct iommu_domain *domain)
index 05939f4830984b50a4821191b57d33e99cd82640..fc78156f9d977e091a9403ec7124fedf5923f538 100644 (file)
@@ -1211,8 +1211,8 @@ struct arm_smmu_cd *arm_smmu_get_cd_ptr(struct arm_smmu_master *master,
        return &l1_desc->l2ptr[ssid % CTXDESC_L2_ENTRIES];
 }
 
-struct arm_smmu_cd *arm_smmu_alloc_cd_ptr(struct arm_smmu_master *master,
-                                         u32 ssid)
+static struct arm_smmu_cd *arm_smmu_alloc_cd_ptr(struct arm_smmu_master *master,
+                                                u32 ssid)
 {
        struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
        struct arm_smmu_device *smmu = master->smmu;
@@ -2412,6 +2412,10 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master,
        int i, j;
        struct arm_smmu_device *smmu = master->smmu;
 
+       master->cd_table.in_ste =
+               FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(target->data[0])) ==
+               STRTAB_STE_0_CFG_S1_TRANS;
+
        for (i = 0; i < master->num_streams; ++i) {
                u32 sid = master->streams[i].id;
                struct arm_smmu_ste *step =
@@ -2632,6 +2636,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 pasid,
+                      const struct arm_smmu_cd *cd)
+{
+       struct arm_smmu_cd *cdptr;
+
+       /* The core code validates pasid */
+
+       if (!master->cd_table.in_ste)
+               return -ENODEV;
+
+       cdptr = arm_smmu_alloc_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)
 {
index b10712d3de66a9ff2f314f9ee9177e44812dcfc3..6a74d3d884fe8d9821b8976ed4ee55b0c2bd89eb 100644 (file)
@@ -602,6 +602,7 @@ struct arm_smmu_ctx_desc_cfg {
        dma_addr_t                      cdtab_dma;
        struct arm_smmu_l1_ctx_desc     *l1_desc;
        unsigned int                    num_l1_ents;
+       u8                              in_ste;
        u8                              s1fmt;
        /* log2 of the maximum number of CDs supported by this table */
        u8                              s1cdmax;
@@ -777,8 +778,6 @@ extern struct mutex arm_smmu_asid_lock;
 void arm_smmu_clear_cd(struct arm_smmu_master *master, ioasid_t ssid);
 struct arm_smmu_cd *arm_smmu_get_cd_ptr(struct arm_smmu_master *master,
                                        u32 ssid);
-struct arm_smmu_cd *arm_smmu_alloc_cd_ptr(struct arm_smmu_master *master,
-                                         u32 ssid);
 void arm_smmu_make_s1_cd(struct arm_smmu_cd *target,
                         struct arm_smmu_master *master,
                         struct arm_smmu_domain *smmu_domain);
@@ -786,6 +785,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,