diff mbox series

[v7,13/14] iommu/arm-smmu-v3: Allow a PASID to be set when RID is IDENTITY/BLOCKED

Message ID 13-v7-9597c885796c+d2-smmuv3_newapi_p2b_jgg@nvidia.com (mailing list archive)
State New
Headers show
Series Update SMMUv3 to the modern iommu API (part 2b/3) | expand

Commit Message

Jason Gunthorpe May 8, 2024, 6:57 p.m. UTC
If the STE doesn't point to the CD table we can upgrade it by
reprogramming the STE with the appropriate S1DSS. We may also need to turn
on ATS at the same time.

Keep track if the installed STE is pointing at the cd_table and the ATS
state to trigger this path.

Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Tested-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 49 ++++++++++++++++++++-
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  3 +-
 2 files changed, 49 insertions(+), 3 deletions(-)

Comments

Nicolin Chen May 13, 2024, 8:31 a.m. UTC | #1
On Wed, May 08, 2024 at 03:57:21PM -0300, Jason Gunthorpe wrote:
> If the STE doesn't point to the CD table we can upgrade it by
> reprogramming the STE with the appropriate S1DSS. We may also need to turn
> on ATS at the same time.
> 
> Keep track if the installed STE is pointing at the cd_table and the ATS
> state to trigger this path.
> 
> Tested-by: Nicolin Chen <nicolinc@nvidia.com>
> Tested-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
> ---
>  drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 49 ++++++++++++++++++++-
>  drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  3 +-
>  2 files changed, 49 insertions(+), 3 deletions(-)
  
> @@ -2819,6 +2851,19 @@ static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
>  		arm_smmu_atc_inv_master(master, pasid);
>  	arm_smmu_remove_master_domain(master, &smmu_domain->domain, pasid);
>  	mutex_unlock(&arm_smmu_asid_lock);
> +
> +	/*
> +	 * When the last user of the CD table goes away downgrade the STE back
> +	 * to a non-cd_table one.
> +	 */
> +	if (!arm_smmu_ssids_in_use(&master->cd_table)) {
> +		struct iommu_domain *sid_domain =
> +			iommu_get_domain_for_dev(master->dev);
> +
> +		if (domain->type == IOMMU_DOMAIN_IDENTITY ||
> +		    domain->type == IOMMU_DOMAIN_BLOCKED)
> +			sid_domain->ops->attach_dev(sid_domain, dev);

Should we check against sid_domain->type instead?

Thanks
Nicolin
Jason Gunthorpe May 21, 2024, 5:51 p.m. UTC | #2
On Mon, May 13, 2024 at 01:31:31AM -0700, Nicolin Chen wrote:
> On Wed, May 08, 2024 at 03:57:21PM -0300, Jason Gunthorpe wrote:
> > If the STE doesn't point to the CD table we can upgrade it by
> > reprogramming the STE with the appropriate S1DSS. We may also need to turn
> > on ATS at the same time.
> > 
> > Keep track if the installed STE is pointing at the cd_table and the ATS
> > state to trigger this path.
> > 
> > Tested-by: Nicolin Chen <nicolinc@nvidia.com>
> > Tested-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
> > ---
> >  drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 49 ++++++++++++++++++++-
> >  drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  3 +-
> >  2 files changed, 49 insertions(+), 3 deletions(-)
>   
> > @@ -2819,6 +2851,19 @@ static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
> >  		arm_smmu_atc_inv_master(master, pasid);
> >  	arm_smmu_remove_master_domain(master, &smmu_domain->domain, pasid);
> >  	mutex_unlock(&arm_smmu_asid_lock);
> > +
> > +	/*
> > +	 * When the last user of the CD table goes away downgrade the STE back
> > +	 * to a non-cd_table one.
> > +	 */
> > +	if (!arm_smmu_ssids_in_use(&master->cd_table)) {
> > +		struct iommu_domain *sid_domain =
> > +			iommu_get_domain_for_dev(master->dev);
> > +
> > +		if (domain->type == IOMMU_DOMAIN_IDENTITY ||
> > +		    domain->type == IOMMU_DOMAIN_BLOCKED)
> > +			sid_domain->ops->attach_dev(sid_domain, dev);
> 
> Should we check against sid_domain->type instead?

Yes, I typo'd that after some edit

Thanks,
Jason
diff mbox series

Patch

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 41d7a0664a445d..ccc10ad3ab996d 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2435,6 +2435,9 @@  static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master,
 	master->cd_table.in_ste =
 		FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(target->data[0])) ==
 		STRTAB_STE_0_CFG_S1_TRANS;
+	master->ste_ats_enabled =
+		FIELD_GET(STRTAB_STE_1_EATS, le64_to_cpu(target->data[1])) ==
+		STRTAB_STE_1_EATS_TRANS;
 
 	for (i = 0; i < master->num_streams; ++i) {
 		u32 sid = master->streams[i].id;
@@ -2765,10 +2768,36 @@  static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	return 0;
 }
 
+static void arm_smmu_update_ste(struct arm_smmu_master *master,
+				struct iommu_domain *sid_domain,
+				bool want_ats)
+{
+	unsigned int s1dss = STRTAB_STE_1_S1DSS_TERMINATE;
+	struct arm_smmu_ste ste;
+
+	if (master->cd_table.in_ste && master->ste_ats_enabled == want_ats)
+		return;
+
+	if (sid_domain->type == IOMMU_DOMAIN_IDENTITY)
+		s1dss = STRTAB_STE_1_S1DSS_BYPASS;
+	else
+		WARN_ON(sid_domain->type != IOMMU_DOMAIN_BLOCKED);
+
+	/*
+	 * Change the STE into a cdtable one with SID IDENTITY/BLOCKED behavior
+	 * using s1dss if necessary. The cd_table is already installed then
+	 * the S1DSS is correct and this will just update the EATS. Otherwise
+	 * it installs the entire thing. This will be hitless.
+	 */
+	arm_smmu_make_cdtable_ste(&ste, master, want_ats, s1dss);
+	arm_smmu_install_ste_for_dev(master, &ste);
+}
+
 int arm_smmu_set_pasid(struct arm_smmu_master *master,
 		       struct arm_smmu_domain *smmu_domain, ioasid_t pasid,
 		       const struct arm_smmu_cd *cd)
 {
+	struct iommu_domain *sid_domain = iommu_get_domain_for_dev(master->dev);
 	struct attach_state state = {
 		/*
 		 * For now the core code prevents calling this when a domain is
@@ -2784,8 +2813,10 @@  int arm_smmu_set_pasid(struct arm_smmu_master *master,
 	if (smmu_domain->smmu != master->smmu)
 		return -EINVAL;
 
-	if (!master->cd_table.in_ste)
-		return -ENODEV;
+	if (!master->cd_table.in_ste &&
+	    sid_domain->type != IOMMU_DOMAIN_IDENTITY &&
+	    sid_domain->type != IOMMU_DOMAIN_BLOCKED)
+		return -EINVAL;
 
 	cdptr = arm_smmu_alloc_cd_ptr(master, pasid);
 	if (!cdptr)
@@ -2797,6 +2828,7 @@  int arm_smmu_set_pasid(struct arm_smmu_master *master,
 		goto out_unlock;
 
 	arm_smmu_write_cd_entry(master, pasid, cdptr, cd);
+	arm_smmu_update_ste(master, sid_domain, state.want_ats);
 
 	arm_smmu_attach_commit(master, &state);
 
@@ -2819,6 +2851,19 @@  static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
 		arm_smmu_atc_inv_master(master, pasid);
 	arm_smmu_remove_master_domain(master, &smmu_domain->domain, pasid);
 	mutex_unlock(&arm_smmu_asid_lock);
+
+	/*
+	 * When the last user of the CD table goes away downgrade the STE back
+	 * to a non-cd_table one.
+	 */
+	if (!arm_smmu_ssids_in_use(&master->cd_table)) {
+		struct iommu_domain *sid_domain =
+			iommu_get_domain_for_dev(master->dev);
+
+		if (domain->type == IOMMU_DOMAIN_IDENTITY ||
+		    domain->type == IOMMU_DOMAIN_BLOCKED)
+			sid_domain->ops->attach_dev(sid_domain, dev);
+	}
 }
 
 static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
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 71ecced1db0226..3c189a48e29757 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -705,7 +705,8 @@  struct arm_smmu_master {
 	/* Locked by the iommu core using the group mutex */
 	struct arm_smmu_ctx_desc_cfg	cd_table;
 	unsigned int			num_streams;
-	bool				ats_enabled;
+	bool				ats_enabled : 1;
+	bool				ste_ats_enabled : 1;
 	bool				stall_enabled;
 	bool				sva_enabled;
 	bool				iopf_enabled;