* [PATCH 0/2] mm: fix PMD level mTHP accounting bugs
@ 2026-07-02 17:25 Nico Pache
2026-07-02 17:25 ` [PATCH 1/2] mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio() Nico Pache
2026-07-02 17:25 ` [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting Nico Pache
0 siblings, 2 replies; 9+ messages in thread
From: Nico Pache @ 2026-07-02 17:25 UTC (permalink / raw)
To: Barry Song, David Hildenbrand, linux-cxl, linux-kernel, linux-mm
Cc: ljs, willy, Nico Pache, Oscar Salvador, Andrew Morton, Zi Yan,
Matthew Brost, Joshua Hahn, Rakie Kim, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple
While running selftests I noticed the PMD level per-mTHP stats (nr_anon)
remained elevated after each run. After further investigation I noticed
this accounting error occurs for both the migration.private_anon_htlb_test
and the HMM tests.
In the HMM case this is due to folio_add_new_anon_rmap() incrementing the
mTHP stats, but never containing a corresponding decrement in
free_zone_device_folio(). We solve this by making sure to decrement the
counter when freeing device memory.
In the migration case, we are incrementing this counter without first
checking whether this folio is a hugetlb folio, which relies on a separate
accounting system. We solve this by adding the proper hugetlb check before
incrementing this counter.
With these changes in place, the two tests no longer cause elevated PMD
level accounting issues.
Co-developed-by: David Hildenbrand <david@kernel.org>
Signed-off-by: David Hildenbrand <david@kernel.org>
Signed-off-by: Nico Pache <npache@redhat.com>
Nico Pache (2):
mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio()
mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting
mm/memremap.c | 1 +
mm/migrate.c | 6 ++++--
2 files changed, 5 insertions(+), 2 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/2] mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio()
2026-07-02 17:25 [PATCH 0/2] mm: fix PMD level mTHP accounting bugs Nico Pache
@ 2026-07-02 17:25 ` Nico Pache
2026-07-02 17:31 ` Zi Yan
2026-07-02 17:25 ` [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting Nico Pache
1 sibling, 1 reply; 9+ messages in thread
From: Nico Pache @ 2026-07-02 17:25 UTC (permalink / raw)
To: Barry Song, David Hildenbrand, linux-cxl, linux-kernel, linux-mm
Cc: ljs, willy, Nico Pache, Oscar Salvador, Andrew Morton, Zi Yan,
Matthew Brost, Joshua Hahn, Rakie Kim, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple
When a zone device folio is mapped as anonymous, folio_add_new_anon_rmap()
increments MTHP_STAT_NR_ANON. The corresponding decrement lives in
__free_pages_prepare() in page_alloc.c, but zone device folios are freed
via free_zone_device_folio() which never calls __free_pages_prepare().
This causes nr_anon to remain permanently elevated after zone device
folios are freed.
Add the missing mod_mthp_stat() decrement to free_zone_device_folio()
so that the counter is properly balanced.
Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size")
Co-developed-by: David Hildenbrand <david@kernel.org>
Signed-off-by: David Hildenbrand <david@kernel.org>
Signed-off-by: Nico Pache <npache@redhat.com>
---
mm/memremap.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/mm/memremap.c b/mm/memremap.c
index 81766d822400..accba23aef28 100644
--- a/mm/memremap.c
+++ b/mm/memremap.c
@@ -425,6 +425,7 @@ void free_zone_device_folio(struct folio *folio)
mem_cgroup_uncharge(folio);
if (folio_test_anon(folio)) {
+ mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, -1);
for (i = 0; i < nr; i++)
__ClearPageAnonExclusive(folio_page(folio, i));
}
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting
2026-07-02 17:25 [PATCH 0/2] mm: fix PMD level mTHP accounting bugs Nico Pache
2026-07-02 17:25 ` [PATCH 1/2] mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio() Nico Pache
@ 2026-07-02 17:25 ` Nico Pache
2026-07-02 17:33 ` Zi Yan
2026-07-03 3:09 ` Baolin Wang
1 sibling, 2 replies; 9+ messages in thread
From: Nico Pache @ 2026-07-02 17:25 UTC (permalink / raw)
To: Barry Song, David Hildenbrand, linux-cxl, linux-kernel, linux-mm
Cc: ljs, willy, Nico Pache, Oscar Salvador, Andrew Morton, Zi Yan,
Matthew Brost, Joshua Hahn, Rakie Kim, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple
__folio_migrate_mapping() increments MTHP_STAT_NR_ANON for the
destination folio when `folio_test_anon(folio) && folio_test_large(folio)`
is true. However, hugetlb folios satisfy both conditions despite having a
completely separate accounting system; they use hugetlb_add_anon_rmap()
which does not touch mTHP stats, and their free path also bypasses the
mTHP decrement in __free_pages_prepare().
This causes MTHP_STAT_NR_ANON to be incremented on each hugetlb
migration without a corresponding decrement, permanently inflating the
nr_anon counter.
Add a !folio_test_hugetlb() check to both places in
__folio_migrate_mapping() so that only actual mTHP folios are counted.
Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size")
Co-developed-by: David Hildenbrand <david@kernel.org>
Signed-off-by: David Hildenbrand <david@kernel.org>
Signed-off-by: Nico Pache <npache@redhat.com>
---
mm/migrate.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/mm/migrate.c b/mm/migrate.c
index d9b23909d716..9fd50ea25d2d 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -590,7 +590,8 @@ static int __folio_migrate_mapping(struct address_space *mapping,
/* No turning back from here */
newfolio->index = folio->index;
newfolio->mapping = folio->mapping;
- if (folio_test_anon(folio) && folio_test_large(folio))
+ if (folio_test_anon(folio) && folio_test_large(folio) &&
+ !folio_test_hugetlb(folio))
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1);
if (folio_test_swapbacked(folio))
__folio_set_swapbacked(newfolio);
@@ -623,7 +624,8 @@ static int __folio_migrate_mapping(struct address_space *mapping,
*/
newfolio->index = folio->index;
newfolio->mapping = folio->mapping;
- if (folio_test_anon(folio) && folio_test_large(folio))
+ if (folio_test_anon(folio) && folio_test_large(folio) &&
+ !folio_test_hugetlb(folio))
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1);
folio_ref_add(newfolio, nr); /* add cache reference */
if (folio_test_swapbacked(folio))
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio()
2026-07-02 17:25 ` [PATCH 1/2] mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio() Nico Pache
@ 2026-07-02 17:31 ` Zi Yan
0 siblings, 0 replies; 9+ messages in thread
From: Zi Yan @ 2026-07-02 17:31 UTC (permalink / raw)
To: Nico Pache, Barry Song, David Hildenbrand, linux-cxl,
linux-kernel, linux-mm
Cc: ljs, willy, Oscar Salvador, Andrew Morton, Matthew Brost,
Joshua Hahn, Rakie Kim, Byungchul Park, Gregory Price, Ying Huang,
Alistair Popple
On Thu Jul 2, 2026 at 1:25 PM EDT, Nico Pache wrote:
> When a zone device folio is mapped as anonymous, folio_add_new_anon_rmap()
> increments MTHP_STAT_NR_ANON. The corresponding decrement lives in
> __free_pages_prepare() in page_alloc.c, but zone device folios are freed
> via free_zone_device_folio() which never calls __free_pages_prepare().
> This causes nr_anon to remain permanently elevated after zone device
> folios are freed.
>
> Add the missing mod_mthp_stat() decrement to free_zone_device_folio()
> so that the counter is properly balanced.
>
> Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size")
> Co-developed-by: David Hildenbrand <david@kernel.org>
> Signed-off-by: David Hildenbrand <david@kernel.org>
> Signed-off-by: Nico Pache <npache@redhat.com>
> ---
> mm/memremap.c | 1 +
> 1 file changed, 1 insertion(+)
>
LGTM.
Reviewed-by: Zi Yan <ziy@nvidia.com>
--
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting
2026-07-02 17:25 ` [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting Nico Pache
@ 2026-07-02 17:33 ` Zi Yan
2026-07-03 3:09 ` Baolin Wang
1 sibling, 0 replies; 9+ messages in thread
From: Zi Yan @ 2026-07-02 17:33 UTC (permalink / raw)
To: Nico Pache, Barry Song, David Hildenbrand, linux-cxl,
linux-kernel, linux-mm
Cc: ljs, willy, Oscar Salvador, Andrew Morton, Matthew Brost,
Joshua Hahn, Rakie Kim, Byungchul Park, Gregory Price, Ying Huang,
Alistair Popple
On Thu Jul 2, 2026 at 1:25 PM EDT, Nico Pache wrote:
> __folio_migrate_mapping() increments MTHP_STAT_NR_ANON for the
> destination folio when `folio_test_anon(folio) && folio_test_large(folio)`
> is true. However, hugetlb folios satisfy both conditions despite having a
> completely separate accounting system; they use hugetlb_add_anon_rmap()
> which does not touch mTHP stats, and their free path also bypasses the
> mTHP decrement in __free_pages_prepare().
>
> This causes MTHP_STAT_NR_ANON to be incremented on each hugetlb
> migration without a corresponding decrement, permanently inflating the
> nr_anon counter.
>
> Add a !folio_test_hugetlb() check to both places in
> __folio_migrate_mapping() so that only actual mTHP folios are counted.
>
> Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size")
> Co-developed-by: David Hildenbrand <david@kernel.org>
> Signed-off-by: David Hildenbrand <david@kernel.org>
> Signed-off-by: Nico Pache <npache@redhat.com>
> ---
> mm/migrate.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
LGTM. Thanks.
Reviewed-by: Zi Yan <ziy@nvidia.com>
--
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting
2026-07-02 17:25 ` [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting Nico Pache
2026-07-02 17:33 ` Zi Yan
@ 2026-07-03 3:09 ` Baolin Wang
2026-07-03 7:13 ` David Hildenbrand (Arm)
2026-07-03 12:57 ` Nico Pache
1 sibling, 2 replies; 9+ messages in thread
From: Baolin Wang @ 2026-07-03 3:09 UTC (permalink / raw)
To: Nico Pache, Barry Song, David Hildenbrand, linux-cxl,
linux-kernel, linux-mm
Cc: ljs, willy, Oscar Salvador, Andrew Morton, Zi Yan, Matthew Brost,
Joshua Hahn, Rakie Kim, Byungchul Park, Gregory Price, Ying Huang,
Alistair Popple
On 7/3/26 1:25 AM, Nico Pache wrote:
> __folio_migrate_mapping() increments MTHP_STAT_NR_ANON for the
> destination folio when `folio_test_anon(folio) && folio_test_large(folio)`
> is true. However, hugetlb folios satisfy both conditions despite having a
> completely separate accounting system; they use hugetlb_add_anon_rmap()
> which does not touch mTHP stats, and their free path also bypasses the
> mTHP decrement in __free_pages_prepare().
>
> This causes MTHP_STAT_NR_ANON to be incremented on each hugetlb
> migration without a corresponding decrement, permanently inflating the
> nr_anon counter.
>
> Add a !folio_test_hugetlb() check to both places in
> __folio_migrate_mapping() so that only actual mTHP folios are counted.
>
> Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size")
> Co-developed-by: David Hildenbrand <david@kernel.org>
> Signed-off-by: David Hildenbrand <david@kernel.org>
> Signed-off-by: Nico Pache <npache@redhat.com>
> ---
> mm/migrate.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/mm/migrate.c b/mm/migrate.c
> index d9b23909d716..9fd50ea25d2d 100644
> --- a/mm/migrate.c
> +++ b/mm/migrate.c
> @@ -590,7 +590,8 @@ static int __folio_migrate_mapping(struct address_space *mapping,
> /* No turning back from here */
> newfolio->index = folio->index;
> newfolio->mapping = folio->mapping;
> - if (folio_test_anon(folio) && folio_test_large(folio))
> + if (folio_test_anon(folio) && folio_test_large(folio) &&
> + !folio_test_hugetlb(folio))
> mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1);
Yes, anonymous hugetlb mappings can reach here, so this check looks
reasonable to me.
> if (folio_test_swapbacked(folio))
> __folio_set_swapbacked(newfolio);
> @@ -623,7 +624,8 @@ static int __folio_migrate_mapping(struct address_space *mapping,
> */
> newfolio->index = folio->index;
> newfolio->mapping = folio->mapping;
> - if (folio_test_anon(folio) && folio_test_large(folio))
> + if (folio_test_anon(folio) && folio_test_large(folio) &&
> + !folio_test_hugetlb(folio))
If the hugetlb folio has a non-NULL mapping (i.e., it's a shared
mapping), we should migrate it via
hugetlbfs_migrate_folio()->migrate_huge_page_move_mapping(). In other
words, shared hugetlb mappings should not reach this path, so this
hugetlb check can be dropped. Or am I missing something?
> mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1);
> folio_ref_add(newfolio, nr); /* add cache reference */
> if (folio_test_swapbacked(folio))
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting
2026-07-03 3:09 ` Baolin Wang
@ 2026-07-03 7:13 ` David Hildenbrand (Arm)
2026-07-03 8:25 ` Richard Cheng
2026-07-03 12:57 ` Nico Pache
1 sibling, 1 reply; 9+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-03 7:13 UTC (permalink / raw)
To: Baolin Wang, Nico Pache, Barry Song, linux-cxl, linux-kernel,
linux-mm
Cc: ljs, willy, Oscar Salvador, Andrew Morton, Zi Yan, Matthew Brost,
Joshua Hahn, Rakie Kim, Byungchul Park, Gregory Price, Ying Huang,
Alistair Popple
>> newfolio->index = folio->index;
>> newfolio->mapping = folio->mapping;
>> - if (folio_test_anon(folio) && folio_test_large(folio))
>> + if (folio_test_anon(folio) && folio_test_large(folio) &&
>> + !folio_test_hugetlb(folio))
>
> If the hugetlb folio has a non-NULL mapping (i.e., it's a shared mapping), we
> should migrate it via hugetlbfs_migrate_folio()-
>>migrate_huge_page_move_mapping(). In other words, shared hugetlb mappings
> should not reach this path, so this hugetlb check can be dropped. Or am I
> missing something?
Good point: we should only reach here with anonymous folios that are in the
swapcache. That doesn't apply to hugetlb.
--
Cheers,
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting
2026-07-03 7:13 ` David Hildenbrand (Arm)
@ 2026-07-03 8:25 ` Richard Cheng
0 siblings, 0 replies; 9+ messages in thread
From: Richard Cheng @ 2026-07-03 8:25 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Baolin Wang, Nico Pache, Barry Song, linux-cxl, linux-kernel,
linux-mm, ljs, willy, Oscar Salvador, Andrew Morton, Zi Yan,
Matthew Brost, Joshua Hahn, Rakie Kim, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple
On Fri, Jul 03, 2026 at 09:13:59AM +0800, David Hildenbrand (Arm) wrote:
>
> >> newfolio->index = folio->index;
> >> newfolio->mapping = folio->mapping;
> >> - if (folio_test_anon(folio) && folio_test_large(folio))
> >> + if (folio_test_anon(folio) && folio_test_large(folio) &&
> >> + !folio_test_hugetlb(folio))
> >
> > If the hugetlb folio has a non-NULL mapping (i.e., it's a shared mapping), we
> > should migrate it via hugetlbfs_migrate_folio()-
> >>migrate_huge_page_move_mapping(). In other words, shared hugetlb mappings
> > should not reach this path, so this hugetlb check can be dropped. Or am I
> > missing something?
>
> Good point: we should only reach here with anonymous folios that are in the
> swapcache. That doesn't apply to hugetlb.
>
> --
> Cheers,
>
> David
>
I think this is right. THe second site requires folio_test_anon() with a
non-NULL mapping, i. e. an anon folio in the swap cache, and hugetlb folios
never enter the swap cache. Shared hugetlb goes through migrate_huge_page_move_mapping()
as you say, and the only other callers of folio_migrate_mapping() never pass
hugetlb folios either. So the check in the second hunk can't fire.
--Richard
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting
2026-07-03 3:09 ` Baolin Wang
2026-07-03 7:13 ` David Hildenbrand (Arm)
@ 2026-07-03 12:57 ` Nico Pache
1 sibling, 0 replies; 9+ messages in thread
From: Nico Pache @ 2026-07-03 12:57 UTC (permalink / raw)
To: Baolin Wang
Cc: Barry Song, David Hildenbrand, linux-cxl, linux-kernel, linux-mm,
ljs, willy, Oscar Salvador, Andrew Morton, Zi Yan, Matthew Brost,
Joshua Hahn, Rakie Kim, Byungchul Park, Gregory Price, Ying Huang,
Alistair Popple
On Thu, Jul 2, 2026 at 9:10 PM Baolin Wang
<baolin.wang@linux.alibaba.com> wrote:
>
>
>
> On 7/3/26 1:25 AM, Nico Pache wrote:
> > __folio_migrate_mapping() increments MTHP_STAT_NR_ANON for the
> > destination folio when `folio_test_anon(folio) && folio_test_large(folio)`
> > is true. However, hugetlb folios satisfy both conditions despite having a
> > completely separate accounting system; they use hugetlb_add_anon_rmap()
> > which does not touch mTHP stats, and their free path also bypasses the
> > mTHP decrement in __free_pages_prepare().
> >
> > This causes MTHP_STAT_NR_ANON to be incremented on each hugetlb
> > migration without a corresponding decrement, permanently inflating the
> > nr_anon counter.
> >
> > Add a !folio_test_hugetlb() check to both places in
> > __folio_migrate_mapping() so that only actual mTHP folios are counted.
> >
> > Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size")
> > Co-developed-by: David Hildenbrand <david@kernel.org>
> > Signed-off-by: David Hildenbrand <david@kernel.org>
> > Signed-off-by: Nico Pache <npache@redhat.com>
> > ---
> > mm/migrate.c | 6 ++++--
> > 1 file changed, 4 insertions(+), 2 deletions(-)
> >
> > diff --git a/mm/migrate.c b/mm/migrate.c
> > index d9b23909d716..9fd50ea25d2d 100644
> > --- a/mm/migrate.c
> > +++ b/mm/migrate.c
> > @@ -590,7 +590,8 @@ static int __folio_migrate_mapping(struct address_space *mapping,
> > /* No turning back from here */
> > newfolio->index = folio->index;
> > newfolio->mapping = folio->mapping;
> > - if (folio_test_anon(folio) && folio_test_large(folio))
> > + if (folio_test_anon(folio) && folio_test_large(folio) &&
> > + !folio_test_hugetlb(folio))
> > mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1);
>
> Yes, anonymous hugetlb mappings can reach here, so this check looks
> reasonable to me.
>
> > if (folio_test_swapbacked(folio))
> > __folio_set_swapbacked(newfolio);
> > @@ -623,7 +624,8 @@ static int __folio_migrate_mapping(struct address_space *mapping,
> > */
> > newfolio->index = folio->index;
> > newfolio->mapping = folio->mapping;
> > - if (folio_test_anon(folio) && folio_test_large(folio))
> > + if (folio_test_anon(folio) && folio_test_large(folio) &&
> > + !folio_test_hugetlb(folio))
>
> If the hugetlb folio has a non-NULL mapping (i.e., it's a shared
> mapping), we should migrate it via
> hugetlbfs_migrate_folio()->migrate_huge_page_move_mapping(). In other
> words, shared hugetlb mappings should not reach this path, so this
> hugetlb check can be dropped. Or am I missing something?
I double checked (and others seem to have confirmed), you are correct;
these hugetlb pages shouldn't be reachable here.
Ill drop this hunk and retest :)
Thank you,
-- Nico
>
> > mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1);
> > folio_ref_add(newfolio, nr); /* add cache reference */
> > if (folio_test_swapbacked(folio))
>
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-07-03 12:56 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-02 17:25 [PATCH 0/2] mm: fix PMD level mTHP accounting bugs Nico Pache
2026-07-02 17:25 ` [PATCH 1/2] mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio() Nico Pache
2026-07-02 17:31 ` Zi Yan
2026-07-02 17:25 ` [PATCH 2/2] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting Nico Pache
2026-07-02 17:33 ` Zi Yan
2026-07-03 3:09 ` Baolin Wang
2026-07-03 7:13 ` David Hildenbrand (Arm)
2026-07-03 8:25 ` Richard Cheng
2026-07-03 12:57 ` Nico Pache
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox