Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arch, mm: streamline HIGHMEM freeing

All architectures that support HIGHMEM have their code that frees high
memory pages to the buddy allocator while __free_memory_core() is limited
to freeing only low memory.

There is no actual reason for that. The memory map is completely ready by
the time memblock_free_all() is called and high pages can be released to
the buddy allocator along with low memory.

Remove low memory limit from __free_memory_core() and drop per-architecture
code that frees high memory pages.

Link: https://lkml.kernel.org/r/20250313135003.836600-12-rppt@kernel.org
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Acked-by: Dave Hansen <dave.hansen@linux.intel.com> [x86]
Tested-by: Mark Brown <broonie@kernel.org>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Andreas Larsson <andreas@gaisler.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Borislav Betkov <bp@alien8.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Dinh Nguyen <dinguyen@kernel.org>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Guo Ren (csky) <guoren@kernel.org>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Helge Deller <deller@gmx.de>
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiaxun Yang <jiaxun.yang@flygoat.com>
Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Matt Turner <mattst88@gmail.com>
Cc: Max Filippov <jcmvbkbc@gmail.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Michal Simek <monstr@monstr.eu>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Richard Weinberger <richard@nod.at>
Cc: Russel King <linux@armlinux.org.uk>
Cc: Stafford Horne <shorne@gmail.com>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Thomas Gleinxer <tglx@linutronix.de>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Vineet Gupta <vgupta@kernel.org>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Mike Rapoport (Microsoft) and committed by
Andrew Morton
6faea342 e120d1bc

+2 -239
+1 -5
arch/arc/mm/init.c
··· 160 160 static void __init highmem_init(void) 161 161 { 162 162 #ifdef CONFIG_HIGHMEM 163 - unsigned long tmp; 164 - 165 163 memblock_phys_free(high_mem_start, high_mem_sz); 166 - for (tmp = min_high_pfn; tmp < max_high_pfn; tmp++) 167 - free_highmem_page(pfn_to_page(tmp)); 168 164 #endif 169 165 } 170 166 ··· 172 176 */ 173 177 void __init mem_init(void) 174 178 { 175 - memblock_free_all(); 176 179 highmem_init(); 180 + memblock_free_all(); 177 181 178 182 BUILD_BUG_ON((PTRS_PER_PGD * sizeof(pgd_t)) > PAGE_SIZE); 179 183 BUILD_BUG_ON((PTRS_PER_PUD * sizeof(pud_t)) > PAGE_SIZE);
-29
arch/arm/mm/init.c
··· 237 237 *p++ = 0xe7fddef0; 238 238 } 239 239 240 - static void __init free_highpages(void) 241 - { 242 - #ifdef CONFIG_HIGHMEM 243 - unsigned long max_low = max_low_pfn; 244 - phys_addr_t range_start, range_end; 245 - u64 i; 246 - 247 - /* set highmem page free */ 248 - for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, 249 - &range_start, &range_end, NULL) { 250 - unsigned long start = PFN_UP(range_start); 251 - unsigned long end = PFN_DOWN(range_end); 252 - 253 - /* Ignore complete lowmem entries */ 254 - if (end <= max_low) 255 - continue; 256 - 257 - /* Truncate partial highmem entries */ 258 - if (start < max_low) 259 - start = max_low; 260 - 261 - for (; start < end; start++) 262 - free_highmem_page(pfn_to_page(start)); 263 - } 264 - #endif 265 - } 266 - 267 240 /* 268 241 * mem_init() marks the free areas in the mem_map and tells us how much 269 242 * memory is free. This is done after various parts of the system have ··· 255 282 256 283 /* this will put all unused low memory onto the freelists */ 257 284 memblock_free_all(); 258 - 259 - free_highpages(); 260 285 261 286 /* 262 287 * Check boundaries twice: Some fundamental inconsistencies can
-14
arch/csky/mm/init.c
··· 44 44 45 45 void __init mem_init(void) 46 46 { 47 - #ifdef CONFIG_HIGHMEM 48 - unsigned long tmp; 49 - #endif 50 - 51 47 memblock_free_all(); 52 - 53 - #ifdef CONFIG_HIGHMEM 54 - for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) { 55 - struct page *page = pfn_to_page(tmp); 56 - 57 - /* FIXME not sure about */ 58 - if (!memblock_is_reserved(tmp << PAGE_SHIFT)) 59 - free_highmem_page(page); 60 - } 61 - #endif 62 48 } 63 49 64 50 void free_initmem(void)
-16
arch/microblaze/mm/init.c
··· 52 52 map_page(PKMAP_BASE, 0, 0); /* XXX gross */ 53 53 pkmap_page_table = virt_to_kpte(PKMAP_BASE); 54 54 } 55 - 56 - static void __meminit highmem_setup(void) 57 - { 58 - unsigned long pfn; 59 - 60 - for (pfn = max_low_pfn; pfn < max_pfn; ++pfn) { 61 - struct page *page = pfn_to_page(pfn); 62 - 63 - /* FIXME not sure about */ 64 - if (!memblock_is_reserved(pfn << PAGE_SHIFT)) 65 - free_highmem_page(page); 66 - } 67 - } 68 55 #endif /* CONFIG_HIGHMEM */ 69 56 70 57 /* ··· 109 122 { 110 123 /* this will put all memory onto the freelists */ 111 124 memblock_free_all(); 112 - #ifdef CONFIG_HIGHMEM 113 - highmem_setup(); 114 - #endif 115 125 116 126 mem_init_done = 1; 117 127 }
-20
arch/mips/mm/init.c
··· 425 425 static struct kcore_list kcore_kseg0; 426 426 #endif 427 427 428 - static inline void __init mem_init_free_highmem(void) 429 - { 430 - #ifdef CONFIG_HIGHMEM 431 - unsigned long tmp; 432 - 433 - if (cpu_has_dc_aliases) 434 - return; 435 - 436 - for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) { 437 - struct page *page = pfn_to_page(tmp); 438 - 439 - if (!memblock_is_memory(PFN_PHYS(tmp))) 440 - SetPageReserved(page); 441 - else 442 - free_highmem_page(page); 443 - } 444 - #endif 445 - } 446 - 447 428 void __init mem_init(void) 448 429 { 449 430 /* ··· 435 454 436 455 maar_init(); 437 456 setup_zero_pages(); /* Setup zeroed pages. */ 438 - mem_init_free_highmem(); 439 457 memblock_free_all(); 440 458 441 459 #ifdef CONFIG_64BIT
-14
arch/powerpc/mm/mem.c
··· 297 297 298 298 memblock_free_all(); 299 299 300 - #ifdef CONFIG_HIGHMEM 301 - { 302 - unsigned long pfn, highmem_mapnr; 303 - 304 - highmem_mapnr = lowmem_end_addr >> PAGE_SHIFT; 305 - for (pfn = highmem_mapnr; pfn < max_mapnr; ++pfn) { 306 - phys_addr_t paddr = (phys_addr_t)pfn << PAGE_SHIFT; 307 - struct page *page = pfn_to_page(pfn); 308 - if (memblock_is_memory(paddr) && !memblock_is_reserved(paddr)) 309 - free_highmem_page(page); 310 - } 311 - } 312 - #endif /* CONFIG_HIGHMEM */ 313 - 314 300 #if defined(CONFIG_PPC_E500) && !defined(CONFIG_SMP) 315 301 /* 316 302 * If smp is enabled, next_tlbcam_idx is initialized in the cpu up
-25
arch/sparc/mm/init_32.c
··· 232 232 } 233 233 } 234 234 235 - static void map_high_region(unsigned long start_pfn, unsigned long end_pfn) 236 - { 237 - unsigned long tmp; 238 - 239 - #ifdef CONFIG_DEBUG_HIGHMEM 240 - printk("mapping high region %08lx - %08lx\n", start_pfn, end_pfn); 241 - #endif 242 - 243 - for (tmp = start_pfn; tmp < end_pfn; tmp++) 244 - free_highmem_page(pfn_to_page(tmp)); 245 - } 246 - 247 235 void __init mem_init(void) 248 236 { 249 237 int i; ··· 264 276 taint_real_pages(); 265 277 266 278 memblock_free_all(); 267 - 268 - for (i = 0; sp_banks[i].num_bytes != 0; i++) { 269 - unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT; 270 - unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT; 271 - 272 - if (end_pfn <= highstart_pfn) 273 - continue; 274 - 275 - if (start_pfn < highstart_pfn) 276 - start_pfn = highstart_pfn; 277 - 278 - map_high_region(start_pfn, end_pfn); 279 - } 280 279 } 281 280 282 281 void sparc_flush_page_to_ram(struct page *page)
-3
arch/x86/include/asm/highmem.h
··· 69 69 arch_flush_lazy_mmu_mode(); \ 70 70 } while (0) 71 71 72 - extern void add_highpages_with_active_regions(int nid, unsigned long start_pfn, 73 - unsigned long end_pfn); 74 - 75 72 #endif /* __KERNEL__ */ 76 73 77 74 #endif /* _ASM_X86_HIGHMEM_H */
-4
arch/x86/include/asm/numa.h
··· 41 41 } 42 42 #endif /* CONFIG_NUMA */ 43 43 44 - #ifdef CONFIG_X86_32 45 - # include <asm/numa_32.h> 46 - #endif 47 - 48 44 #ifdef CONFIG_NUMA 49 45 extern void numa_set_node(int cpu, int node); 50 46 extern void numa_clear_node(int cpu);
-13
arch/x86/include/asm/numa_32.h
··· 1 - /* SPDX-License-Identifier: GPL-2.0 */ 2 - #ifndef _ASM_X86_NUMA_32_H 3 - #define _ASM_X86_NUMA_32_H 4 - 5 - #ifdef CONFIG_HIGHMEM 6 - extern void set_highmem_pages_init(void); 7 - #else 8 - static inline void set_highmem_pages_init(void) 9 - { 10 - } 11 - #endif 12 - 13 - #endif /* _ASM_X86_NUMA_32_H */
-2
arch/x86/mm/Makefile
··· 42 42 obj-$(CONFIG_PTDUMP) += dump_pagetables.o 43 43 obj-$(CONFIG_PTDUMP_DEBUGFS) += debug_pagetables.o 44 44 45 - obj-$(CONFIG_HIGHMEM) += highmem_32.o 46 - 47 45 KASAN_SANITIZE_kasan_init_$(BITS).o := n 48 46 obj-$(CONFIG_KASAN) += kasan_init_$(BITS).o 49 47
-34
arch/x86/mm/highmem_32.c
··· 1 - // SPDX-License-Identifier: GPL-2.0-only 2 - #include <linux/highmem.h> 3 - #include <linux/export.h> 4 - #include <linux/swap.h> /* for totalram_pages */ 5 - #include <linux/memblock.h> 6 - #include <asm/numa.h> 7 - 8 - void __init set_highmem_pages_init(void) 9 - { 10 - struct zone *zone; 11 - int nid; 12 - 13 - /* 14 - * Explicitly reset zone->managed_pages because set_highmem_pages_init() 15 - * is invoked before memblock_free_all() 16 - */ 17 - reset_all_zones_managed_pages(); 18 - for_each_zone(zone) { 19 - unsigned long zone_start_pfn, zone_end_pfn; 20 - 21 - if (!is_highmem(zone)) 22 - continue; 23 - 24 - zone_start_pfn = zone->zone_start_pfn; 25 - zone_end_pfn = zone_start_pfn + zone->spanned_pages; 26 - 27 - nid = zone_to_nid(zone); 28 - printk(KERN_INFO "Initializing %s for node %d (%08lx:%08lx)\n", 29 - zone->name, nid, zone_start_pfn, zone_end_pfn); 30 - 31 - add_highpages_with_active_regions(nid, zone_start_pfn, 32 - zone_end_pfn); 33 - } 34 - }
-28
arch/x86/mm/init_32.c
··· 394 394 395 395 pkmap_page_table = virt_to_kpte(vaddr); 396 396 } 397 - 398 - void __init add_highpages_with_active_regions(int nid, 399 - unsigned long start_pfn, unsigned long end_pfn) 400 - { 401 - phys_addr_t start, end; 402 - u64 i; 403 - 404 - for_each_free_mem_range(i, nid, MEMBLOCK_NONE, &start, &end, NULL) { 405 - unsigned long pfn = clamp_t(unsigned long, PFN_UP(start), 406 - start_pfn, end_pfn); 407 - unsigned long e_pfn = clamp_t(unsigned long, PFN_DOWN(end), 408 - start_pfn, end_pfn); 409 - for ( ; pfn < e_pfn; pfn++) 410 - if (pfn_valid(pfn)) 411 - free_highmem_page(pfn_to_page(pfn)); 412 - } 413 - } 414 397 #else 415 398 static inline void permanent_kmaps_init(pgd_t *pgd_base) 416 399 { ··· 698 715 #ifdef CONFIG_FLATMEM 699 716 BUG_ON(!mem_map); 700 717 #endif 701 - /* 702 - * With CONFIG_DEBUG_PAGEALLOC initialization of highmem pages has to 703 - * be done before memblock_free_all(). Memblock use free low memory for 704 - * temporary data (see find_range_array()) and for this purpose can use 705 - * pages that was already passed to the buddy allocator, hence marked as 706 - * not accessible in the page tables when compiled with 707 - * CONFIG_DEBUG_PAGEALLOC. Otherwise order of initialization is not 708 - * important here. 709 - */ 710 - set_highmem_pages_init(); 711 - 712 718 /* this will put all low memory onto the freelists */ 713 719 memblock_free_all(); 714 720
-29
arch/xtensa/mm/init.c
··· 129 129 print_vm_layout(); 130 130 } 131 131 132 - static void __init free_highpages(void) 133 - { 134 - #ifdef CONFIG_HIGHMEM 135 - unsigned long max_low = max_low_pfn; 136 - phys_addr_t range_start, range_end; 137 - u64 i; 138 - 139 - /* set highmem page free */ 140 - for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, 141 - &range_start, &range_end, NULL) { 142 - unsigned long start = PFN_UP(range_start); 143 - unsigned long end = PFN_DOWN(range_end); 144 - 145 - /* Ignore complete lowmem entries */ 146 - if (end <= max_low) 147 - continue; 148 - 149 - /* Truncate partial highmem entries */ 150 - if (start < max_low) 151 - start = max_low; 152 - 153 - for (; start < end; start++) 154 - free_highmem_page(pfn_to_page(start)); 155 - } 156 - #endif 157 - } 158 - 159 132 /* 160 133 * Initialize memory pages. 161 134 */ 162 135 163 136 void __init mem_init(void) 164 137 { 165 - free_highpages(); 166 - 167 138 memblock_free_all(); 168 139 } 169 140
-1
include/linux/mm.h
··· 3275 3275 3276 3276 /* Free the reserved page into the buddy system, so it gets managed. */ 3277 3277 void free_reserved_page(struct page *page); 3278 - #define free_highmem_page(page) free_reserved_page(page) 3279 3278 3280 3279 static inline void mark_page_reserved(struct page *page) 3281 3280 {
+1 -2
mm/memblock.c
··· 2164 2164 phys_addr_t end) 2165 2165 { 2166 2166 unsigned long start_pfn = PFN_UP(start); 2167 - unsigned long end_pfn = min_t(unsigned long, 2168 - PFN_DOWN(end), max_low_pfn); 2167 + unsigned long end_pfn = PFN_DOWN(end); 2169 2168 2170 2169 if (start_pfn >= end_pfn) 2171 2170 return 0;