From: Hugh Dickins <hughd@google.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Ackerley Tng <ackerleytng@google.com>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Alexandre Ghiti <alex@ghiti.fr>,
Baolin Wang <baolin.wang@linux.alibaba.com>,
Barry Song <baohua@kernel.org>,
Binbin Wu <binbin.wu@linux.intel.com>,
Christian Brauner <brauner@kernel.org>,
Christoph Hellwig <hch@lst.de>,
Christoph Lameter <cl@gentwo.org>,
Claudio Imbrenda <imbrenda@linux.ibm.com>,
David Hildenbrand <david@kernel.org>,
JP Kobryn <jp.kobryn@linux.dev>, Jan Kara <jack@suse.cz>,
Jens Axboe <axboe@kernel.dk>,
Johannes Weiner <hannes@cmpxchg.org>,
Kairui Song <ryncsn@gmail.com>, Kiryl Shutsemau <kas@kernel.org>,
Lance Yang <lance.yang@linux.dev>,
Leonardo Bras <leobras.c@gmail.com>,
Lorenzo Stoakes <ljs@kernel.org>,
Marcelo Tosatti <mtosatti@redhat.com>,
Matthew Wilcox <willy@infradead.org>,
Mel Gorman <mgorman@techsingularity.net>,
Miaohe Lin <linmiaohe@huawei.com>,
Michal Hocko <mhocko@suse.com>, Minchan Kim <minchan@kernel.org>,
Muchun Song <muchun.song@linux.dev>,
Oscar Salvador <osalvador@suse.de>,
Peter Zijlstra <peterz@infradead.org>,
Qi Zheng <qi.zheng@linux.dev>, Rik van Riel <riel@surriel.com>,
Sebastian Andrzej Siewior <bigeasy@linutronix.de>,
Shakeel Butt <shakeel.butt@linux.dev>,
Suren Baghdasaryan <surenb@google.com>,
Vlastimil Babka <vbabka@kernel.org>,
Yang Shi <yang@os.amperecomputing.com>,
Yu Zhao <yuzhao@google.com>, Zach O'Keefe <zokeefe@google.com>,
Zi Yan <ziy@nvidia.com>,
linux-block@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-mm@kvack.org
Subject: [PATCH v2 06/26] mm/fbatch: fbatch_drain_lazyfree(onstack fbatch) before ptl unlock
Date: Wed, 9 Sep 2026 02:53:35 -0700 (PDT) [thread overview]
Message-ID: <17e1a6c3-525b-1cc3-0731-349f0850e3ea@google.com> (raw)
In-Reply-To: <e28f9a94-4339-f8ac-8301-6be3c9b5b7ce@google.com>
Re-enable lazyfree batching for MADV_FREE. But it's not safe now to leave
potentially stale (then reused) folios in a per-cpu fbatch for lazyfree.
Instead, madvise_free_pte_range() keep an fbatch on its stack, and drain
it each time before dropping pagetable lock, while the folios are secure.
Ignore folio_may_be_lru_cached() and lru_cache_disabled(): limitations
irrelevant to this fbatch drained under spinlock (even if RT); though
in practice madvise_free_huge_pmd() does have to drain every time.
Signed-off-by: Hugh Dickins <hughd@google.com>
---
include/linux/huge_mm.h | 6 ++++--
mm/folio.c | 34 +++++++++++++++++++++-------------
mm/huge_memory.c | 6 ++++--
mm/internal.h | 3 ++-
mm/madvise.c | 9 +++++++--
5 files changed, 38 insertions(+), 20 deletions(-)
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index c745f7ad2298..d50906327d1d 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -24,9 +24,11 @@ static inline void huge_pud_set_accessed(struct vm_fault *vmf, pud_t orig_pud)
}
#endif
-vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf);
+struct folio_batch;
bool madvise_free_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
- pmd_t *pmd, unsigned long addr, unsigned long next);
+ pmd_t *pmd, unsigned long addr, unsigned long next,
+ struct folio_batch *fbatch);
+vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf);
bool zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma, pmd_t *pmd,
unsigned long addr);
int zap_huge_pud(struct mmu_gather *tlb, struct vm_area_struct *vma, pud_t *pud,
diff --git a/mm/folio.c b/mm/folio.c
index e743cd539b9e..a18d8ef6afd5 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -51,7 +51,6 @@ struct cpu_fbatches {
struct folio_batch lru_activate;
struct folio_batch lru_deactivate_file;
struct folio_batch lru_deactivate;
- struct folio_batch lru_lazyfree;
/* Protecting the following batches which require disabling interrupts */
local_lock_t lock_irq;
struct folio_batch lru_move_tail;
@@ -194,8 +193,6 @@ static void __folio_batch_add_and_move(struct folio_batch __percpu *fbatch,
local_lock(&cpu_fbatches.lock);
if (!folio_batch_add(this_cpu_ptr(fbatch), folio) ||
- /* XXX Temporarily disable lazyfree batching */
- fbatch == &cpu_fbatches.lru_lazyfree ||
!folio_may_be_lru_cached(folio) || lru_cache_disabled())
folio_batch_move_lru(this_cpu_ptr(fbatch), move_fn);
@@ -585,10 +582,6 @@ void lru_add_drain_cpu(int cpu)
fbatch = &fbatches->lru_deactivate;
if (folio_batch_count(fbatch))
folio_batch_move_lru(fbatch, lru_deactivate);
-
- fbatch = &fbatches->lru_lazyfree;
- if (folio_batch_count(fbatch))
- folio_batch_move_lru(fbatch, lru_lazyfree);
}
/**
@@ -634,19 +627,35 @@ void folio_deactivate(struct folio *folio)
/**
* folio_mark_lazyfree - make an anon folio lazyfree
- * @folio: folio to deactivate
+ * @fbatch: batch to which folio will be added
+ * @folio: folio to be lazily freed
*
- * folio_mark_lazyfree() moves @folio to the inactive file list.
- * This is done to accelerate the reclaim of @folio.
+ * folio_mark_lazyfree() moves @folio to the inactive file list
+ * via @fbatch. This is done to accelerate the reclaim of @folio.
*/
-void folio_mark_lazyfree(struct folio *folio)
+void folio_mark_lazyfree(struct folio_batch *fbatch, struct folio *folio)
{
if (!folio_test_anon(folio) || !folio_test_swapbacked(folio) ||
!folio_test_lru(folio) ||
folio_test_swapcache(folio) || folio_test_unevictable(folio))
return;
- folio_batch_add_and_move(folio, lru_lazyfree);
+ if (!folio_batch_add(fbatch, folio))
+ folio_batch_move_lru(fbatch, lru_lazyfree);
+}
+
+/**
+ * fbatch_drain_lazyfree - drain the caller's folio batch
+ * @fbatch: batch of folios to be lazily freed
+ *
+ * Must be called before caller drops the page table lock: that is,
+ * before dropping the last certain reference to the folios in @fbatch.
+ * It would be very bad to lazyfree a folio after it was freed and reused.
+ */
+void fbatch_drain_lazyfree(struct folio_batch *fbatch)
+{
+ if (folio_batch_count(fbatch))
+ folio_batch_move_lru(fbatch, lru_lazyfree);
}
void lru_add_drain(void)
@@ -700,7 +709,6 @@ static bool cpu_needs_drain(unsigned int cpu)
folio_batch_count(&fbatches->lru_move_tail) ||
folio_batch_count(&fbatches->lru_deactivate_file) ||
folio_batch_count(&fbatches->lru_deactivate) ||
- folio_batch_count(&fbatches->lru_lazyfree) ||
need_mlock_drain(cpu)) ||
has_bh_in_lru(cpu, NULL);
}
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index c7510d875433..abebd8a23e56 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2356,7 +2356,8 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf)
* Otherwise, return false.
*/
bool madvise_free_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
- pmd_t *pmd, unsigned long addr, unsigned long next)
+ pmd_t *pmd, unsigned long addr, unsigned long next,
+ struct folio_batch *fbatch)
{
spinlock_t *ptl;
pmd_t orig_pmd;
@@ -2417,7 +2418,8 @@ bool madvise_free_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
}
- folio_mark_lazyfree(folio);
+ folio_mark_lazyfree(fbatch, folio);
+ fbatch_drain_lazyfree(fbatch);
ret = true;
out:
spin_unlock(ptl);
diff --git a/mm/internal.h b/mm/internal.h
index 38b1165212c9..0d78406eb126 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -63,7 +63,8 @@ void lru_add_drain(void);
void lru_add_drain_cpu(int cpu);
void lru_add_drain_cpu_zone(struct zone *zone);
void folio_deactivate(struct folio *folio);
-void folio_mark_lazyfree(struct folio *folio);
+void folio_mark_lazyfree(struct folio_batch *fbatch, struct folio *folio);
+void fbatch_drain_lazyfree(struct folio_batch *fbatch);
/* mm/vmscan.c */
unsigned long zone_reclaimable_pages(struct zone *zone);
diff --git a/mm/madvise.c b/mm/madvise.c
index eeee82cf2b3f..b2eab519af19 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -27,6 +27,7 @@
#include <linux/file.h>
#include <linux/blk_plug.h>
#include <linux/backing-dev.h>
+#include <linux/folio_batch.h>
#include <linux/pagewalk.h>
#include <linux/swap.h>
#include <linux/leafops.h>
@@ -666,6 +667,7 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
struct mmu_gather *tlb = walk->private;
struct mm_struct *mm = tlb->mm;
struct vm_area_struct *vma = walk->vma;
+ struct folio_batch fbatch;
spinlock_t *ptl;
pte_t *start_pte, *pte, ptent;
struct folio *folio;
@@ -673,9 +675,10 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
unsigned long next;
int nr, max_nr;
+ folio_batch_init(&fbatch);
next = pmd_addr_end(addr, end);
if (pmd_trans_huge(*pmd))
- if (madvise_free_huge_pmd(tlb, vma, pmd, addr, next))
+ if (madvise_free_huge_pmd(tlb, vma, pmd, addr, next, &fbatch))
return 0;
tlb_change_page_size(tlb, PAGE_SIZE);
@@ -733,6 +736,7 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
continue;
folio_get(folio);
lazy_mmu_mode_disable();
+ fbatch_drain_lazyfree(&fbatch);
pte_unmap_unlock(start_pte, ptl);
start_pte = NULL;
err = split_folio(folio);
@@ -777,13 +781,14 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
clear_young_dirty_ptes(vma, addr, pte, nr, cydp_flags);
tlb_remove_tlb_entries(tlb, pte, nr, addr);
}
- folio_mark_lazyfree(folio);
+ folio_mark_lazyfree(&fbatch, folio);
}
if (nr_swap)
add_mm_counter(mm, MM_SWAPENTS, nr_swap);
if (start_pte) {
lazy_mmu_mode_disable();
+ fbatch_drain_lazyfree(&fbatch);
pte_unmap_unlock(start_pte, ptl);
}
cond_resched();
--
2.51.0
next prev parent reply other threads:[~2026-09-09 9:53 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 9:39 [PATCH v2 00/26] mm/fbatch: drain lru_add_drain() and _all() Hugh Dickins
2026-09-09 9:42 ` [PATCH v2 01/26] mm/fbatch: remove !CONFIG_SMP special case of folio_activate() Hugh Dickins
2026-09-09 9:44 ` [PATCH v2 02/26] mm/fbatch: allow folios_put_refs() to skip xa_is_value() entries Hugh Dickins
2026-09-09 9:46 ` [PATCH v2 03/26] mm/fbatch: temporarily disable lazyfree and mlock+munlock batching Hugh Dickins
2026-09-09 9:49 ` [PATCH v2 04/26] mm/fbatch: lru bit set, no extra ref, while folio on per-cpu fbatch Hugh Dickins
2026-09-09 12:25 ` Vlastimil Babka (SUSE)
2026-09-12 19:30 ` Hugh Dickins
2026-09-09 9:51 ` [PATCH v2 05/26] mm/fbatch: lru_add_del_folio()+folio_add_lru() after clear_lru() Hugh Dickins
2026-09-09 15:04 ` Vlastimil Babka (SUSE)
2026-09-12 21:59 ` Hugh Dickins
2026-09-09 9:53 ` Hugh Dickins [this message]
2026-09-09 21:02 ` [PATCH v2 06/26] mm/fbatch: fbatch_drain_lazyfree(onstack fbatch) before ptl unlock Vlastimil Babka (SUSE)
2026-09-12 22:07 ` Hugh Dickins
2026-09-09 9:55 ` [PATCH v2 07/26] mm/fbatch: LRU_NEXT_ACTIVATE bit to optimize folio_activate() Hugh Dickins
2026-09-10 12:01 ` Vlastimil Babka (SUSE)
2026-09-12 22:35 ` Hugh Dickins
2026-09-14 19:55 ` Hugh Dickins
2026-09-10 16:42 ` Kiryl Shutsemau
2026-09-12 23:33 ` Hugh Dickins
2026-09-14 20:19 ` Hugh Dickins
2026-09-15 13:08 ` Kiryl Shutsemau
2026-09-09 9:57 ` [PATCH v2 08/26] mm/fbatch: replace mlock_new_folio() by __folio_add_lru(,mlockit) Hugh Dickins
2026-09-10 17:47 ` Vlastimil Babka (SUSE)
2026-09-09 9:59 ` [PATCH v2 09/26] mm/fbatch: restore mlock+munlock batching, without extra ref Hugh Dickins
2026-09-10 21:05 ` Vlastimil Babka (SUSE)
2026-09-12 23:46 ` Hugh Dickins
2026-09-14 8:16 ` Vlastimil Babka (SUSE)
2026-09-09 10:01 ` [PATCH v2 10/26] mm/fbatch: remove several uses of mlock_drain_local() Hugh Dickins
2026-09-09 10:03 ` [PATCH v2 11/26] mm/fbatch: remove migration's PAGE_WAS_MLOCKED lru_add_drain() Hugh Dickins
2026-09-09 10:05 ` [PATCH v2 12/26] mm/fbatch: remove percpu_pvec_drained and folios_put() Hugh Dickins
2026-09-09 10:08 ` [PATCH v2 13/26] mm/fbatch: no lru_add_drain to collect_longterm_unpinnable_folios() Hugh Dickins
2026-09-09 10:10 ` [PATCH v2 14/26] mm/fbatch: no lru_add_drain() nor _all() for memfd_wait_for_pins() Hugh Dickins
2026-09-09 10:12 ` [PATCH v2 15/26] mm/fbatch: remove shake_folio() shake_page() from memory-failure Hugh Dickins
2026-09-09 10:14 ` [PATCH v2 16/26] mm/fbatch: remove lru_cache_disable() from NUMA folio migration Hugh Dickins
2026-09-09 10:16 ` [PATCH v2 17/26] mm/fbatch: no lru_cache_disable() in __alloc_contig_migrate_range() Hugh Dickins
2026-09-09 10:18 ` [PATCH v2 18/26] mm/fbatch: remove lru_add_drain() and _all() calls from various Hugh Dickins
2026-09-09 10:20 ` [PATCH v2 19/26] mm/fbatch: vm/stat_refresh include lru_add_drain() on each cpu Hugh Dickins
2026-09-09 10:23 ` [PATCH v2 20/26] s390/fbatch: no lru_add_drain_all() in s390_wiggle_split_folio() Hugh Dickins
2026-09-09 10:25 ` [PATCH v2 21/26] block/fbatch: no lru_add_drain_all() in invalidate_bdev() Hugh Dickins
2026-09-09 10:27 ` [PATCH v2 22/26] fs/fbatch: drop_caches invalidate_bh_lrus() not lru_add_drain_all() Hugh Dickins
2026-09-09 10:30 ` [PATCH v2 23/26] fs,mm/fbatch: use invalidate_bh_lrus() not invalidate_bh_lrus_cpu() Hugh Dickins
2026-09-09 10:33 ` [PATCH v2 24/26] fs,mm/fbatch: lru_cache_disable() keep off buffer_head lrus only Hugh Dickins
2026-09-09 10:35 ` [PATCH v2 25/26] mm/fbatch: move lru_add_drain_all() declaration to mm/internal.h Hugh Dickins
2026-09-09 10:37 ` [PATCH v2 26/26] mm/fbatch: drop reference inside the loop when draining Hugh Dickins
2026-09-09 10:41 ` [PATCH v2 27/26] mm/fbatch: paranoid folio vmstats in folio_batch_move_lru() Hugh Dickins
2026-09-26 2:07 ` [PATCH v2 00/26] mm/fbatch: drain lru_add_drain() and _all() JP Kobryn
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=17e1a6c3-525b-1cc3-0731-349f0850e3ea@google.com \
--to=hughd@google.com \
--cc=ackerleytng@google.com \
--cc=akpm@linux-foundation.org \
--cc=alex@ghiti.fr \
--cc=axboe@kernel.dk \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=bigeasy@linutronix.de \
--cc=binbin.wu@linux.intel.com \
--cc=brauner@kernel.org \
--cc=cl@gentwo.org \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=hch@lst.de \
--cc=imbrenda@linux.ibm.com \
--cc=jack@suse.cz \
--cc=jp.kobryn@linux.dev \
--cc=kas@kernel.org \
--cc=lance.yang@linux.dev \
--cc=leobras.c@gmail.com \
--cc=linmiaohe@huawei.com \
--cc=linux-block@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mgorman@techsingularity.net \
--cc=mhocko@suse.com \
--cc=minchan@kernel.org \
--cc=mtosatti@redhat.com \
--cc=muchun.song@linux.dev \
--cc=osalvador@suse.de \
--cc=peterz@infradead.org \
--cc=qi.zheng@linux.dev \
--cc=riel@surriel.com \
--cc=ryncsn@gmail.com \
--cc=shakeel.butt@linux.dev \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
--cc=viro@zeniv.linux.org.uk \
--cc=willy@infradead.org \
--cc=yang@os.amperecomputing.com \
--cc=yuzhao@google.com \
--cc=ziy@nvidia.com \
--cc=zokeefe@google.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.