[PATCH 1/2] iommu: Add __iommu_debug_unmap_phys() to drop refs by physical address

From: Yuanhe Shu

Date: Thu Aug 27 2026 - 11:03:07 EST


The IOMMU_DEBUG_PAGEALLOC sanitizer takes a reference on each page at
iommu_map() time, keyed by physical address, and the only way to drop
them is the IOVA based iommu_debug_unmap_begin()/end(). A path that
tears down a page table with mappings still installed has no IOVA to
unmap with, so add a physical address based counterpart of
__iommu_debug_map() for those paths, sharing the counting loop through
__iommu_debug_update_phys().

Export the iommu_debug_initialized static key, as the generic_pt format
code using these helpers can be built as a module.

Signed-off-by: Yuanhe Shu <xiangzao@xxxxxxxxxxxxxxxxx>
---
Build tested as built-in and as a module (AMD_IOMMU=n,
CONFIG_IOMMU_PT_AMDV1=m).

drivers/iommu/iommu-debug-pagealloc.c | 30 ++++++++++++++++++++++++---
drivers/iommu/iommu-priv.h | 24 +++++++++++++++++++++
2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/iommu-debug-pagealloc.c b/drivers/iommu/iommu-debug-pagealloc.c
index 80164df5bab1..a2e55164e6a4 100644
--- a/drivers/iommu/iommu-debug-pagealloc.c
+++ b/drivers/iommu/iommu-debug-pagealloc.c
@@ -15,6 +15,8 @@

static bool needed;
DEFINE_STATIC_KEY_FALSE(iommu_debug_initialized);
+/* The generic_pt format code using the key can be built as a module */
+EXPORT_SYMBOL_GPL(iommu_debug_initialized);

struct iommu_debug_metadata {
atomic_t ref;
@@ -96,7 +98,8 @@ void __iommu_debug_check_unmapped(const struct page *page, int numpages)
}
}

-void __iommu_debug_map(struct iommu_domain *domain, phys_addr_t phys, size_t size)
+static void __iommu_debug_update_phys(struct iommu_domain *domain,
+ phys_addr_t phys, size_t size, bool inc)
{
size_t off, end;
size_t page_size = iommu_debug_page_size(domain);
@@ -104,9 +107,30 @@ void __iommu_debug_map(struct iommu_domain *domain, phys_addr_t phys, size_t siz
if (WARN_ON(!phys || check_add_overflow(phys, size, &end)))
return;

- for (off = 0 ; off < size ; off += page_size)
- iommu_debug_inc_page(phys + off);
+ for (off = 0 ; off < size ; off += page_size) {
+ if (inc)
+ iommu_debug_inc_page(phys + off);
+ else
+ iommu_debug_dec_page(phys + off);
+ }
+}
+
+void __iommu_debug_map(struct iommu_domain *domain, phys_addr_t phys, size_t size)
+{
+ __iommu_debug_update_phys(domain, phys, size, true);
+}
+
+/*
+ * Physical address counterpart of __iommu_debug_map(), for teardown paths
+ * that destroy mapped entries without an IOVA. The OAs must have been
+ * accounted by a prior iommu_map().
+ */
+void __iommu_debug_unmap_phys(struct iommu_domain *domain, phys_addr_t phys,
+ size_t size)
+{
+ __iommu_debug_update_phys(domain, phys, size, false);
}
+EXPORT_SYMBOL_GPL(__iommu_debug_unmap_phys);

static void __iommu_debug_update_iova(struct iommu_domain *domain,
unsigned long iova, size_t size, bool inc)
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index aaffad5854fc..12528a40bcd8 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -71,11 +71,18 @@ int iommu_replace_device_pasid(struct iommu_domain *domain,

void __iommu_debug_map(struct iommu_domain *domain, phys_addr_t phys,
size_t size);
+void __iommu_debug_unmap_phys(struct iommu_domain *domain, phys_addr_t phys,
+ size_t size);
void __iommu_debug_unmap_begin(struct iommu_domain *domain,
unsigned long iova, size_t size);
void __iommu_debug_unmap_end(struct iommu_domain *domain,
unsigned long iova, size_t size, size_t unmapped);

+static inline bool iommu_debug_pagealloc_enabled(void)
+{
+ return static_branch_unlikely(&iommu_debug_initialized);
+}
+
static inline void iommu_debug_map(struct iommu_domain *domain,
phys_addr_t phys, size_t size)
{
@@ -83,6 +90,13 @@ static inline void iommu_debug_map(struct iommu_domain *domain,
__iommu_debug_map(domain, phys, size);
}

+static inline void iommu_debug_unmap_phys(struct iommu_domain *domain,
+ phys_addr_t phys, size_t size)
+{
+ if (static_branch_unlikely(&iommu_debug_initialized))
+ __iommu_debug_unmap_phys(domain, phys, size);
+}
+
static inline void iommu_debug_unmap_begin(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
@@ -101,11 +115,21 @@ static inline void iommu_debug_unmap_end(struct iommu_domain *domain,
void iommu_debug_init(void);

#else
+static inline bool iommu_debug_pagealloc_enabled(void)
+{
+ return false;
+}
+
static inline void iommu_debug_map(struct iommu_domain *domain,
phys_addr_t phys, size_t size)
{
}

+static inline void iommu_debug_unmap_phys(struct iommu_domain *domain,
+ phys_addr_t phys, size_t size)
+{
+}
+
static inline void iommu_debug_unmap_begin(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
--
2.43.5