The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v1 0/2] Fix some nits in nested domain allocation
@ 2026-07-09 19:57 Tycho Andersen
  2026-07-09 19:57 ` [PATCH v1 1/2] iommu/amd: Fix IRQ unsafe locking in gdom allocation Tycho Andersen
  2026-07-09 19:57 ` [PATCH v1 2/2] iommu/amd: Fix nested domain leak Tycho Andersen
  0 siblings, 2 replies; 5+ messages in thread
From: Tycho Andersen @ 2026-07-09 19:57 UTC (permalink / raw)
  To: Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
	Will Deacon, Robin Murphy
  Cc: Joerg Roedel, iommu, linux-kernel, Tycho Andersen (AMD)

From: "Tycho Andersen (AMD)" <tycho@kernel.org>

Here are a couple of small things I ran across while testing some code.

Thanks,

Tycho

Tycho Andersen (AMD) (2):
  iommu/amd: Fix IRQ unsafe locking in gdom allocation
  iommu/amd: Fix nested domain leak

 drivers/iommu/amd/nested.c | 34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)


base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
-- 
2.55.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v1 1/2] iommu/amd: Fix IRQ unsafe locking in gdom allocation
  2026-07-09 19:57 [PATCH v1 0/2] Fix some nits in nested domain allocation Tycho Andersen
@ 2026-07-09 19:57 ` Tycho Andersen
  2026-07-10 15:03   ` Ankit Soni
  2026-07-09 19:57 ` [PATCH v1 2/2] iommu/amd: Fix nested domain leak Tycho Andersen
  1 sibling, 1 reply; 5+ messages in thread
From: Tycho Andersen @ 2026-07-09 19:57 UTC (permalink / raw)
  To: Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
	Will Deacon, Robin Murphy
  Cc: Joerg Roedel, iommu, linux-kernel, Tycho Andersen (AMD)

From: "Tycho Andersen (AMD)" <tycho@kernel.org>

Lockdep complains:

  [  259.410489] =====================================================
  [  259.417287] WARNING: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected
  [  259.424667] 7.0.0-g51db1d8d2113 #54 Not tainted
  [  259.429718] -----------------------------------------------------
  [  259.436516] qemu-system-x86/10143 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire:
  [  259.444670] ff3b2b1c60305170 (&xa->xa_lock#25){+.+.}-{3:3}, at: __domain_flush_pages+0x17c/0x4b0
  [  259.454485]
                 and this task is already holding:
  [  259.460991] ff3b2b1c98504cc0 (&domain->lock){-.-.}-{3:3}, at: amd_iommu_iotlb_sync+0x25/0x60
  [  259.470408] which would create a new lock dependency:
  [  259.476041]  (&domain->lock){-.-.}-{3:3} -> (&xa->xa_lock#25){+.+.}-{3:3}
  [  259.483615]
                 but this new dependency connects a HARDIRQ-irq-safe lock:
  [  259.492447]  (&domain->lock){-.-.}-{3:3}
  [  259.492449]
                 ... which became HARDIRQ-irq-safe at:
  [  259.503705]   lock_acquire+0xb6/0x2e0
  [  259.507790]   _raw_spin_lock_irqsave+0x3e/0x60
  [  259.512748]   amd_iommu_flush_iotlb_all+0x20/0x50
  [  259.517996]   iommu_dma_free_iova.isra.0+0x1b8/0x1e0
  [  259.523534]   __iommu_dma_unmap+0xc2/0x140
  [  259.528100]   iommu_dma_unmap_phys+0x55/0xc0
  [  259.532863]   dma_unmap_phys+0x274/0x2e0
  [  259.537238]   dma_unmap_page_attrs+0x17/0x30
  [  259.542000]   nvme_unmap_data+0x13e/0x280
  [  259.546473]   nvme_pci_complete_batch+0x45/0x70
  [  259.551524]   nvme_irq+0x83/0x90
  [  259.555123]   __handle_irq_event_percpu+0x92/0x360
  [  259.560466]   handle_irq_event+0x39/0x80
  [  259.564841]   handle_edge_irq+0xb2/0x1a0
  [  259.569214]   __common_interrupt+0x4e/0x130
  [  259.573882]   common_interrupt+0x88/0xa0
  [  259.578256]   asm_common_interrupt+0x27/0x40
  [  259.583019]   cpuidle_enter_state+0x119/0x5d0
  [  259.587877]   cpuidle_enter+0x2e/0x50
  [  259.591962]   do_idle+0x153/0x2c0
  [  259.595657]   cpu_startup_entry+0x29/0x30
  [  259.600128]   start_secondary+0x118/0x150
  [  259.604601]   common_startup_64+0x13e/0x141
  [  259.609266]
                 to a HARDIRQ-irq-unsafe lock:
  [  259.615384]  (&xa->xa_lock#25){+.+.}-{3:3}
  [  259.615386]
                 ... which became HARDIRQ-irq-unsafe at:
  [  259.627039] ...
  [  259.627039]   lock_acquire+0xb6/0x2e0
  [  259.633071]   _raw_spin_lock+0x2f/0x50
  [  259.637250]   amd_iommu_alloc_domain_nested+0x140/0x3c0
  [  259.643078]   iommufd_hwpt_alloc+0x272/0x800 [iommufd]
  [  259.648813]   iommufd_fops_ioctl+0x14e/0x200 [iommufd]
  [  259.654547]   __x64_sys_ioctl+0x9d/0xf0
  ...

Since amd_iommu_domain_flush_pages() necessarily holds domain->lock to do the
flush, switch the allocation side in gdom_info_load_or_alloc_locked() to
HARDIRQ-safe allocation. The IOMMU_DESTROY->free path has the same issue,
so switch that path to HARDIRQ-safe locking as well.

Fixes: 757d2b1fdf5b ("iommu/amd: Introduce gDomID-to-hDomID Mapping and handle parent domain invalidation")
Signed-off-by: Tycho Andersen (AMD) <tycho@kernel.org>
---
 drivers/iommu/amd/nested.c | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c
index 5b902598e68a..5c9405223f91 100644
--- a/drivers/iommu/amd/nested.c
+++ b/drivers/iommu/amd/nested.c
@@ -59,7 +59,9 @@ static int validate_gdte_nested(struct iommu_hwpt_amd_guest *gdte)
 	return 0;
 }
 
-static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long index)
+static void *gdom_info_load_or_alloc_locked(struct xarray *xa,
+					    unsigned long index,
+					    unsigned long *flags)
 {
 	struct guest_domain_mapping_info *elm, *res;
 
@@ -67,13 +69,13 @@ static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long ind
 	if (elm)
 		return elm;
 
-	xa_unlock(xa);
+	xa_unlock_irqrestore(xa, *flags);
 	elm = kzalloc_obj(struct guest_domain_mapping_info);
-	xa_lock(xa);
+	xa_lock_irqsave(xa, *flags);
 	if (!elm)
 		return ERR_PTR(-ENOMEM);
 
-	res = __xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL);
+	res = __xa_cmpxchg(xa, index, NULL, elm, GFP_ATOMIC);
 	if (xa_is_err(res))
 		res = ERR_PTR(xa_err(res));
 
@@ -95,6 +97,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
 			      const struct iommu_user_data *user_data)
 {
 	int ret;
+	unsigned long irqflags;
 	struct nested_domain *ndom;
 	struct guest_domain_mapping_info *gdom_info;
 	struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
@@ -136,11 +139,12 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
 	 * keep track of the gDomID mapping. When the S2 is changed, the INVALIDATE_IOMMU_PAGES
 	 * command must be issued for each hDomID in the xarray.
 	 */
-	xa_lock(&aviommu->gdomid_array);
+	xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
 
-	gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, ndom->gdom_id);
+	gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array,
+						   ndom->gdom_id, &irqflags);
 	if (IS_ERR(gdom_info)) {
-		xa_unlock(&aviommu->gdomid_array);
+		xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
 		ret = PTR_ERR(gdom_info);
 		goto out_err;
 	}
@@ -148,7 +152,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
 	/* Check if gDomID exist */
 	if (refcount_inc_not_zero(&gdom_info->users)) {
 		ndom->gdom_info = gdom_info;
-		xa_unlock(&aviommu->gdomid_array);
+		xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
 
 		pr_debug("%s: Found gdom_id=%#x, hdom_id=%#x\n",
 			  __func__, ndom->gdom_id, gdom_info->hdom_id);
@@ -161,7 +165,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
 	if (gdom_info->hdom_id <= 0) {
 		__xa_cmpxchg(&aviommu->gdomid_array,
 			     ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC);
-		xa_unlock(&aviommu->gdomid_array);
+		xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
 		ret = -ENOSPC;
 		goto out_err_gdom_info;
 	}
@@ -169,7 +173,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
 	ndom->gdom_info = gdom_info;
 	refcount_set(&gdom_info->users, 1);
 
-	xa_unlock(&aviommu->gdomid_array);
+	xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
 
 	pr_debug("%s: Allocate gdom_id=%#x, hdom_id=%#x\n",
 		 __func__, ndom->gdom_id, gdom_info->hdom_id);
@@ -257,14 +261,15 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev,
 
 static void nested_domain_free(struct iommu_domain *dom)
 {
+	unsigned long irqflags;
 	struct guest_domain_mapping_info *curr;
 	struct nested_domain *ndom = to_ndomain(dom);
 	struct amd_iommu_viommu *aviommu = ndom->viommu;
 
-	xa_lock(&aviommu->gdomid_array);
+	xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
 
 	if (!refcount_dec_and_test(&ndom->gdom_info->users)) {
-		xa_unlock(&aviommu->gdomid_array);
+		xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
 		return;
 	}
 
@@ -275,7 +280,7 @@ static void nested_domain_free(struct iommu_domain *dom)
 	curr = __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id,
 			    ndom->gdom_info, NULL, GFP_ATOMIC);
 
-	xa_unlock(&aviommu->gdomid_array);
+	xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
 	if (WARN_ON(!curr || xa_err(curr)))
 		return;
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v1 2/2] iommu/amd: Fix nested domain leak
  2026-07-09 19:57 [PATCH v1 0/2] Fix some nits in nested domain allocation Tycho Andersen
  2026-07-09 19:57 ` [PATCH v1 1/2] iommu/amd: Fix IRQ unsafe locking in gdom allocation Tycho Andersen
@ 2026-07-09 19:57 ` Tycho Andersen
  2026-07-10 15:06   ` Ankit Soni
  1 sibling, 1 reply; 5+ messages in thread
From: Tycho Andersen @ 2026-07-09 19:57 UTC (permalink / raw)
  To: Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
	Will Deacon, Robin Murphy
  Cc: Joerg Roedel, iommu, linux-kernel, Tycho Andersen (AMD)

From: "Tycho Andersen (AMD)" <tycho@kernel.org>

A couple of runs of different AI tools have generated something like the
following bug report:

    In nested_domain_free(), when refcount_dec_and_test() returns false
    (other nested domains still reference the same gdom_info), the function
    returns without calling kfree(ndom), leaking the nested_domain
    structure. This problem wasn't introduced by this patch, but exists in
    the code from commit 757d2b1fdf5b that the patch modifies. Each
    nested_domain (ndom) is allocated individually in
    amd_iommu_alloc_domain_nested() via kzalloc_obj(*ndom). The .free
    callback is the sole point responsible for freeing this domain. When
    the refcount is > 0, only the xa_unlock_irqrestore is performed and the
    function returns, leaving ndom permanently allocated. This leak occurs
    every time a nested domain sharing a gDomID is destroyed while other
    domains still use that gDomID.

There is a similar leak later in this function in the WARN_ON() test when
the mapping is already NULL. Switch to a RAII-based cleanup for ndom, since
it should always be freed in this function.

Fixes: 757d2b1fdf5b ("iommu/amd: Introduce gDomID-to-hDomID Mapping and handle parent domain invalidation")
Signed-off-by: Tycho Andersen (AMD) <tycho@kernel.org>
---
 drivers/iommu/amd/nested.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c
index 5c9405223f91..63b53b29e029 100644
--- a/drivers/iommu/amd/nested.c
+++ b/drivers/iommu/amd/nested.c
@@ -263,7 +263,7 @@ static void nested_domain_free(struct iommu_domain *dom)
 {
 	unsigned long irqflags;
 	struct guest_domain_mapping_info *curr;
-	struct nested_domain *ndom = to_ndomain(dom);
+	struct nested_domain *ndom __free(kfree) = to_ndomain(dom);
 	struct amd_iommu_viommu *aviommu = ndom->viommu;
 
 	xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
@@ -290,7 +290,6 @@ static void nested_domain_free(struct iommu_domain *dom)
 
 	amd_iommu_pdom_id_free(ndom->gdom_info->hdom_id);
 	kfree(curr);
-	kfree(ndom);
 }
 
 static const struct iommu_domain_ops nested_domain_ops = {
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v1 1/2] iommu/amd: Fix IRQ unsafe locking in gdom allocation
  2026-07-09 19:57 ` [PATCH v1 1/2] iommu/amd: Fix IRQ unsafe locking in gdom allocation Tycho Andersen
@ 2026-07-10 15:03   ` Ankit Soni
  0 siblings, 0 replies; 5+ messages in thread
From: Ankit Soni @ 2026-07-10 15:03 UTC (permalink / raw)
  To: Tycho Andersen
  Cc: Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
	Will Deacon, Robin Murphy, Joerg Roedel, iommu, linux-kernel

On Thu, Jul 09, 2026 at 01:57:35PM -0600, Tycho Andersen wrote:
> From: "Tycho Andersen (AMD)" <tycho@kernel.org>
> 
> Lockdep complains:
> 
>   [  259.410489] =====================================================
>   [  259.417287] WARNING: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected
>   [  259.424667] 7.0.0-g51db1d8d2113 #54 Not tainted
>   [  259.429718] -----------------------------------------------------
>   [  259.436516] qemu-system-x86/10143 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire:
>   [  259.444670] ff3b2b1c60305170 (&xa->xa_lock#25){+.+.}-{3:3}, at: __domain_flush_pages+0x17c/0x4b0
>   [  259.454485]
>                  and this task is already holding:
>   [  259.460991] ff3b2b1c98504cc0 (&domain->lock){-.-.}-{3:3}, at: amd_iommu_iotlb_sync+0x25/0x60
>   [  259.470408] which would create a new lock dependency:
>   [  259.476041]  (&domain->lock){-.-.}-{3:3} -> (&xa->xa_lock#25){+.+.}-{3:3}
>   [  259.483615]
>                  but this new dependency connects a HARDIRQ-irq-safe lock:
>   [  259.492447]  (&domain->lock){-.-.}-{3:3}
>   [  259.492449]
>                  ... which became HARDIRQ-irq-safe at:
>   [  259.503705]   lock_acquire+0xb6/0x2e0
>   [  259.507790]   _raw_spin_lock_irqsave+0x3e/0x60
>   [  259.512748]   amd_iommu_flush_iotlb_all+0x20/0x50
>   [  259.517996]   iommu_dma_free_iova.isra.0+0x1b8/0x1e0
>   [  259.523534]   __iommu_dma_unmap+0xc2/0x140
>   [  259.528100]   iommu_dma_unmap_phys+0x55/0xc0
>   [  259.532863]   dma_unmap_phys+0x274/0x2e0
>   [  259.537238]   dma_unmap_page_attrs+0x17/0x30
>   [  259.542000]   nvme_unmap_data+0x13e/0x280
>   [  259.546473]   nvme_pci_complete_batch+0x45/0x70
>   [  259.551524]   nvme_irq+0x83/0x90
>   [  259.555123]   __handle_irq_event_percpu+0x92/0x360
>   [  259.560466]   handle_irq_event+0x39/0x80
>   [  259.564841]   handle_edge_irq+0xb2/0x1a0
>   [  259.569214]   __common_interrupt+0x4e/0x130
>   [  259.573882]   common_interrupt+0x88/0xa0
>   [  259.578256]   asm_common_interrupt+0x27/0x40
>   [  259.583019]   cpuidle_enter_state+0x119/0x5d0
>   [  259.587877]   cpuidle_enter+0x2e/0x50
>   [  259.591962]   do_idle+0x153/0x2c0
>   [  259.595657]   cpu_startup_entry+0x29/0x30
>   [  259.600128]   start_secondary+0x118/0x150
>   [  259.604601]   common_startup_64+0x13e/0x141
>   [  259.609266]
>                  to a HARDIRQ-irq-unsafe lock:
>   [  259.615384]  (&xa->xa_lock#25){+.+.}-{3:3}
>   [  259.615386]
>                  ... which became HARDIRQ-irq-unsafe at:
>   [  259.627039] ...
>   [  259.627039]   lock_acquire+0xb6/0x2e0
>   [  259.633071]   _raw_spin_lock+0x2f/0x50
>   [  259.637250]   amd_iommu_alloc_domain_nested+0x140/0x3c0
>   [  259.643078]   iommufd_hwpt_alloc+0x272/0x800 [iommufd]
>   [  259.648813]   iommufd_fops_ioctl+0x14e/0x200 [iommufd]
>   [  259.654547]   __x64_sys_ioctl+0x9d/0xf0
>   ...
> 
> Since amd_iommu_domain_flush_pages() necessarily holds domain->lock to do the
> flush, switch the allocation side in gdom_info_load_or_alloc_locked() to
> HARDIRQ-safe allocation. The IOMMU_DESTROY->free path has the same issue,
> so switch that path to HARDIRQ-safe locking as well.
> 
> Fixes: 757d2b1fdf5b ("iommu/amd: Introduce gDomID-to-hDomID Mapping and handle parent domain invalidation")
> Signed-off-by: Tycho Andersen (AMD) <tycho@kernel.org>

LGTM.

Reviewed-by: Ankit Soni <Ankit.Soni@amd.com>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v1 2/2] iommu/amd: Fix nested domain leak
  2026-07-09 19:57 ` [PATCH v1 2/2] iommu/amd: Fix nested domain leak Tycho Andersen
@ 2026-07-10 15:06   ` Ankit Soni
  0 siblings, 0 replies; 5+ messages in thread
From: Ankit Soni @ 2026-07-10 15:06 UTC (permalink / raw)
  To: Tycho Andersen
  Cc: Joerg Roedel (AMD), Suravee Suthikulpanit, Vasant Hegde,
	Will Deacon, Robin Murphy, Joerg Roedel, iommu, linux-kernel

On Thu, Jul 09, 2026 at 01:57:36PM -0600, Tycho Andersen wrote:
> From: "Tycho Andersen (AMD)" <tycho@kernel.org>
> 
> A couple of runs of different AI tools have generated something like the
> following bug report:
> 
>     In nested_domain_free(), when refcount_dec_and_test() returns false
>     (other nested domains still reference the same gdom_info), the function
>     returns without calling kfree(ndom), leaking the nested_domain
>     structure. This problem wasn't introduced by this patch, but exists in
>     the code from commit 757d2b1fdf5b that the patch modifies. Each
>     nested_domain (ndom) is allocated individually in
>     amd_iommu_alloc_domain_nested() via kzalloc_obj(*ndom). The .free
>     callback is the sole point responsible for freeing this domain. When
>     the refcount is > 0, only the xa_unlock_irqrestore is performed and the
>     function returns, leaving ndom permanently allocated. This leak occurs
>     every time a nested domain sharing a gDomID is destroyed while other
>     domains still use that gDomID.
> 
> There is a similar leak later in this function in the WARN_ON() test when
> the mapping is already NULL. Switch to a RAII-based cleanup for ndom, since
> it should always be freed in this function.
> 
> Fixes: 757d2b1fdf5b ("iommu/amd: Introduce gDomID-to-hDomID Mapping and handle parent domain invalidation")
> Signed-off-by: Tycho Andersen (AMD) <tycho@kernel.org>

Reviewed-by: Ankit Soni <Ankit.Soni@amd.com>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-07-10 15:07 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 19:57 [PATCH v1 0/2] Fix some nits in nested domain allocation Tycho Andersen
2026-07-09 19:57 ` [PATCH v1 1/2] iommu/amd: Fix IRQ unsafe locking in gdom allocation Tycho Andersen
2026-07-10 15:03   ` Ankit Soni
2026-07-09 19:57 ` [PATCH v1 2/2] iommu/amd: Fix nested domain leak Tycho Andersen
2026-07-10 15:06   ` Ankit Soni

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox