From: "David Hildenbrand (Arm)" <david@kernel.org>
To: Baolin Wang <baolin.wang@linux.alibaba.com>, akpm@linux-foundation.org
Cc: 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, pfalcato@suse.de,
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:25:33 +0200 [thread overview]
Message-ID: <6660d3bb-3485-4c34-8734-3ea99df920f1@kernel.org> (raw)
In-Reply-To: <405811f5-ee36-4b3c-80d0-7dc6827ab5d4@linux.alibaba.com>
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().
So I agree about doing the tlb_flush_rmaps() before zapping the PMD. But on the
zap_pmd_range() front that's really just what sync_with_folio_pmd_zap() wants to
handle.
--
Cheers,
David
next prev parent reply other threads:[~2026-10-14 13:25 UTC|newest]
Thread overview: 11+ 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) [this message]
2026-10-14 14:05 ` Pedro Falcato
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-16 1:47 ` Baolin Wang
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=6660d3bb-3485-4c34-8734-3ea99df920f1@kernel.org \
--to=david@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=ayushr@modal.com \
--cc=baolin.wang@linux.alibaba.com \
--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=pfalcato@suse.de \
--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®