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:08 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.