At the moment each SMMU has a 8- or 16-bit ASID set and allocates one ASID
per device via a bitmap. ASIDs are used to differentiate address spaces in
SMMU TLB entries. With SVM, sharing process address spaces with the SMMU,
we need to use CPU ASIDs in SMMU contexts, to ensure that broadcast TLB
invalidations reach the right IOTLB entries.

When binding a process address space to a device, we become slaves to the
arch ASID allocator. We have to use whatever ASID they give us. If a
domain is currently using it, then we'll either abort or steal that ASID.

To make matters worse, tasks are global, while domains are per-SMMU. SMMU
ASIDs can be aliased across different SMMUs, but the CPU ASID space is
unique across the whole system.

Introduce an IDR for SMMU ASID allocation. It allows to keep information
about an ASID, for instance which domain it is assigned to or how many
devices are using it.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.bruc...@arm.com>
---
 drivers/iommu/arm-smmu-v3.c | 53 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 44 insertions(+), 9 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 96347aad605f..71fc3a2c8a95 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -640,6 +640,10 @@ struct arm_smmu_strtab_cfg {
        u32                             strtab_base_cfg;
 };
 
+struct arm_smmu_asid_state {
+       struct arm_smmu_domain          *domain;
+};
+
 /* An SMMUv3 instance */
 struct arm_smmu_device {
        struct device                   *dev;
@@ -681,7 +685,8 @@ struct arm_smmu_device {
 
 #define ARM_SMMU_MAX_ASIDS             (1 << 16)
        unsigned int                    asid_bits;
-       DECLARE_BITMAP(asid_map, ARM_SMMU_MAX_ASIDS);
+       struct idr                      asid_idr;
+       spinlock_t                      asid_lock;
 
 #define ARM_SMMU_MAX_VMIDS             (1 << 16)
        unsigned int                    vmid_bits;
@@ -1828,7 +1833,11 @@ static void arm_smmu_domain_free(struct iommu_domain 
*domain)
                struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
                if (cfg->num_contexts) {
                        arm_smmu_free_cd_tables(smmu_domain);
-                       arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);
+
+                       spin_lock(&smmu->asid_lock);
+                       kfree(idr_find(&smmu->asid_idr, cfg->cd.asid));
+                       idr_remove(&smmu->asid_idr, cfg->cd.asid);
+                       spin_unlock(&smmu->asid_lock);
                }
        } else {
                struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
@@ -1844,25 +1853,48 @@ static int arm_smmu_domain_finalise_s1(struct 
arm_smmu_domain *smmu_domain,
 {
        int ret;
        int asid;
+       struct arm_smmu_asid_state *asid_state;
        struct arm_smmu_device *smmu = smmu_domain->smmu;
        struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
 
-       asid = arm_smmu_bitmap_alloc(smmu->asid_map, smmu->asid_bits);
-       if (asid < 0)
-               return asid;
-
        ret = arm_smmu_alloc_cd_tables(smmu_domain);
        if (ret)
-               goto out_free_asid;
+               return ret;
+
+       asid_state = kzalloc(sizeof(*asid_state), GFP_KERNEL);
+       if (!asid_state) {
+               ret = -ENOMEM;
+               goto out_free_tables;
+       }
+
+       asid_state->domain = smmu_domain;
+
+       idr_preload(GFP_KERNEL);
+       spin_lock(&smmu->asid_lock);
+       asid = idr_alloc_cyclic(&smmu->asid_idr, asid_state, 0,
+                               1 << smmu->asid_bits, GFP_ATOMIC);
 
        cfg->cd.asid    = (u16)asid;
        cfg->cd.ttbr    = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
        cfg->cd.tcr     = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
        cfg->cd.mair    = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
+
+       spin_unlock(&smmu->asid_lock);
+       idr_preload_end();
+
+       if (asid < 0) {
+               ret = asid;
+               goto out_free_asid_state;
+       }
+
        return 0;
 
-out_free_asid:
-       arm_smmu_bitmap_free(smmu->asid_map, asid);
+out_free_asid_state:
+       kfree(asid_state);
+
+out_free_tables:
+       arm_smmu_free_cd_tables(smmu_domain);
+
        return ret;
 }
 
@@ -2506,6 +2538,9 @@ static int arm_smmu_init_structures(struct 
arm_smmu_device *smmu)
 {
        int ret;
 
+       spin_lock_init(&smmu->asid_lock);
+       idr_init(&smmu->asid_idr);
+
        ret = arm_smmu_init_queues(smmu);
        if (ret)
                return ret;
-- 
2.13.3

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to