diff --git a/include/linux/io.h b/include/linux/io.h index 8394c56babc2..416b936b19bc 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -21,6 +21,8 @@ void __ioread32_copy(void *to, const void __iomem *from, size_t count); void __iowrite64_copy(void __iomem *to, const void *from, size_t count); #ifdef CONFIG_MMU +void ioremap_phys_range_hook(phys_addr_t phys_addr, size_t size, pgprot_t prot); +void iounmap_phys_range_hook(phys_addr_t phys_addr, size_t size); int ioremap_page_range(unsigned long addr, unsigned long end, phys_addr_t phys_addr, pgprot_t prot); #else diff --git a/mm/Kconfig b/mm/Kconfig index 884ba3860ec8..4fa83145139a 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -888,4 +888,9 @@ config ARCH_HAS_HUGEPD config MAPPING_DIRTY_HELPERS bool +# Some architectures want callbacks for all IO mappings in order to +# track the physical addresses that get used as devices. +config ARCH_HAS_IOREMAP_PHYS_HOOKS + bool + endmenu diff --git a/mm/ioremap.c b/mm/ioremap.c index 5fa1ab41d152..17b1d49f79c5 100644 --- a/mm/ioremap.c +++ b/mm/ioremap.c @@ -225,6 +225,7 @@ int ioremap_page_range(unsigned long addr, pgd_t *pgd; unsigned long start; unsigned long next; + phys_addr_t phys_start = phys_addr; int err; pgtbl_mod_mask mask = 0; @@ -246,6 +247,9 @@ int ioremap_page_range(unsigned long addr, if (mask & ARCH_PAGE_TABLE_SYNC_MASK) arch_sync_kernel_mappings(start, end); + if (IS_ENABLED(CONFIG_ARCH_HAS_IOREMAP_PHYS_HOOKS) && !err) + ioremap_phys_range_hook(phys_start, end - start, prot); + return err; } diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d5615977e7f3..775dd8476686 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -69,7 +70,6 @@ static void free_work(struct work_struct *w) } /*** Page table manipulation functions ***/ - static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, pgtbl_mod_mask *mask) { @@ -2263,6 +2263,10 @@ static void __vunmap(const void *addr, int deallocate_pages) kasan_poison_vmalloc(area->addr, get_vm_area_size(area)); + if (IS_ENABLED(CONFIG_ARCH_HAS_IOREMAP_PHYS_HOOKS) && + area->flags & VM_IOREMAP) + iounmap_phys_range_hook(area->phys_addr, get_vm_area_size(area)); + vm_remove_mappings(area, deallocate_pages); if (deallocate_pages) {