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 --- 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