[PATCH 14/27] iommu/arm-smmu-v3: Make changing domains be hitless for ATS
Jason Gunthorpe
jgg at nvidia.com
Thu Oct 26 07:38:09 PDT 2023
On Thu, Oct 26, 2023 at 03:00:11PM +0800, Michael Shavit wrote:
> Huh. Can this be checked by calling iommu_get_domain_for_dev() and
> comparing against the input iommu_domain parameter, or is
> iommu_get_domain_for_dev() cleared in this scenario?
Yes, that should work, but I dislike the frailty.
Hmm. When I first wrote this the whole picture wasn't finished, now I
think we can just take the optimization out. If the same master has
two master_domains in the list for a moment it is not actually a
problem. The commit stage will remove only one.
So, like this:
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 15d1688d7f7034..31ce450448a7f0 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2490,6 +2490,10 @@ static void arm_smmu_remove_master_domain(struct arm_smmu_master *master,
struct arm_smmu_master_domain *master_domain;
unsigned long flags;
+ /* NULL means the old domain is IDENTITY/BLOCKED which we don't track */
+ if (!smmu_domain)
+ return;
+
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
master_domain = arm_smmu_find_master_domain(smmu_domain, master);
if (master_domain) {
@@ -2502,8 +2506,7 @@ static void arm_smmu_remove_master_domain(struct arm_smmu_master *master,
}
struct attach_state {
- bool existing_master_domain : 1;
- bool want_ats : 1;
+ bool want_ats;
};
/*
@@ -2514,7 +2517,6 @@ static int arm_smmu_attach_prepare(struct arm_smmu_master *master,
struct arm_smmu_domain *smmu_domain,
struct attach_state *state)
{
- struct arm_smmu_master_domain *cur_master_domain;
struct arm_smmu_master_domain *master_domain;
unsigned long flags;
@@ -2541,17 +2543,14 @@ static int arm_smmu_attach_prepare(struct arm_smmu_master *master,
* It is tempting to make this list only track masters that are using
* ATS, but arm_smmu_share_asid() also uses this to change the ASID of a
* domain, unrelated to ATS.
+ *
+ * Notice if we are re-attaching the same domain then the list will have
+ * two identical entries and commit will remove only one of them.
*/
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
- cur_master_domain = arm_smmu_find_master_domain(smmu_domain, master);
- if (cur_master_domain) {
- kfree(master_domain);
- state->existing_master_domain = true;
- } else {
- if (state->want_ats)
- atomic_inc(&smmu_domain->nr_ats_masters);
- list_add(&master_domain->devices_elm, &smmu_domain->devices);
- }
+ if (state->want_ats)
+ atomic_inc(&smmu_domain->nr_ats_masters);
+ list_add(&master_domain->devices_elm, &smmu_domain->devices);
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
return 0;
}
@@ -2581,13 +2580,9 @@ static void arm_smmu_attach_commit(struct arm_smmu_master *master,
arm_smmu_atc_inv_master(master);
}
- if (!state->existing_master_domain) {
- struct arm_smmu_domain *old_smmu_domain = to_smmu_domain_safe(
- iommu_get_domain_for_dev(master->dev));
-
- if (old_smmu_domain)
- arm_smmu_remove_master_domain(master, old_smmu_domain);
- }
+ arm_smmu_remove_master_domain(
+ master,
+ to_smmu_domain_safe(iommu_get_domain_for_dev(master->dev)));
}
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
More information about the linux-arm-kernel
mailing list