summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHongfu Li <lihongfu@kylinos.cn>2026-09-23 10:05:16 +0800
committerAndrew Morton <akpm@linux-foundation.org>2026-10-13 15:54:50 -0700
commit9ffd65464199ff079ffccba2250e20174ef40075 (patch)
treecdac64c8b8216bfdb3e3bae28fc6066f49b31c96
parentd88f71be3d740ad5df1afaba5e78999f15dd39da (diff)
downloadlinux-next-9ffd65464199ff079ffccba2250e20174ef40075.tar.gz
linux-next-9ffd65464199ff079ffccba2250e20174ef40075.zip
mm/memcg: migrate per-node hugetlb lruvec stat together with hugetlb folio
memory.numa_stat exposes per-node hugetlb counters from per-node lruvec stats. These stats are accounted against folio_nid(): incremented on the folio's node when handed to a user, decremented when the folio is returned to the pool. During hugetlb folio migration, mem_cgroup_migrate() moves the charge to the new folio and drops the memcg data of the old one, so the free of the old folio right after the migration skips the memcg per-node lruvec decrement. The hugetlb count stays attributed to the old node for the rest of the life of the charge, while the target folio gets no increment on the new node; its later free decrements a counter that was never incremented. Migrate the per-node lruvec accounting alongside migration. Global memcg totals remain balanced because they track resource consumption, not node placement. Link: https://lore.kernel.org/20260923-for-hugetlb_state3-v2-2-e8a36245bfab@kylinos.cn Fixes: 05d4532b60e3 ("memcg/hugetlb: add hugeTLB counters to memcg") Signed-off-by: Hongfu Li <lihongfu@kylinos.cn> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Tested-by: Joshua Hahn <joshua.hahnjy@gmail.com> Reviewed-by: Joshua Hahn <joshua.hahnjy@gmail.com> Reviewed-by: Oscar Salvador <osalvador@suse.de> Acked-by: Muchun Song <muchun.song@linux.dev> Cc: David Hildenbrand <david@kernel.org> Cc: Shakeel Butt <shakeel.butt@linux.dev> Cc: Michal Hocko <mhocko@suse.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Nhat Pham <nphamcs@gmail.com> Cc: Michal Hocko <mhocko@kernel.org> Cc: Roman Gushchin <roman.gushchin@linux.dev> Cc: Chris Down <chris@chrisdown.name> Cc: <stable@vger.kernel.org>
-rw-r--r--include/linux/memcontrol.h8
-rw-r--r--mm/hugetlb.c25
-rw-r--r--mm/memcontrol.c5
3 files changed, 35 insertions, 3 deletions
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 64c183be8cbf..cd223f60b3a2 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -983,6 +983,9 @@ unsigned long lruvec_page_state_monotonic(const struct lruvec *lruvec,
unsigned long lruvec_page_state_local(const struct lruvec *lruvec,
enum node_stat_item idx);
+void mod_memcg_lruvec_state(struct lruvec *lruvec,
+ enum node_stat_item idx, int val);
+
void mem_cgroup_flush_stats(struct mem_cgroup *memcg);
void mem_cgroup_flush_stats_ratelimited(struct mem_cgroup *memcg);
@@ -1451,6 +1454,11 @@ static inline unsigned long lruvec_page_state_local(const struct lruvec *lruvec,
return node_page_state(lruvec_pgdat(lruvec), idx);
}
+static inline void mod_memcg_lruvec_state(struct lruvec *lruvec,
+ enum node_stat_item idx, int val)
+{
+}
+
static inline void mem_cgroup_flush_stats(struct mem_cgroup *memcg)
{
}
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index af0fdb41f5d7..c6080213937d 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -23,6 +23,7 @@
#include <linux/mmdebug.h>
#include <linux/sched/signal.h>
#include <linux/rmap.h>
+#include <linux/rcupdate.h>
#include <linux/string_choices.h>
#include <linux/string_helpers.h>
#include <linux/swap.h>
@@ -7378,12 +7379,36 @@ void folio_putback_hugetlb(struct folio *folio)
folio_put(folio);
}
+static void move_hugetlb_lruvec_stat(struct folio *old_folio,
+ struct folio *new_folio)
+{
+ struct mem_cgroup *memcg;
+ long nr_pages = folio_nr_pages(old_folio);
+ int old_nid = folio_nid(old_folio);
+ int new_nid = folio_nid(new_folio);
+
+ if (old_nid == new_nid)
+ return;
+
+ guard(rcu)();
+
+ memcg = folio_memcg(new_folio);
+ if (!memcg)
+ return;
+
+ mod_memcg_lruvec_state(mem_cgroup_lruvec(memcg, NODE_DATA(old_nid)),
+ NR_HUGETLB, -nr_pages);
+ mod_memcg_lruvec_state(mem_cgroup_lruvec(memcg, NODE_DATA(new_nid)),
+ NR_HUGETLB, nr_pages);
+}
+
void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,
enum migrate_reason reason)
{
struct hstate *h = folio_hstate(old_folio);
hugetlb_cgroup_migrate(old_folio, new_folio);
+ move_hugetlb_lruvec_stat(old_folio, new_folio);
folio_set_owner_migrate_reason(new_folio, reason);
/*
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 4d00748c8a5b..f2ad4de8d805 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1015,9 +1015,8 @@ static void __mod_memcg_lruvec_state(struct mem_cgroup_per_node *pn,
put_cpu();
}
-static void mod_memcg_lruvec_state(struct lruvec *lruvec,
- enum node_stat_item idx,
- int val)
+void mod_memcg_lruvec_state(struct lruvec *lruvec,
+ enum node_stat_item idx, int val)
{
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
struct mem_cgroup_per_node *pn;