From: Nirmoy Das <nirmoyd@nvidia.com>
To: Nicolin Chen <nicolinc@nvidia.com>, <jgg@nvidia.com>,
<will@kernel.org>, <robin.murphy@arm.com>, <bhelgaas@google.com>
Cc: <joro@8bytes.org>, <praan@google.com>, <baolu.lu@linux.intel.com>,
<kevin.tian@intel.com>, <miko.lenczewski@arm.com>,
<linux-arm-kernel@lists.infradead.org>, <iommu@lists.linux.dev>,
<linux-kernel@vger.kernel.org>, <linux-pci@vger.kernel.org>,
<dan.j.williams@intel.com>, <jonathan.cameron@huawei.com>,
<vsethi@nvidia.com>, <linux-cxl@vger.kernel.org>
Subject: Re: [PATCH v3 3/3] iommu/arm-smmu-v3: Allow ATS to be always on
Date: Sun, 8 Mar 2026 21:52:24 +0100 [thread overview]
Message-ID: <54bbce49-e226-436a-bcf3-025e1455cc09@nvidia.com> (raw)
In-Reply-To: <0e8d1ee1557c54943dd15ff836576de4c3aa58b6.1772833963.git.nicolinc@nvidia.com>
On 07.03.26 00:41, Nicolin Chen wrote:
> When a device's default substream attaches to an identity domain, the SMMU
> driver currently sets the device's STE between two modes:
>
> Mode 1: Cfg=Translate, S1DSS=Bypass, EATS=1
> Mode 2: Cfg=bypass (EATS is ignored by HW)
>
> When there is an active PASID (non-default substream), mode 1 is used. And
> when there is no PASID support or no active PASID, mode 2 is used.
>
> The driver will also downgrade an STE from mode 1 to mode 2, when the last
> active substream becomes inactive.
>
> However, there are PCIe devices that demand ATS to be always on. For these
> devices, their STEs have to use the mode 1 as HW ignores EATS with mode 2.
>
> Change the driver accordingly:
> - always use the mode 1
> - never downgrade to mode 2
> - allocate and retain a CD table (see note below)
>
> Note that these devices might not support PASID, i.e. doing non-PASID ATS.
> In such a case, the ssid_bits is set to 0. However, s1cdmax must be set to
> a !0 value in order to keep the S1DSS field effective. Thus, when a master
> requires ats_always_on, set its s1cdmax to minimal 1, meaning the CD table
> will have a dummy entry (SSID=1) that will be never used.
>
> Now, for these device, arm_smmu_cdtab_allocated() will always return true,
> v.s. false prior to this change. When its default substream is attached to
> an IDENTITY domain, its first CD is NULL in the table, which is a totally
> valid case. Thus, add "!master->ats_always_on" to the condition.
>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Tested-by: Nirmoy Das <nirmoyd@nvidia.com>
Acked-by: Nirmoy Das <nirmoyd@nvidia.com>
> ---
> drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
> drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 72 ++++++++++++++++++---
> 2 files changed, 65 insertions(+), 8 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 3c6d65d36164f..f966d474b61fd 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
> @@ -848,6 +848,7 @@ struct arm_smmu_master {
> bool ats_enabled : 1;
> bool ste_ats_enabled : 1;
> bool stall_enabled;
> + bool ats_always_on;
> unsigned int ssid_bits;
> unsigned int iopf_refcount;
> };
> 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 4d00d796f0783..ef98e79b4e75d 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> @@ -1482,8 +1482,11 @@ void arm_smmu_clear_cd(struct arm_smmu_master *master, ioasid_t ssid)
> if (!arm_smmu_cdtab_allocated(&master->cd_table))
> return;
> cdptr = arm_smmu_get_cd_ptr(master, ssid);
> - if (WARN_ON(!cdptr))
> + if (!cdptr) {
> + /* Only ats_always_on allows a NULL CD on default substream */
> + WARN_ON(!master->ats_always_on || ssid);
> return;
> + }
> arm_smmu_write_cd_entry(master, ssid, cdptr, &target);
> }
>
> @@ -1496,6 +1499,22 @@ static int arm_smmu_alloc_cd_tables(struct arm_smmu_master *master)
> struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
>
> cd_table->s1cdmax = master->ssid_bits;
> +
> + /*
> + * When a device doesn't support PASID (non default SSID), ssid_bits is
> + * set to 0. This also sets S1CDMAX to 0, which disables the substreams
> + * and ignores the S1DSS field.
> + *
> + * On the other hand, if a device demands ATS to be always on even when
> + * its default substream is IOMMU bypassed, it has to use EATS that is
> + * only effective with an STE (CFG=S1translate, S1DSS=Bypass). For such
> + * use cases, S1CDMAX has to be !0, in order to make use of S1DSS/EATS.
> + *
> + * Set S1CDMAX no lower than 1. This would add a dummy substream in the
> + * CD table but it should never be used by an actual CD.
> + */
> + if (master->ats_always_on)
> + cd_table->s1cdmax = max_t(u8, cd_table->s1cdmax, 1);
> max_contexts = 1 << cd_table->s1cdmax;
>
> if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB) ||
> @@ -3250,7 +3269,8 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
> * When the last user of the CD table goes away downgrade the STE back
> * to a non-cd_table one, by re-attaching its sid_domain.
> */
> - if (!arm_smmu_ssids_in_use(&master->cd_table)) {
> + if (!master->ats_always_on &&
> + !arm_smmu_ssids_in_use(&master->cd_table)) {
> struct iommu_domain *sid_domain =
> iommu_driver_get_domain_for_dev(master->dev);
>
> @@ -3274,6 +3294,8 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
> .old_domain = old_domain,
> .ssid = IOMMU_NO_PASID,
> };
> + bool ats_always_on = master->ats_always_on &&
> + s1dss != STRTAB_STE_1_S1DSS_TERMINATE;
>
> /*
> * Do not allow any ASID to be changed while are working on the STE,
> @@ -3285,7 +3307,7 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
> * 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)) {
> + if (ats_always_on || arm_smmu_ssids_in_use(&master->cd_table)) {
> /*
> * If a CD table has to be present then we need to run with ATS
> * on because we have to assume a PASID is using ATS. For
> @@ -3581,6 +3603,40 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
> kfree(master->streams);
> }
>
> +static int arm_smmu_master_prepare_ats(struct arm_smmu_master *master)
> +{
> + bool s1p = master->smmu->features & ARM_SMMU_FEAT_TRANS_S1;
> + unsigned int stu = __ffs(master->smmu->pgsize_bitmap);
> + struct pci_dev *pdev = to_pci_dev(master->dev);
> + int ret;
> +
> + if (!arm_smmu_ats_supported(master))
> + return 0;
> +
> + if (!pci_ats_always_on(pdev))
> + goto out_prepare;
> +
> + /*
> + * S1DSS is required for ATS to be always on for identity domain cases.
> + * However, the S1DSS field is ignored if !IDR0_S1P or !IDR1_SSIDSIZE.
> + */
> + if (!s1p || !master->smmu->ssid_bits) {
> + dev_info_once(master->dev,
> + "SMMU doesn't support ATS to be always on\n");
> + goto out_prepare;
> + }
> +
> + master->ats_always_on = true;
> +
> + ret = arm_smmu_alloc_cd_tables(master);
> + if (ret)
> + return ret;
> +
> +out_prepare:
> + pci_prepare_ats(pdev, stu);
> + return 0;
> +}
> +
> static struct iommu_device *arm_smmu_probe_device(struct device *dev)
> {
> int ret;
> @@ -3629,14 +3685,14 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
> smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
> master->stall_enabled = true;
>
> - if (dev_is_pci(dev)) {
> - unsigned int stu = __ffs(smmu->pgsize_bitmap);
> -
> - pci_prepare_ats(to_pci_dev(dev), stu);
> - }
> + ret = arm_smmu_master_prepare_ats(master);
> + if (ret)
> + goto err_disable_pasid;
>
> return &smmu->iommu;
>
> +err_disable_pasid:
> + arm_smmu_disable_pasid(master);
> err_free_master:
> kfree(master);
> return ERR_PTR(ret);
prev parent reply other threads:[~2026-03-08 20:52 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-06 23:41 [PATCH v3 0/3] Allow ATS to be always on for certain ATS-capable devices Nicolin Chen
2026-03-06 23:41 ` [PATCH v3 1/3] PCI: Allow ATS to be always on for CXL.cache capable devices Nicolin Chen
2026-03-08 20:49 ` Nirmoy Das
2026-03-08 20:53 ` Nirmoy Das
2026-03-09 11:48 ` Jonathan Cameron
2026-03-26 21:38 ` Bjorn Helgaas
2026-03-26 21:51 ` Jason Gunthorpe
2026-03-06 23:41 ` [PATCH v3 2/3] PCI: Allow ATS to be always on for pre-CXL devices Nicolin Chen
2026-03-08 20:50 ` Nirmoy Das
2026-03-08 20:54 ` Nirmoy Das
2026-03-09 11:50 ` Jonathan Cameron
2026-03-06 23:41 ` [PATCH v3 3/3] iommu/arm-smmu-v3: Allow ATS to be always on Nicolin Chen
2026-03-08 20:52 ` Nirmoy Das [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=54bbce49-e226-436a-bcf3-025e1455cc09@nvidia.com \
--to=nirmoyd@nvidia.com \
--cc=baolu.lu@linux.intel.com \
--cc=bhelgaas@google.com \
--cc=dan.j.williams@intel.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@nvidia.com \
--cc=jonathan.cameron@huawei.com \
--cc=joro@8bytes.org \
--cc=kevin.tian@intel.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=miko.lenczewski@arm.com \
--cc=nicolinc@nvidia.com \
--cc=praan@google.com \
--cc=robin.murphy@arm.com \
--cc=vsethi@nvidia.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox