From: Nicolin Chen <nicolinc@nvidia.com>
To: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>
Cc: Will Deacon <will@kernel.org>,
Robin Murphy <robin.murphy@arm.com>,
"Jason Gunthorpe" <jgg@ziepe.ca>,
"Joerg Roedel (AMD)" <joro@8bytes.org>,
"Thierry Reding" <thierry.reding@kernel.org>,
Krishna Reddy <vdumpa@nvidia.com>,
Jonathan Hunter <jonathanh@nvidia.com>,
Jean-Philippe Brucker <jpb@kernel.org>,
<linux-arm-kernel@lists.infradead.org>, <iommu@lists.linux.dev>,
<linux-kernel@vger.kernel.org>, <linux-tegra@vger.kernel.org>,
Peng Fan <peng.fan@nxp.com>
Subject: Re: [PATCH RFC v7 4/7] iommu/arm-smmu-v3: Use a per-domain CD table for non-PASID streams
Date: Fri, 30 Oct 2026 18:07:16 -0700 [thread overview]
Message-ID: <auU/RARTx8dv5mAE@nvidia.com> (raw)
In-Reply-To: <20261030-smmu-shared-sid-ver7-2026-10-26-v7-4-961726ebd6ab@Example.Com>
On Fri, Oct 30, 2026 at 08:09:36PM +0800, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
>
> The CD describes the domain context, so non-PASID devices attached to the
> same S1 domain do not need private copies of it. Store a CD table in
> struct arm_smmu_domain and make non-PASID streams point their STE at that
> table.
> @@ -3044,6 +3055,9 @@ static void arm_smmu_domain_free_paging(struct iommu_domain *domain)
> mutex_lock(&arm_smmu_asid_lock);
> xa_erase(&arm_smmu_asid_xa, smmu_domain->cd.asid);
> mutex_unlock(&arm_smmu_asid_lock);
> +
> + if (arm_smmu_cdtab_allocated(&smmu_domain->cd_table))
> + arm_smmu_free_domain_cd_tables(smmu_domain);
Same comments as previous: fold arm_smmu_cdtab_allocated in.
> @@ -3854,11 +3879,27 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev,
> return -EINVAL;
>
> if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
> - cdptr = arm_smmu_alloc_cd_ptr(master, IOMMU_NO_PASID);
> + /*
> + * Non-PASID devices use the domain's shared CD table. The
> + * private table is needed for substreams (PASID), for
> + * ATS-always-on (S1DSS), and for the stall model: CD.S is a
> + * per-CD property and cannot be shared by devices that
> + * terminate faults.
> + */
> + if (master->ats_always_on ||
> + master->stall_enabled ||
For readability, let's add a helper:
static bool arm_smmu_master_needs_private_cdtab(struct arm_smmu_master *master)
{
/* Reason 1 */
if (master->ats_always_on)
return true;
/* Reason 2 */
if (master->stall_enabled)
return true;
return false;
}
> @@ -3978,10 +4022,17 @@ int arm_smmu_set_pasid(struct arm_smmu_master *master,
> sid_domain->type != IOMMU_DOMAIN_BLOCKED)
> return -EINVAL;
>
> + first = !arm_smmu_ssids_in_use(&master->cd_table);
> cdptr = arm_smmu_alloc_cd_ptr(master, pasid);
> if (!cdptr)
> return -ENOMEM;
>
> + if (first) {
> + cd0ptr = arm_smmu_alloc_cd_ptr(master, IOMMU_NO_PASID);
> + if (!cd0ptr)
> + return -ENOMEM;
> + }
Maybe allocate cd0ptr in arm_smmu_alloc_cd_tables()?
We can also have a helper to share with arm_smmu_alloc_cd_ptr().
static int arm_smmu_alloc_cd_ptr_l2(struct arm_smmu_master *master, u32 ssid)
{
[...]
*l2ptr = dma_alloc_coherent(master->smmu->dev, sizeof(**l2ptr),
&l2ptr_dma, GFP_KERNEL);
if (!*l2ptr)
return -ENOMEM;
arm_smmu_write_cd_l1_desc(&cd_table->l2.l1tab[idx], l2ptr_dma);
return 0;
}
> @@ -3997,8 +4048,39 @@ int arm_smmu_set_pasid(struct arm_smmu_master *master,
> cd->data[0] |= cpu_to_le64(
> FIELD_PREP(CTXDESC_CD_0_ASID, smmu_domain->cd.asid));
>
> + /*
> + * The private CD table is taking over the STE now. Make sure its
> + * default substream matches the RID domain before the STE points at
> + * it, since non-PASID traffic follows CD[0].
> + */
> + if (first) {
> + if ((sid_domain->type & __IOMMU_DOMAIN_PAGING) &&
> + to_smmu_domain(sid_domain)->stage == ARM_SMMU_DOMAIN_S1) {
> + struct arm_smmu_cd cd0;
> +
> + arm_smmu_make_s1_cd(&cd0, master,
> + to_smmu_domain(sid_domain));
> + arm_smmu_write_cd_entry(master, IOMMU_NO_PASID,
> + cd0ptr, &cd0);
> + } else if (sid_domain->type == IOMMU_DOMAIN_IDENTITY ||
> + sid_domain->type == IOMMU_DOMAIN_BLOCKED) {
> + arm_smmu_clear_cd(master, IOMMU_NO_PASID);
> + } else {
> + WARN_ON(1);
> + }
> + }
> +
> arm_smmu_write_cd_entry(master, pasid, cdptr, cd);
> - arm_smmu_update_ste(master, sid_domain, state.ats_enabled);
> +
> + /*
> + * A repeat PASID attach leaves the STE on the private table with the
> + * same S1DSS/EATS. Only the first attach (or an ATS change) needs to
> + * rewrite it; arm_smmu_write_ste() would otherwise issue a redundant
> + * CMD_PREFETCH_CFG for every stream.
> + */
> + if (first || !master->cd_table.in_ste ||
> + master->ste_ats_enabled != state.ats_enabled)
> + arm_smmu_update_ste(master, sid_domain, state.ats_enabled);
master->cd_table.in_ste could be set when STE walks master's CD
table. Then, you don't need to check "first". And move the guard
back into arm_smmu_update_ste.
> @@ -4017,35 +4099,48 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
> .old_domain = old_domain,
> .ssid = pasid,
> };
> + int ret = 0;
>
> mutex_lock(&arm_smmu_asid_lock);
> arm_smmu_attach_prepare_invs(&state, NULL);
> + /*
> + * Unlink the PASID from the domain before invalidating its CD.
> + * arm_smmu_mm_release() walks this list with only devices_lock held
> + * and writes a valid, faulting CD; unlinking first prevents it from
> + * resurrecting used_ssids after the last PASID is detached.
> + */
> + state.old_master_domain = arm_smmu_remove_master_domain(
> + master, &smmu_domain->domain, pasid);
> arm_smmu_clear_cd(master, pasid);
Sashiko pointed out that it needs devices_lock here.
> /*
> - * 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.
> + * When the last user of the CD table goes away, switch the STE back to
> + * the domain's shared CD table (S1) or to the native bypass/abort STE
> + * (identity/blocked), by re-attaching its sid_domain. The private CD
> + * table is kept allocated for reuse and freed when the device goes
> + * away.
> */
> if (!arm_smmu_ssids_in_use(&master->cd_table)) {
> struct iommu_domain *sid_domain =
> iommu_driver_get_domain_for_dev(master->dev);
> bool ats_always_on = master->ats_always_on &&
> sid_domain->type != IOMMU_DOMAIN_BLOCKED;
> - bool downgrade = sid_domain->type == IOMMU_DOMAIN_IDENTITY ||
> - sid_domain->type == IOMMU_DOMAIN_BLOCKED;
> + bool s1 = (sid_domain->type & __IOMMU_DOMAIN_PAGING) &&
> + to_smmu_domain(sid_domain)->stage ==
> + ARM_SMMU_DOMAIN_S1;
>
> - if (!ats_always_on && downgrade)
> - sid_domain->ops->attach_dev(sid_domain, dev,
> - sid_domain);
> + if (!ats_always_on &&
> + (s1 || sid_domain->type == IOMMU_DOMAIN_IDENTITY ||
> + sid_domain->type == IOMMU_DOMAIN_BLOCKED))
> + ret = sid_domain->ops->attach_dev(sid_domain, dev,
> + sid_domain);
This covers almost all the possible domain types here. So, just
drop that downgrade, and it should cover the other cases:
if (!ats_always_on)
Nicolin
next prev parent reply other threads:[~2026-10-31 1:07 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 12:09 [PATCH RFC v7 0/7] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
2026-10-30 12:09 ` [PATCH RFC v7 1/7] iommu/arm-smmu-v3: Allocate streams individually Peng Fan (OSS)
2026-10-30 12:09 ` [PATCH RFC v7 2/7] iommu/arm-smmu-v3: Delay stream allocation to inside the mutex Peng Fan (OSS)
2026-10-30 12:09 ` [PATCH RFC v7 3/7] iommu/arm-smmu-v3: Plumb the CD table into the CD helpers Peng Fan (OSS)
2026-10-30 20:54 ` Nicolin Chen
2026-10-30 12:09 ` [PATCH RFC v7 4/7] iommu/arm-smmu-v3: Use a per-domain CD table for non-PASID streams Peng Fan (OSS)
2026-10-31 1:07 ` Nicolin Chen [this message]
2026-10-30 12:09 ` [PATCH RFC v7 5/7] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
2026-10-30 12:09 ` [PATCH RFC v7 6/7] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group Peng Fan (OSS)
2026-10-30 12:09 ` [PATCH RFC v7 7/7] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating Peng Fan (OSS)
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=auU/RARTx8dv5mAE@nvidia.com \
--to=nicolinc@nvidia.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@ziepe.ca \
--cc=jonathanh@nvidia.com \
--cc=joro@8bytes.org \
--cc=jpb@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=peng.fan@nxp.com \
--cc=peng.fan@oss.nxp.com \
--cc=robin.murphy@arm.com \
--cc=thierry.reding@kernel.org \
--cc=vdumpa@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
all inboxes | Powered by JetHome®