From: Pedro Falcato <pfalcato@suse.de>
To: Andrew Morton <akpm@linux-foundation.org>,
David Hildenbrand <david@kernel.org>,
Lorenzo Stoakes <ljs@kernel.org>
Cc: Pedro Falcato <pfalcato@suse.de>, Zi Yan <ziy@nvidia.com>,
Baolin Wang <baolin.wang@linux.alibaba.com>,
"Liam R. Howlett" <liam@infradead.org>,
Nico Pache <npache@redhat.com>,
Ryan Roberts <ryan.roberts@arm.com>, Dev Jain <dev.jain@arm.com>,
Barry Song <baohua@kernel.org>, Lance Yang <lance.yang@linux.dev>,
Usama Arif <usama.arif@linux.dev>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org
Subject: [PATCH 1/8] mm/khugepaged: separate out windy folio logic from collapse_file
Date: Mon, 20 Jul 2026 15:29:06 +0100 [thread overview]
Message-ID: <20260720142913.846902-2-pfalcato@suse.de> (raw)
In-Reply-To: <20260720142913.846902-1-pfalcato@suse.de>
Separate out complex folio-related logic from the main collapse_file()
loop, and introduce a new helper struct to help marshal arguments back
and forth.
Signed-off-by: Pedro Falcato <pfalcato@suse.de>
---
mm/khugepaged.c | 189 ++++++++++++++++++++++++++++--------------------
1 file changed, 112 insertions(+), 77 deletions(-)
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 27e8f3077e80..d4de507ac001 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -2218,6 +2218,92 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff)
i_mmap_unlock_read(mapping);
}
+struct collapse_file_state {
+ /* in-out parameter */
+ struct folio *folio;
+ /* in parameters */
+ struct address_space *mapping;
+ struct file *file;
+ struct xa_state *xas;
+ /* collapse end index */
+ pgoff_t end;
+ unsigned int is_shmem : 1;
+};
+
+static enum scan_result prepare_collapse_file_folio(pgoff_t index, struct collapse_file_state *state)
+{
+ struct address_space *mapping = state->mapping;
+ enum scan_result result = SCAN_SUCCEED;
+ const int is_shmem = state->is_shmem;
+ struct folio *folio = state->folio;
+
+ if (is_shmem) {
+ if (xa_is_value(folio) || !folio_test_uptodate(folio)) {
+ xas_unlock_irq(state->xas);
+ /* swap in or instantiate fallocated page */
+ if (shmem_get_folio(mapping->host, index, 0,
+ &folio, SGP_NOALLOC))
+ result = SCAN_FAIL;
+ /* drain lru cache to help folio_isolate_lru() */
+ lru_add_drain();
+ goto xa_unlocked;
+ } else if (folio_trylock(folio)) {
+ folio_get(folio);
+ } else {
+ result = SCAN_PAGE_LOCK;
+ goto xa_locked;
+ }
+ } else { /* !is_shmem */
+ if (!folio || xa_is_value(folio)) {
+ xas_unlock_irq(state->xas);
+ page_cache_sync_readahead(mapping, &state->file->f_ra,
+ state->file, index,
+ state->end - index);
+ /* drain lru cache to help folio_isolate_lru() */
+ lru_add_drain();
+ folio = filemap_lock_folio(mapping, index);
+ if (IS_ERR(folio))
+ result = SCAN_FAIL;
+ goto xa_unlocked;
+ } else if (folio_test_dirty(folio)) {
+ /*
+ * This page is dirty because it hasn't
+ * been flushed since first write.
+ *
+ * Trigger async flush for read-only files and
+ * hope the writeback is done when khugepaged
+ * revisits this page. Writable files can have
+ * their folios dirty at any time; blindly
+ * flushing them would cause undesirable
+ * system-wide writeback.
+ *
+ * This is a one-off situation. We are not
+ * forcing writeback in loop.
+ */
+ xas_unlock_irq(state->xas);
+ if (!inode_is_open_for_write(mapping->host))
+ filemap_flush(mapping);
+ result = SCAN_PAGE_DIRTY_OR_WRITEBACK;
+ goto xa_unlocked;
+ } else if (folio_test_writeback(folio)) {
+ xas_unlock_irq(state->xas);
+ result = SCAN_PAGE_DIRTY_OR_WRITEBACK;
+ goto xa_unlocked;
+ } else if (folio_trylock(folio)) {
+ folio_get(folio);
+ } else {
+ result = SCAN_PAGE_LOCK;
+ goto xa_locked;
+ }
+ }
+
+xa_locked:
+ xas_unlock_irq(state->xas);
+xa_unlocked:
+ state->folio = folio;
+ return result;
+}
+
/**
* collapse_file - collapse filemap/tmpfs/shmem pages into huge one.
*
@@ -2255,6 +2341,13 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr,
enum scan_result result = SCAN_SUCCEED;
int nr_none = 0;
bool is_shmem = shmem_file(file);
+ struct collapse_file_state state = {
+ .is_shmem = is_shmem,
+ .xas = &xas,
+ .mapping = mapping,
+ .file = file,
+ .end = end,
+ };
/*
* MADV_COLLAPSE ignores shmem huge config, so do not check shmem
@@ -2298,87 +2391,29 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr,
folio = xas_load(&xas);
VM_BUG_ON(index != xas.xa_index);
- if (is_shmem) {
- if (!folio) {
- /*
- * Stop if extent has been truncated or
- * hole-punched, and is now completely
- * empty.
- */
- if (index == start) {
- if (!xas_next_entry(&xas, end - 1)) {
- result = SCAN_TRUNCATED;
- goto xa_locked;
- }
- }
- nr_none++;
- index++;
- continue;
- }
-
- if (xa_is_value(folio) || !folio_test_uptodate(folio)) {
- xas_unlock_irq(&xas);
- /* swap in or instantiate fallocated page */
- if (shmem_get_folio(mapping->host, index, 0,
- &folio, SGP_NOALLOC)) {
- result = SCAN_FAIL;
- goto xa_unlocked;
- }
- /* drain lru cache to help folio_isolate_lru() */
- lru_add_drain();
- } else if (folio_trylock(folio)) {
- folio_get(folio);
- xas_unlock_irq(&xas);
- } else {
- result = SCAN_PAGE_LOCK;
- goto xa_locked;
- }
- } else { /* !is_shmem */
- if (!folio || xa_is_value(folio)) {
- xas_unlock_irq(&xas);
- page_cache_sync_readahead(mapping, &file->f_ra,
- file, index,
- end - index);
- /* drain lru cache to help folio_isolate_lru() */
- lru_add_drain();
- folio = filemap_lock_folio(mapping, index);
- if (IS_ERR(folio)) {
- result = SCAN_FAIL;
- goto xa_unlocked;
+ if (is_shmem && !folio) {
+ /*
+ * Stop if extent has been truncated or
+ * hole-punched, and is now completely
+ * empty.
+ */
+ if (index == start) {
+ if (!xas_next_entry(&xas, end - 1)) {
+ result = SCAN_TRUNCATED;
+ goto xa_locked;
}
- } else if (folio_test_dirty(folio)) {
- /*
- * This page is dirty because it hasn't
- * been flushed since first write.
- *
- * Trigger async flush for read-only files and
- * hope the writeback is done when khugepaged
- * revisits this page. Writable files can have
- * their folios dirty at any time; blindly
- * flushing them would cause undesirable
- * system-wide writeback.
- *
- * This is a one-off situation. We are not
- * forcing writeback in loop.
- */
- xas_unlock_irq(&xas);
- if (!inode_is_open_for_write(mapping->host))
- filemap_flush(mapping);
- result = SCAN_PAGE_DIRTY_OR_WRITEBACK;
- goto xa_unlocked;
- } else if (folio_test_writeback(folio)) {
- xas_unlock_irq(&xas);
- result = SCAN_PAGE_DIRTY_OR_WRITEBACK;
- goto xa_unlocked;
- } else if (folio_trylock(folio)) {
- folio_get(folio);
- xas_unlock_irq(&xas);
- } else {
- result = SCAN_PAGE_LOCK;
- goto xa_locked;
}
+ nr_none++;
+ index++;
+ continue;
}
+ /* At this point folio can be NULL, or a value. */
+ state.folio = folio;
+ result = prepare_collapse_file_folio(index, &state);
+ folio = state.folio;
+ if (result != SCAN_SUCCEED)
+ goto xa_unlocked;
/*
* The folio must be locked, so we can drop the i_pages lock
* without racing with truncate.
--
2.55.0
next prev parent reply other threads:[~2026-07-20 14:29 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-20 14:29 [PATCH 0/8] mm/khugepaged: collapse_file() cleanups Pedro Falcato
2026-07-20 14:29 ` Pedro Falcato [this message]
2026-07-20 15:38 ` [PATCH 1/8] mm/khugepaged: separate out windy folio logic from collapse_file Lorenzo Stoakes (ARM)
2026-07-20 15:39 ` Lorenzo Stoakes (ARM)
2026-07-20 14:29 ` [PATCH 2/8] mm/khugepaged: factor out page cache folio reading Pedro Falcato
2026-07-20 16:53 ` Lorenzo Stoakes (ARM)
2026-07-20 14:29 ` [PATCH 3/8] mm/khugepaged: factor out and simplify dirty/writeback handling Pedro Falcato
2026-07-20 14:29 ` [PATCH 4/8] mm/khugepaged: simplify prepare folio locking and exit paths Pedro Falcato
2026-07-20 14:29 ` [PATCH 5/8] mm/khugepaged: add kerneldoc to prepare_collapse_file_folio() Pedro Falcato
2026-07-20 14:29 ` [PATCH 6/8] mm/khugepaged: hoist isolation into collapse_isolate_folio() Pedro Falcato
2026-07-20 14:29 ` [PATCH 7/8] mm/khugepaged: hoist more code " Pedro Falcato
2026-07-20 14:29 ` [PATCH 8/8] mm/khugepaged: fix and flesh out try_to_unmap_flush() comment Pedro Falcato
2026-07-20 15:21 ` [PATCH 0/8] mm/khugepaged: collapse_file() cleanups Nico Pache
2026-07-20 19:49 ` Pedro Falcato
2026-07-20 20:14 ` Nico Pache
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=20260720142913.846902-2-pfalcato@suse.de \
--to=pfalcato@suse.de \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=david@kernel.org \
--cc=dev.jain@arm.com \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=npache@redhat.com \
--cc=ryan.roberts@arm.com \
--cc=usama.arif@linux.dev \
--cc=ziy@nvidia.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.