struct arm_smmu_cd *cdptr,
                             const struct arm_smmu_cd *target)
 {
+       bool target_valid = target->data[0] & cpu_to_le64(CTXDESC_CD_0_V);
+       bool cur_valid = cdptr->data[0] & cpu_to_le64(CTXDESC_CD_0_V);
        struct arm_smmu_cd_writer cd_writer = {
                .writer = {
                        .ops = &arm_smmu_cd_writer_ops,
                .ssid = ssid,
        };
 
+       if (ssid != IOMMU_NO_PASID && cur_valid != target_valid) {
+               if (cur_valid)
+                       master->cd_table.used_ssids--;
+               else
+                       master->cd_table.used_ssids++;
+       }
+
        arm_smmu_write_entry(&cd_writer.writer, cdptr->data, target->data);
 }
 
        state.master = master = dev_iommu_priv_get(dev);
        smmu = master->smmu;
 
-       /*
-        * Checking that SVA is disabled ensures that this device isn't bound to
-        * any mm, and can be safely detached from its old domain. Bonds cannot
-        * be removed concurrently since we're holding the group mutex.
-        */
-       if (arm_smmu_master_sva_enabled(master)) {
-               dev_err(dev, "cannot attach - SVA enabled\n");
-               return -EBUSY;
-       }
-
        mutex_lock(&smmu_domain->init_mutex);
 
        if (!smmu_domain->smmu) {
                cdptr = arm_smmu_alloc_cd_ptr(master, IOMMU_NO_PASID);
                if (!cdptr)
                        return -ENOMEM;
-       }
+       } else if (arm_smmu_ssids_in_use(&master->cd_table))
+               return -EBUSY;
 
        /*
         * Prevent arm_smmu_share_asid() from trying to change the ASID
                .old_domain = iommu_get_domain_for_dev(dev),
        };
 
-       if (arm_smmu_master_sva_enabled(master))
+       if (arm_smmu_ssids_in_use(&master->cd_table))
                return -EBUSY;
 
        /*
 
        dma_addr_t                      cdtab_dma;
        struct arm_smmu_l1_ctx_desc     *l1_desc;
        unsigned int                    num_l1_ents;
+       unsigned int                    used_ssids;
        u8                              in_ste;
        u8                              s1fmt;
        /* log2 of the maximum number of CDs supported by this table */
        u8                              s1cdmax;
 };
 
+/* True if the cd table has SSIDS > 0 in use. */
+static inline bool arm_smmu_ssids_in_use(struct arm_smmu_ctx_desc_cfg *cd_table)
+{
+       return cd_table->used_ssids;
+}
+
 struct arm_smmu_s2_cfg {
        u16                             vmid;
 };