max_mapnr variable is utilized in the pfn_valid() method in order to determine the upper PFN space boundary. Having it uninitialized effectively makes any PFN passed to that method invalid. That in its turn causes the kernel mm-subsystem occasion malfunctions even after the max_mapnr variable is actually properly updated. For instance, pfn_valid() is called in the init_unavailable_range() method in the framework of the calls-chain on MIPS: setup_arch() +-> paging_init() +-> free_area_init() +-> memmap_init() +-> memmap_init_zone_range() +-> init_unavailable_range()
Since pfn_valid() always returns "false" value before max_mapnr is initialized in the mem_init() method, any flatmem page-holes will be left in the poisoned/uninitialized state including the IO-memory pages. Thus any further attempts to map/remap the IO-memory by using MMU may fail. In particular it happened in my case on attempt to map the SRAM region. The kernel bootup procedure just crashed on the unhandled unaligned access bug raised in the __update_cache() method:
Let's fix the problem by initializing the max_mapnr variable as soon as the required data is available. In particular it can be done right in the paging_init() method before free_area_init() is called since all the PFN zone boundaries have already been calculated by that time.
Cc: stable@vger.kernel.org Signed-off-by: Serge Semin fancer.lancer@gmail.com
---
Note I don't really know since what point that problem actually exists. Based on the commits log it might had been persistent even before the boot_mem_map allocator was dropped. On the other hand I hadn't seen it actually come out before moving my working tree from kernel 6.5-rc4 to 6.7-rc1. So after updating the kernel I got the unhandled unaligned access BUG() due to the access to compound head pointer the __update_cache() method (see the commit log). After enabling the DEBUG_VM config I managed to find out that the IO-memory pages were just left uninitialized and poisoned:
Which in its turn made me digging deeper into the way the MMIO-space pages are initialized. That's how I got into the pfn_valid() and init_unavailable_range() working improperly on my setup.
Anyway none of the problems above I spotted on kernel 6.5-rc4. So what actually triggered having them finally popped up isn't that easy to be foundn seeing the involved code hasn't changed much. --- arch/mips/mm/init.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 5dcb525a8995..6e368a4658b5 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -422,7 +422,12 @@ void __init paging_init(void) (highend_pfn - max_low_pfn) << (PAGE_SHIFT - 10)); max_zone_pfns[ZONE_HIGHMEM] = max_low_pfn; } + + max_mapnr = highend_pfn ? highend_pfn : max_low_pfn; +#else + max_mapnr = max_low_pfn; #endif + high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT);
free_area_init(max_zone_pfns); } @@ -458,13 +463,6 @@ void __init mem_init(void) */ BUILD_BUG_ON(IS_ENABLED(CONFIG_32BIT) && (PFN_PTE_SHIFT > PAGE_SHIFT));
-#ifdef CONFIG_HIGHMEM - max_mapnr = highend_pfn ? highend_pfn : max_low_pfn; -#else - max_mapnr = max_low_pfn; -#endif - high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); - maar_init(); memblock_free_all(); setup_zero_pages(); /* Setup zeroed pages. */
linux-stable-mirror@lists.linaro.org