The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] mm/thp: expose deferred split folio memory usage in meminfo
@ 2026-07-17  6:30 Ye Liu
  2026-07-17  8:08 ` Lorenzo Stoakes (ARM)
  2026-07-17  8:37 ` David Hildenbrand (Arm)
  0 siblings, 2 replies; 14+ messages in thread
From: Ye Liu @ 2026-07-17  6:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes
  Cc: Ye Liu, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Kairui Song, Qi Zheng,
	Shakeel Butt, Barry Song, Axel Rasmussen, Yuanchu Xie, Wei Xu,
	Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain,
	Lance Yang, Usama Arif, driver-core, linux-kernel, linux-mm,
	linux-fsdevel

From: Ye Liu <liuye@kylinos.cn>

Folios on the deferred split list hold physical memory that is
invisible in meminfo. When a THP becomes partially mapped, the
unmapped pages are removed from AnonPages but remain physically
allocated until the shrinker splits the folio. This creates a
memory accounting gap where used memory cannot be attributed to
any meminfo field.

Add NR_DEFERRED_SPLIT_PAGES to track the total memory consumed by
folios currently on the deferred_split_lru, updated via
mod_node_page_state() at all enqueue/dequeue points. The new field
DeferredSplitPages is visible in /proc/meminfo, /proc/vmstat, and
per-node /sys/devices/system/node/node*/meminfo.

Signed-off-by: Ye Liu <liuye@kylinos.cn>
---
 drivers/base/node.c    |  4 +++-
 fs/proc/meminfo.c      |  2 ++
 include/linux/mmzone.h |  1 +
 mm/huge_memory.c       | 24 +++++++++++++++++++++---
 mm/vmstat.c            |  1 +
 5 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/drivers/base/node.c b/drivers/base/node.c
index 3da91929ad4e..69463438300e 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -519,6 +519,7 @@ static ssize_t node_read_meminfo(struct device *dev,
 			     "Node %d ShmemPmdMapped: %8lu kB\n"
 			     "Node %d FileHugePages:  %8lu kB\n"
 			     "Node %d FilePmdMapped:  %8lu kB\n"
+			     "Node %d DeferredSplitPages: %8lu kB\n"
 #endif
 #ifdef CONFIG_UNACCEPTED_MEMORY
 			     "Node %d Unaccepted:     %8lu kB\n"
@@ -553,7 +554,8 @@ static ssize_t node_read_meminfo(struct device *dev,
 			     nid, K(node_page_state(pgdat, NR_SHMEM_THPS)),
 			     nid, K(node_page_state(pgdat, NR_SHMEM_PMDMAPPED)),
 			     nid, K(node_page_state(pgdat, NR_FILE_THPS)),
-			     nid, K(node_page_state(pgdat, NR_FILE_PMDMAPPED))
+			     nid, K(node_page_state(pgdat, NR_FILE_PMDMAPPED)),
+			     nid, K(node_page_state(pgdat, NR_DEFERRED_SPLIT_PAGES))
 #endif
 #ifdef CONFIG_UNACCEPTED_MEMORY
 			     ,
diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c
index b2813ff13cb2..76dbf51ad1e0 100644
--- a/fs/proc/meminfo.c
+++ b/fs/proc/meminfo.c
@@ -149,6 +149,8 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
 		    global_node_page_state(NR_FILE_THPS));
 	show_val_kb(m, "FilePmdMapped:  ",
 		    global_node_page_state(NR_FILE_PMDMAPPED));
+	show_val_kb(m, "DeferredSplitPages: ",
+		    global_node_page_state(NR_DEFERRED_SPLIT_PAGES));
 #endif
 
 #ifdef CONFIG_CMA
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 0507193b3ae3..1e75adb73d00 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -267,6 +267,7 @@ enum node_stat_item {
 	NR_FILE_THPS,
 	NR_FILE_PMDMAPPED,
 	NR_ANON_THPS,
+	NR_DEFERRED_SPLIT_PAGES, /* THP/mTHP pages pending deferred split */
 	NR_VMSCAN_WRITE,
 	NR_VMSCAN_IMMEDIATE,	/* Prioritise for reclaim when writeback ends */
 	NR_DIRTIED,		/* page dirtyings since bootup */
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index c642bec967fa..1094b844675c 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -77,6 +77,14 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
 					 struct shrink_control *sc);
 static bool split_underused_thp = true;
 
+#define deferred_split_pages_add(folio) \
+	mod_node_page_state(NODE_DATA(folio_nid(folio)), \
+			    NR_DEFERRED_SPLIT_PAGES, folio_nr_pages(folio))
+
+#define deferred_split_pages_del(folio) \
+	mod_node_page_state(NODE_DATA(folio_nid(folio)), \
+			    NR_DEFERRED_SPLIT_PAGES, -folio_nr_pages(folio))
+
 static atomic_t huge_zero_refcount;
 struct folio *huge_zero_folio __read_mostly;
 unsigned long huge_zero_pfn __read_mostly = ~0UL;
@@ -3913,8 +3921,10 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
 		struct lruvec *lruvec;
 
 		if (dequeue_deferred) {
-			__list_lru_del(&deferred_split_lru, lru,
-				       &folio->_deferred_list, folio_nid(folio));
+			if (__list_lru_del(&deferred_split_lru, lru,
+					  &folio->_deferred_list, folio_nid(folio)) &&
+			    folio_test_partially_mapped(folio))
+				deferred_split_pages_del(folio);
 			if (folio_test_partially_mapped(folio)) {
 				folio_clear_partially_mapped(folio);
 				mod_mthp_stat(old_order,
@@ -4415,6 +4425,7 @@ bool __folio_unqueue_deferred_split(struct folio *folio)
 	lru = list_lru_lock_irqsave(&deferred_split_lru, nid, &memcg, &flags);
 	if (__list_lru_del(&deferred_split_lru, lru, &folio->_deferred_list, nid)) {
 		if (folio_test_partially_mapped(folio)) {
+			deferred_split_pages_del(folio);
 			folio_clear_partially_mapped(folio);
 			mod_mthp_stat(folio_order(folio),
 				      MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
@@ -4473,6 +4484,8 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)
 		VM_WARN_ON_FOLIO(folio_test_partially_mapped(folio), folio);
 	}
 	__list_lru_add(&deferred_split_lru, lru, &folio->_deferred_list, nid, memcg);
+	if (partially_mapped)
+		deferred_split_pages_add(folio);
 	list_lru_unlock_irqrestore(lru, &flags);
 	rcu_read_unlock();
 }
@@ -4524,8 +4537,11 @@ static enum lru_status deferred_split_isolate(struct list_head *item,
 {
 	struct folio *folio = container_of(item, struct folio, _deferred_list);
 	struct list_head *freeable = cb_arg;
+	bool partially_mapped = folio_test_partially_mapped(folio);
 
 	if (folio_try_get(folio)) {
+		if (partially_mapped)
+			deferred_split_pages_del(folio);
 		list_lru_isolate_move(lru, item, freeable);
 		return LRU_REMOVED;
 	}
@@ -4535,7 +4551,8 @@ static enum lru_status deferred_split_isolate(struct list_head *item,
 	 * isolate: folio_unqueue_deferred_split() checks list_empty()
 	 * locklessly, so once removed the folio can be freed any time.
 	 */
-	if (folio_test_partially_mapped(folio)) {
+	if (partially_mapped) {
+		deferred_split_pages_del(folio);
 		folio_clear_partially_mapped(folio);
 		mod_mthp_stat(folio_order(folio),
 			      MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
@@ -4596,6 +4613,7 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
 					 &folio->_deferred_list,
 					 folio_nid(folio),
 					 folio_memcg(folio));
+			deferred_split_pages_add(folio);
 			rcu_read_unlock();
 		}
 		folio_put(folio);
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 3b5cb1031f72..ba71cc3e4c30 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1248,6 +1248,7 @@ const char * const vmstat_text[] = {
 	[I(NR_FILE_THPS)]			= "nr_file_hugepages",
 	[I(NR_FILE_PMDMAPPED)]			= "nr_file_pmdmapped",
 	[I(NR_ANON_THPS)]			= "nr_anon_transparent_hugepages",
+	[I(NR_DEFERRED_SPLIT_PAGES)]		= "nr_deferred_split_pages",
 	[I(NR_VMSCAN_WRITE)]			= "nr_vmscan_write",
 	[I(NR_VMSCAN_IMMEDIATE)]		= "nr_vmscan_immediate_reclaim",
 	[I(NR_DIRTIED)]				= "nr_dirtied",
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-07-17 10:29 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-17  6:30 [PATCH] mm/thp: expose deferred split folio memory usage in meminfo Ye Liu
2026-07-17  8:08 ` Lorenzo Stoakes (ARM)
2026-07-17  9:24   ` Ye Liu
2026-07-17  9:38     ` liuye
2026-07-17  9:47       ` Lorenzo Stoakes (ARM)
2026-07-17 10:29       ` Johannes Weiner
2026-07-17  8:37 ` David Hildenbrand (Arm)
2026-07-17  8:47   ` Lorenzo Stoakes (ARM)
2026-07-17  9:31   ` Ye Liu
2026-07-17  9:33     ` David Hildenbrand (Arm)
2026-07-17  9:48       ` Lorenzo Stoakes (ARM)
2026-07-17  9:54         ` Ye Liu
2026-07-17 10:29           ` David Hildenbrand (Arm)
2026-07-17 10:29     ` Vlastimil Babka (SUSE)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox