[PATCH v3 01/32] iommu: introduce iova_to_phys_length in iommu_domain_ops
From: Guanghui Feng
Date: Wed Jun 03 2026 - 12:09:47 EST
Add iova_to_phys_length callback to struct iommu_domain_ops alongside
the existing iova_to_phys. The new callback returns both the physical
address and the PTE mapping page size in a single page table walk.
Add iommu_iova_to_phys_length() core function that:
- Checks ops->iova_to_phys_length first (preferred path)
- Falls back to ops->iova_to_phys for unmigrated drivers
This enables callers like VFIO to efficiently traverse IOVA space
by actual mapping granularity instead of fixed PAGE_SIZE steps.
Signed-off-by: Guanghui Feng <guanghuifeng@xxxxxxxxxxxxxxxxx>
Acked-by: Shiqiang Zhang <shiyu.zsq@xxxxxxxxxxxxxxxxx>
Acked-by: Simon Guo <wei.guo.simon@xxxxxxxxxxxxxxxxx>
---
drivers/iommu/iommu.c | 50 ++++++++++++++++++++++++++++++++++++++-----
include/linux/iommu.h | 9 ++++++++
2 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index d1a9e713d3a0..320ea13488e7 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -2545,15 +2545,55 @@ void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
}
EXPORT_SYMBOL_GPL(iommu_detach_group);
-phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
+/**
+ * iommu_iova_to_phys_length - Translate IOVA and return mapping page size
+ * @domain: IOMMU domain to query
+ * @iova: IO virtual address to translate
+ * @mapped_length: Output parameter for the PTE page size (e.g. 4KB/2MB/1GB)
+ *
+ * Like iommu_iova_to_phys() but additionally returns the page size of the
+ * PTE mapping at @iova through @mapped_length.
+ *
+ * Return: The physical address for the given IOVA, or PHYS_ADDR_MAX if no
+ * translation exists.
+ */
+phys_addr_t iommu_iova_to_phys_length(struct iommu_domain *domain,
+ dma_addr_t iova,
+ size_t *mapped_length)
{
- if (domain->type == IOMMU_DOMAIN_IDENTITY)
+ phys_addr_t phys;
+
+ if (domain->type == IOMMU_DOMAIN_IDENTITY) {
+ if (mapped_length)
+ *mapped_length = PAGE_SIZE;
return iova;
+ }
- if (domain->type == IOMMU_DOMAIN_BLOCKED)
- return 0;
+ if (mapped_length)
+ *mapped_length = 0;
+
+ if (domain->ops->iova_to_phys_length)
+ return domain->ops->iova_to_phys_length(domain, iova, mapped_length);
+
+ /* Fallback to legacy iova_to_phys without length info */
+ if (!domain->ops->iova_to_phys)
+ return PHYS_ADDR_MAX;
+
+ phys = domain->ops->iova_to_phys(domain, iova);
+ if (!phys)
+ return PHYS_ADDR_MAX;
+
+ if (mapped_length)
+ *mapped_length = PAGE_SIZE;
+ return phys;
+}
+EXPORT_SYMBOL_GPL(iommu_iova_to_phys_length);
+
+phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
+{
+ phys_addr_t phys = iommu_iova_to_phys_length(domain, iova, NULL);
- return domain->ops->iova_to_phys(domain, iova);
+ return (phys == PHYS_ADDR_MAX) ? 0 : phys;
}
EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index e587d4ac4d33..19da84c2922c 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -747,6 +747,9 @@ struct iommu_ops {
* invalidation requests. The driver data structure
* must be defined in include/uapi/linux/iommufd.h
* @iova_to_phys: translate iova to physical address
+ * @iova_to_phys_length: translate iova to physical address and additionally
+ * return the page size of the PTE mapping at @iova
+ * through @mapped_length.
* @enforce_cache_coherency: Prevent any kind of DMA from bypassing IOMMU_CACHE,
* including no-snoop TLPs on PCIe or other platform
* specific mechanisms.
@@ -776,6 +779,9 @@ struct iommu_domain_ops {
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain,
dma_addr_t iova);
+ phys_addr_t (*iova_to_phys_length)(struct iommu_domain *domain,
+ dma_addr_t iova,
+ size_t *mapped_length);
bool (*enforce_cache_coherency)(struct iommu_domain *domain);
int (*set_pgtable_quirks)(struct iommu_domain *domain,
@@ -930,6 +936,9 @@ extern ssize_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, unsigned int nents,
int prot, gfp_t gfp);
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
+extern phys_addr_t iommu_iova_to_phys_length(struct iommu_domain *domain,
+ dma_addr_t iova,
+ size_t *mapped_length);
extern void iommu_set_fault_handler(struct iommu_domain *domain,
iommu_fault_handler_t handler, void *token);
--
2.43.7