FAILED: patch "[PATCH] mm/sparse-vmemmap: fix DAX vmemmap accounting with" failed to apply to 6.18-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 721a73e30c9e3e8fcffe1725bcede1bbd20b4918
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026072034-plow-sliceable-b741@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 721a73e30c9e3e8fcffe1725bcede1bbd20b4918 Mon Sep 17 00:00:00 2001
From: Muchun Song <[email protected]>
Date: Tue, 28 Apr 2026 16:18:53 +0800
Subject: [PATCH] mm/sparse-vmemmap: fix DAX vmemmap accounting with
optimization
When vmemmap optimization is enabled for DAX, the nr_memmap_pages counter in /proc/vmstat is incorrect. The current code always accounts for the full, non-optimized vmemmap size, but vmemmap optimization reduces the actual number of vmemmap pages by reusing tail pages. This causes the system to overcount vmemmap usage, leading to inaccurate page statistics in /proc/vmstat.
Fix this by introducing section_nr_vmemmap_pages(), which returns the exact vmemmap page count for a given pfn range based on whether optimization is in effect.
mm/sparse-vmemmap.c +30 -4
@@ -647,6 +647,31 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn) } } +static int __meminit section_nr_vmemmap_pages (unsigned long pfn,unsigned long nr_pages, +struct vmem_altmap *altmap,struct dev_pagemap *pgmap) +{ +const unsigned int order = pgmap ? pgmap->vmemmap_shift :0 ; +const unsigned long pages_per_compound =1UL << order; + +VM_WARN_ON_ONCE (!IS_ALIGNED (pfn | nr_pages,PAGES_PER_SUBSECTION )); +VM_WARN_ON_ONCE (nr_pages >PAGES_PER_SECTION ); + +if (!vmemmap_can_optimize (altmap, pgmap)) +return DIV_ROUND_UP (nr_pages *sizeof (struct page ),PAGE_SIZE ); + +if (order <PFN_SECTION_SHIFT ) { +VM_WARN_ON_ONCE (!IS_ALIGNED (pfn | nr_pages, pages_per_compound)); +return VMEMMAP_RESERVE_NR * nr_pages / pages_per_compound; + } + +VM_WARN_ON_ONCE (!IS_ALIGNED (pfn | nr_pages,PAGES_PER_SECTION )); + +if (IS_ALIGNED (pfn, pages_per_compound)) +return VMEMMAP_RESERVE_NR ; + +return 0 ; +} +static struct page *__meminit populate_section_memmap (unsigned long pfn,unsigned long nr_pages,int nid,struct vmem_altmap *altmap,struct dev_pagemap *pgmap)
@@ -654,7 +679,7 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn,struct page *page =__populate_section_memmap (pfn, nr_pages, nid, altmap, pgmap); - memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); +memmap_pages_add (section_nr_vmemmap_pages (pfn, nr_pages, altmap, pgmap));return page; }
@@ -665,7 +690,7 @@ static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages,unsigned long start = (unsigned long )pfn_to_page (pfn);unsigned long end = start + nr_pages *sizeof (struct page ); -memmap_pages_add (-1L * (DIV_ROUND_UP (nr_pages *sizeof (struct page ),PAGE_SIZE ))); +memmap_pages_add (-section_nr_vmemmap_pages (pfn, nr_pages, altmap, pgmap));vmemmap_free (start, end, altmap); }
@@ -673,9 +698,10 @@ static void free_map_bootmem(struct page *memmap) {unsigned long start = (unsigned long )memmap;unsigned long end = (unsigned long )(memmap +PAGES_PER_SECTION ); +unsigned long pfn =page_to_pfn (memmap); -memmap_boot_pages_add (-1L * (DIV_ROUND_UP (PAGES_PER_SECTION *sizeof (struct page ), -PAGE_SIZE ))); +memmap_boot_pages_add (-section_nr_vmemmap_pages (pfn,PAGES_PER_SECTION , +NULL ,NULL ));vmemmap_free (start, end,NULL ); }
Sasha Levin
maintainerPATCH 1/2 mm: prepare to move subsection_map_init() to mm/sparse-vmemmap.c
From: "David Hildenbrand (Arm)" <[email protected]>
[ Upstream commit fead6dcff83b02f8d6dc3c1ebbe4e09c05c54ee5 ]
We want to move subsection_map_init() to mm/sparse-vmemmap.c.
To prepare for getting rid of subsection_map_init() in mm/sparse.c completely, use a static inline function for !CONFIG_SPARSEMEM_VMEMMAP.
While at it, move the declaration to internal.h and rename it to "sparse_init_subsection_map()".
Stable-dep-of: 721a73e30c9e ("mm/sparse-vmemmap: fix DAX vmemmap accounting with optimization")
--- arch/arm64/mm/mmu.c | 5 +- arch/loongarch/mm/init.c | 5 +- arch/powerpc/mm/mem.c | 5 +- arch/riscv/mm/init.c | 5 +- arch/s390/mm/init.c | 5 +- arch/x86/mm/init_64.c | 5 +- include/linux/memory_hotplug.h | 8 +- include/linux/mmzone.h | 8 +- mm/Kconfig | 2 +- mm/internal.h | 50 ++++ mm/memory_hotplug.c | 13 +- mm/memremap.c | 4 +- mm/mm_init.c | 2 +- mm/sparse-vmemmap.c | 322 +++++++++++++++++++++++++ mm/sparse.c | 428 +-------------------------------- 15 files changed, 410 insertions(+), 457 deletions(-)
arch/arm64/mm/mmu.c +3 -2
@@ -2031,12 +2031,13 @@ int arch_add_memory(int nid, u64 start, u64 size,return ret; } -void arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap) +void arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap, +struct dev_pagemap *pgmap) {unsigned long start_pfn = start >>PAGE_SHIFT ;unsigned long nr_pages = size >>PAGE_SHIFT ; -__remove_pages (start_pfn, nr_pages, altmap); +__remove_pages (start_pfn, nr_pages, altmap, pgmap);__remove_pgd_mapping (swapper_pg_dir,__phys_to_virt (start), size); }
arch/loongarch/mm/init.c +3 -2
@@ -93,12 +93,13 @@ int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params) return ret; } -void arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap) +void arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap, +struct dev_pagemap *pgmap) {unsigned long start_pfn = start >>PAGE_SHIFT ;unsigned long nr_pages = size >>PAGE_SHIFT ; -__remove_pages (start_pfn, nr_pages, altmap); +__remove_pages (start_pfn, nr_pages, altmap, pgmap); }#endif
arch/powerpc/mm/mem.c +3 -2
@@ -157,12 +157,13 @@ int __ref arch_add_memory(int nid, u64 start, u64 size, return rc; } -void __ref arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap) +void __ref arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap, +struct dev_pagemap *pgmap) {unsigned long start_pfn = start >>PAGE_SHIFT ;unsigned long nr_pages = size >>PAGE_SHIFT ; -__remove_pages (start_pfn, nr_pages, altmap); +__remove_pages (start_pfn, nr_pages, altmap, pgmap);arch_remove_linear_mapping (start, size); }#endif
arch/riscv/mm/init.c +3 -2
@@ -1851,9 +1851,10 @@ int __ref arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *paramreturn ret; } -void __ref arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap) +void __ref arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap, +struct dev_pagemap *pgmap) { -__remove_pages (start >>PAGE_SHIFT , size >>PAGE_SHIFT , altmap); +__remove_pages (start >>PAGE_SHIFT , size >>PAGE_SHIFT , altmap, pgmap);remove_linear_mapping (start, size);flush_tlb_all (); }
arch/s390/mm/init.c +3 -2
@@ -279,12 +279,13 @@ int arch_add_memory(int nid, u64 start, u64 size, return rc; } -void arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap) +void arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap, +struct dev_pagemap *pgmap) {unsigned long start_pfn = start >>PAGE_SHIFT ;unsigned long nr_pages = size >>PAGE_SHIFT ; -__remove_pages (start_pfn, nr_pages, altmap); +__remove_pages (start_pfn, nr_pages, altmap, pgmap);vmem_remove_mapping (start, size); }#endif /* CONFIG_MEMORY_HOTPLUG */
arch/x86/mm/init_64.c +3 -2
@@ -1307,12 +1307,13 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end) remove_pagetable(start, end, true, NULL); } -void __ref arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap) +void __ref arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap, +struct dev_pagemap *pgmap) {unsigned long start_pfn = start >>PAGE_SHIFT ;unsigned long nr_pages = size >>PAGE_SHIFT ; -__remove_pages (start_pfn, nr_pages, altmap); +__remove_pages (start_pfn, nr_pages, altmap, pgmap);kernel_physical_mapping_remove (start, start + size); }#endif /* CONFIG_MEMORY_HOTPLUG */
include/linux/memory_hotplug.h +5 -3
@@ -151,9 +151,10 @@ static inline bool movable_node_is_enabled(void)return movable_node_enabled; } -extern void arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap); +extern void arch_remove_memory (u64 start,u64 size,struct vmem_altmap *altmap, +struct dev_pagemap *pgmap);extern void __remove_pages (unsigned long start_pfn,unsigned long nr_pages, -struct vmem_altmap *altmap); +struct vmem_altmap *altmap,struct dev_pagemap *pgmap);/* reasonably generic interface to expand the physical pages */ extern int __add_pages(int nid, unsigned long start_pfn, unsigned long nr_pages,
@@ -323,7 +324,8 @@ extern int sparse_add_section(int nid, unsigned long pfn, unsigned long nr_pages, struct vmem_altmap *altmap, struct dev_pagemap *pgmap);extern void sparse_remove_section (unsigned long pfn,unsigned long nr_pages, -struct vmem_altmap *altmap); +struct vmem_altmap *altmap, +struct dev_pagemap *pgmap);extern struct page *sparse_decode_mem_map (unsigned long coded_mem_map,unsigned long pnum); extern struct zone *zone_for_pfn_range(int online_type, int nid,
include/linux/mmzone.h +2 -6
@@ -1900,15 +1900,13 @@ struct mem_section_usage {unsigned long pageblock_flags[0 ]; }; -void subsection_map_init (unsigned long pfn,unsigned long nr_pages); -struct page ;struct page_ext ;struct mem_section {/* * This is, logically, a pointer to an array of struct * pages. However, it is stored with some other magic. -* (see sparse.c::sparse_init_one_section()) +* (see sparse_init_one_section()) * * Additionally during early boot we encode node id of * the location of the section here to guide allocation.
@@ -2292,11 +2290,9 @@ static inline unsigned long next_present_section_nr(unsigned long section_nr)void sparse_init (void );#else #define sparse_init () do {} while (0) -#define sparse_index_init (_sec, _nid) do {} while (0) -#define sparse_vmemmap_init_nid_early (_nid, _use) do {} while (0) +#define sparse_vmemmap_init_nid_early (_nid) do {} while (0)#define sparse_vmemmap_init_nid_late (_nid) do {} while (0)#define pfn_in_present_section pfn_valid -#define subsection_map_init (_pfn, _nr_pages) do {} while (0)#endif /* CONFIG_SPARSEMEM */ /*
mm/Kconfig +1 -1
@@ -483,7 +483,7 @@ config ARCH_ENABLE_MEMORY_HOTREMOVE menuconfig MEMORY_HOTPLUG bool "Memory hotplug" select MEMORY_ISOLATION - depends on SPARSEMEM + depends on SPARSEMEM_VMEMMAP depends on ARCH_ENABLE_MEMORY_HOTPLUG depends on 64BIT select NUMA_KEEP_MEMINFO if NUMA
mm/internal.h +50 -0
56 lines not on this page · raw message
mm/memory_hotplug.c +7 -6
@@ -584,6 +584,7 @@ void remove_pfn_range_from_zone(struct zone *zone,* @pfn: starting pageframe (must be aligned to start of a section) * @nr_pages: number of pages to remove (must be multiple of section size) * @altmap: alternative device page map or %NULL if default memmap is used +* @pgmap: device page map or %NULL if not ZONE_DEVICE * * Generic helper function to remove section mappings and sysfs entries * for the section of the memory we are removing. Caller needs to make
@@ -591,7 +592,7 @@ void remove_pfn_range_from_zone(struct zone *zone,* calling offline_pages(). */ void __remove_pages (unsigned long pfn,unsigned long nr_pages, -struct vmem_altmap *altmap) +struct vmem_altmap *altmap,struct dev_pagemap *pgmap) {const unsigned long end_pfn = pfn + nr_pages;unsigned long cur_nr_pages;
@@ -606,7 +607,7 @@ void __remove_pages(unsigned long pfn, unsigned long nr_pages,/* Select all remaining pages up to the next section boundary */ cur_nr_pages =min (end_pfn - pfn,SECTION_ALIGN_UP (pfn +1 ) - pfn); -sparse_remove_section (pfn, cur_nr_pages, altmap); +sparse_remove_section (pfn, cur_nr_pages, altmap, pgmap); } }
@@ -1444,7 +1445,7 @@ static void remove_memory_blocks_and_altmaps(u64 start, u64 size)remove_memory_block_devices (cur_start, memblock_size); -arch_remove_memory (cur_start, memblock_size, altmap); +arch_remove_memory (cur_start, memblock_size, altmap,NULL );/* Verify that all vmemmap pages have actually been freed. */ WARN (altmap->alloc,"Altmap not fully unmapped" );
@@ -1489,7 +1490,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group, ret =create_memory_block_devices (cur_start, memblock_size, nid, params.altmap, group);if (ret) { -arch_remove_memory (cur_start, memblock_size,NULL ); +arch_remove_memory (cur_start, memblock_size, params.altmap,NULL );kfree (params.altmap); goto out; }
@@ -1575,7 +1576,7 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)/* create memory block devices after memory was added */ ret =create_memory_block_devices (start, size, nid,NULL , group);if (ret) { -arch_remove_memory (start, size, params.altmap); +arch_remove_memory (start, size, params.altmap,NULL ); goto error; } }
@@ -2282,7 +2283,7 @@ static int try_remove_memory(u64 start, u64 size)* No altmaps present, do the removal directly */ remove_memory_block_devices (start, size); -arch_remove_memory (start, size,NULL ); +arch_remove_memory (start, size,NULL ,NULL ); }else {/* all memblocks in the range have altmaps */ remove_memory_blocks_and_altmaps (start, size);
mm/memremap.c +2 -2
@@ -97,10 +97,10 @@ static void pageunmap_range(struct dev_pagemap *pgmap, int range_id) PHYS_PFN(range_len(range)));if (pgmap->type ==MEMORY_DEVICE_PRIVATE ) {__remove_pages (PHYS_PFN (range->start), -PHYS_PFN (range_len (range)),NULL ); +PHYS_PFN (range_len (range)),NULL , pgmap); }else {arch_remove_memory (range->start,range_len (range), -pgmap_altmap (pgmap)); +pgmap_altmap (pgmap), pgmap);kasan_remove_zero_shadow (__va (range->start),range_len (range)); }mem_hotplug_done ();
mm/mm_init.c +1 -1
@@ -1891,7 +1891,7 @@ void __init free_area_init(unsigned long *max_zone_pfn)pr_info (" node %3d: [mem %#018Lx-%#018Lx]\n" , nid, (u64 )start_pfn <<PAGE_SHIFT , ((u64 )end_pfn <<PAGE_SHIFT ) -1 ); -subsection_map_init (start_pfn, end_pfn - start_pfn); +sparse_init_subsection_map (start_pfn, end_pfn - start_pfn); }/* Initialise every node */
mm/sparse-vmemmap.c +322 -0
325 lines not on this page · raw message
mm/sparse.c +2 -426
467 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 mm/sparse-vmemmap: fix DAX vmemmap accounting with optimization
From: Muchun Song <[email protected]>
[ Upstream commit 721a73e30c9e3e8fcffe1725bcede1bbd20b4918 ]
When vmemmap optimization is enabled for DAX, the nr_memmap_pages counter in /proc/vmstat is incorrect. The current code always accounts for the full, non-optimized vmemmap size, but vmemmap optimization reduces the actual number of vmemmap pages by reusing tail pages. This causes the system to overcount vmemmap usage, leading to inaccurate page statistics in /proc/vmstat.
Fix this by introducing section_nr_vmemmap_pages(), which returns the exact vmemmap page count for a given pfn range based on whether optimization is in effect.
--- mm/sparse-vmemmap.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-)
mm/sparse-vmemmap.c +30 -4
@@ -679,6 +679,31 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn) } } +static int __meminit section_nr_vmemmap_pages (unsigned long pfn,unsigned long nr_pages, +struct vmem_altmap *altmap,struct dev_pagemap *pgmap) +{ +const unsigned int order = pgmap ? pgmap->vmemmap_shift :0 ; +const unsigned long pages_per_compound =1UL << order; + +VM_WARN_ON_ONCE (!IS_ALIGNED (pfn | nr_pages,PAGES_PER_SUBSECTION )); +VM_WARN_ON_ONCE (nr_pages >PAGES_PER_SECTION ); + +if (!vmemmap_can_optimize (altmap, pgmap)) +return DIV_ROUND_UP (nr_pages *sizeof (struct page ),PAGE_SIZE ); + +if (order <PFN_SECTION_SHIFT ) { +VM_WARN_ON_ONCE (!IS_ALIGNED (pfn | nr_pages, pages_per_compound)); +return VMEMMAP_RESERVE_NR * nr_pages / pages_per_compound; + } + +VM_WARN_ON_ONCE (!IS_ALIGNED (pfn | nr_pages,PAGES_PER_SECTION )); + +if (IS_ALIGNED (pfn, pages_per_compound)) +return VMEMMAP_RESERVE_NR ; + +return 0 ; +} +static struct page *__meminit populate_section_memmap (unsigned long pfn,unsigned long nr_pages,int nid,struct vmem_altmap *altmap,struct dev_pagemap *pgmap)
@@ -686,7 +711,7 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn,struct page *page =__populate_section_memmap (pfn, nr_pages, nid, altmap, pgmap); - memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); +memmap_pages_add (section_nr_vmemmap_pages (pfn, nr_pages, altmap, pgmap));return page; }
@@ -697,7 +722,7 @@ static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages,unsigned long start = (unsigned long )pfn_to_page (pfn);unsigned long end = start + nr_pages *sizeof (struct page ); -memmap_pages_add (-1L * (DIV_ROUND_UP (nr_pages *sizeof (struct page ),PAGE_SIZE ))); +memmap_pages_add (-section_nr_vmemmap_pages (pfn, nr_pages, altmap, pgmap));vmemmap_free (start, end, altmap); }
@@ -705,9 +730,10 @@ static void free_map_bootmem(struct page *memmap) {unsigned long start = (unsigned long )memmap;unsigned long end = (unsigned long )(memmap +PAGES_PER_SECTION ); +unsigned long pfn =page_to_pfn (memmap); -memmap_boot_pages_add (-1L * (DIV_ROUND_UP (PAGES_PER_SECTION *sizeof (struct page ), -PAGE_SIZE ))); +memmap_boot_pages_add (-section_nr_vmemmap_pages (pfn,PAGES_PER_SECTION , +NULL ,NULL ));vmemmap_free (start, end,NULL ); }
Signature · 1 line
2.53.0
Re: [PATCH 6.18.y 1/2] mm: prepare to move subsection_map_init() to mm/sparse-vmemmap.c
In reply to Sasha LevinQuoted 26 lines · Sasha Levin
From: "David Hildenbrand (Arm)" <[email protected]>
[ Upstream commit
fead6dcff83b02f8d6dc3c1ebbe4e09c05c54ee5]We want to move
subsection_map_init()tomm/sparse-vmemmap.c.To prepare for getting rid of
subsection_map_init()inmm/sparse.ccompletely, use a static inline function for !CONFIG_SPARSEMEM_VMEMMAP.While at it, move the declaration to
internal.hand rename it to "sparse_init_subsection_map()".Signed-off-by David Hildenbrand (Arm) <[email protected]>Reviewed-by Lorenzo Stoakes (Oracle) <[email protected]>Reviewed-by Mike Rapoport (Microsoft) <[email protected]>Cc Axel Rasmussen <[email protected]>Cc Liam Howlett <[email protected]>Cc Michal Hocko <[email protected]>Cc Oscar Salvador <[email protected]>Cc Sidhartha Kumar <[email protected]>Cc Suren Baghdasaryan <[email protected]>Cc Vlastimil Babka <[email protected]>Cc Wei Xu <[email protected]>Cc Yuanchu Xie <[email protected]>Signed-off-by Andrew Morton <[email protected]>
Quoted 2 lines
Stable-dep-of:
721a73e30c9e("mm/sparse-vmemmap: fix DAX vmemmap accounting with optimization")Signed-off-by Sasha Levin <[email protected]>
Both of these don't apply :(