From: Pedro Falcato <pfalcato@suse.de>
To: "David Hildenbrand (Arm)" <david@kernel.org>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>,
akpm@linux-foundation.org, ljs@kernel.org, liam@infradead.org,
vbabka@kernel.org, rppt@kernel.org, surenb@google.com,
mhocko@suse.com, qi.zheng@linux.dev, jack@suse.cz,
ayushr@modal.com, linux-mm@kvack.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] mm: memory: fix truncation removing mapped folios
Date: Wed, 14 Oct 2026 15:05:10 +0100 [thread overview]
Message-ID: <as-L6bGPpGRiF4-Q@pedro-suse.tail5790ac.ts.net> (raw)
In-Reply-To: <6660d3bb-3485-4c34-8734-3ea99df920f1@kernel.org>
On Wed, Oct 14, 2026 at 03:25:33PM +0200, David Hildenbrand (Arm) wrote:
> On 10/13/26 11:36, Baolin Wang wrote:
> >
> >
> > On 10/12/26 6:23 PM, David Hildenbrand (Arm) wrote:
> >> On 10/10/26 09:22, Baolin Wang wrote:
> >>> Ayush reported an issue that shmem/tmpfs folios are still mapped when
> >>> truncating them on production:
> >>> "
> >>> BUG: Bad page cache in process ... pfn:...
> >>> page dumped because: still mapped when deleted
> >>> "
> >>>
> >>> I reproduced the issue with Ayush's reproducer [1] with the latest mm-unstable
> >>> branch, and I got the following crash info. From the dump message, we can
> >>> see that truncate_inode_folio() is really trying to remove mapped folios,
> >>> which is incorrect.
> >>>
> >>> "
> >>> [ 189.171977] page: refcount:3 mapcount:1 mapping:00000000fe168ee1
> >>> index:0x3880 pfn:0x191647
> >>> [ 189.171995] memcg:ffff0000cc073d40
> >>> [ 189.171997] aops:shmem_aops ino:3c01 dentry name(?):"memfd:runsc-memory"
> >>> [ 189.172006] flags: 0x17fffef0002022d(locked|referenced|uptodate|lru|
> >>> workingset|swapbacked|node=0|zone=2|lastcpupid=0x3ffff)
> >>> .......
> >>> [ 189.172017] page dumped because: VM_BUG_ON_FOLIO(folio_mapped(folio))
> >>> [ 189.172026] ------------[ cut here ]------------
> >>> [ 189.172027] kernel BUG at mm/filemap.c:155!
> >>> [ 189.172057] Internal error: Oops - BUG: 00000000f2000800 [#1] SMP
> >>> .......
> >>> [ 189.177721] CPU: 3 UID: 0 PID: 5521 Comm: repro Kdump: loaded Tainted:
> >>> 7.3.0-rc4+ #277 PREEMPT(lazy)
> >>> .......
> >>> [ 189.183986] Call trace:
> >>> [ 189.184124] filemap_unaccount_folio+0xf0/0x1e8 (P)
> >>> [ 189.184391] __filemap_remove_folio+0x34/0x160
> >>> [ 189.184633] filemap_remove_folio+0x4c/0xb0
> >>> [ 189.184859] truncate_inode_folio+0x34/0x58
> >>> [ 189.185087] shmem_undo_range+0x220/0x658
> >>> [ 189.185308] shmem_fallocate+0x2f0/0x470
> >>> [ 189.185527] vfs_fallocate+0x128/0x328
> >>> ......
> >>> [ 189.187078] el0t_64_sync+0x184/0x188
> >>> "
> >>>
> >>> After analysis, I believe the race exists between truncation and MADV_DONTNEED,
> >>> and shmem's ->map_pages() merely makes the issue easier to reproduce. Since
> >>> MADV_DONTNEED synchronously releases the pagetable page before calling
> >>> tlb_flush_rmaps(), this could cause another thread's truncation to skip
> >>> zap_pte_range() (due to pmd is none) but still observe the folio's mapcount
> >>> as non-zero. A possible race scenario is as follows:
> >>>
> >>> CPU 0 CPU 1
> >>> shmem_fallocate madvise_dontneed_single_vma
> >>> unmap_mapping_range .......
> >>> ......
> >>> (filemap_map_pages() remap folios)
> >>> zap_pte_range
> >>> truncate_inode_folio zap_empty_pte_table(pmd clear)
> >>> unmap_mapping_folio
> >>> ......
> >>> zap_pmd_range (saw pmd none, skip)
> >>>
> >>> filemap_remove_folio
> >>> BUG_ON(folio_mapped)
> >>
> >> That reminds me of sync_with_folio_pmd_zap() usage in page_vma_mapped_walk().
> >>
> >> Isn't that the better approach?
> >
> > I had considered this approach before, but it is not feasible. IIUC, the
> > sync_with_folio_pmd_zap() is used to address the race with zap_huge_pmd(), and
> > both the PMD clear and the folio mapcount decrement are performed under the PMD
> > lock in zap_huge_pmd().
>
> Just to be clear, I am thinking about something like the following on the
> zap_pmd_range() side:
>
> diff --git a/mm/internal.h b/mm/internal.h
> index 38b1165212c94..a8820ad7e2f21 100644
> --- a/mm/internal.h
> +++ b/mm/internal.h
> @@ -584,14 +584,15 @@ void free_pgtables(struct mmu_gather *tlb, struct
> unmap_desc *desc);
> void pmd_install(struct mm_struct *mm, pmd_t *pmd, pgtable_t *pte);
>
> /**
> - * sync_with_folio_pmd_zap - sync with concurrent zapping of a folio PMD
> + * sync_with_folio_pmd_zap - sync with concurrent zapping of folios
> * @mm: The mm_struct.
> * @pmdp: Pointer to the pmd that was found to be pmd_none().
> *
> * When we find a pmd_none() while unmapping a folio without holding the PTL,
> - * zap_huge_pmd() may have cleared the PMD but not yet modified the folio to
> - * indicate that it's unmapped. Skipping the PMD without synchronization could
> - * make folio unmapping code assume that unmapping failed.
> + * zap_huge_pmd() or zap_empty_pte_table() may have cleared the PMD but not
> + * yet modified the mapped folios to indicate they are unmapped. Skipping the
> + * PMD without synchronization could make folio unmapping code assume that
> + * unmapping failed.
> *
> * Wait for concurrent zapping to complete by grabbing the PTL.
> */
> diff --git a/mm/memory.c b/mm/memory.c
> index 8b0c2c735d3de..c585f5eb255c9 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -2139,9 +2139,7 @@ static inline unsigned long zap_pmd_range(struct
> mmu_gather *tlb,
> continue;
> }
> /* fall through */
> - } else if (details && details->single_folio &&
> - folio_test_pmd_mappable(details->single_folio) &&
> - next - addr == HPAGE_PMD_SIZE && pmd_none(*pmd)) {
> + } else if (details && details->single_folio && pmd_none(*pmd)) {
> sync_with_folio_pmd_zap(tlb->mm, pmd);
> }
> if (pmd_none(*pmd)) {
>
> >
> > However, the race here is different: tlb_flush_rmaps() is under the PTE lock,
> > while zap_empty_pte_table() is under the PMD lock, and reordering between the
> > two functions is also possible. That is, the folio mapcount operation could
> > execute after the acquire barrier (spin_lock()) of zap_empty_pte_table(),
> > allowing a reader on CPU 0 to incorrectly observe that the PMD is none while
> > mapcount > 0.
>
> I think we really just have to make sure to flush rmaps before zapping the PMD.
> Then it's really just the same thing we had before with sync_with_folio_pmd_zap().
Hmm, could we viably replace sync_with_folio_pmd_zap() with the memory barriers
instead? Seems lighter-weight and functionally equivalent?
--
Pedro
next prev parent reply other threads:[~2026-10-14 14:05 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-10 7:22 Baolin Wang
2026-10-12 10:23 ` David Hildenbrand (Arm)
2026-10-13 9:36 ` Baolin Wang
2026-10-14 13:25 ` David Hildenbrand (Arm)
2026-10-14 14:05 ` Pedro Falcato [this message]
2026-10-14 18:44 ` David Hildenbrand (Arm)
2026-10-15 6:41 ` Baolin Wang
2026-10-15 19:00 ` David Hildenbrand (Arm)
2026-10-13 10:24 ` Pedro Falcato
2026-10-14 9:51 ` Baolin Wang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=as-L6bGPpGRiF4-Q@pedro-suse.tail5790ac.ts.net \
--to=pfalcato@suse.de \
--cc=akpm@linux-foundation.org \
--cc=ayushr@modal.com \
--cc=baolin.wang@linux.alibaba.com \
--cc=david@kernel.org \
--cc=jack@suse.cz \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=qi.zheng@linux.dev \
--cc=rppt@kernel.org \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®