[PATCH 25/27] iommu/arm-smmu-v3: Allow IDENTITY/BLOCKED to be set while PASID is used

Jason Gunthorpe jgg at nvidia.com
Wed Oct 25 08:10:28 PDT 2023


On Wed, Oct 11, 2023 at 08:26:01PM -0300, Jason Gunthorpe wrote:
> -static int arm_smmu_attach_dev_ste(struct device *dev,
> -				   struct arm_smmu_ste *ste)
> +static void arm_smmu_attach_dev_ste(struct device *dev,
> +				    struct arm_smmu_ste *ste,
> +				    unsigned int s1dss)
>  {
>  	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
> -
> -	if (arm_smmu_ssids_in_use(&master->cd_table))
> -		return -EBUSY;
> +	struct arm_smmu_domain *old_domain =
> +		to_smmu_domain_safe(iommu_get_domain_for_dev(master->dev));
>  
>  	/*
>  	 * Do not allow any ASID to be changed while are working on the STE,
> @@ -2755,6 +2760,19 @@ static int arm_smmu_attach_dev_ste(struct device *dev,
>  	 */
>  	mutex_lock(&master->smmu->asid_lock);
>  
> +	/*
> +	 * If the CD table is still in use then we need to keep it installed and
> +	 * use the S1DSS to change the mode.
> +	 */
> +	if (arm_smmu_ssids_in_use(&master->cd_table)) {
> +		arm_smmu_make_cdtable_ste(ste, master, &master->cd_table,
> +					  master->ats_enabled, s1dss);
> +		arm_smmu_remove_master_domain(master, old_domain,
> +					      IOMMU_NO_PASID);
> +	} else {
> +		arm_smmu_attach_remove(master, old_domain, IOMMU_NO_PASID);
> +	}
> +
>  	/*
>  	 * The SMMU does not support enabling ATS with bypass/abort. When the
>  	 * STE is in bypass (STE.Config[2:0] == 0b100), ATS Translation Requests
> @@ -2762,11 +2780,6 @@ static int arm_smmu_attach_dev_ste(struct device *dev,
>  	 * the stream (STE.EATS == 0b00), causing F_BAD_ATS_TREQ and
>  	 * F_TRANSL_FORBIDDEN events (IHI0070Ea 5.2 Stream Table Entry).
>  	 */
> -	arm_smmu_attach_remove(
> -		master,
> -		to_smmu_domain_safe(iommu_get_domain_for_dev(master->dev)),
> -		IOMMU_NO_PASID);
> -
>  	arm_smmu_install_ste_for_dev(master, ste);
>  	mutex_unlock(&master->smmu->asid_lock);

This is the last bit that had the ATC invalidation sequenced wrong. I
changed this entirely to look like this, with a clear order for the
ATC invalidation after the STE is changed. This probably eliminates
the need for the wmb() as the STE change and ATC invalidate will be
sequenced by the SMMU logic. Even if the device issues another ATS
after the invalidation is sent it will be rejected by the SMMU.

	/*
	 * If the CD table is not in use we can use the provided STE, otherwise
	 * we use a cdtable STE with the provided S1DSS.
	 */
	if (!arm_smmu_ssids_in_use(&master->cd_table)) {
		/*
		 * The SMMU does not support enabling ATS with bypass/abort.
		 * When the STE is in bypass (STE.Config[2:0] == 0b100), ATS
		 * Translation Requests and Translated transactions are denied
		 * as though ATS is disabled for the stream (STE.EATS == 0b00),
		 * causing F_BAD_ATS_TREQ and F_TRANSL_FORBIDDEN events
		 * (IHI0070Ea 5.2 Stream Table Entry).
		 */
		if (master->ats_enabled) {
			pci_disable_ats(to_pci_dev(master->dev));
			/*
			 * Ensure ATS is disabled at the endpoint before we
			 * issue the ATC invalidation via the SMMU.
			 */
			wmb();
		}
	} else {
		/*
		 * It also does not support ATS with S1DSS = bypass but we have
		 * no idea what the other PASIDs are doing so it has to be left
		 * on.
		 */
		arm_smmu_make_cdtable_ste(ste, master, &master->cd_table,
					  master->ats_enabled, s1dss);
	}

	arm_smmu_install_ste_for_dev(master, ste);

	if (old_domain) {
		if (master->ats_enabled)
			arm_smmu_atc_inv_master(master, IOMMU_NO_PASID);
		arm_smmu_remove_master_domain(master, old_domain,
					      IOMMU_NO_PASID);
	}

	if (!arm_smmu_ssids_in_use(&master->cd_table



More information about the linux-arm-kernel mailing list