Skip to content

Commit 9d85731

Browse files
soleenakpm00
authored andcommitted
mm: don't account memmap per-node
Fix invalid access to pgdat during hot-remove operation: ndctl users reported a GPF when trying to destroy a namespace: $ ndctl destroy-namespace all -r all -f Segmentation fault dmesg: Oops: general protection fault, probably for non-canonical address 0xdffffc0000005650: 0000 [#1] PREEMPT SMP KASAN PTI KASAN: probably user-memory-access in range [0x000000000002b280-0x000000000002b287] CPU: 26 UID: 0 PID: 1868 Comm: ndctl Not tainted 6.11.0-rc1 #1 Hardware name: Dell Inc. PowerEdge R640/08HT8T, BIOS 2.20.1 09/13/2023 RIP: 0010:mod_node_page_state+0x2a/0x110 cxl-test users report a GPF when trying to unload the test module: $ modrpobe -r cxl-test dmesg BUG: unable to handle page fault for address: 0000000000004200 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: Oops: 0000 [#1] PREEMPT SMP PTI CPU: 0 UID: 0 PID: 1076 Comm: modprobe Tainted: G O N 6.11.0-rc1 #197 Tainted: [O]=OOT_MODULE, [N]=TEST Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/15 RIP: 0010:mod_node_page_state+0x6/0x90 Currently, when memory is hot-plugged or hot-removed the accounting is done based on the assumption that memmap is allocated from the same node as the hot-plugged/hot-removed memory, which is not always the case. In addition, there are challenges with keeping the node id of the memory that is being remove to the time when memmap accounting is actually performed: since this is done after remove_pfn_range_from_zone(), and also after remove_memory_block_devices(). Meaning that we cannot use pgdat nor walking though memblocks to get the nid. Given all of that, account the memmap overhead system wide instead. For this we are going to be using global atomic counters, but given that memmap size is rarely modified, and normally is only modified either during early boot when there is only one CPU, or under a hotplug global mutex lock, therefore there is no need for per-cpu optimizations. Also, while we are here rename nr_memmap to nr_memmap_pages, and nr_memmap_boot to nr_memmap_boot_pages to be self explanatory that the units are in page count. [pasha.tatashin@soleen.com: address a few nits from David Hildenbrand] Link: https://lkml.kernel.org/r/20240809191020.1142142-4-pasha.tatashin@soleen.com Link: https://lkml.kernel.org/r/20240809191020.1142142-4-pasha.tatashin@soleen.com Link: https://lkml.kernel.org/r/20240808213437.682006-4-pasha.tatashin@soleen.com Fixes: 15995a3 ("mm: report per-page metadata information") Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com> Reported-by: Yi Zhang <yi.zhang@redhat.com> Closes: https://lore.kernel.org/linux-cxl/CAHj4cs9Ax1=CoJkgBGP_+sNu6-6=6v=_L-ZBZY0bVLD3wUWZQg@mail.gmail.com Reported-by: Alison Schofield <alison.schofield@intel.com> Closes: https://lore.kernel.org/linux-mm/Zq0tPd2h6alFz8XF@aschofie-mobl2/#t Tested-by: Dan Williams <dan.j.williams@intel.com> Tested-by: Alison Schofield <alison.schofield@intel.com> Acked-by: David Hildenbrand <david@redhat.com> Acked-by: David Rientjes <rientjes@google.com> Tested-by: Yi Zhang <yi.zhang@redhat.com> Cc: Domenico Cerasuolo <cerasuolodomenico@gmail.com> Cc: Fan Ni <fan.ni@samsung.com> Cc: Joel Granados <j.granados@samsung.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Li Zhijian <lizhijian@fujitsu.com> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Mike Rapoport <rppt@kernel.org> Cc: Muchun Song <muchun.song@linux.dev> Cc: Nhat Pham <nphamcs@gmail.com> Cc: Sourav Panda <souravpanda@google.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Yosry Ahmed <yosryahmed@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent f4cb78a commit 9d85731

File tree

9 files changed

+41
-60
lines changed

9 files changed

+41
-60
lines changed

include/linux/mmzone.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,6 @@ enum node_stat_item {
220220
PGDEMOTE_KSWAPD,
221221
PGDEMOTE_DIRECT,
222222
PGDEMOTE_KHUGEPAGED,
223-
NR_MEMMAP, /* page metadata allocated through buddy allocator */
224-
NR_MEMMAP_BOOT, /* page metadata allocated through boot allocator */
225223
NR_VM_NODE_STAT_ITEMS
226224
};
227225

include/linux/vmstat.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ struct reclaim_stat {
3838
enum vm_stat_item {
3939
NR_DIRTY_THRESHOLD,
4040
NR_DIRTY_BG_THRESHOLD,
41+
NR_MEMMAP_PAGES, /* page metadata allocated through buddy allocator */
42+
NR_MEMMAP_BOOT_PAGES, /* page metadata allocated through boot allocator */
4143
NR_VM_STAT_ITEMS,
4244
};
4345

@@ -618,7 +620,6 @@ static inline void lruvec_stat_sub_folio(struct folio *folio,
618620
lruvec_stat_mod_folio(folio, idx, -folio_nr_pages(folio));
619621
}
620622

621-
void __meminit mod_node_early_perpage_metadata(int nid, long delta);
622-
void __meminit store_early_perpage_metadata(void);
623-
623+
void memmap_boot_pages_add(long delta);
624+
void memmap_pages_add(long delta);
624625
#endif /* _LINUX_VMSTAT_H */

mm/hugetlb_vmemmap.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,11 @@ static int vmemmap_remap_range(unsigned long start, unsigned long end,
185185
static inline void free_vmemmap_page(struct page *page)
186186
{
187187
if (PageReserved(page)) {
188+
memmap_boot_pages_add(-1);
188189
free_bootmem_page(page);
189-
mod_node_page_state(page_pgdat(page), NR_MEMMAP_BOOT, -1);
190190
} else {
191+
memmap_pages_add(-1);
191192
__free_page(page);
192-
mod_node_page_state(page_pgdat(page), NR_MEMMAP, -1);
193193
}
194194
}
195195

@@ -341,7 +341,7 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
341341
copy_page(page_to_virt(walk.reuse_page),
342342
(void *)walk.reuse_addr);
343343
list_add(&walk.reuse_page->lru, vmemmap_pages);
344-
mod_node_page_state(NODE_DATA(nid), NR_MEMMAP, 1);
344+
memmap_pages_add(1);
345345
}
346346

347347
/*
@@ -396,7 +396,7 @@ static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
396396
goto out;
397397
list_add(&page->lru, list);
398398
}
399-
mod_node_page_state(NODE_DATA(nid), NR_MEMMAP, nr_pages);
399+
memmap_pages_add(nr_pages);
400400

401401
return 0;
402402
out:

mm/mm_init.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,8 +1623,7 @@ static void __init alloc_node_mem_map(struct pglist_data *pgdat)
16231623
panic("Failed to allocate %ld bytes for node %d memory map\n",
16241624
size, pgdat->node_id);
16251625
pgdat->node_mem_map = map + offset;
1626-
mod_node_early_perpage_metadata(pgdat->node_id,
1627-
DIV_ROUND_UP(size, PAGE_SIZE));
1626+
memmap_boot_pages_add(DIV_ROUND_UP(size, PAGE_SIZE));
16281627
pr_debug("%s: node %d, pgdat %08lx, node_mem_map %08lx\n",
16291628
__func__, pgdat->node_id, (unsigned long)pgdat,
16301629
(unsigned long)pgdat->node_mem_map);

mm/page_alloc.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5755,7 +5755,6 @@ void __init setup_per_cpu_pageset(void)
57555755
for_each_online_pgdat(pgdat)
57565756
pgdat->per_cpu_nodestats =
57575757
alloc_percpu(struct per_cpu_nodestat);
5758-
store_early_perpage_metadata();
57595758
}
57605759

57615760
__meminit void zone_pcp_init(struct zone *zone)

mm/page_ext.c

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,7 @@ static int __init alloc_node_page_ext(int nid)
214214
return -ENOMEM;
215215
NODE_DATA(nid)->node_page_ext = base;
216216
total_usage += table_size;
217-
mod_node_page_state(NODE_DATA(nid), NR_MEMMAP_BOOT,
218-
DIV_ROUND_UP(table_size, PAGE_SIZE));
217+
memmap_boot_pages_add(DIV_ROUND_UP(table_size, PAGE_SIZE));
219218
return 0;
220219
}
221220

@@ -275,10 +274,8 @@ static void *__meminit alloc_page_ext(size_t size, int nid)
275274
else
276275
addr = vzalloc_node(size, nid);
277276

278-
if (addr) {
279-
mod_node_page_state(NODE_DATA(nid), NR_MEMMAP,
280-
DIV_ROUND_UP(size, PAGE_SIZE));
281-
}
277+
if (addr)
278+
memmap_pages_add(DIV_ROUND_UP(size, PAGE_SIZE));
282279

283280
return addr;
284281
}
@@ -323,25 +320,18 @@ static void free_page_ext(void *addr)
323320
{
324321
size_t table_size;
325322
struct page *page;
326-
struct pglist_data *pgdat;
327323

328324
table_size = page_ext_size * PAGES_PER_SECTION;
325+
memmap_pages_add(-1L * (DIV_ROUND_UP(table_size, PAGE_SIZE)));
329326

330327
if (is_vmalloc_addr(addr)) {
331-
page = vmalloc_to_page(addr);
332-
pgdat = page_pgdat(page);
333328
vfree(addr);
334329
} else {
335330
page = virt_to_page(addr);
336-
pgdat = page_pgdat(page);
337331
BUG_ON(PageReserved(page));
338332
kmemleak_free(addr);
339333
free_pages_exact(addr, table_size);
340334
}
341-
342-
mod_node_page_state(pgdat, NR_MEMMAP,
343-
-1L * (DIV_ROUND_UP(table_size, PAGE_SIZE)));
344-
345335
}
346336

347337
static void __free_page_ext(unsigned long pfn)

mm/sparse-vmemmap.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -469,13 +469,10 @@ struct page * __meminit __populate_section_memmap(unsigned long pfn,
469469
if (r < 0)
470470
return NULL;
471471

472-
if (system_state == SYSTEM_BOOTING) {
473-
mod_node_early_perpage_metadata(nid, DIV_ROUND_UP(end - start,
474-
PAGE_SIZE));
475-
} else {
476-
mod_node_page_state(NODE_DATA(nid), NR_MEMMAP,
477-
DIV_ROUND_UP(end - start, PAGE_SIZE));
478-
}
472+
if (system_state == SYSTEM_BOOTING)
473+
memmap_boot_pages_add(DIV_ROUND_UP(end - start, PAGE_SIZE));
474+
else
475+
memmap_pages_add(DIV_ROUND_UP(end - start, PAGE_SIZE));
479476

480477
return pfn_to_page(pfn);
481478
}

mm/sparse.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ static void __init sparse_buffer_init(unsigned long size, int nid)
463463
sparsemap_buf = memmap_alloc(size, section_map_size(), addr, nid, true);
464464
sparsemap_buf_end = sparsemap_buf + size;
465465
#ifndef CONFIG_SPARSEMEM_VMEMMAP
466-
mod_node_early_perpage_metadata(nid, DIV_ROUND_UP(size, PAGE_SIZE));
466+
memmap_boot_pages_add(DIV_ROUND_UP(size, PAGE_SIZE));
467467
#endif
468468
}
469469

@@ -643,8 +643,7 @@ static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages,
643643
unsigned long start = (unsigned long) pfn_to_page(pfn);
644644
unsigned long end = start + nr_pages * sizeof(struct page);
645645

646-
mod_node_page_state(page_pgdat(pfn_to_page(pfn)), NR_MEMMAP,
647-
-1L * (DIV_ROUND_UP(end - start, PAGE_SIZE)));
646+
memmap_pages_add(-1L * (DIV_ROUND_UP(end - start, PAGE_SIZE)));
648647
vmemmap_free(start, end, altmap);
649648
}
650649
static void free_map_bootmem(struct page *memmap)

mm/vmstat.c

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,24 @@ unsigned long node_page_state(struct pglist_data *pgdat,
10331033
}
10341034
#endif
10351035

1036+
/*
1037+
* Count number of pages "struct page" and "struct page_ext" consume.
1038+
* nr_memmap_boot_pages: # of pages allocated by boot allocator
1039+
* nr_memmap_pages: # of pages that were allocated by buddy allocator
1040+
*/
1041+
static atomic_long_t nr_memmap_boot_pages = ATOMIC_LONG_INIT(0);
1042+
static atomic_long_t nr_memmap_pages = ATOMIC_LONG_INIT(0);
1043+
1044+
void memmap_boot_pages_add(long delta)
1045+
{
1046+
atomic_long_add(delta, &nr_memmap_boot_pages);
1047+
}
1048+
1049+
void memmap_pages_add(long delta)
1050+
{
1051+
atomic_long_add(delta, &nr_memmap_pages);
1052+
}
1053+
10361054
#ifdef CONFIG_COMPACTION
10371055

10381056
struct contig_page_info {
@@ -1255,11 +1273,11 @@ const char * const vmstat_text[] = {
12551273
"pgdemote_kswapd",
12561274
"pgdemote_direct",
12571275
"pgdemote_khugepaged",
1258-
"nr_memmap",
1259-
"nr_memmap_boot",
12601276
/* system-wide enum vm_stat_item counters */
12611277
"nr_dirty_threshold",
12621278
"nr_dirty_background_threshold",
1279+
"nr_memmap_pages",
1280+
"nr_memmap_boot_pages",
12631281

12641282
#if defined(CONFIG_VM_EVENT_COUNTERS) || defined(CONFIG_MEMCG)
12651283
/* enum vm_event_item counters */
@@ -1827,6 +1845,8 @@ static void *vmstat_start(struct seq_file *m, loff_t *pos)
18271845

18281846
global_dirty_limits(v + NR_DIRTY_BG_THRESHOLD,
18291847
v + NR_DIRTY_THRESHOLD);
1848+
v[NR_MEMMAP_PAGES] = atomic_long_read(&nr_memmap_pages);
1849+
v[NR_MEMMAP_BOOT_PAGES] = atomic_long_read(&nr_memmap_boot_pages);
18301850
v += NR_VM_STAT_ITEMS;
18311851

18321852
#ifdef CONFIG_VM_EVENT_COUNTERS
@@ -2285,25 +2305,3 @@ static int __init extfrag_debug_init(void)
22852305
module_init(extfrag_debug_init);
22862306

22872307
#endif
2288-
2289-
/*
2290-
* Page metadata size (struct page and page_ext) in pages
2291-
*/
2292-
static unsigned long early_perpage_metadata[MAX_NUMNODES] __meminitdata;
2293-
2294-
void __meminit mod_node_early_perpage_metadata(int nid, long delta)
2295-
{
2296-
early_perpage_metadata[nid] += delta;
2297-
}
2298-
2299-
void __meminit store_early_perpage_metadata(void)
2300-
{
2301-
int nid;
2302-
struct pglist_data *pgdat;
2303-
2304-
for_each_online_pgdat(pgdat) {
2305-
nid = pgdat->node_id;
2306-
mod_node_page_state(NODE_DATA(nid), NR_MEMMAP_BOOT,
2307-
early_perpage_metadata[nid]);
2308-
}
2309-
}

0 commit comments

Comments
 (0)