Re: [PATCH V2 1/2] mm/khugepaged: do synchronous writeback for MADV_COLLAPSE

From: David Hildenbrand (Red Hat)

Date: Thu Nov 20 2025 - 08:35:38 EST


On 11/20/25 07:50, Shivank Garg wrote:
When MADV_COLLAPSE is called on file-backed mappings (e.g., executable
text sections), the pages may still be dirty from recent writes and
cause collapse to fail with -EINVAL. This is particularly problematic
for freshly copied executables on filesystems, where page cache folios
remain dirty until background writeback completes.

The current code in collapse_file() triggers async writeback via
filemap_flush() and expects khugepaged to revisit the page later.
However, MADV_COLLAPSE is a synchronous operation where userspace
expects immediate results.

Perform synchronous writeback in madvise_collapse() before attempting
collapse to avoid failing on first attempt.

Reported-by: Branden Moore <Branden.Moore@xxxxxxx>
Closes: https://lore.kernel.org/all/4e26fe5e-7374-467c-a333-9dd48f85d7cc@xxxxxxx
Fixes: 34488399fa08 ("mm/madvise: add file and shmem support to MADV_COLLAPSE")
Suggested-by: David Hildenbrand <david@xxxxxxxxxx>
Signed-off-by: Shivank Garg <shivankg@xxxxxxx>
---
mm/khugepaged.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 97d1b2824386..066a332c76ad 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -22,6 +22,7 @@
#include <linux/dax.h>
#include <linux/ksm.h>
#include <linux/pgalloc.h>
+#include <linux/backing-dev.h>
#include <asm/tlb.h>
#include "internal.h"
@@ -2784,6 +2785,31 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start,
hstart = (start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
hend = end & HPAGE_PMD_MASK;
+ /*
+ * For file-backed VMAs, perform synchronous writeback to ensure
+ * dirty folios are flushed before attempting collapse. This avoids
+ * failing on the first attempt when freshly-written executable text
+ * is still dirty in the page cache.
+ */
+ if (!vma_is_anonymous(vma) && vma->vm_file) {
+ struct address_space *mapping = vma->vm_file->f_mapping;
+
+ if (mapping_can_writeback(mapping)) {
+ pgoff_t pgoff_start = linear_page_index(vma, hstart);
+ pgoff_t pgoff_end = linear_page_index(vma, hend);
+ loff_t lstart = (loff_t)pgoff_start << PAGE_SHIFT;
+ loff_t lend = ((loff_t)pgoff_end << PAGE_SHIFT) - 1;
+

Hm, so we always do that, without any indication that there actually is something dirty there.

Internally filemap_write_and_wait_range() uses something called mapping_needs_writeback(), but it also applies to the complete file, not a range.

Wouldn't it be better do do that only if we detect that there is actually a dirty folio in the range?

That is, if we find any dirty folio in hpage_collapse_scan_file() and we are in madvise, do that dance here and retry?

--
Cheers

David