Message ID | 20240827181717.187245-4-robdclark@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | io-pgtable-arm + drm/msm: Extend iova fault debugging | expand |
On Tue, Aug 27, 2024 at 11:17:11AM -0700, Rob Clark wrote: > From: Rob Clark <robdclark@chromium.org> > > Add an io-pgtable method to walk the pgtable returning the raw PTEs that > would be traversed for a given iova access. > > Signed-off-by: Rob Clark <robdclark@chromium.org> > --- > drivers/iommu/io-pgtable-arm.c | 25 +++++++++++++++++++++++++ > include/linux/io-pgtable.h | 15 +++++++++++++++ > 2 files changed, 40 insertions(+) > > diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c > index efed8e519111..11af49119f37 100644 > --- a/drivers/iommu/io-pgtable-arm.c > +++ b/drivers/iommu/io-pgtable-arm.c > @@ -759,6 +759,30 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, > return iopte_to_paddr(d.pte, data) | iova; > } > > +static int visit_pgtable_walk(struct io_pgtable_walk_data *walk_data, int lvl, > + arm_lpae_iopte *ptep, size_t size) > +{ > + struct arm_lpae_io_pgtable_walk_data *data = walk_data->data; > + data->ptes[data->level++] = *ptep; > + return 0; > +} I'm struggling to see how this works given that the ->visit() callback is only called for leaf entries in the page-table. What am I missing? Will
On Fri, Aug 30, 2024 at 6:20 AM Will Deacon <will@kernel.org> wrote: > > On Tue, Aug 27, 2024 at 11:17:11AM -0700, Rob Clark wrote: > > From: Rob Clark <robdclark@chromium.org> > > > > Add an io-pgtable method to walk the pgtable returning the raw PTEs that > > would be traversed for a given iova access. > > > > Signed-off-by: Rob Clark <robdclark@chromium.org> > > --- > > drivers/iommu/io-pgtable-arm.c | 25 +++++++++++++++++++++++++ > > include/linux/io-pgtable.h | 15 +++++++++++++++ > > 2 files changed, 40 insertions(+) > > > > diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c > > index efed8e519111..11af49119f37 100644 > > --- a/drivers/iommu/io-pgtable-arm.c > > +++ b/drivers/iommu/io-pgtable-arm.c > > @@ -759,6 +759,30 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, > > return iopte_to_paddr(d.pte, data) | iova; > > } > > > > +static int visit_pgtable_walk(struct io_pgtable_walk_data *walk_data, int lvl, > > + arm_lpae_iopte *ptep, size_t size) > > +{ > > + struct arm_lpae_io_pgtable_walk_data *data = walk_data->data; > > + data->ptes[data->level++] = *ptep; > > + return 0; > > +} > > I'm struggling to see how this works given that the ->visit() callback > is only called for leaf entries in the page-table. What am I missing? Yeah, we should just go back to my v6 before I tried to re-use the arm_lpae_read_and_clear_dirty() walker.. addressing review comments is just making it worse since then BR, -R
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index efed8e519111..11af49119f37 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -759,6 +759,30 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, return iopte_to_paddr(d.pte, data) | iova; } +static int visit_pgtable_walk(struct io_pgtable_walk_data *walk_data, int lvl, + arm_lpae_iopte *ptep, size_t size) +{ + struct arm_lpae_io_pgtable_walk_data *data = walk_data->data; + data->ptes[data->level++] = *ptep; + return 0; +} + +static int arm_lpae_pgtable_walk(struct io_pgtable_ops *ops, unsigned long iova, + void *wd) +{ + struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); + struct io_pgtable_walk_data walk_data = { + .data = wd, + .visit = visit_pgtable_walk, + .addr = iova, + .end = iova + 1, + }; + + ((struct arm_lpae_io_pgtable_walk_data *)wd)->level = 0; + + return __arm_lpae_iopte_walk(data, &walk_data, data->pgd, data->start_level); +} + static int io_pgtable_visit(struct arm_lpae_io_pgtable *data, struct io_pgtable_walk_data *walk_data, arm_lpae_iopte *ptep, int lvl) @@ -930,6 +954,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) .unmap_pages = arm_lpae_unmap_pages, .iova_to_phys = arm_lpae_iova_to_phys, .read_and_clear_dirty = arm_lpae_read_and_clear_dirty, + .pgtable_walk = arm_lpae_pgtable_walk, }; return data; diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h index f9a81761bfce..76eabd890e6a 100644 --- a/include/linux/io-pgtable.h +++ b/include/linux/io-pgtable.h @@ -174,12 +174,26 @@ struct io_pgtable_cfg { }; }; +/** + * struct arm_lpae_io_pgtable_walk_data - information from a pgtable walk + * + * @ptes: The recorded PTE values from the walk + * @level: The level of the last PTE + * + * @level also specifies the last valid index in @ptes + */ +struct arm_lpae_io_pgtable_walk_data { + u64 ptes[4]; + int level; +}; + /** * struct io_pgtable_ops - Page table manipulation API for IOMMU drivers. * * @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. + * @pgtable_walk: (optional) Perform a page table walk for a given iova. * * These functions map directly onto the iommu_ops member functions with * the same names. @@ -193,6 +207,7 @@ struct io_pgtable_ops { struct iommu_iotlb_gather *gather); phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops, unsigned long iova); + 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, unsigned long flags,