Skip to contentSkip to conversation

FAILED: patch "[PATCH] mm: call ->free_folio() directly in folio_unmap_invalidate()" failed to apply to 6.18-stable tree

PATCH In review [email protected] 3 participants 7 messages Apr 20 – May 1

Subsystems (4)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 615d9bb2ccad42f9e21d837431e401db2e471195
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026042002-idealness-evade-7213@gregkh' --subject-prefix 'PATCH 6.18.y' HEAD^..

Possible dependencies:

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From 615d9bb2ccad42f9e21d837431e401db2e471195 Mon Sep 17 00:00:00 2001
From: "Matthew Wilcox (Oracle)" <[email protected]>
Date: Mon, 13 Apr 2026 19:43:11 +0100
Subject: [PATCH] mm: call ->free_folio() directly in folio_unmap_invalidate()

We can only call filemap_free_folio() if we have a reference to (or hold a lock on) the mapping. Otherwise, we've already removed the folio from the mapping so it no longer pins the mapping and the mapping can be removed, causing a use-after-free when accessing mapping->a_ops.

Follow the same pattern as __remove_mapping() and load the free_folio function pointer before dropping the lock on the mapping. That lets us make filemap_free_folio() static as this was the only caller outside filemap.c.

Fixes fb7d3bc41493 ("mm/filemap: drop streaming/uncached pages when writeback completes")
Cc Jens Axboe <[email protected]>
Cc Jan Kara <[email protected]>
mm/filemap.c +2 -1
index 3c1e785542dd..793bf4816ea3 100644
@@ -228,7 +228,8 @@ void __filemap_remove_folio(struct folio *folio, void *shadow)
 	page_cache_delete(mapping, folio, shadow);
 }
 
-void filemap_free_folio(struct address_space *mapping, struct folio *folio)
+static void filemap_free_folio(const struct address_space *mapping,
+		struct folio *folio)
 {
 	void (*free_folio)(struct folio *);
 
mm/internal.h +0 -1
index cb0af847d7d9..546114d3ee44 100644
@@ -540,7 +540,6 @@ unsigned find_lock_entries(struct address_space *mapping, pgoff_t *start,
 		pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
 unsigned find_get_entries(struct address_space *mapping, pgoff_t *start,
 		pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
-void filemap_free_folio(struct address_space *mapping, struct folio *folio);
 int truncate_inode_folio(struct address_space *mapping, struct folio *folio);
 bool truncate_inode_partial_folio(struct folio *folio, loff_t start,
 		loff_t end);
mm/truncate.c +5 -1
index 12467c1bd711..8617a12cb169 100644
@@ -622,6 +622,7 @@ static int folio_launder(struct address_space *mapping, struct folio *folio)
 int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio,
 			   gfp_t gfp)
 {
+	void (*free_folio)(struct folio *);
 	int ret;
 
 	VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
@@ -648,9 +649,12 @@ int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio,
 	xa_unlock_irq(&mapping->i_pages);
 	if (mapping_shrinkable(mapping))
 		inode_lru_list_add(mapping->host);
+	free_folio = mapping->a_ops->free_folio;
 	spin_unlock(&mapping->host->i_lock);
 
-	filemap_free_folio(mapping, folio);
+	if (free_folio)
+		free_folio(folio);
+	folio_put_refs(folio, folio_nr_pages(folio));
 	return 1;
 failed:
 	xa_unlock_irq(&mapping->i_pages);
#1 lore raw

PATCH 1/1 mm: call ->free_folio() directly in folio_unmap_invalidate()

We can only call filemap_free_folio() if we have a reference to (or hold a lock on) the mapping. Otherwise, we've already removed the folio from the mapping so it no longer pins the mapping and the mapping can be removed, causing a use-after-free when accessing mapping->a_ops.

Follow the same pattern as __remove_mapping() and load the free_folio function pointer before dropping the lock on the mapping. That lets us make filemap_free_folio() static as this was the only caller outside filemap.c.

Fixes fb7d3bc41493 ("mm/filemap: drop streaming/uncached pages when writeback completes")
Cc Jens Axboe <[email protected]>
Cc Jan Kara <[email protected]>
(cherry picked from commit 615d9bb2ccad42f9e21d837431e401db2e471195)
---
 mm/filemap.c  | 3 ++-
 mm/internal.h | 1 -
 mm/truncate.c | 6 +++++-
 3 files changed, 7 insertions(+), 3 deletions(-)
mm/filemap.c +2 -1
index d8d9c0f0beb6..76bbfa69aca0 100644
@@ -233,7 +233,8 @@ void __filemap_remove_folio(struct folio *folio, void *shadow)
 	page_cache_delete(mapping, folio, shadow);
 }
 
-void filemap_free_folio(struct address_space *mapping, struct folio *folio)
+static void filemap_free_folio(const struct address_space *mapping,
+		struct folio *folio)
 {
 	void (*free_folio)(struct folio *);
 	int refs = 1;
mm/internal.h +0 -1
index 9e0577413087..f046099d8eff 100644
@@ -401,7 +401,6 @@ unsigned find_lock_entries(struct address_space *mapping, pgoff_t *start,
 		pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
 unsigned find_get_entries(struct address_space *mapping, pgoff_t *start,
 		pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
-void filemap_free_folio(struct address_space *mapping, struct folio *folio);
 int truncate_inode_folio(struct address_space *mapping, struct folio *folio);
 bool truncate_inode_partial_folio(struct folio *folio, loff_t start,
 		loff_t end);
mm/truncate.c +5 -1
index fb5c20b57bd4..6bbe22ae3ab8 100644
@@ -574,6 +574,7 @@ EXPORT_SYMBOL(invalidate_mapping_pages);
 static int invalidate_complete_folio2(struct address_space *mapping,
 					struct folio *folio)
 {
+	void (*free_folio)(struct folio *);
 	if (folio->mapping != mapping)
 		return 0;
 
@@ -590,9 +591,12 @@ static int invalidate_complete_folio2(struct address_space *mapping,
 	xa_unlock_irq(&mapping->i_pages);
 	if (mapping_shrinkable(mapping))
 		inode_add_lru(mapping->host);
+	free_folio = mapping->a_ops->free_folio;
 	spin_unlock(&mapping->host->i_lock);
 
-	filemap_free_folio(mapping, folio);
+	if (free_folio)
+		free_folio(folio);
+	folio_put_refs(folio, folio_nr_pages(folio));
 	return 1;
 failed:
 	xa_unlock_irq(&mapping->i_pages);
Signature · 1 line
2.47.3
author maintainer
#2 lore raw

Re: [PATCH 6.18.y] mm: call ->free_folio() directly in folio_unmap_invalidate()

Quoted 20 lines · Matthew Wilcox (Oracle)

We can only call filemap_free_folio() if we have a reference to (or hold a lock on) the mapping. Otherwise, we've already removed the folio from the mapping so it no longer pins the mapping and the mapping can be removed, causing a use-after-free when accessing mapping->a_ops.

Follow the same pattern as __remove_mapping() and load the free_folio function pointer before dropping the lock on the mapping. That lets us make filemap_free_folio() static as this was the only caller outside filemap.c.

Fixes fb7d3bc41493 ("mm/filemap: drop streaming/uncached pages when writeback completes")
Cc Jens Axboe <[email protected]>
Cc Jan Kara <[email protected]>
(cherry picked from commit 615d9bb2ccad42f9e21d837431e401db2e471195)
Quoted 5 lines
---
 mm/filemap.c  | 3 ++-
 mm/internal.h | 1 -
 mm/truncate.c | 6 +++++-
 3 files changed, 7 insertions(+), 3 deletions(-)

This is odd, it's applying with a ton of fuzz, which puts things in the wrong functions and breaks the build. Let me go fix it up by hand...

thanks,

greg k-h

author maintainer
#3 lore raw

Re: [PATCH 6.18.y] mm: call ->free_folio() directly in folio_unmap_invalidate()

In reply to Matthew Wilcox (Oracle)
Quoted 65 lines · Matthew Wilcox (Oracle)

We can only call filemap_free_folio() if we have a reference to (or hold a lock on) the mapping. Otherwise, we've already removed the folio from the mapping so it no longer pins the mapping and the mapping can be removed, causing a use-after-free when accessing mapping->a_ops.

Follow the same pattern as __remove_mapping() and load the free_folio function pointer before dropping the lock on the mapping. That lets us make filemap_free_folio() static as this was the only caller outside filemap.c.

Fixes fb7d3bc41493 ("mm/filemap: drop streaming/uncached pages when writeback completes")
Cc Jens Axboe <[email protected]>
Cc Jan Kara <[email protected]>
(cherry picked from commit 615d9bb2ccad42f9e21d837431e401db2e471195)
---
 mm/filemap.c  | 3 ++-
 mm/internal.h | 1 -
 mm/truncate.c | 6 +++++-
 3 files changed, 7 insertions(+), 3 deletions(-)
mm/filemap.c +2 -1
index d8d9c0f0beb6..76bbfa69aca0 100644
@@ -233,7 +233,8 @@ void __filemap_remove_folio(struct folio *folio, void *shadow)
 	page_cache_delete(mapping, folio, shadow);
 }
 
-void filemap_free_folio(struct address_space *mapping, struct folio *folio)
+static void filemap_free_folio(const struct address_space *mapping,
+		struct folio *folio)
 {
 	void (*free_folio)(struct folio *);
 	int refs = 1;
mm/internal.h +0 -1
index 9e0577413087..f046099d8eff 100644
@@ -401,7 +401,6 @@ unsigned find_lock_entries(struct address_space *mapping, pgoff_t *start,
 		pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
 unsigned find_get_entries(struct address_space *mapping, pgoff_t *start,
 		pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
-void filemap_free_folio(struct address_space *mapping, struct folio *folio);
 int truncate_inode_folio(struct address_space *mapping, struct folio *folio);
 bool truncate_inode_partial_folio(struct folio *folio, loff_t start,
 		loff_t end);
mm/truncate.c +1 -0
index fb5c20b57bd4..6bbe22ae3ab8 100644
@@ -574,6 +574,7 @@ EXPORT_SYMBOL(invalidate_mapping_pages);
 static int invalidate_complete_folio2(struct address_space *mapping,
 					struct folio *folio)
 {
+	void (*free_folio)(struct folio *);
 	if (folio->mapping != mapping)
 		return 0;
 

mm/truncate.c static int invalidate_complete_folio2(struct address_space *mapping, · line 594

Quoted 4 lines
mm/truncate.c +1 -0
@@ -590,9 +591,12 @@ static int invalidate_complete_folio2(struct address_space *mapping,
 	xa_unlock_irq(&mapping->i_pages);
 	if (mapping_shrinkable(mapping))
 		inode_add_lru(mapping->host);
+	free_folio = mapping->a_ops->free_folio;

Wait, I see what's wrong, this function isn't even in 6.18.y, which is why when I apply this it has fuzz and blows up.

So this isn't going to work at all here, did you send the wrong backport?

thanks,

greg k-h

maintainer
#4 lore raw

Re: [PATCH 6.18.y] mm: call ->free_folio() directly in folio_unmap_invalidate()

Quoted 15 lines · Greg KH
Quoted 5 lines · Matthew Wilcox (Oracle)

We can only call filemap_free_folio() if we have a reference to (or hold a lock on) the mapping. Otherwise, we've already removed the folio from the mapping so it no longer pins the mapping and the mapping can be removed, causing a use-after-free when accessing mapping->a_ops.

Quoted 8 lines

Follow the same pattern as __remove_mapping() and load the free_folio function pointer before dropping the lock on the mapping. That lets us make filemap_free_folio() static as this was the only caller outside filemap.c.

Fixes fb7d3bc41493 ("mm/filemap: drop streaming/uncached pages when writeback completes")
Quoted 5 lines

Wait, I see what's wrong, this function isn't even in 6.18.y, which is why when I apply this it has fuzz and blows up.

So this isn't going to work at all here, did you send the wrong backport?

Oh blimey. Somehow I did the backport to 6.12.y. Sorry about that; I'll do 6.18 now.

#5 lore raw

PATCH 1/1 mm: call ->free_folio() directly in folio_unmap_invalidate()

In reply to [email protected]

We can only call filemap_free_folio() if we have a reference to (or hold a lock on) the mapping. Otherwise, we've already removed the folio from the mapping so it no longer pins the mapping and the mapping can be removed, causing a use-after-free when accessing mapping->a_ops.

Follow the same pattern as __remove_mapping() and load the free_folio function pointer before dropping the lock on the mapping. That lets us make filemap_free_folio() static as this was the only caller outside filemap.c.

Fixes fb7d3bc41493 ("mm/filemap: drop streaming/uncached pages when writeback completes")
Cc Jens Axboe <[email protected]>
Cc Jan Kara <[email protected]>
(cherry picked from commit 615d9bb2ccad42f9e21d837431e401db2e471195)
---
 mm/filemap.c  | 3 ++-
 mm/internal.h | 1 -
 mm/truncate.c | 6 +++++-
 3 files changed, 7 insertions(+), 3 deletions(-)
mm/filemap.c +2 -1
index 91dcfe14a67b..e3339cf37a1f 100644
@@ -227,7 +227,8 @@ void __filemap_remove_folio(struct folio *folio, void *shadow)
 	page_cache_delete(mapping, folio, shadow);
 }
 
-void filemap_free_folio(struct address_space *mapping, struct folio *folio)
+static void filemap_free_folio(const struct address_space *mapping,
+		struct folio *folio)
 {
 	void (*free_folio)(struct folio *);
 
mm/internal.h +0 -1
index c80c6f566c2d..4cf6a12ff491 100644
@@ -471,7 +471,6 @@ unsigned find_lock_entries(struct address_space *mapping, pgoff_t *start,
 		pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
 unsigned find_get_entries(struct address_space *mapping, pgoff_t *start,
 		pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
-void filemap_free_folio(struct address_space *mapping, struct folio *folio);
 int truncate_inode_folio(struct address_space *mapping, struct folio *folio);
 bool truncate_inode_partial_folio(struct folio *folio, loff_t start,
 		loff_t end);
mm/truncate.c +5 -1
index 3c5a50ae3274..4bf64fd610c9 100644
@@ -622,6 +622,7 @@ static int folio_launder(struct address_space *mapping, struct folio *folio)
 int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio,
 			   gfp_t gfp)
 {
+	void (*free_folio)(struct folio *);
 	int ret;
 
 	VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
@@ -648,9 +649,12 @@ int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio,
 	xa_unlock_irq(&mapping->i_pages);
 	if (mapping_shrinkable(mapping))
 		inode_add_lru(mapping->host);
+	free_folio = mapping->a_ops->free_folio;
 	spin_unlock(&mapping->host->i_lock);
 
-	filemap_free_folio(mapping, folio);
+	if (free_folio)
+		free_folio(folio);
+	folio_put_refs(folio, folio_nr_pages(folio));
 	return 1;
 failed:
 	xa_unlock_irq(&mapping->i_pages);
Signature · 1 line
2.47.3
maintainer
#6 lore raw

Re: [PATCH 6.18.y] mm: call ->free_folio() directly in folio_unmap_invalidate()

Quoted 9 lines · Matthew Wilcox (Oracle)

We can only call filemap_free_folio() if we have a reference to (or hold a lock on) the mapping. Otherwise, we've already removed the folio from the mapping so it no longer pins the mapping and the mapping can be removed, causing a use-after-free when accessing mapping->a_ops.

Follow the same pattern as __remove_mapping() and load the free_folio function pointer before dropping the lock on the mapping. That lets us make filemap_free_folio() static as this was the only caller outside filemap.c.

Thanks, queued for 6.18.y.

--
Thanks,
Sasha