Now, struct arm_smmu_attach_state has the IOTLB cache tags copied from the
cd->asid or s2_cfg->vmid of an smmu_domain.
Pass it down to arm_smmu_make_s1_cd() and arm_smmu_make_s2_domain_ste() to
set in the CD and STE, removing the references of smmu_domain for its asid
or vmid.
Note the two set_dev_pasid callbacks finalize CDs in arm_smmu_set_pasid().
So, it is safe for arm_smmu_make_sva_cd() and arm_smmu_make_s1_cd() to use
a dummy iotlb tag (asid=0) because arm_smmu_set_pasid() will fix it.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 5 +--
.../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 26 ++++++++++------
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c | 22 ++++++++++---
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c | 12 +++++--
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 31 +++++++++++--------
5 files changed, 65 insertions(+), 31 deletions(-)
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 73cb59c7d4b1..11879148dad0 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1005,7 +1005,7 @@ void arm_smmu_make_abort_ste(struct arm_smmu_ste *target);
void arm_smmu_make_s2_domain_ste(struct arm_smmu_ste *target,
struct arm_smmu_master *master,
struct arm_smmu_domain *smmu_domain,
- bool ats_enabled);
+ struct arm_smmu_inv *tag, bool ats_enabled);
#if IS_ENABLED(CONFIG_KUNIT)
void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits);
@@ -1070,7 +1070,8 @@ struct arm_smmu_cd *arm_smmu_get_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);
+ struct arm_smmu_domain *smmu_domain,
+ struct arm_smmu_inv *tag);
void arm_smmu_write_cd_entry(struct arm_smmu_master *master, int ssid,
struct arm_smmu_cd *cdptr,
const struct arm_smmu_cd *target);
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
index 93fdadd07431..34c7bd4cfd84 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
@@ -39,12 +39,15 @@ void *arm_smmu_hw_info(struct device *dev, u32 *length,
return info;
}
-static void arm_smmu_make_nested_cd_table_ste(
- struct arm_smmu_ste *target, struct arm_smmu_master *master,
- struct arm_smmu_nested_domain *nested_domain, bool ats_enabled)
+static void
+arm_smmu_make_nested_cd_table_ste(struct arm_smmu_ste *target,
+ struct arm_smmu_master *master,
+ struct arm_smmu_nested_domain *nested_domain,
+ struct arm_smmu_inv *tag, bool ats_enabled)
{
- arm_smmu_make_s2_domain_ste(
- target, master, nested_domain->vsmmu->s2_parent, ats_enabled);
+ arm_smmu_make_s2_domain_ste(target, master,
+ nested_domain->vsmmu->s2_parent, tag,
+ ats_enabled);
target->data[0] = cpu_to_le64(STRTAB_STE_0_V |
FIELD_PREP(STRTAB_STE_0_CFG,
@@ -64,9 +67,11 @@ static void arm_smmu_make_nested_cd_table_ste(
* - Bypass STE (install the S2, no CD table)
* - CD table STE (install the S2 and the userspace CD table)
*/
-static void arm_smmu_make_nested_domain_ste(
- struct arm_smmu_ste *target, struct arm_smmu_master *master,
- struct arm_smmu_nested_domain *nested_domain, bool ats_enabled)
+static void
+arm_smmu_make_nested_domain_ste(struct arm_smmu_ste *target,
+ struct arm_smmu_master *master,
+ struct arm_smmu_nested_domain *nested_domain,
+ struct arm_smmu_inv *tag, bool ats_enabled)
{
unsigned int cfg =
FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(nested_domain->ste[0]));
@@ -82,12 +87,12 @@ static void arm_smmu_make_nested_domain_ste(
switch (cfg) {
case STRTAB_STE_0_CFG_S1_TRANS:
arm_smmu_make_nested_cd_table_ste(target, master, nested_domain,
- ats_enabled);
+ tag, ats_enabled);
break;
case STRTAB_STE_0_CFG_BYPASS:
arm_smmu_make_s2_domain_ste(target, master,
nested_domain->vsmmu->s2_parent,
- ats_enabled);
+ tag, ats_enabled);
break;
case STRTAB_STE_0_CFG_ABORT:
default:
@@ -185,6 +190,7 @@ static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
}
arm_smmu_make_nested_domain_ste(&ste, master, nested_domain,
+ &state.new_domain_invst.tag,
state.ats_enabled);
arm_smmu_install_ste_for_dev(master, &ste);
arm_smmu_attach_commit(&state);
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 f1f8e01a7e91..dff494584008 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
@@ -24,12 +24,18 @@ arm_smmu_update_s1_domain_cd_entry(struct arm_smmu_domain *smmu_domain)
list_for_each_entry(master_domain, &smmu_domain->devices, devices_elm) {
struct arm_smmu_master *master = master_domain->master;
struct arm_smmu_cd *cdptr;
+ struct arm_smmu_inv tag;
cdptr = arm_smmu_get_cd_ptr(master, master_domain->ssid);
if (WARN_ON(!cdptr))
continue;
- arm_smmu_make_s1_cd(&target_cd, master, smmu_domain);
+ if (WARN_ON(arm_smmu_domain_get_iotlb_tag(smmu_domain,
+ master->smmu, &tag)))
+ continue;
+ if (WARN_ON(tag.type != INV_TYPE_S1_ASID))
+ continue;
+ arm_smmu_make_s1_cd(&target_cd, master, smmu_domain, &tag);
arm_smmu_write_cd_entry(master, master_domain->ssid, cdptr,
&target_cd);
}
@@ -158,12 +164,18 @@ static void arm_smmu_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
struct arm_smmu_master *master = master_domain->master;
struct arm_smmu_cd target;
struct arm_smmu_cd *cdptr;
+ struct arm_smmu_inv tag;
cdptr = arm_smmu_get_cd_ptr(master, master_domain->ssid);
if (WARN_ON(!cdptr))
continue;
- arm_smmu_make_sva_cd(&target, master, NULL,
- smmu_domain->cd.asid);
+
+ if (WARN_ON(arm_smmu_domain_get_iotlb_tag(smmu_domain,
+ master->smmu, &tag)))
+ continue;
+ if (WARN_ON(tag.type != INV_TYPE_S1_ASID))
+ continue;
+ arm_smmu_make_sva_cd(&target, master, NULL, tag.id);
arm_smmu_write_cd_entry(master, master_domain->ssid, cdptr,
&target);
}
@@ -262,10 +274,12 @@ static int arm_smmu_sva_set_dev_pasid(struct iommu_domain *domain,
return -EINVAL;
/*
+ * Use a dummy asid and fix it in arm_smmu_set_pasid().
+ *
* This does not need the arm_smmu_asid_lock because SVA domains never
* get reassigned
*/
- arm_smmu_make_sva_cd(&target, master, domain->mm, smmu_domain->cd.asid);
+ arm_smmu_make_sva_cd(&target, master, domain->mm, 0);
ret = arm_smmu_set_pasid(master, smmu_domain, id, &target, old);
mmput(domain->mm);
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
index 238bfd328b5b..81551fad727b 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
@@ -306,6 +306,9 @@ static void arm_smmu_test_make_s2_ste(struct arm_smmu_ste *ste,
struct arm_smmu_domain smmu_domain = {
.pgtbl_ops = &io_pgtable.ops,
};
+ struct arm_smmu_inv tag = {
+ .type = INV_TYPE_S2_VMID,
+ };
io_pgtable.cfg.arm_lpae_s2_cfg.vttbr = 0xdaedbeefdeadbeefULL;
io_pgtable.cfg.arm_lpae_s2_cfg.vtcr.ps = 1;
@@ -316,7 +319,8 @@ static void arm_smmu_test_make_s2_ste(struct arm_smmu_ste *ste,
io_pgtable.cfg.arm_lpae_s2_cfg.vtcr.sl = 3;
io_pgtable.cfg.arm_lpae_s2_cfg.vtcr.tsz = 4;
- arm_smmu_make_s2_domain_ste(ste, &master, &smmu_domain, ats_enabled);
+ arm_smmu_make_s2_domain_ste(ste, &master, &smmu_domain, &tag,
+ ats_enabled);
}
static void arm_smmu_v3_write_ste_test_s2_to_abort(struct kunit *test)
@@ -461,6 +465,10 @@ static void arm_smmu_test_make_s1_cd(struct arm_smmu_cd *cd, unsigned int asid)
.asid = asid,
},
};
+ struct arm_smmu_inv tag = {
+ .type = INV_TYPE_S1_ASID,
+ .id = asid,
+ };
io_pgtable.cfg.arm_lpae_s1_cfg.ttbr = 0xdaedbeefdeadbeefULL;
io_pgtable.cfg.arm_lpae_s1_cfg.tcr.ips = 1;
@@ -471,7 +479,7 @@ static void arm_smmu_test_make_s1_cd(struct arm_smmu_cd *cd, unsigned int asid)
io_pgtable.cfg.arm_lpae_s1_cfg.tcr.tsz = 4;
io_pgtable.cfg.arm_lpae_s1_cfg.mair = 0xabcdef012345678ULL;
- arm_smmu_make_s1_cd(cd, &master, &smmu_domain);
+ arm_smmu_make_s1_cd(cd, &master, &smmu_domain, &tag);
}
static void arm_smmu_v3_write_cd_test_s1_clear(struct kunit *test)
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 325eabb51c81..cf0543f276f3 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1613,14 +1613,16 @@ void arm_smmu_write_cd_entry(struct arm_smmu_master *master, int ssid,
void arm_smmu_make_s1_cd(struct arm_smmu_cd *target,
struct arm_smmu_master *master,
- struct arm_smmu_domain *smmu_domain)
+ struct arm_smmu_domain *smmu_domain,
+ struct arm_smmu_inv *tag)
{
- struct arm_smmu_ctx_desc *cd = &smmu_domain->cd;
const struct io_pgtable_cfg *pgtbl_cfg =
&io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops)->cfg;
typeof(&pgtbl_cfg->arm_lpae_s1_cfg.tcr) tcr =
&pgtbl_cfg->arm_lpae_s1_cfg.tcr;
+ WARN_ON(tag->type != INV_TYPE_S1_ASID);
+
memset(target, 0, sizeof(*target));
target->data[0] = cpu_to_le64(
@@ -1640,7 +1642,7 @@ void arm_smmu_make_s1_cd(struct arm_smmu_cd *target,
CTXDESC_CD_0_R |
CTXDESC_CD_0_A |
CTXDESC_CD_0_ASET |
- FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid)
+ FIELD_PREP(CTXDESC_CD_0_ASID, tag->id)
);
/* To enable dirty flag update, set both Access flag and dirty state update */
@@ -1897,9 +1899,8 @@ EXPORT_SYMBOL_IF_KUNIT(arm_smmu_make_cdtable_ste);
void arm_smmu_make_s2_domain_ste(struct arm_smmu_ste *target,
struct arm_smmu_master *master,
struct arm_smmu_domain *smmu_domain,
- bool ats_enabled)
+ struct arm_smmu_inv *tag, bool ats_enabled)
{
- struct arm_smmu_s2_cfg *s2_cfg = &smmu_domain->s2_cfg;
const struct io_pgtable_cfg *pgtbl_cfg =
&io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops)->cfg;
typeof(&pgtbl_cfg->arm_lpae_s2_cfg.vtcr) vtcr =
@@ -1907,6 +1908,8 @@ void arm_smmu_make_s2_domain_ste(struct arm_smmu_ste *target,
u64 vtcr_val;
struct arm_smmu_device *smmu = master->smmu;
+ WARN_ON(tag->type != INV_TYPE_S2_VMID);
+
memset(target, 0, sizeof(*target));
target->data[0] = cpu_to_le64(
STRTAB_STE_0_V |
@@ -1930,7 +1933,7 @@ void arm_smmu_make_s2_domain_ste(struct arm_smmu_ste *target,
FIELD_PREP(STRTAB_STE_2_VTCR_S2TG, vtcr->tg) |
FIELD_PREP(STRTAB_STE_2_VTCR_S2PS, vtcr->ps);
target->data[2] = cpu_to_le64(
- FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) |
+ FIELD_PREP(STRTAB_STE_2_S2VMID, tag->id) |
FIELD_PREP(STRTAB_STE_2_VTCR, vtcr_val) |
STRTAB_STE_2_S2AA64 |
#ifdef __BIG_ENDIAN
@@ -3671,7 +3674,8 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev,
case ARM_SMMU_DOMAIN_S1: {
struct arm_smmu_cd target_cd;
- arm_smmu_make_s1_cd(&target_cd, master, smmu_domain);
+ arm_smmu_make_s1_cd(&target_cd, master, smmu_domain,
+ &state.new_domain_invst.tag);
arm_smmu_write_cd_entry(master, IOMMU_NO_PASID, cdptr,
&target_cd);
arm_smmu_make_cdtable_ste(&target, master, state.ats_enabled,
@@ -3681,6 +3685,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev,
}
case ARM_SMMU_DOMAIN_S2:
arm_smmu_make_s2_domain_ste(&target, master, smmu_domain,
+ &state.new_domain_invst.tag,
state.ats_enabled);
arm_smmu_install_ste_for_dev(master, &target);
arm_smmu_clear_cd(master, IOMMU_NO_PASID);
@@ -3702,6 +3707,9 @@ static int arm_smmu_s1_set_dev_pasid(struct iommu_domain *domain,
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
struct arm_smmu_device *smmu = master->smmu;
+ struct arm_smmu_inv tag = {
+ .type = INV_TYPE_S1_ASID,
+ };
struct arm_smmu_cd target_cd;
if (smmu_domain->smmu != smmu)
@@ -3710,11 +3718,8 @@ static int arm_smmu_s1_set_dev_pasid(struct iommu_domain *domain,
if (smmu_domain->stage != ARM_SMMU_DOMAIN_S1)
return -EINVAL;
- /*
- * We can read cd.asid outside the lock because arm_smmu_set_pasid()
- * will fix it
- */
- arm_smmu_make_s1_cd(&target_cd, master, smmu_domain);
+ /* Use a dummy asid and fix it in arm_smmu_set_pasid() */
+ arm_smmu_make_s1_cd(&target_cd, master, smmu_domain, &tag);
return arm_smmu_set_pasid(master, to_smmu_domain(domain), id,
&target_cd, old);
}
@@ -3782,7 +3787,7 @@ int arm_smmu_set_pasid(struct arm_smmu_master *master,
*/
cd->data[0] &= ~cpu_to_le64(CTXDESC_CD_0_ASID);
cd->data[0] |= cpu_to_le64(
- FIELD_PREP(CTXDESC_CD_0_ASID, smmu_domain->cd.asid));
+ FIELD_PREP(CTXDESC_CD_0_ASID, state.new_domain_invst.tag.id));
arm_smmu_write_cd_entry(master, pasid, cdptr, cd);
arm_smmu_update_ste(master, sid_domain, state.ats_enabled);
--
2.43.0
On Wed, Jan 21, 2026 at 05:24:20PM -0800, Nicolin Chen wrote: > Now, struct arm_smmu_attach_state has the IOTLB cache tags copied from the > cd->asid or s2_cfg->vmid of an smmu_domain. > > Pass it down to arm_smmu_make_s1_cd() and arm_smmu_make_s2_domain_ste() to > set in the CD and STE, removing the references of smmu_domain for its asid > or vmid. > > Note the two set_dev_pasid callbacks finalize CDs in arm_smmu_set_pasid(). > So, it is safe for arm_smmu_make_sva_cd() and arm_smmu_make_s1_cd() to use > a dummy iotlb tag (asid=0) because arm_smmu_set_pasid() will fix it. Maybe it is time to fix that up? I think if you make arm_smmu_set_pasid() take in a function pointer of signature: void (*func)(struct arm_smmu_cd *target, struct arm_smmu_master *master, struct arm_smmu_domain *smmu_domain, struct arm_smmu_inv *tag) It can accept both arm_smmu_make_s1_cd() and arm_smmu_make_sva_cd() then call them at the right point once the tag is finally known. It may have been tortured like this because of the (removed) BTM support, but when we bring that back the ASID for BTM should come from arm_smmu_domain_get_iotlb_tag(). Otherwise this looks OK Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Jason
On Mon, Jan 26, 2026 at 04:53:58PM -0400, Jason Gunthorpe wrote: > On Wed, Jan 21, 2026 at 05:24:20PM -0800, Nicolin Chen wrote: > > Now, struct arm_smmu_attach_state has the IOTLB cache tags copied from the > > cd->asid or s2_cfg->vmid of an smmu_domain. > > > > Pass it down to arm_smmu_make_s1_cd() and arm_smmu_make_s2_domain_ste() to > > set in the CD and STE, removing the references of smmu_domain for its asid > > or vmid. > > > > Note the two set_dev_pasid callbacks finalize CDs in arm_smmu_set_pasid(). > > So, it is safe for arm_smmu_make_sva_cd() and arm_smmu_make_s1_cd() to use > > a dummy iotlb tag (asid=0) because arm_smmu_set_pasid() will fix it. > > Maybe it is time to fix that up? > > I think if you make arm_smmu_set_pasid() take in a function pointer of signature: > > void (*func)(struct arm_smmu_cd *target, > struct arm_smmu_master *master, > struct arm_smmu_domain *smmu_domain, > struct arm_smmu_inv *tag) > > It can accept both arm_smmu_make_s1_cd() and arm_smmu_make_sva_cd() > then call them at the right point once the tag is finally known. Ack. That's cleaner. I will add a preparatory patch doing this. Thanks! Nicolin
© 2016 - 2026 Red Hat, Inc.