Skip to contentSkip to conversation

mm: remove page_mapped()

PATCH Applied David Hildenbrand (Arm) 7 participants 23 messages Apr 27 – Apr 28

Subsystems (6)
Memory management BPF MM core SuperH +2 more subsystems
lore
Share & tools
lore

David Hildenbrand (Arm)

author maintainer
#0 lore raw

cover mm: remove page_mapped()

While preparing my slides for an LSF/MM talk, I realized that I did not yet remove page_mapped().

So let's do that. In the BPF arena code it's unclear which memdesc we would want to allocate in the future: certainly something with a refcount, but likely none with a mapcount. So let's just rely on the page refcount instead to decide whether we want to try zapping the page from user page tables.

---
David Hildenbrand (Arm) (3):
sh: use folio_mapped() instead of page_mapped() in sh4_flush_cache_page()
bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()
mm: remove page_mapped()

 arch/sh/mm/cache-sh4.c |  2 +-
 include/linux/mm.h     | 10 ----------
 kernel/bpf/arena.c     |  2 +-
 mm/memory.c            |  2 +-
 mm/rmap.c              |  8 ++++----
 5 files changed, 7 insertions(+), 17 deletions(-)

---

base-commit: a2ddbfd1af0f54ea84bf17f0400088815d012e8d

change-id: 20260422-page_mapped-a49a892fa432

--

Cheers,

David

author maintainer
#1 lore raw

PATCH 1/3 sh: use folio_mapped() instead of page_mapped() in sh4_flush_cache_page()

We already have the folio in our hands, so let's just use folio_mapped().

---
 arch/sh/mm/cache-sh4.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/sh/mm/cache-sh4.c +1 -1
index 83fb34b39ca7..8bc9ce541c14 100644
@@ -248,7 +248,7 @@ static void sh4_flush_cache_page(void *args)
 		 */
 		map_coherent = (current_cpu_data.dcache.n_aliases &&
 			test_bit(PG_dcache_clean, folio_flags(folio, 0)) &&
-			page_mapped(page));
+			folio_mapped(folio));
 		if (map_coherent)
 			vaddr = kmap_coherent(page, address);
 		else
Signature · 1 line
2.43.0
author maintainer
#3 lore raw

PATCH 2/3 bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

In reply to David Hildenbrand (Arm)

Pages that BPF arena code maps are allocated through bpf_map_alloc_pages(), which does not allocate folios but pages.

In the future, pages will not have a mapcount, only folios will. Converting the code to use folios and rely on folio_mapped() sounds like the wrong approach.

Should BPF arena code allocate folios and use folio_mapped() here? But likely we would not want to use folios here longterm, as we don't really need folio information.

Hard to tell. But in the meantime, we can simply use the page refcount instead, as a heuristic whether the page might be mapped to user space and we would want to try zapping it, so we can get rid of page_mapped().

Page allocation will give us a page with a refcount of 1. Any user space mapping adds a page reference. While there can be references from other subsystems (e.g., GUP), in the common case for this test here relying on the page count is good enough.

---
 kernel/bpf/arena.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/bpf/arena.c +1 -1
index 802656c6fd3c..608c55c260bc 100644
@@ -729,7 +729,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
 
 	llist_for_each_safe(pos, t, __llist_del_all(&free_pages)) {
 		page = llist_entry(pos, struct page, pcp_llist);
-		if (page_cnt == 1 && page_mapped(page)) /* mapped by some user process */
+		if (page_cnt == 1 && page_ref_count(page) > 1) /* maybe mapped by user space */
 			/* Optimization for the common case of page_cnt==1:
 			 * If page wasn't mapped into some user vma there
 			 * is no need to call zap_pages which is slow. When
Signature · 1 line
2.43.0
maintainer
#4 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

Quoted 15 lines · On Mon, 27 Apr 2026 13:43:15 +0200 "David Hildenbrand (Arm)"…

Pages that BPF arena code maps are allocated through bpf_map_alloc_pages(), which does not allocate folios but pages.

In the future, pages will not have a mapcount, only folios will. Converting the code to use folios and rely on folio_mapped() sounds like the wrong approach.

Should BPF arena code allocate folios and use folio_mapped() here? But likely we would not want to use folios here longterm, as we don't really need folio information.

Hard to tell. But in the meantime, we can simply use the page refcount instead, as a heuristic whether the page might be mapped to user space and we would want to try zapping it, so we can get rid of page_mapped().

Quoted 8 lines

Page allocation will give us a page with a refcount of 1. Any user space mapping adds a page reference. While there can be references from other subsystems (e.g., GUP), in the common case for this test here relying on the page count is good enough.

---
 kernel/bpf/arena.c | 2 +-

BPF maintainers will probably want to carry this in the BPF tree. That's fine - please go ahead and add it. I'll carry a duplicate in mm.git so it compiles.

#5 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

Quoted 11 lines · Andrew Morton
Quoted 8 lines · On Mon, 27 Apr 2026 13:43:15 +0200 "David Hildenbrand (Arm)"…

Pages that BPF arena code maps are allocated through bpf_map_alloc_pages(), which does not allocate folios but pages.

In the future, pages will not have a mapcount, only folios will. Converting the code to use folios and rely on folio_mapped() sounds like the wrong approach.

Should BPF arena code allocate folios and use folio_mapped() here?

no

Quoted 3 lines

But

Quoted 2 lines

likely we would not want to use folios here longterm, as we don't really need folio information.

exactly

Quoted 8 lines
Quoted 8 lines

Hard to tell. But in the meantime, we can simply use the page refcount instead, as a heuristic whether the page might be mapped to user space and we would want to try zapping it, so we can get rid of page_mapped().

Page allocation will give us a page with a refcount of 1. Any user space mapping adds a page reference. While there can be references from other subsystems (e.g., GUP), in the common case for this test here relying on the page count is good enough.

makes sense to me.

Quoted 8 lines
Quoted 4 lines
---
 kernel/bpf/arena.c | 2 +-

BPF maintainers will probably want to carry this in the BPF tree. That's fine - please go ahead and add it. I'll carry a duplicate in mm.git so it compiles.

We cannot carry the same patch in 2 trees. Sooner or later it will create problems for linux-next and issues during merge window if more changes are done in the same area.
The only way to share a patch between trees is to create a stable branch and pull it into 2 trees then sha will be the same,
but mm tree has its own way of doing things, so this patch needs to stay in mm only and if no one should be touching adjacent lines :(

maintainer
#6 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

Quoted 10 lines · On Mon, 27 Apr 2026 16:00:59 +0100 Alexei Starovoitov…
Quoted 8 lines
Quoted 4 lines
---
 kernel/bpf/arena.c | 2 +-

BPF maintainers will probably want to carry this in the BPF tree. That's fine - please go ahead and add it. I'll carry a duplicate in mm.git so it compiles.

We cannot carry the same patch in 2 trees.

Git is fine with that.

Quoted 6 lines

Sooner or later it will create problems for linux-next and issues during merge window if more changes are done in the same area.
The only way to share a patch between trees is to create a stable branch and pull it into 2 trees then sha will be the same,

For a single one-line patch?

#7 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

Quoted 16 lines · Andrew Morton
Quoted 10 lines · On Mon, 27 Apr 2026 16:00:59 +0100 Alexei Starovoitov…
Quoted 8 lines
Quoted 4 lines
---
 kernel/bpf/arena.c | 2 +-

BPF maintainers will probably want to carry this in the BPF tree. That's fine - please go ahead and add it. I'll carry a duplicate in mm.git so it compiles.

We cannot carry the same patch in 2 trees.

Git is fine with that.

Quoted 8 lines
Quoted 6 lines

Sooner or later it will create problems for linux-next and issues during merge window if more changes are done in the same area.
The only way to share a patch between trees is to create a stable branch and pull it into 2 trees then sha will be the same,

For a single one-line patch?

Number of changed lines is irrelevant.
If there will be a commit on top that moves this line or lines within the hunk, git will be confused depending on order of pulls.
We learned this lesson the hard way.

author maintainer
#8 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

In reply to Alexei Starovoitov
Quoted 9 lines
Quoted 8 lines
Quoted 4 lines
---
 kernel/bpf/arena.c | 2 +-

BPF maintainers will probably want to carry this in the BPF tree. That's fine - please go ahead and add it. I'll carry a duplicate in mm.git so it compiles.

Quoted 7 lines

We cannot carry the same patch in 2 trees. Sooner or later it will create problems for linux-next and issues during merge window if more changes are done in the same area.
The only way to share a patch between trees is to create a stable branch and pull it into 2 trees then sha will be the same,

As lack of stable trees was raised a couple of times in the past, once we're done with a transition to a fully git-based mm-next workflow [1], we should be able to provide stable trees.

[1]
https://lore.kernel.org/r/[email protected]

Signature · 3 lines
Cheers,

David
#9 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

Quoted 18 lines · David Hildenbrand (Arm)
Quoted 10 lines
Quoted 9 lines
Quoted 5 lines
---
 kernel/bpf/arena.c | 2 +-

BPF maintainers will probably want to carry this in the BPF tree. That's fine - please go ahead and add it. I'll carry a duplicate in mm.git so it compiles.

Quoted 7 lines

We cannot carry the same patch in 2 trees. Sooner or later it will create problems for linux-next and issues during merge window if more changes are done in the same area.
The only way to share a patch between trees is to create a stable branch and pull it into 2 trees then sha will be the same,

Quoted 6 lines

As lack of stable trees was raised a couple of times in the past, once we're done with a transition to a fully git-based mm-next workflow [1], we should be able to provide stable trees.

[1]
https://lore.kernel.org/r/[email protected]

Ohh. Thanks for the link. Full git sounds great to me. Reading the thread it sounds like you guys want to explore your own way of managing the tree.
If there is interest we can share during lsfmm how net and bpf trees are maintained from git pov.

author maintainer
#10 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

Quoted 16 lines · Alexei Starovoitov
Quoted 14 lines · David Hildenbrand (Arm)
Quoted 9 lines

We cannot carry the same patch in 2 trees. Sooner or later it will create problems for linux-next and issues during merge window if more changes are done in the same area.
The only way to share a patch between trees is to create a stable branch and pull it into 2 trees then sha will be the same,

As lack of stable trees was raised a couple of times in the past, once we're done with a transition to a fully git-based mm-next workflow [1], we should be able to provide stable trees.

Quoted 8 lines
Quoted 2 lines

[1]
https://lore.kernel.org/r/[email protected]

Ohh. Thanks for the link. Full git sounds great to me. Reading the thread it sounds like you guys want to explore your own way of managing the tree.
If there is interest we can share during lsfmm how net and bpf trees are maintained from git pov.

Yes, I was planning on reaching out either way, talking at lsf/mm would be great!

Signature · 3 lines
Cheers,

David
#11 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

In reply to Alexei Starovoitov
Quoted 5 lines · Alexei Starovoitov

We cannot carry the same patch in 2 trees. Sooner or later it will create problems for linux-next and issues during merge window if more changes are done in the same area.

Agreed.

Quoted 3 lines

The only way to share a patch between trees is to create a stable branch and pull it into 2 trees then sha will be the same,

Yes, but...

Quoted 3 lines

but mm tree has its own way of doing things, so this patch needs to stay in mm only and if no one should be touching adjacent lines :(

... this should also be fine even if that happens. Mark knows how to resolve (and report) trivial conflicts in linux-next, and Linus AFAIK also prefers to resolve conflicts to other workarounds.

The shared branch would IMHO only truly be necessary if bpf tree itself needed to base something off on this change; for resolving trivial conflicts it might be an overkill.

Reviewed-by on v1 2/3
#12 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

In reply to David Hildenbrand (Arm)
Quoted 11 lines · David Hildenbrand (Arm)

Pages that BPF arena code maps are allocated through bpf_map_alloc_pages(), which does not allocate folios but pages.

In the future, pages will not have a mapcount, only folios will. Converting the code to use folios and rely on folio_mapped() sounds like the wrong approach.

Should BPF arena code allocate folios and use folio_mapped() here? But likely we would not want to use folios here longterm, as we don't really need folio information.

Quoted 8 lines

Hard to tell. But in the meantime, we can simply use the page refcount instead, as a heuristic whether the page might be mapped to user space and we would want to try zapping it, so we can get rid of page_mapped().

Page allocation will give us a page with a refcount of 1. Any user space mapping adds a page reference. While there can be references from other subsystems (e.g., GUP), in the common case for this test here relying on the page count is good enough.

Well, if we GUPped the page, it must have been mapped at some point ;-)

Anyway, I approve of this patch; it lets us defer the hard decisions and work until we know what we really need.

#13 lore raw

Re: [PATCH 2/3] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

In reply to David Hildenbrand (Arm)
Sashiko AI review found 2 potential issue(s): - [Critical] Replacing `page_mapped()` with `page_ref_count() > 1` removes the synchronous mapcount check, allowing the lockless `zap_pages()` page…

Sashiko AI review found 2 potential issue(s):
- [Critical] Replacing page_mapped() with page_ref_count() > 1 removes the synchronous mapcount check, allowing the lockless zap_pages() page table walk to execute concurrently with free_pgtables() during munmap(), leading to a page table use-after-free.
- [Medium] The patch incorrectly uses a lifetime counter (page_ref_count) as a semantic indicator to determine if a page is mapped in user space, violating MM guidelines.
--

commit 7d590c0af75e613441c8863162be310388ecc992
Author: David Hildenbrand (Arm) <[email protected]>

bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages()

This commit changes the heuristic in arena_free_pages() to use page_ref_count(page) > 1 instead of page_mapped() to determine whether to call zap_pages(). This avoids mapcount usage as pages will not have a mapcount in the future.

Quoted 4 lines
kernel/bpf/arena.c +0 -0
index 802656c6fd3c..608c55c260bc 100644

[ ... ]

kernel/bpf/arena.c static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, · line 732

Quoted 6 lines
kernel/bpf/arena.c +1 -1
@@ -729,7 +729,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
 
 	llist_for_each_safe(pos, t, __llist_del_all(&free_pages)) {
 		page = llist_entry(pos, struct page, pcp_llist);
-		if (page_cnt == 1 && page_mapped(page)) /* mapped by some user process */
+		if (page_cnt == 1 && page_ref_count(page) > 1) /* maybe mapped by user space */

Can this lead to a page table use-after-free during concurrent munmap()?

zap_pages() iterates over arena->vma_list and walks user page tables locklessly. During a concurrent munmap(), unmap_region() executes unmap_vmas() (which drops mapcounts), free_pgtables() (which unlinks and frees page tables), and finally tlb_finish_mmu() (which drops the batched page refcounts via mmu_gather).

The VMA is only removed from arena->vma_list later during arena_vm_close(), meaning it is still visible to zap_pages() during free_pgtables().

Since mmu_gather retains the page reference until tlb_finish_mmu(), page_ref_count(page) > 1 will remain true during free_pgtables().

With the old page_mapped() check, it would correctly evaluate to false because unmap_vmas() had already dropped the mapcount to 0. It appears this change could allow zap_pages() to walk page tables while they are actively being freed. This could also be forced by artificially inflating the refcount using GUP pins.

Also, does this use of page_ref_count() conflict with the MM guideline that refcount should be treated as a lifetime counter rather than a semantic indicator?

Speculative references (like GUP pins or page_idle) can transiently inflate the refcount independently of user space mappings.

Quoted 3 lines
kernel/bpf/arena.c +0 -0
 			/* Optimization for the common case of page_cnt==1:
 			 * If page wasn't mapped into some user vma there
 			 * is no need to call zap_pages which is slow. When
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
author maintainer
#14 lore raw

PATCH 3/3 mm: remove page_mapped()

In reply to David Hildenbrand (Arm)

Let's replace the last user of page_mapped() by folio_mapped() so we can get rid of page_mapped().

Replace the remaining occurrences of page_mapped() in rmap documentation by folio_mapped().

---
 include/linux/mm.h | 10 ----------
 mm/memory.c        |  2 +-
 mm/rmap.c          |  8 ++++----
 3 files changed, 5 insertions(+), 15 deletions(-)
include/linux/mm.h +0 -10
index af23453e9dbd..87fcd068303a 100644
@@ -1888,16 +1888,6 @@ static inline bool folio_mapped(const struct folio *folio)
 	return folio_mapcount(folio) >= 1;
 }
 
-/*
- * Return true if this page is mapped into pagetables.
- * For compound page it returns true if any sub-page of compound page is mapped,
- * even if this particular sub-page is not itself mapped by any PTE or PMD.
- */
-static inline bool page_mapped(const struct page *page)
-{
-	return folio_mapped(page_folio(page));
-}
-
 static inline struct page *virt_to_head_page(const void *x)
 {
 	struct page *page = virt_to_page(x);
mm/memory.c +1 -1
index ea6568571131..99854e6a2793 100644
@@ -5464,7 +5464,7 @@ static vm_fault_t __do_fault(struct vm_fault *vmf)
 	if (unlikely(PageHWPoison(vmf->page))) {
 		vm_fault_t poisonret = VM_FAULT_HWPOISON;
 		if (ret & VM_FAULT_LOCKED) {
-			if (page_mapped(vmf->page))
+			if (folio_mapped(folio))
 				unmap_mapping_folio(folio);
 			/* Retry if a clean folio was removed from the cache. */
 			if (mapping_evict_folio(folio->mapping, folio))
mm/rmap.c +4 -4
index 78b7fb5f367c..fb3c351f8c45 100644
@@ -571,7 +571,7 @@ void __init anon_vma_init(void)
  * In case it was remapped to a different anon_vma, the new anon_vma will be a
  * child of the old anon_vma, and the anon_vma lifetime rules will therefore
  * ensure that any anon_vma obtained from the page will still be valid for as
- * long as we observe page_mapped() [ hence all those page_mapped() tests ].
+ * long as we observe folio_mapped() [ hence all those folio_mapped() tests ].
  *
  * All users of this function must be very careful when walking the anon_vma
  * chain and verify that the page in question is indeed mapped in it
@@ -1999,7 +1999,7 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
 	/*
 	 * When racing against e.g. zap_pte_range() on another cpu,
 	 * in between its ptep_get_and_clear_full() and folio_remove_rmap_*(),
-	 * try_to_unmap() may return before page_mapped() has become false,
+	 * try_to_unmap() may return before folio_mapped() has become false,
 	 * if page table locking is skipped: use TTU_SYNC to wait for that.
 	 */
 	if (flags & TTU_SYNC)
@@ -2426,7 +2426,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
 	/*
 	 * When racing against e.g. zap_pte_range() on another cpu,
 	 * in between its ptep_get_and_clear_full() and folio_remove_rmap_*(),
-	 * try_to_migrate() may return before page_mapped() has become false,
+	 * try_to_migrate() may return before folio_mapped() has become false,
 	 * if page table locking is skipped: use TTU_SYNC to wait for that.
 	 */
 	if (flags & TTU_SYNC)
@@ -2927,7 +2927,7 @@ static struct anon_vma *rmap_walk_anon_lock(const struct folio *folio,
 
 	/*
 	 * Note: remove_migration_ptes() cannot use folio_lock_anon_vma_read()
-	 * because that depends on page_mapped(); but not all its usages
+	 * because that depends on folio_mapped(); but not all its usages
 	 * are holding mmap_lock. Users without mmap_lock are required to
 	 * take a reference count to prevent the anon_vma disappearing
 	 */
Signature · 1 line
2.43.0
Reviewed-by on v1 3/3
#15 lore raw
Quoted 2 lines · David Hildenbrand (Arm)

Let's replace the last user of page_mapped() by folio_mapped() so we can get rid of page_mapped().

Yay!

Quoted 2 lines

Replace the remaining occurrences of page_mapped() in rmap documentation by folio_mapped().

mm/memory.c static vm_fault_t __do_fault(struct vm_fault *vmf) · line 5468

Quoted 8 lines

+++ b/mm/memory.c

mm/memory.c +1 -1
@@ -5464,7 +5464,7 @@ static vm_fault_t __do_fault(struct vm_fault *vmf)
 	if (unlikely(PageHWPoison(vmf->page))) {
 		vm_fault_t poisonret = VM_FAULT_HWPOISON;
 		if (ret & VM_FAULT_LOCKED) {
-			if (page_mapped(vmf->page))
+			if (folio_mapped(folio))
 				unmap_mapping_folio(folio);

The idiot who authored 01d1e0e6b7d9 really should have done this at the time ... Oh, wait, I see what I was trying to do.

I believe my thinking was that we only needed to unmap the folio if this specific page that had hardware poison was mapped. But no, we need to unmap the entire folio if any page in it is mapped.

Does the affect recoverability from hwpoison? I don't think so. When we detect hwpoison, the first thing we try to do is split the folio. Of course that can fail, but if we do, we kill the process.

So yes, my R-b above stands.

maintainer
#16 lore raw
In reply to David Hildenbrand (Arm)
Quoted 5 lines · On Mon, 27 Apr 2026 13:43:16 +0200 "David Hildenbrand (Arm)"…

Let's replace the last user of page_mapped() by folio_mapped() so we can get rid of page_mapped().

Replace the remaining occurrences of page_mapped() in rmap documentation by folio_mapped().

This broke Breno's "mm/memory-failure: add panic option for unrecoverable pages"
(https://lore.kernel.org/[email protected]),
which added a new page_mapped() call. I made the below adjustment to Breno's patch:

mm/memory-failure.c renamed from mm/memory-failure.c~mm-memory-failure-add-panic-option-for-unrecoverable-pages-fix +1 -1
@@ -1353,7 +1353,7 @@ static bool panic_on_unrecoverable_mf(un
 		cpu_relax();
 		return page_count(p) == 0 &&
 		       !PageLRU(p) &&
-		       !page_mapped(p) &&
+		       !folio_mapped(page_folio(p)) &&
 		       !page_folio(p)->mapping &&
 		       !is_free_buddy_page(p);
 	default:

_

author maintainer
#17 lore raw
Quoted 17 lines · Andrew Morton
Quoted 5 lines · On Mon, 27 Apr 2026 13:43:16 +0200 "David Hildenbrand (Arm)"…

Let's replace the last user of page_mapped() by folio_mapped() so we can get rid of page_mapped().

Replace the remaining occurrences of page_mapped() in rmap documentation by folio_mapped().

This broke Breno's "mm/memory-failure: add panic option for unrecoverable pages"
(https://lore.kernel.org/[email protected]),
which added a new page_mapped() call. I made the below adjustment to Breno's patch:

mm/memory-failure.c static bool panic_on_unrecoverable_mf(un · line 1357

Quoted 6 lines
mm/memory-failure.c renamed from mm/memory-failure.c~mm-memory-failure-add-panic-option-for-unrecoverable-pages-fix +1 -1
@@ -1353,7 +1353,7 @@ static bool panic_on_unrecoverable_mf(un
 		cpu_relax();
 		return page_count(p) == 0 &&
 		       !PageLRU(p) &&
-		       !page_mapped(p) &&
+		       !folio_mapped(page_folio(p)) &&
 		       !page_folio(p)->mapping &&

If we have a folio, we should really lookup the folio once. Not 4 times.

Breno's patch likely needs some love. :)

Signature · 3 lines
Cheers,

David
#18 lore raw

Hello David,

Quoted 15 lines · David Hildenbrand (Arm)
Quoted 14 lines · Andrew Morton
Quoted 5 lines · On Mon, 27 Apr 2026 13:43:16 +0200 "David Hildenbrand (Arm)"…

Let's replace the last user of page_mapped() by folio_mapped() so we can get rid of page_mapped().

Replace the remaining occurrences of page_mapped() in rmap documentation by folio_mapped().

This broke Breno's "mm/memory-failure: add panic option for unrecoverable pages"
(https://lore.kernel.org/[email protected]),
which added a new page_mapped() call. I made the below adjustment to Breno's patch:

Quoted 11 lines

mm/memory-failure.c static bool panic_on_unrecoverable_mf(un · line 1357

Quoted 9 lines
mm/memory-failure.c renamed from mm/memory-failure.c~mm-memory-failure-add-panic-option-for-unrecoverable-pages-fix +1 -1
@@ -1353,7 +1353,7 @@ static bool panic_on_unrecoverable_mf(un
 		cpu_relax();
 		return page_count(p) == 0 &&
 		       !PageLRU(p) &&
-		       !page_mapped(p) &&
+		       !folio_mapped(page_folio(p)) &&
 		       !page_folio(p)->mapping &&

If we have a folio, we should really lookup the folio once. Not 4 times.

Why 4 times?

Quoted 1 line

Breno's patch likely needs some love. :)

Would something like the following give it all the love in the world?

folio = page_folio(p);
return page_count(p) == 0 &&

        !PageLRU(p) &&
        !folio_mapped(folio) &&
        !folio->mapping &&
        !is_free_buddy_page(p);

Thanks,
--breno

#19 lore raw
Quoted 11 lines · Breno Leitao
Quoted 9 lines

mm/memory-failure.c static bool panic_on_unrecoverable_mf(un · line 1357

Quoted 7 lines
mm/memory-failure.c renamed from mm/memory-failure.c~mm-memory-failure-add-panic-option-for-unrecoverable-pages-fix +1 -1
@@ -1353,7 +1353,7 @@ static bool panic_on_unrecoverable_mf(un
 		cpu_relax();
 		return page_count(p) == 0 &&
 		       !PageLRU(p) &&
-		       !page_mapped(p) &&
+		       !folio_mapped(page_folio(p)) &&
 		       !page_folio(p)->mapping &&

If we have a folio, we should really lookup the folio once. Not 4 times.

Why 4 times?

Because there are page_folio() calls hidden in PageLRU, page_mapped() and page_count().

Quoted 10 lines
Quoted 1 line

Breno's patch likely needs some love. :)

Would something like the following give it all the love in the world?

folio = page_folio(p);
return page_count(p) == 0 &&

        !PageLRU(p) &&
        !folio_mapped(folio) &&
        !folio->mapping &&
        !is_free_buddy_page(p);

No. You need to immerse yourself more deeply in the folio transition ;-)

author maintainer
#20 lore raw
In reply to David Hildenbrand (Arm)
Quoted 11 lines · David Hildenbrand (Arm)

While preparing my slides for an LSF/MM talk, I realized that I did not yet remove page_mapped().

So let's do that. In the BPF arena code it's unclear which memdesc we would want to allocate in the future: certainly something with a refcount, but likely none with a mapcount. So let's just rely on the page refcount instead to decide whether we want to try zapping the page from user page tables.

---

I scanned AI review and I think it founds something that is not related to this patch.

We use the page_mapped()->page_ref_count() check as an optimization to avoid calling zap_vma_range(). We must be able to call it even without that optimization.

Just like the bulk zap call earlier

	if (page_cnt > 1)
		/* bulk zap if multiple pages being freed */
		zap_pages(arena, full_uaddr, page_cnt);

It talks about concurrent "munmap(), unmap_region() executes unmap_vmas()" racing with our zap_vma_range().

Looking into the details, arena_map_mmap() calls remember_vma(). We reject mremap and VMA split. arena_vm_close() removes the VMA from the list. The arena->lock protects our VMA list.

So in zap_pages, the VMA cannot go away. If we find a VMA, ->close could not have been called yet.

In vma.c, we call remove_vma() after vms_clear_pte(). So after unmapping the pages and freeing the page tables.

So munmap() can indeed race with zap_vma_range(), and the page_mapped() check would not have changed anything about that really.

@BPF folks: does BPF take anywhere the mmap lock in read mode before calling zap_vma_range()? It should do that.

Signature · 3 lines
Cheers,

David
#21 lore raw
Quoted 39 lines · David Hildenbrand (Arm)
Quoted 11 lines · David Hildenbrand (Arm)

While preparing my slides for an LSF/MM talk, I realized that I did not yet remove page_mapped().

So let's do that. In the BPF arena code it's unclear which memdesc we would want to allocate in the future: certainly something with a refcount, but likely none with a mapcount. So let's just rely on the page refcount instead to decide whether we want to try zapping the page from user page tables.

---

I scanned AI review and I think it founds something that is not related to this patch.

We use the page_mapped()->page_ref_count() check as an optimization to avoid calling zap_vma_range(). We must be able to call it even without that optimization.

Just like the bulk zap call earlier

        if (page_cnt > 1)
                /* bulk zap if multiple pages being freed */
                zap_pages(arena, full_uaddr, page_cnt);

It talks about concurrent "munmap(), unmap_region() executes unmap_vmas()" racing with our zap_vma_range().

Looking into the details, arena_map_mmap() calls remember_vma(). We reject mremap and VMA split. arena_vm_close() removes the VMA from the list. The arena->lock protects our VMA list.

So in zap_pages, the VMA cannot go away. If we find a VMA, ->close could not have been called yet.

In vma.c, we call remove_vma() after vms_clear_pte(). So after unmapping the pages and freeing the page tables.

Quoted 6 lines

So munmap() can indeed race with zap_vma_range(), and the page_mapped() check would not have changed anything about that really.

@BPF folks: does BPF take anywhere the mmap lock in read mode before calling zap_vma_range()? It should do that.

Yes, but do NOT. As I explained to Andrew.
It's a git mess. I don't want any more changes that cross trees.

author maintainer
#22 lore raw
Quoted 41 lines · Alexei Starovoitov
Quoted 39 lines · David Hildenbrand (Arm)
Quoted 11 lines · David Hildenbrand (Arm)

While preparing my slides for an LSF/MM talk, I realized that I did not yet remove page_mapped().

So let's do that. In the BPF arena code it's unclear which memdesc we would want to allocate in the future: certainly something with a refcount, but likely none with a mapcount. So let's just rely on the page refcount instead to decide whether we want to try zapping the page from user page tables.

---

I scanned AI review and I think it founds something that is not related to this patch.

We use the page_mapped()->page_ref_count() check as an optimization to avoid calling zap_vma_range(). We must be able to call it even without that optimization.

Just like the bulk zap call earlier

        if (page_cnt > 1)
                /* bulk zap if multiple pages being freed */
                zap_pages(arena, full_uaddr, page_cnt);

It talks about concurrent "munmap(), unmap_region() executes unmap_vmas()" racing with our zap_vma_range().

Looking into the details, arena_map_mmap() calls remember_vma(). We reject mremap and VMA split. arena_vm_close() removes the VMA from the list. The arena->lock protects our VMA list.

So in zap_pages, the VMA cannot go away. If we find a VMA, ->close could not have been called yet.

In vma.c, we call remove_vma() after vms_clear_pte(). So after unmapping the pages and freeing the page tables.

Quoted 8 lines
Quoted 6 lines

So munmap() can indeed race with zap_vma_range(), and the page_mapped() check would not have changed anything about that really.

@BPF folks: does BPF take anywhere the mmap lock in read mode before calling zap_vma_range()? It should do that.

Yes, but do NOT.

In general, do NOT talk to me like that.

As I discussed recently with Ryan, we should update the locking requirements for zap_vma_range().

I assume friendly BPF folks will take care of fixing this.

Signature · 3 lines
Cheers,

David