summaryrefslogtreecommitdiff
path: root/mm/page_alloc.h
AgeCommit message (Collapse)Author
24 hoursMerge https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git ↵David Hildenbrand (Arm)
mm-unstable into for-next Signed-off-by: David Hildenbrand (Arm) <david@kernel.org> # Conflicts: # mm/sparse-vmemmap.c # Conflict resolution: # # diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c # index 11543f4b6304..5d7e982f94d0 100644 # --- a/mm/sparse-vmemmap.c # +++ b/mm/sparse-vmemmap.c # @@ -36,11 +36,7 @@ # #include "mm_init.h" # #include "sparse.h" # # -<<<<<<< HEAD # -void * __meminit vmemmap_alloc_block(unsigned long size, int node) # -======= # void __ref *vmemmap_alloc_block(unsigned long size, int node) # ->>>>>>> akpm/mm-unstable # { # /* If the main allocator is up use that, fallback to bootmem. */ # if (slab_is_available()) { # @@ -60,11 +56,7 @@ void __ref *vmemmap_alloc_block(unsigned long size, int node) # } # return NULL; # } else # -<<<<<<< HEAD # return memmap_alloc(size, size, __pa(MAX_DMA_ADDRESS), node); # -======= # - return memmap_alloc(size, size, __pa(MAX_DMA_ADDRESS), node, false); # ->>>>>>> akpm/mm-unstable # } # # static void * __meminit altmap_alloc_block_buf(unsigned long size,
32 hoursmm: page_alloc: make defrag_mode retries follow the promoted orderKiryl Shutsemau (Meta)
Since commit 7e8756d7ad22 ("mm: page_alloc: fix non-movable reclaim storm in defrag_mode"), direct reclaim and compaction for non-movable requests under defrag_mode run at pageblock_order, to produce the whole blocks that ALLOC_NOFRAGMENT needs. The retry decisions that follow still use the request order. An order-0 request can therefore retry indefinitely without ever reaching the ALLOC_NOFRAGMENT fallback: - Reclaim at pageblock_order gives up after one pass as soon as a zone looks compaction_ready(), and do_try_to_free_pages() then returns 1 even though nothing was reclaimed. It returns before the retry that would reclaim memory.low-protected cgroups, so when most memory is protected, the pass that did run finds next to nothing. - Compaction at pageblock_order fails or is deferred. - should_reclaim_retry() takes the reported progress as progress for the order-0 request and resets no_progress_loops. The request retries. Order 1-3 requests loop the same way, and should_compact_retry() also checks their pageblock_order compaction result against the request order. On a production host (64G, defrag_mode, memory.low covering most of the workload), 95% of direct reclaim runs were order-9 runs that returned 1 with nothing reclaimed, at up to 60k runs per second. Across ~200M should_reclaim_retry() calls in a day, no_progress_loops never left 0. The spinning allocations were SLUB slab refills for inode and dentry caches. The time spent registers as memory pressure, and pressure-based OOM killing takes down both workloads and system services. For promoted requests: - Reclaim progress does not reset no_progress_loops, as for costly orders. - should_compact_retry() checks the compaction result at the promoted order and does not retry COMPACT_SKIPPED, since the request can fall back. The compaction priority floor and the COMPACT_SUCCESS retry limit stay those of the request order, so a non-costly request still gets its COMPACT_PRIO_SYNC_FULL pass before it falls back. When a promoted request takes the fallback, reset the retry counters, so that the fallback attempt gets a full retry budget before the OOM killer is considered. A request whose reclaim was not promoted spent its budget on the request itself and keeps its counters, as Usama Arif pointed out. __alloc_pages_slowpath() computes the promoted order once per iteration and keeps it in alloc_context for direct reclaim, direct compaction and the two retry helpers, so no callee has to recompute it. Harry Yoo asked for the promoted order to be explicit rather than derived in each callee, and Vlastimil Babka suggested alloc_context over new function parameters. In a VM reproducer (32G, defrag_mode, inode churn under memory.low): before after should_reclaim_retry() calls 63M 293k peak memory pressure (PSI some avg10) 99% 12% File creation runs 5.7x faster. Link: https://lore.kernel.org/as5q14OsbK2CmOrh@thinkstation Fixes: 7e8756d7ad22 ("mm: page_alloc: fix non-movable reclaim storm in defrag_mode") Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Assisted-by: LLM Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: David Hildenbrand <david@kernel.org> Cc: Harry Yoo <harry@kernel.org> Cc: Usama Arif <usama.arif@linux.dev> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Michal Hocko <mhocko@suse.com> Cc: Brendan Jackman <brendan.jackman@linux.dev> Cc: Zi Yan <ziy@nvidia.com> Cc: Shakeel Butt <shakeel.butt@linux.dev> Cc: <stable@vger.kernel.org>
2026-09-23mm/memory_hotplug: optimize zone contiguous check when changing pfn rangeYuan Liu
When move_pfn_range_to_zone() or remove_pfn_range_from_zone() updates a zone, set_zone_contiguous() rescans the entire zone pageblock-by-pageblock to rebuild zone->contiguous. For large zones this is a significant cost during memory hotplug and hot-unplug. Add a new zone member, pages_with_online_memmap, that tracks the number of pages within the zone span that have an online memory map, including present pages and memory holes whose memory map has been initialized and for which pfn_to_online_page() succeeds. For early boot memory, pages_with_online_memmap is calculated in memmap_init_zone_range(). PFNs initialized by memmap_init_range() are included in pages_with_online_memmap, and hole PFNs for which pfn_to_online_page() succeeds are also counted in init_unavailable_range(). For hotplugged memory, pages_with_online_memmap is updated through adjust_present_page_count(), which is called during memory online and offline operations. When spanned_pages == pages_with_online_memmap, every PFN in the zone span has a valid memmap entry, so pfn_to_page() can be called for any PFN within the zone span without an additional pfn_valid() check. Note: this counter may temporarily undercount when pages with an online memory map exist outside the current zone span. Such pages are only created during boot, when initializing the memory map of pages that do not fall into any zone span. The undercount itself can only happen after boot, during memory hotplug, when growing the zone to cover such pages and later shrinking it back, which may result in a "too small" value. This is safe: it merely prevents detecting a contiguous zone. Here is an example (page numbers are just for illustration purposes): after boot: [ zone span ] [ zone pages ] spanned=10, initialized=15, online=10 online == spanned -> contiguous growing after hotplug (hotplug 5): [ zone span ] [ zone pages ] [ zone pages ] spanned=30, initialized=20, online=15 online != spanned -> not contiguous shrinking after hotunplug (hotunplug 5 again): [ zone span ] [ zone pages ] spanned=15, initialized=15, online=10 online != spanned -> not contiguous although contiguous The contiguity check using pages_with_online_memmap is stricter than the old pageblock-by-pageblock scan. The old set_zone_contiguous() iterated at pageblock granularity via pageblock_pfn_to_page(), so a zone could be marked contiguous even if a subsection-sized hole existed within a pageblock. The new check requires spanned_pages == pages_with_online_memmap, meaning every PFN in the zone span must satisfy pfn_to_online_page(). The following test cases of memory hotplug for a VM [1], tested in the environment [2], show that this optimization can significantly reduce the memory hotplug time [3]. +----------------+------+---------------+--------------+----------------+ | | Size | Time (before) | Time (after) | Time Reduction | | +------+---------------+--------------+----------------+ | Plug Memory | 256G | 10s | 3s | 70% | | +------+---------------+--------------+----------------+ | | 512G | 36s | 7s | 81% | +----------------+------+---------------+--------------+----------------+ +----------------+------+---------------+--------------+----------------+ | | Size | Time (before) | Time (after) | Time Reduction | | +------+---------------+--------------+----------------+ | Unplug Memory | 256G | 11s | 4s | 64% | | +------+---------------+--------------+----------------+ | | 512G | 36s | 9s | 75% | +----------------+------+---------------+--------------+----------------+ [1] Qemu commands to hotplug 256G/512G memory for a VM: object_add memory-backend-ram,id=hotmem0,size=256G/512G,share=on device_add virtio-mem-pci,id=vmem1,memdev=hotmem0,bus=port1 qom-set vmem1 requested-size 256G/512G (Plug Memory) qom-set vmem1 requested-size 0G (Unplug Memory) [2] Hardware : Intel Icelake server Guest Kernel : v7.3-rc3 Qemu : v9.0.0 Launch VM : qemu-system-x86_64 -accel kvm -cpu host \ -drive file=./Centos10_cloud.qcow2,format=qcow2,if=virtio \ -drive file=./seed.img,format=raw,if=virtio \ -smp 3,cores=3,threads=1,sockets=1,maxcpus=3 \ -m 2G,slots=10,maxmem=2052472M \ -device pcie-root-port,id=port1,bus=pcie.0,slot=1,multifunction=on \ -device pcie-root-port,id=port2,bus=pcie.0,slot=2 \ -nographic -machine q35 \ -nic user,hostfwd=tcp::3000-:22 Guest kernel auto-onlines newly added memory blocks: echo online > /sys/devices/system/memory/auto_online_blocks [3] The time from typing the QEMU commands in [1] to when the output of 'grep MemTotal /proc/meminfo' on Guest reflects that all hotplugged memory is recognized. Reported-by: Nanhai Zou <nanhai.zou@intel.com> Reported-by: Chen Zhang <zhangchen.kidd@jd.com> Tested-by: Yuan Liu <yuan1.liu@intel.com> Reviewed-by: Jason Zeng <jason.zeng@intel.com> Reviewed-by: Chen Yu <yu.c.chen@intel.com> Reviewed-by: Pan Deng <pan.deng@intel.com> Co-developed-by: Tianyou Li <tianyou.li@intel.com> Signed-off-by: Tianyou Li <tianyou.li@intel.com> Signed-off-by: Yuan Liu <yuan1.liu@intel.com> Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org> Reviewed-by: Wei Yang <richard.weiyang@gmail.com> Link: https://patch.msgid.link/20260920084946.3266279-3-yuan1.liu@intel.com Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
2026-07-30mm: replace __GFP_NO_CODETAG with ALLOC_NO_CODETAGBrendan Jackman
Now that alloc_pages has an entrypoint that allows passing alloc_flags, we can take advantage of this to start removing GFP flags that are only used for mm-internal stuff. This requires also plumbing the alloc_flags into some more of the allocator code, in particular __alloc_pages[_noprof]() gets an alloc_flags arg to go along with its callees, and we now need to pass those flags deeper into the allocator so they can reach the alloc_tag code. While moving the flag definition into page_alloc.h, also update the comment per Hao's suggestion. No functional change intended. Link: https://lore.kernel.org/all/b4916118-3537-4e19-8bc8-1d103dd0d225@linux.dev/ Link: https://lore.kernel.org/20260703-alloc-trylock-v5-15-c87b714e19d3@google.com Signed-off-by: Brendan Jackman <jackmanb@google.com> Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Tested-by: Hao Ge <hao.ge@linux.dev> Acked-by: Hao Ge <hao.ge@linux.dev> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2026-07-30mm: move __alloc_pages() to mm/page_alloc.hBrendan Jackman
It's no longer used outside of mm/. Since this means __alloc_pages_noprof() is no longer visible from gfp.h, this also means moving the definition of alloc_pages_node_noprof into the .c file. Also remove references to this API from the documentation tree - referring to the specific function name was already questionable but now the function is not even public it definitely seems wrong. Link: https://lore.kernel.org/20260703-alloc-trylock-v5-14-c87b714e19d3@google.com Signed-off-by: Brendan Jackman <jackmanb@google.com> Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Reviewed-by: Zi Yan <ziy@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2026-07-30mm: move some stuff to mm/page_alloc.hBrendan Jackman
Some of this stuff in the public header is only used internally so shrink the scope to avoid silently growing new users. drain_local_pages() is still used from kernel/power/snapshot.c so that needs to stay behind. Link: https://lore.kernel.org/20260703-alloc-trylock-v5-7-c87b714e19d3@google.com Signed-off-by: Brendan Jackman <jackmanb@google.com> Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Reviewed-by: Zi Yan <ziy@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2026-07-30mm/page_alloc: unify __alloc_frozen_pages[_nolock]_noprof()Brendan Jackman
Currently the core allocator code is controlled by ALLOC_NOLOCK, but the main entry point function is significantly different from the normal __alloc_frozen_pages_nolock(), this is tiring when reading the code. Plumb the ALLOC_NOLOCK control one layer up in the call stack: create an alloc_flags argument to __alloc_frozen_pages_nolock() (which is only exposed to mm/) and then turn the nolock variant into a thin wrapper that just sets that flag (as well as handling NUMA_NO_NODE, similar to how some of the wrappers in gfp.h do). For consistency, set ALLOC_WMARK_MIN explicitly in fastpath_alloc_flags for the new ALLOC_NOLOCK path. This was already "done" silently in __alloc_frozen_pages_nolock_noprof(): ALLOC_WMARK_MIN is 0. Rationale that this doesn't change anything: 1. Simple bits: A bunch of the nolock-specific handling is just moved to the new alloc_order_allowed(), alloc_nolock_allowed() and gfp_nolock. 2. __alloc_frozen_pages_noprof() has some extra logic that wasn't previously in the nolock variant: a. Application of gfp_allowed_mask; this only affects early boot, only flags that affect the slowpath get changed here, and the nolock allocation path isn't allowed to the GFP_BOOT_MASK flags. b. Application of current_gfp_context() - also only affects the slowpath 3. The slowpath itself: this is now just explicitly skipped under !ALLOC_NOLOCK. Ulterior motive: adding an alloc_flags arg to the allocator's mm-internal entrypoint can later be used to do more allocation customisation without needing to create new GFP flags. No functional change intended. Link: https://lore.kernel.org/20260703-alloc-trylock-v5-5-c87b714e19d3@google.com Signed-off-by: Brendan Jackman <jackmanb@google.com> Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Acked-by: Zi Yan <ziy@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2026-07-30mm: split out internal page_alloc.hBrendan Jackman
internal.h is a bit bloated, seems like time for a page_alloc.h. Where it wasn't obvious, the heuristic for deciding what goes into this new header was "does it support/correspond to a definition in mm/page_alloc.c?" Only need to include it from ~20 .c files out of ~150 so this does seem like a genuine reduction in scopes, which is nice. And there's no circular internal.h<->page_alloc.h dependency, so it seems worthwhile to split this up before that inevitably emerges! Link: https://lore.kernel.org/20260703-alloc-trylock-v5-4-c87b714e19d3@google.com Signed-off-by: Brendan Jackman <jackmanb@google.com> Suggested-by: "David Hildenbrand (Arm)" <david@kernel.org> Link: https://lore.kernel.org/all/41e92bab-6882-401a-8de9-154adbdcfb36@kernel.org/ Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Acked-by: Zi Yan <ziy@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>