* [PATCH v2 0/2] mm/huge_memory: take a reference on a device DAX PUD copied by fork
@ 2026-11-05 17:00 Konstantin Khorenko
2026-11-05 17:00 ` [PATCH v2 1/2] mm/huge_memory: take a reference on a folio " Konstantin Khorenko
0 siblings, 1 reply; 6+ messages in thread
From: Konstantin Khorenko @ 2026-11-05 17:00 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Zi Yan, Lorenzo Stoakes,
Alistair Popple, Dan Williams
Cc: linux-mm, nvdimm, linux-kselftest, linux-kernel,
Konstantin Khorenko, stable
copy_huge_pud() copies a device DAX PUD into the child without the
reference, rmap and counter that vmf_insert_folio_pud() took, so the
child's unmap takes them from the parent. fork copies a device DAX VMA
once it has a guard region, so it takes no more than:
1. map a devdax instance with 1 GiB alignment and fault in a PUD;
2. MADV_GUARD_INSTALL a page elsewhere in the VMA;
3. fork, and let the child exit.
Patch 1 is the fix, patch 2 a selftest for it. I found it while
reworking copy_huge_pmd() for fork after David's review [1]; it is
independent of that series.
v2 is based on mm-hotfixes-unstable, as patch 1 is a fix for stable.
Testing
=======
* mm-hotfixes-unstable x86_64 KVM guest with CONFIG_DEBUG_VM
* pmem from memmap=3G!4G
* devdax made with "ndctl create-namespace -m devdax -a 1G":
+----------------------+--------------+------------------------------+
| kernel | fork_dax_pud | log |
+----------------------+--------------+------------------------------+
| mm-hotfixes-unstable | not ok | BUG: Bad rss-counter state |
| | | mm:... type:MM_FILEPAGES |
| | | val:-262144 |
+----------------------+--------------+------------------------------+
| + this series | ok | clean |
+----------------------+--------------+------------------------------+
A variant that writes to the device and forks three times also hit,
on mm-hotfixes-unstable:
WARNING: ./include/linux/rmap.h:159 at
__folio_large_mapcount_sanity_checks+0x90/0x150
With the series it is clean, and the guard-regions selftest passes.
The selftest only runs on a device given in FORK_DAX_PUD_DEVICE or
argv[1], as on an unfixed kernel it leaves that device's folio with a
mapcount below zero. On pmem emulated in RAM, with
CONFIG_X86_PMEM_LEGACY and CONFIG_DEV_DAX_PMEM:
boot with memmap=3G!4G
ndctl create-namespace -f -e namespace0.0 -m devdax -a 1G
FORK_DAX_PUD_DEVICE=/dev/dax0.0 ./fork_dax_pud
[1] https://lore.kernel.org/linux-mm/20261014-b4-prep-vstor-149847-copy_huge_pmd-recheck-special-pmd-v3-0-85eaec8417f6@virtuozzo.com/
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
Changes in v2:
- Patch 1: copy_huge_pud() takes only the VMAs, and the TODO moves next
to the rmap duplication, as in David's fix for the same bug:
https://lore.kernel.org/all/26862633-c3d1-4b82-8ca2-346cb78570d7@kernel.org/
- Patch 2: read_sysfs() and a new rss_file() in vm_util instead of local
helpers (Sashiko).
- Assisted-by moved from the patches to this cover letter.
- Rebased onto mm-hotfixes-unstable.
- Link to v1: https://patch.msgid.link/20261021-vstor-150143-copy_huge_pud-folio-refcount-v1-0-8bd88551f9be@virtuozzo.com
---
Konstantin Khorenko (2):
mm/huge_memory: take a reference on a folio PUD copied by fork
selftests/mm: test fork of a device DAX PUD
MAINTAINERS | 1 +
include/linux/huge_mm.h | 5 +-
include/linux/rmap.h | 22 ++++++
mm/huge_memory.c | 27 ++++---
mm/memory.c | 4 +-
tools/testing/selftests/mm/Makefile | 1 +
tools/testing/selftests/mm/fork_dax_pud.c | 114 ++++++++++++++++++++++++++++++
tools/testing/selftests/mm/run_vmtests.sh | 1 +
tools/testing/selftests/mm/vm_util.c | 21 ++++++
tools/testing/selftests/mm/vm_util.h | 1 +
10 files changed, 183 insertions(+), 14 deletions(-)
---
base-commit: af05d765a4476d2e48099148c80f9b13f462e309
change-id: 20261021-vstor-150143-copy_huge_pud-folio-refcount-ea269e668270
Best regards,
--
Konstantin Khorenko,
Virtuozzo Linux Kernel Team
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/2] mm/huge_memory: take a reference on a folio PUD copied by fork
2026-11-05 17:00 [PATCH v2 0/2] mm/huge_memory: take a reference on a device DAX PUD copied by fork Konstantin Khorenko
@ 2026-11-05 17:00 ` Konstantin Khorenko
2026-11-05 19:28 ` David Hildenbrand (Arm)
2026-11-05 20:19 ` Zi Yan
0 siblings, 2 replies; 6+ messages in thread
From: Konstantin Khorenko @ 2026-11-05 17:00 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Zi Yan, Lorenzo Stoakes,
Alistair Popple, Dan Williams
Cc: linux-mm, nvdimm, linux-kselftest, linux-kernel,
Konstantin Khorenko, stable
Device DAX maps its memory with vmf_insert_folio_pud(), which takes a
folio reference, adds a PUD rmap and accounts the pages to the mm.
zap_huge_pud() drops all three, but copy_huge_pud() copies the PUD into
the child without taking any of them. Each child's unmap then drops
what the parent took:
- the folio is a reference short, so with two children its refcount
drops to zero and free_zone_device_folio() runs while the parent
still maps it;
- the child's file counter goes negative, "Bad rss-counter state" when
its mm is freed;
- the parent's unmap drives the entire mapcount below zero.
fork copies the page tables of a device DAX mapping that has a guard
region, and commit f807123d578d ("mm: allow guard regions in file-backed
and read-only mappings") allowed MADV_GUARD_INSTALL there.
Take the reference, the rmap and the counter for a PUD that
vm_normal_page_pud() resolves to a page, as copy_present_ptes() does for
a PTE, with a new folio_dup_file_rmap_pud() helper. copy_huge_pud() now
needs both VMAs, so pass only them and take the mms from them.
Fixes: aed877c2b425 ("device/dax: properly refcount device dax pages when mapping")
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
include/linux/huge_mm.h | 5 ++---
include/linux/rmap.h | 22 ++++++++++++++++++++++
mm/huge_memory.c | 27 +++++++++++++++++++--------
mm/memory.c | 4 +---
4 files changed, 44 insertions(+), 14 deletions(-)
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index c745f7ad22987..fac05120ab774 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -12,9 +12,8 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long addr,
struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma);
bool huge_pmd_set_accessed(struct vm_fault *vmf);
-int copy_huge_pud(struct mm_struct *dst_mm, struct mm_struct *src_mm,
- pud_t *dst_pud, pud_t *src_pud, unsigned long addr,
- struct vm_area_struct *vma);
+int copy_huge_pud(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
+ pud_t *dst_pud, pud_t *src_pud, unsigned long addr);
#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
void huge_pud_set_accessed(struct vm_fault *vmf, pud_t orig_pud);
diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index 74cca0e3c7264..52fff8dc74213 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -564,6 +564,28 @@ static inline void folio_dup_file_rmap_pmd(struct folio *folio,
#endif
}
+/**
+ * folio_dup_file_rmap_pud - duplicate a PUD mapping of a page range of a folio
+ * @folio: The folio to duplicate the mapping of
+ * @page: The first page to duplicate the mapping of
+ * @dst_vma: The destination vm area
+ *
+ * The page range of the folio is defined by [page, page + HPAGE_PUD_NR)
+ *
+ * The caller needs to hold the page table lock.
+ */
+static inline void folio_dup_file_rmap_pud(struct folio *folio,
+ struct page *page, struct vm_area_struct *dst_vma)
+{
+#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && \
+ defined(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD)
+ __folio_dup_file_rmap(folio, page, HPAGE_PUD_NR, dst_vma,
+ PGTABLE_LEVEL_PUD);
+#else
+ WARN_ON_ONCE(true);
+#endif
+}
+
static __always_inline int __folio_try_dup_anon_rmap(struct folio *folio,
struct page *page, int nr_pages, struct vm_area_struct *dst_vma,
struct vm_area_struct *src_vma, enum pgtable_level level)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 14599d0fff013..30c21e85d21bf 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2072,11 +2072,13 @@ void touch_pud(struct vm_area_struct *vma, unsigned long addr,
update_mmu_cache_pud(vma, addr, pud);
}
-int copy_huge_pud(struct mm_struct *dst_mm, struct mm_struct *src_mm,
- pud_t *dst_pud, pud_t *src_pud, unsigned long addr,
- struct vm_area_struct *vma)
+int copy_huge_pud(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
+ pud_t *dst_pud, pud_t *src_pud, unsigned long addr)
{
+ struct mm_struct *dst_mm = dst_vma->vm_mm;
+ struct mm_struct *src_mm = src_vma->vm_mm;
spinlock_t *dst_ptl, *src_ptl;
+ struct page *page;
pud_t pud;
int ret;
@@ -2089,11 +2091,20 @@ int copy_huge_pud(struct mm_struct *dst_mm, struct mm_struct *src_mm,
if (unlikely(!pud_trans_huge(pud)))
goto out_unlock;
- /*
- * TODO: once we support anonymous pages, use
- * folio_try_dup_anon_rmap_*() and split if duplicating fails.
- */
- if (vma_is_cow_mapping(vma) && pud_write(pud)) {
+ page = vm_normal_page_pud(src_vma, addr, pud);
+ if (page) {
+ struct folio *folio = page_folio(page);
+
+ folio_get(folio);
+ /*
+ * TODO: once we support anonymous pages, use
+ * folio_try_dup_anon_rmap_*() and split if duplicating fails.
+ */
+ folio_dup_file_rmap_pud(folio, page, dst_vma);
+ add_mm_counter(dst_mm, mm_counter_file(folio), HPAGE_PUD_NR);
+ }
+
+ if (vma_is_cow_mapping(src_vma) && pud_write(pud)) {
pudp_set_wrprotect(src_mm, addr, src_pud);
pud = pud_wrprotect(pud);
}
diff --git a/mm/memory.c b/mm/memory.c
index b65b56fe97148..66fbe8ed5bac0 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1463,7 +1463,6 @@ copy_pud_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
unsigned long end)
{
struct mm_struct *dst_mm = dst_vma->vm_mm;
- struct mm_struct *src_mm = src_vma->vm_mm;
pud_t *src_pud, *dst_pud;
unsigned long next;
@@ -1477,8 +1476,7 @@ copy_pud_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
int err;
VM_BUG_ON_VMA(next-addr != HPAGE_PUD_SIZE, src_vma);
- err = copy_huge_pud(dst_mm, src_mm,
- dst_pud, src_pud, addr, src_vma);
+ err = copy_huge_pud(dst_vma, src_vma, dst_pud, src_pud, addr);
if (err == -ENOMEM)
return -ENOMEM;
if (!err)
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/2] mm/huge_memory: take a reference on a folio PUD copied by fork
2026-11-05 17:00 ` [PATCH v2 1/2] mm/huge_memory: take a reference on a folio " Konstantin Khorenko
@ 2026-11-05 19:28 ` David Hildenbrand (Arm)
2026-11-05 20:19 ` Zi Yan
1 sibling, 0 replies; 6+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-05 19:28 UTC (permalink / raw)
To: Konstantin Khorenko, Andrew Morton, Zi Yan, Lorenzo Stoakes,
Alistair Popple, Dan Williams
Cc: linux-mm, nvdimm, linux-kselftest, linux-kernel, stable
On 11/5/26 18:00, Konstantin Khorenko wrote:
> Device DAX maps its memory with vmf_insert_folio_pud(), which takes a
> folio reference, adds a PUD rmap and accounts the pages to the mm.
> zap_huge_pud() drops all three, but copy_huge_pud() copies the PUD into
> the child without taking any of them. Each child's unmap then drops
> what the parent took:
>
> - the folio is a reference short, so with two children its refcount
> drops to zero and free_zone_device_folio() runs while the parent
> still maps it;
> - the child's file counter goes negative, "Bad rss-counter state" when
> its mm is freed;
> - the parent's unmap drives the entire mapcount below zero.
>
> fork copies the page tables of a device DAX mapping that has a guard
> region, and commit f807123d578d ("mm: allow guard regions in file-backed
> and read-only mappings") allowed MADV_GUARD_INSTALL there.
>
> Take the reference, the rmap and the counter for a PUD that
> vm_normal_page_pud() resolves to a page, as copy_present_ptes() does for
> a PTE, with a new folio_dup_file_rmap_pud() helper. copy_huge_pud() now
> needs both VMAs, so pass only them and take the mms from them.
>
> Fixes: aed877c2b425 ("device/dax: properly refcount device dax pages when mapping")
> Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
> Cc: stable@vger.kernel.org
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
> ---
Thanks, I'll wait a couple of days and then queue this.
Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/2] mm/huge_memory: take a reference on a folio PUD copied by fork
2026-11-05 17:00 ` [PATCH v2 1/2] mm/huge_memory: take a reference on a folio " Konstantin Khorenko
2026-11-05 19:28 ` David Hildenbrand (Arm)
@ 2026-11-05 20:19 ` Zi Yan
2026-11-05 20:31 ` David Hildenbrand (Arm)
1 sibling, 1 reply; 6+ messages in thread
From: Zi Yan @ 2026-11-05 20:19 UTC (permalink / raw)
To: Konstantin Khorenko, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Alistair Popple, Dan Williams
Cc: linux-mm, nvdimm, linux-kselftest, linux-kernel, stable
On Thu Nov 5, 2026 at 12:00 PM EST, Konstantin Khorenko wrote:
> Device DAX maps its memory with vmf_insert_folio_pud(), which takes a
> folio reference, adds a PUD rmap and accounts the pages to the mm.
> zap_huge_pud() drops all three, but copy_huge_pud() copies the PUD into
> the child without taking any of them. Each child's unmap then drops
> what the parent took:
>
> - the folio is a reference short, so with two children its refcount
> drops to zero and free_zone_device_folio() runs while the parent
> still maps it;
> - the child's file counter goes negative, "Bad rss-counter state" when
> its mm is freed;
> - the parent's unmap drives the entire mapcount below zero.
>
> fork copies the page tables of a device DAX mapping that has a guard
> region, and commit f807123d578d ("mm: allow guard regions in file-backed
> and read-only mappings") allowed MADV_GUARD_INSTALL there.
>
> Take the reference, the rmap and the counter for a PUD that
> vm_normal_page_pud() resolves to a page, as copy_present_ptes() does for
> a PTE, with a new folio_dup_file_rmap_pud() helper. copy_huge_pud() now
> needs both VMAs, so pass only them and take the mms from them.
>
> Fixes: aed877c2b425 ("device/dax: properly refcount device dax pages when mapping")
> Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
> Cc: stable@vger.kernel.org
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
> ---
> include/linux/huge_mm.h | 5 ++---
> include/linux/rmap.h | 22 ++++++++++++++++++++++
> mm/huge_memory.c | 27 +++++++++++++++++++--------
> mm/memory.c | 4 +---
> 4 files changed, 44 insertions(+), 14 deletions(-)
>
I assume this supersedes David's version[1], which uses
folio_add_file_rmap_pud().
[1] https://lore.kernel.org/all/20261031-fixes_core-v3-11-d6437171ecc3@kernel.org/
LGTM.
Reviewed-by: Zi Yan <ziy@nvidia.com>
--
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/2] mm/huge_memory: take a reference on a folio PUD copied by fork
2026-11-05 20:19 ` Zi Yan
@ 2026-11-05 20:31 ` David Hildenbrand (Arm)
2026-11-06 16:32 ` Konstantin Khorenko
0 siblings, 1 reply; 6+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-05 20:31 UTC (permalink / raw)
To: Zi Yan, Konstantin Khorenko, Andrew Morton, Lorenzo Stoakes,
Alistair Popple, Dan Williams
Cc: linux-mm, nvdimm, linux-kselftest, linux-kernel, stable
On 11/5/26 21:19, Zi Yan wrote:
> On Thu Nov 5, 2026 at 12:00 PM EST, Konstantin Khorenko wrote:
>> Device DAX maps its memory with vmf_insert_folio_pud(), which takes a
>> folio reference, adds a PUD rmap and accounts the pages to the mm.
>> zap_huge_pud() drops all three, but copy_huge_pud() copies the PUD into
>> the child without taking any of them. Each child's unmap then drops
>> what the parent took:
>>
>> - the folio is a reference short, so with two children its refcount
>> drops to zero and free_zone_device_folio() runs while the parent
>> still maps it;
>> - the child's file counter goes negative, "Bad rss-counter state" when
>> its mm is freed;
>> - the parent's unmap drives the entire mapcount below zero.
>>
>> fork copies the page tables of a device DAX mapping that has a guard
>> region, and commit f807123d578d ("mm: allow guard regions in file-backed
>> and read-only mappings") allowed MADV_GUARD_INSTALL there.
>>
>> Take the reference, the rmap and the counter for a PUD that
>> vm_normal_page_pud() resolves to a page, as copy_present_ptes() does for
>> a PTE, with a new folio_dup_file_rmap_pud() helper. copy_huge_pud() now
>> needs both VMAs, so pass only them and take the mms from them.
>>
>> Fixes: aed877c2b425 ("device/dax: properly refcount device dax pages when mapping")
>> Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
>> ---
>> include/linux/huge_mm.h | 5 ++---
>> include/linux/rmap.h | 22 ++++++++++++++++++++++
>> mm/huge_memory.c | 27 +++++++++++++++++++--------
>> mm/memory.c | 4 +---
>> 4 files changed, 44 insertions(+), 14 deletions(-)
>>
>
> I assume this supersedes David's version[1], which uses
> folio_add_file_rmap_pud().
>
> [1] https://lore.kernel.org/all/20261031-fixes_core-v3-11-d6437171ecc3@kernel.org/
Ugh, I thought I dropped it to wait for this one here. But turns out the flood
of patches confused me :( (hoping I didn't drop another one by mistake)
It's already part of the fixes PULL request:
https://lore.kernel.org/linux-mm/20261105084704.5703-1-david@kernel.org/
Konstantin, sorry! Can you send a follow up to introduce+use the dup variant?
--
Cheers,
David
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/2] mm/huge_memory: take a reference on a folio PUD copied by fork
2026-11-05 20:31 ` David Hildenbrand (Arm)
@ 2026-11-06 16:32 ` Konstantin Khorenko
0 siblings, 0 replies; 6+ messages in thread
From: Konstantin Khorenko @ 2026-11-06 16:32 UTC (permalink / raw)
To: David Hildenbrand (Arm), Zi Yan, Andrew Morton, Lorenzo Stoakes,
Alistair Popple, Dan Williams
Cc: linux-mm, nvdimm, linux-kselftest, linux-kernel, stable
On 11/5/26 21:31, David Hildenbrand (Arm) wrote:
> On 11/5/26 21:19, Zi Yan wrote:
...
>> I assume this supersedes David's version[1], which uses
>> folio_add_file_rmap_pud().
>>
>> [1] https://lore.kernel.org/all/20261031-fixes_core-v3-11-d6437171ecc3@kernel.org/
>
> Ugh, I thought I dropped it to wait for this one here. But turns out the flood
> of patches confused me :( (hoping I didn't drop another one by mistake)
>
> It's already part of the fixes PULL request:
>
> https://lore.kernel.org/linux-mm/20261105084704.5703-1-david@kernel.org/
>
> Konstantin, sorry! Can you send a follow up to introduce+use the dup variant?
No problem, will do the follow-up on top of thp-7.5.misc in the mm/core tree.
--
Cheers,
Konstantin.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-06 16:32 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 17:00 [PATCH v2 0/2] mm/huge_memory: take a reference on a device DAX PUD copied by fork Konstantin Khorenko
2026-11-05 17:00 ` [PATCH v2 1/2] mm/huge_memory: take a reference on a folio " Konstantin Khorenko
2026-11-05 19:28 ` David Hildenbrand (Arm)
2026-11-05 20:19 ` Zi Yan
2026-11-05 20:31 ` David Hildenbrand (Arm)
2026-11-06 16:32 ` Konstantin Khorenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox