lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <f7fa8725aab1fbd2c2a1d564e7493b16a586e88c.1766088962.git.nicolinc@nvidia.com>
Date: Thu, 18 Dec 2025 12:26:49 -0800
From: Nicolin Chen <nicolinc@...dia.com>
To: <will@...nel.org>, <robin.murphy@....com>, <jgg@...dia.com>
CC: <joro@...tes.org>, <jpb@...nel.org>, <praan@...gle.com>,
	<miko.lenczewski@....com>, <linux-arm-kernel@...ts.infradead.org>,
	<iommu@...ts.linux.dev>, <linux-kernel@...r.kernel.org>,
	<patches@...ts.linux.dev>
Subject: [PATCH v1 3/9] iommu/arm-smmu-v3: Store ASIDs and VMID in arm_smmu_master

Currently, ASID is allocated per smmu_domain, stored in the domain, and
freed with the domain.

Practically, ASID is only used in a CD as an iotlb tag. Therefore, ASID
doesn't really follow the life cycle of a domain but domain attachment.

On the other hand, the CD carrying ASID is installed to a device's STE.

This applies to the VMID as well, which is installed in an STE directly.

Since a device can only have one ASID per SSID and one VMID per SID, add
an ASID array and VMID in the arm_smmu_master structure, to decouple the
ASID/VMID from the domain structure.

Signed-off-by: Nicolin Chen <nicolinc@...dia.com>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h   |  4 +++
 .../iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c  | 13 ++++++---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c   | 28 +++++++++++++++++++
 3 files changed, 41 insertions(+), 4 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 b275673c03ce..e21e95936b05 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -953,6 +953,8 @@ struct arm_smmu_master {
 	bool				stall_enabled;
 	unsigned int			ssid_bits;
 	unsigned int			iopf_refcount;
+	/* Store allocated ASID[1 << ssid_bits] and VMID */
+	u16				*asid, vmid;
 };
 
 /* SMMU private data for an IOMMU domain */
@@ -1117,11 +1119,13 @@ static inline bool arm_smmu_master_canwbs(struct arm_smmu_master *master)
  * @new_invs: for new domain, this is the new invs array to update domain->invs;
  *            for old domain, this is the master->build_invs to pass in as the
  *            to_unref argument to an arm_smmu_invs_unref() call
+ * @iotlb_tag: copy of the first entry in the build_invs for the domain
  */
 struct arm_smmu_inv_state {
 	struct arm_smmu_invs __rcu **invs_ptr;
 	struct arm_smmu_invs *old_invs;
 	struct arm_smmu_invs *new_invs;
+	struct arm_smmu_inv iotlb_tag;
 };
 
 struct arm_smmu_attach_state {
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 e4bdb4cfdacd..ead0d84cc9a0 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
@@ -449,7 +449,8 @@ static void arm_smmu_v3_test_cd_expect_hitless_transition(
 					      num_syncs_expected, true);
 }
 
-static void arm_smmu_test_make_s1_cd(struct arm_smmu_cd *cd, unsigned int asid)
+static void arm_smmu_test_make_s1_cd(struct kunit *test, struct arm_smmu_cd *cd,
+				     unsigned int asid)
 {
 	struct arm_smmu_master master = {
 		.smmu = &smmu,
@@ -471,6 +472,10 @@ 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;
 
+	master.asid = kunit_kzalloc(test, sizeof(*master.asid), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, master.asid);
+
+	master.asid[IOMMU_NO_PASID] = asid;
 	arm_smmu_make_s1_cd(cd, &master, &smmu_domain, IOMMU_NO_PASID);
 }
 
@@ -479,7 +484,7 @@ static void arm_smmu_v3_write_cd_test_s1_clear(struct kunit *test)
 	struct arm_smmu_cd cd = {};
 	struct arm_smmu_cd cd_2;
 
-	arm_smmu_test_make_s1_cd(&cd_2, 1997);
+	arm_smmu_test_make_s1_cd(test, &cd_2, 1997);
 	arm_smmu_v3_test_cd_expect_non_hitless_transition(
 		test, &cd, &cd_2, NUM_EXPECTED_SYNCS(2));
 	arm_smmu_v3_test_cd_expect_non_hitless_transition(
@@ -491,8 +496,8 @@ static void arm_smmu_v3_write_cd_test_s1_change_asid(struct kunit *test)
 	struct arm_smmu_cd cd = {};
 	struct arm_smmu_cd cd_2;
 
-	arm_smmu_test_make_s1_cd(&cd, 778);
-	arm_smmu_test_make_s1_cd(&cd_2, 1997);
+	arm_smmu_test_make_s1_cd(test, &cd, 778);
+	arm_smmu_test_make_s1_cd(test, &cd_2, 1997);
 	arm_smmu_v3_test_cd_expect_hitless_transition(test, &cd, &cd_2,
 						      NUM_EXPECTED_SYNCS(1));
 	arm_smmu_v3_test_cd_expect_hitless_transition(test, &cd_2, &cd,
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 8a2b7064d29b..1bf7b7233109 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3391,6 +3391,7 @@ static int arm_smmu_attach_prepare_invs(struct arm_smmu_attach_state *state,
 			arm_smmu_invs_merge(invst->old_invs, build_invs);
 		if (IS_ERR(invst->new_invs))
 			return PTR_ERR(invst->new_invs);
+		invst->iotlb_tag = build_invs->inv[0];
 	}
 
 	if (old_smmu_domain) {
@@ -3440,6 +3441,11 @@ arm_smmu_install_new_domain_invs(struct arm_smmu_attach_state *state)
 	 */
 	smp_mb();
 	kfree_rcu(invst->old_invs, rcu);
+
+	if (invst->iotlb_tag.type == INV_TYPE_S1_ASID)
+		state->master->asid[state->ssid] = invst->iotlb_tag.id;
+	else
+		state->master->vmid = invst->iotlb_tag.id;
 }
 
 /*
@@ -3471,8 +3477,11 @@ static void arm_smmu_inv_flush_iotlb_tag(struct arm_smmu_inv *inv)
 static void
 arm_smmu_install_old_domain_invs(struct arm_smmu_attach_state *state)
 {
+	struct arm_smmu_inv *new_iotlb_tag = &state->new_domain_invst.iotlb_tag;
+	struct arm_smmu_inv *old_iotlb_tag = &state->old_domain_invst.iotlb_tag;
 	struct arm_smmu_inv_state *invst = &state->old_domain_invst;
 	struct arm_smmu_invs *old_invs = invst->old_invs;
+	struct arm_smmu_master *master = state->master;
 	struct arm_smmu_invs *new_invs;
 
 	lockdep_assert_held(&arm_smmu_asid_lock);
@@ -3482,6 +3491,7 @@ arm_smmu_install_old_domain_invs(struct arm_smmu_attach_state *state)
 
 	arm_smmu_invs_unref(old_invs, invst->new_invs,
 			    arm_smmu_inv_flush_iotlb_tag);
+	*old_iotlb_tag = invst->new_invs->inv[0];
 
 	new_invs = arm_smmu_invs_purge(old_invs);
 	if (!new_invs)
@@ -3506,6 +3516,14 @@ arm_smmu_install_old_domain_invs(struct arm_smmu_attach_state *state)
 	 */
 	smp_mb();
 	kfree_rcu(old_invs, rcu);
+
+	/* Make sure we don't clear the stored new iotlb tag */
+	if (!new_iotlb_tag->id) {
+		if (old_iotlb_tag->type == INV_TYPE_S1_ASID)
+			cmpxchg(&master->asid[state->ssid], old_iotlb_tag->id, 0);
+		else
+			cmpxchg(&master->vmid, old_iotlb_tag->id, 0);
+	}
 }
 
 /*
@@ -4286,6 +4304,13 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
 		master->ssid_bits = min_t(u8, master->ssid_bits,
 					  CTXDESC_LINEAR_CDMAX);
 
+	master->asid = kcalloc(1 << master->ssid_bits, sizeof(*master->asid),
+			       GFP_KERNEL);
+	if (!master->asid) {
+		ret = -ENOMEM;
+		goto err_disable_pasid;
+	}
+
 	if ((smmu->features & ARM_SMMU_FEAT_STALLS &&
 	     device_property_read_bool(dev, "dma-can-stall")) ||
 	    smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
@@ -4299,6 +4324,9 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
 
 	return &smmu->iommu;
 
+err_disable_pasid:
+	arm_smmu_disable_pasid(master);
+	arm_smmu_remove_master(master);
 err_free_master:
 	kfree(master);
 	return ERR_PTR(ret);
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ