mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Peng Fan <peng.fan@linux.dev>
To: Nicolin Chen <nicolinc@nvidia.com>
Cc: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>,
	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: Sat, 7 Nov 2026 21:08:18 +0800	[thread overview]
Message-ID: <au8iwj68zXO1us_8@fedora> (raw)
In-Reply-To: <auU/RARTx8dv5mAE@nvidia.com>

On Fri, Oct 30, 2026 at 06:07:16PM -0700, Nicolin Chen wrote:
>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)

Thanks, I agree the reachable set here is only S1/identity/blocked, and the
outer condition is effectively !ats_always_on. The separate downgrade
check is gone in v8.

However, I don't think we can fold the inner check into a single
attach_dev() call for all cases. For an S1 RID domain this is a PASID
teardown path, and arm_smmu_attach_dev() -> arm_smmu_attach_prepare() can
allocate with GFP_KERNEL and return -ENOMEM. The IOMMU core requires
set_dev_pasid() not to fail here, so that would trip the WARN_ON() in
iommu_remove_dev_pasid(). It also flushes the RID ATC unnecessarily.

So v8 keeps the outer !ats_always_on gate, but handles S1 directly:

if (!ats_always_on) {
        if (S1 RID domain) {
                /*
                 * Switch the STE directly to sid_domain->cd_table.
                 * Skip this for stalling masters, which must stay on
                 * their private CD table.
                 */
        } else {
                /* identity/blocked: use attach_dev() */
        }
}

The private CD table is kept allocated for reuse and freed at device
release. This keeps the teardown non-failing while still using the
!ats_always_on condition you suggested.

Or I may miss something.

Thanks
Peng
>
>Nicolin
>

  reply	other threads:[~2026-11-07 13:08 UTC|newest]

Thread overview: 14+ 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
2026-11-07 13:08     ` Peng Fan [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-11-02 18:31   ` Nicolin Chen
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)
2026-11-02 18:49   ` Nicolin Chen
2026-11-07 13:14     ` Peng Fan

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=au8iwj68zXO1us_8@fedora \
    --to=peng.fan@linux.dev \
    --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=nicolinc@nvidia.com \
    --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®