[PATCH v2 02/30] iommu/io-pgtable-arm: introduce iova_to_phys_length in io_pgtable_ops

From: Guanghui Feng

Date: Tue Jun 02 2026 - 06:49:54 EST


Add iova_to_phys_length to struct io_pgtable_ops alongside iova_to_phys.
Implement in ARM LPAE backend: returns ARM_LPAE_BLOCK_SIZE at the resolved level.
The old iova_to_phys is kept as a thin wrapper for backward compat.

Signed-off-by: Guanghui Feng <guanghuifeng@xxxxxxxxxxxxxxxxx>
---
drivers/iommu/io-pgtable-arm.c | 23 +++++++++++++++++++++--
include/linux/io-pgtable.h | 7 +++++++
2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 0208e5897c29..f33a86fa0f6c 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -731,8 +731,21 @@ static int visit_iova_to_phys(struct io_pgtable_walk_data *walk_data, int lvl,
return 0;
}

+static phys_addr_t arm_lpae_iova_to_phys_length(struct io_pgtable_ops *ops,
+ unsigned long iova,
+ size_t *mapped_length);
+
static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
unsigned long iova)
+{
+ phys_addr_t phys = arm_lpae_iova_to_phys_length(ops, iova, NULL);
+
+ return (phys == PHYS_ADDR_MAX) ? 0 : phys;
+}
+
+static phys_addr_t arm_lpae_iova_to_phys_length(struct io_pgtable_ops *ops,
+ unsigned long iova,
+ size_t *mapped_length)
{
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
struct iova_to_phys_data d;
@@ -742,13 +755,18 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
.addr = iova,
.end = iova + 1,
};
+ size_t block_size;
int ret;

ret = __arm_lpae_iopte_walk(data, &walk_data, data->pgd, data->start_level);
if (ret)
- return 0;
+ return PHYS_ADDR_MAX;
+
+ block_size = ARM_LPAE_BLOCK_SIZE(d.lvl, data);
+ if (mapped_length)
+ *mapped_length = block_size;

- iova &= (ARM_LPAE_BLOCK_SIZE(d.lvl, data) - 1);
+ iova &= (block_size - 1);
return iopte_to_paddr(d.pte, data) | iova;
}

@@ -948,6 +966,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg)
.map_pages = arm_lpae_map_pages,
.unmap_pages = arm_lpae_unmap_pages,
.iova_to_phys = arm_lpae_iova_to_phys,
+ .iova_to_phys_length = arm_lpae_iova_to_phys_length,
.read_and_clear_dirty = arm_lpae_read_and_clear_dirty,
.pgtable_walk = arm_lpae_pgtable_walk,
};
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index e19872e37e06..42bcdd309b88 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -203,6 +203,10 @@ struct arm_lpae_io_pgtable_walk_data {
* @map_pages: Map a physically contiguous range of pages of the same size.
* @unmap_pages: Unmap a range of virtually contiguous pages of the same size.
* @iova_to_phys: Translate iova to physical address.
+ * @iova_to_phys_length: Translate iova to physical address and return the
+ * remaining mapped length from iova to the end of the
+ * mapping entry via @mapped_length. If @mapped_length is
+ * NULL, only the physical address is returned.
* @pgtable_walk: (optional) Perform a page table walk for a given iova.
* @read_and_clear_dirty: Record dirty info per IOVA. If an IOVA is dirty,
* clear its dirty state from the PTE unless the
@@ -220,6 +224,9 @@ struct io_pgtable_ops {
struct iommu_iotlb_gather *gather);
phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,
unsigned long iova);
+ phys_addr_t (*iova_to_phys_length)(struct io_pgtable_ops *ops,
+ unsigned long iova,
+ size_t *mapped_length);
int (*pgtable_walk)(struct io_pgtable_ops *ops, unsigned long iova, void *wd);
int (*read_and_clear_dirty)(struct io_pgtable_ops *ops,
unsigned long iova, size_t size,
--
2.43.7