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 <[email protected]>
---
 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

Reply via email to