]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iommu/io-pgtable-arm: Make pgtable walker more generic
authorRob Clark <robdclark@chromium.org>
Tue, 10 Dec 2024 16:51:19 +0000 (08:51 -0800)
committerWill Deacon <will@kernel.org>
Tue, 7 Jan 2025 15:42:23 +0000 (15:42 +0000)
We can re-use this basic pgtable walk logic in a few places.

Signed-off-by: Rob Clark <robdclark@chromium.org>
Reviewed-by: Mostafa Saleh <smostafa@google.com>
Link: https://lore.kernel.org/r/20241210165127.600817-2-robdclark@gmail.com
Signed-off-by: Will Deacon <will@kernel.org>
drivers/iommu/io-pgtable-arm.c

index 7e53ee51270bdf2cc941eb2bf21eabd428abbe20..e8e9f77a617000cc92df744d3def788558dc131c 100644 (file)
@@ -741,33 +741,33 @@ found_translation:
 }
 
 struct io_pgtable_walk_data {
-       struct iommu_dirty_bitmap       *dirty;
+       struct io_pgtable               *iop;
+       void                            *data;
+       int (*visit)(struct io_pgtable_walk_data *walk_data, int lvl,
+                    arm_lpae_iopte *ptep, size_t size);
        unsigned long                   flags;
        u64                             addr;
        const u64                       end;
 };
 
-static int __arm_lpae_iopte_walk_dirty(struct arm_lpae_io_pgtable *data,
-                                      struct io_pgtable_walk_data *walk_data,
-                                      arm_lpae_iopte *ptep,
-                                      int lvl);
+static int __arm_lpae_iopte_walk(struct arm_lpae_io_pgtable *data,
+                                struct io_pgtable_walk_data *walk_data,
+                                arm_lpae_iopte *ptep,
+                                int lvl);
 
-static int io_pgtable_visit_dirty(struct arm_lpae_io_pgtable *data,
-                                 struct io_pgtable_walk_data *walk_data,
-                                 arm_lpae_iopte *ptep, int lvl)
+static int io_pgtable_visit(struct arm_lpae_io_pgtable *data,
+                           struct io_pgtable_walk_data *walk_data,
+                           arm_lpae_iopte *ptep, int lvl)
 {
        struct io_pgtable *iop = &data->iop;
        arm_lpae_iopte pte = READ_ONCE(*ptep);
 
-       if (iopte_leaf(pte, lvl, iop->fmt)) {
-               size_t size = ARM_LPAE_BLOCK_SIZE(lvl, data);
+       size_t size = ARM_LPAE_BLOCK_SIZE(lvl, data);
+       int ret = walk_data->visit(walk_data, lvl, ptep, size);
+       if (ret)
+               return ret;
 
-               if (iopte_writeable_dirty(pte)) {
-                       iommu_dirty_bitmap_record(walk_data->dirty,
-                                                 walk_data->addr, size);
-                       if (!(walk_data->flags & IOMMU_DIRTY_NO_CLEAR))
-                               iopte_set_writeable_clean(ptep);
-               }
+       if (iopte_leaf(pte, lvl, iop->fmt)) {
                walk_data->addr += size;
                return 0;
        }
@@ -776,13 +776,13 @@ static int io_pgtable_visit_dirty(struct arm_lpae_io_pgtable *data,
                return -EINVAL;
 
        ptep = iopte_deref(pte, data);
-       return __arm_lpae_iopte_walk_dirty(data, walk_data, ptep, lvl + 1);
+       return __arm_lpae_iopte_walk(data, walk_data, ptep, lvl + 1);
 }
 
-static int __arm_lpae_iopte_walk_dirty(struct arm_lpae_io_pgtable *data,
-                                      struct io_pgtable_walk_data *walk_data,
-                                      arm_lpae_iopte *ptep,
-                                      int lvl)
+static int __arm_lpae_iopte_walk(struct arm_lpae_io_pgtable *data,
+                                struct io_pgtable_walk_data *walk_data,
+                                arm_lpae_iopte *ptep,
+                                int lvl)
 {
        u32 idx;
        int max_entries, ret;
@@ -797,7 +797,7 @@ static int __arm_lpae_iopte_walk_dirty(struct arm_lpae_io_pgtable *data,
 
        for (idx = ARM_LPAE_LVL_IDX(walk_data->addr, lvl, data);
             (idx < max_entries) && (walk_data->addr < walk_data->end); ++idx) {
-               ret = io_pgtable_visit_dirty(data, walk_data, ptep + idx, lvl);
+               ret = io_pgtable_visit(data, walk_data, ptep + idx, lvl);
                if (ret)
                        return ret;
        }
@@ -805,6 +805,23 @@ static int __arm_lpae_iopte_walk_dirty(struct arm_lpae_io_pgtable *data,
        return 0;
 }
 
+static int visit_dirty(struct io_pgtable_walk_data *walk_data, int lvl,
+                      arm_lpae_iopte *ptep, size_t size)
+{
+       struct iommu_dirty_bitmap *dirty = walk_data->data;
+
+       if (!iopte_leaf(*ptep, lvl, walk_data->iop->fmt))
+               return 0;
+
+       if (iopte_writeable_dirty(*ptep)) {
+               iommu_dirty_bitmap_record(dirty, walk_data->addr, size);
+               if (!(walk_data->flags & IOMMU_DIRTY_NO_CLEAR))
+                       iopte_set_writeable_clean(ptep);
+       }
+
+       return 0;
+}
+
 static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
                                         unsigned long iova, size_t size,
                                         unsigned long flags,
@@ -813,7 +830,9 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
        struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
        struct io_pgtable_cfg *cfg = &data->iop.cfg;
        struct io_pgtable_walk_data walk_data = {
-               .dirty = dirty,
+               .iop = &data->iop,
+               .data = dirty,
+               .visit = visit_dirty,
                .flags = flags,
                .addr = iova,
                .end = iova + size,
@@ -828,7 +847,7 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
        if (data->iop.fmt != ARM_64_LPAE_S1)
                return -EINVAL;
 
-       return __arm_lpae_iopte_walk_dirty(data, &walk_data, ptep, lvl);
+       return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
 }
 
 static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg)