* [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-12 10:42 ` David Hildenbrand (Arm)
2026-11-12 12:55 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes David Hildenbrand (Arm)
` (9 subsequent siblings)
10 siblings, 2 replies; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, Chris Mason, stable
With large file folios, overflowing the folio mapcount and refcount
became possible more easily. For anon folios it is currently not really
an issue: each large anon folio can only be mapped once into a process; on
4k kernels it would be more processes than usually allowed on a system,
and on 16k or 64k kernels the processes alone would consume hundreds of
GiB. Something to be sorted out at some point, but it's not as urgent.
Being able to overflow the refcount is not really big news, but it's about
time that we do something about it because some modern technology might
find ways to abuse it :)
One of the enablers for that was commit ddc58f27f9ee ("mm: drop tail page
refcounting"), however, file thp support was added later.
File folios can be mapped multiple times into the same process, allowing
for processes to hold many references to the same folio.
As a short-term solution, let's introduce two folio refcount limits:
one for ordinary consumers and one for admins. Leave some headroom
before the refcount goes negative, which we really don't want (having
the mapcount go negative is also not expected).
Start reporting SIGBUS if we run into limits. When restoring mappings
from migration entries, instead zap the pte/pmd entry. We allow for
slightly exceeding the limit to keep things simpler.
During fork(), prefer not copying page table entries over failing fork.
Export __folio_ref_count_over_limit() right away as that will be needed
from fuse code next. Use has_capability_noaudit(current, CAP_SYS_RESOURCE),
so filesystem credential overrides do not affect access to the admin
reserve, and to not generate auditing messages simply because we access
optional reserves.
Don't grant access to the admin reserve from kernel threads, from
interrupt context, where "current" is just the interrupted task, or with
IRQs disabled, as even unaudited capability checks can take locks.
This is far from ideal, but better than all easy alternatives we can
backport comfortably. For now it fixes the big problem: letting
the refcount (+mapcount) go negative and cause trouble.
This is only a temporary fix. Keep the mapping checks explicit and
separate from reference acquisition: most are expected to disappear when
we rework the underlying refcounting.
Check order 0 folios as well for consistency: each
folio_add_file_rmap*() and folio_dup_file_rmap*() needs a prior check.
Adjust the check folio_get() to just check for invalid refcounts and
drop folio_ref_zero_or_close_to_overflow(): we really don't want
negative refcounts or negative mapcounts. Convert to VM_WARN_ON_FOLIO as
we touch it.
Also, just document -EHWPOISON for copy_present_ptes() while at it.
Note: When zapping file migration entries, uffd-wp is preserved using PTE
markers, but uffd-rwp protection is lost because there is no corresponding
marker. try_to_unmap() already has the same problem when unmapping file
folios, so we'll happily ignore it.
Note2: For now we don't let migration or PTE table collapse exceed any
limits. Admins can always refault, and if we are already operating in
admin reserves, something is already off.
Reported-by: Chris Mason <mason@kernel.org>
Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
Cc: stable@vger.kernel.org
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
Acked-by: Zi Yan <ziy@nvidia.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
include/linux/mm.h | 35 +++++++++++++++++++++++++++++++----
mm/filemap.c | 6 ++++++
mm/huge_memory.c | 29 +++++++++++++++++++++++++++--
mm/memory.c | 33 +++++++++++++++++++++++++++++----
mm/migrate.c | 11 +++++++++++
mm/userfaultfd.c | 4 ++++
mm/util.c | 18 ++++++++++++++++++
7 files changed, 126 insertions(+), 10 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index e6da76fbe8e83..6c7c29a086bd1 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2229,9 +2229,36 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
* back into memory.
*/
-/* 127: arbitrary random number, small enough to assemble well */
-#define folio_ref_zero_or_close_to_overflow(folio) \
- ((unsigned int) folio_ref_count(folio) + 127u <= 127u)
+/* Leave separate reserves for administrators and concurrent reference gains. */
+#define FOLIO_REF_COUNT_LIMIT (INT_MAX - (1U << 26))
+#define FOLIO_REF_COUNT_ADMIN_LIMIT (INT_MAX - (1U << 25))
+
+bool __folio_ref_count_over_limit(unsigned int ref_count);
+
+/**
+ * folio_ref_count_over_limit - Check whether to refuse more folio references.
+ * @folio: The folio.
+ *
+ * Check before adding page table mappings or other significant refcount users
+ * to pagecache folios (!hugetlb), either before acquiring references or
+ * afterwards with rollback if the operation is refused.
+ *
+ * Checking other folios does no harm.
+ *
+ * This check is an approximation; we leave some headroom such that concurrent
+ * increments, batches, temporary references, or other bounded refcount
+ * consumers are not a problem.
+ *
+ * Return: True if the caller should refuse the operation, false otherwise.
+ */
+static inline bool folio_ref_count_over_limit(const struct folio *folio)
+{
+ const unsigned int ref_count = folio_ref_count(folio);
+
+ if (likely(ref_count < FOLIO_REF_COUNT_LIMIT))
+ return false;
+ return __folio_ref_count_over_limit(ref_count);
+}
/**
* folio_get - Increment the reference count on a folio.
@@ -2243,7 +2270,7 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
*/
static inline void folio_get(struct folio *folio)
{
- VM_BUG_ON_FOLIO(folio_ref_zero_or_close_to_overflow(folio), folio);
+ VM_WARN_ON_FOLIO(folio_ref_count(folio) <= 0, folio);
folio_ref_inc(folio);
}
diff --git a/mm/filemap.c b/mm/filemap.c
index 9871b4f32a916..0f6d57432a7e5 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -3968,6 +3968,11 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
unsigned long end;
vm_fault_t map_ret;
+ if (unlikely(folio_ref_count_over_limit(folio))) {
+ folio_ref_dec(folio);
+ goto next;
+ }
+
addr += (xas.xa_index - last_pgoff) << PAGE_SHIFT;
vmf->pte += xas.xa_index - last_pgoff;
last_pgoff = xas.xa_index;
@@ -4009,6 +4014,7 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
mmap_miss - 1);
}
+next:
folio_unlock(folio);
} while ((folio = next_uptodate_folio(&xas, mapping, end_pgoff)) != NULL);
add_mm_counter(vma->vm_mm, folio_type, rss);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 4608540e40d46..439cfe446cea4 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1626,6 +1626,7 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
{
struct mm_struct *mm = vma->vm_mm;
pgtable_t pgtable = NULL;
+ vm_fault_t ret = VM_FAULT_NOPAGE;
spinlock_t *ptl;
pmd_t entry;
@@ -1662,6 +1663,10 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
if (is_huge_zero_folio(fop.folio)) {
entry = pmd_mkspecial(entry);
} else {
+ if (unlikely(folio_ref_count_over_limit(fop.folio))) {
+ ret = VM_FAULT_SIGBUS;
+ goto out_unlock;
+ }
folio_get(fop.folio);
folio_add_file_rmap_pmd(fop.folio, &fop.folio->page, vma);
add_mm_counter(mm, mm_counter_file(fop.folio), HPAGE_PMD_NR);
@@ -1688,7 +1693,7 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
spin_unlock(ptl);
if (pgtable)
pte_free(mm, pgtable);
- return VM_FAULT_NOPAGE;
+ return ret;
}
/**
@@ -1758,6 +1763,7 @@ static vm_fault_t insert_pud(struct vm_area_struct *vma, unsigned long addr,
pud_t *pud, struct folio_or_pfn fop, pgprot_t prot, bool write)
{
struct mm_struct *mm = vma->vm_mm;
+ vm_fault_t ret = VM_FAULT_NOPAGE;
spinlock_t *ptl;
pud_t entry;
@@ -1783,6 +1789,10 @@ static vm_fault_t insert_pud(struct vm_area_struct *vma, unsigned long addr,
if (fop.is_folio) {
entry = folio_mk_pud(fop.folio, vma->vm_page_prot);
+ if (unlikely(folio_ref_count_over_limit(fop.folio))) {
+ ret = VM_FAULT_SIGBUS;
+ goto out_unlock;
+ }
folio_get(fop.folio);
folio_add_file_rmap_pud(fop.folio, &fop.folio->page, vma);
add_mm_counter(mm, mm_counter_file(fop.folio), HPAGE_PUD_NR);
@@ -1798,7 +1808,7 @@ static vm_fault_t insert_pud(struct vm_area_struct *vma, unsigned long addr,
update_mmu_cache_pud(vma, addr, pud);
out_unlock:
spin_unlock(ptl);
- return VM_FAULT_NOPAGE;
+ return ret;
}
/**
@@ -2093,6 +2103,11 @@ int copy_huge_pud(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma
if (page) {
struct folio *folio = page_folio(page);
+ if (unlikely(folio_ref_count_over_limit(folio))) {
+ /* Skip this PUD; it can be faulted in later. */
+ ret = (dst_vma->vm_flags & VM_MIXEDMAP) ? -ENOMEM : 0;
+ goto out_unlock;
+ }
folio_get(folio);
/*
* TODO: once we support anonymous pages, use
@@ -5223,6 +5238,16 @@ void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct folio *folio
if (!(pvmw->pmd && !pvmw->pte))
return;
+ if (!folio_test_anon(folio) && unlikely(folio_ref_count_over_limit(folio))) {
+ /* Leave the file mapping absent so it can be faulted in. */
+ pmd_clear(pvmw->pmd);
+ if (arch_needs_pgtable_deposit())
+ zap_deposited_table(mm, pvmw->pmd);
+ update_hiwater_rss(mm);
+ add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR);
+ return;
+ }
+
entry = softleaf_from_pmd(*pvmw->pmd);
folio_get(folio);
pmde = folio_mk_pmd(folio, READ_ONCE(vma->vm_page_prot));
diff --git a/mm/memory.c b/mm/memory.c
index c9d64170e47ab..977e037388963 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1168,7 +1168,10 @@ static __always_inline void __copy_present_ptes(struct vm_area_struct *dst_vma,
* consecutive pages of the same folio by copying them as well.
*
* Returns -EAGAIN if one preallocated page is required to copy the next PTE.
- * Otherwise, returns the number of copied PTEs (at least 1).
+ * Returns -ENOMEM if the file folio refcount is too high and the PTE must
+ * be copied.
+ * Returns -EHWPOISON if copying a page fails.
+ * Otherwise, returns the number of processed PTEs (at least 1).
*/
static inline int
copy_present_ptes(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
@@ -1186,6 +1189,16 @@ copy_present_ptes(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma
folio = page_folio(page);
+ if (!folio_test_anon(folio) && unlikely(folio_ref_count_over_limit(folio))) {
+ if (dst_vma->vm_flags & VM_MIXEDMAP)
+ return -ENOMEM;
+ if (userfaultfd_pte_rwp(dst_vma, pte))
+ return -ENOMEM;
+ /* Preserve uffd-wp protection when faulting the page back in. */
+ cond_install_uffd_wp_ptes(dst_vma, addr, dst_pte, pte, 1);
+ return 1;
+ }
+
/*
* If we likely have to copy, just don't bother with batching. Make
* sure that the common "small folio" case is as fast as possible
@@ -1367,9 +1380,9 @@ copy_pte_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
/*
* If we need a pre-allocated page for this pte, drop the
* locks, allocate, and try again.
- * If copy failed due to hwpoison in source page, break out.
+ * Other errors abort the copy after releasing the page table locks.
*/
- if (unlikely(ret == -EAGAIN || ret == -EHWPOISON))
+ if (unlikely(ret < 0))
break;
if (unlikely(prealloc)) {
/*
@@ -1399,7 +1412,7 @@ copy_pte_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
goto out;
}
entry.val = 0;
- } else if (ret == -EBUSY || unlikely(ret == -EHWPOISON)) {
+ } else if (ret == -EBUSY || ret == -ENOMEM || unlikely(ret == -EHWPOISON)) {
goto out;
} else if (ret == -EAGAIN) {
prealloc = folio_prealloc(src_mm, src_vma, addr, false);
@@ -2474,6 +2487,8 @@ static int insert_page_into_pte_locked(struct vm_area_struct *vma, pte_t *pte,
if (unlikely(is_zero_folio(folio))) {
pteval = pte_mkspecial(pteval);
} else {
+ if (unlikely(folio_ref_count_over_limit(folio)))
+ return -EOVERFLOW;
folio_get(folio);
pteval = mk_pte(page, prot);
if (mkwrite) {
@@ -5754,6 +5769,9 @@ vm_fault_t do_set_pmd(struct vm_fault *vmf, struct folio *folio, struct page *pa
if (unlikely(folio_test_has_hwpoisoned(folio)))
return ret;
+ if (unlikely(folio_ref_count_over_limit(folio)))
+ return VM_FAULT_SIGBUS;
+
/*
* Archs like ppc64 need additional space to store information
* related to pte entry. Use the preallocated table for that.
@@ -5816,6 +5834,9 @@ vm_fault_t do_set_pmd(struct vm_fault *vmf, struct folio *folio, struct page *pa
* @page: The first page to create a PTE for.
* @nr: The number of PTEs to create.
* @addr: The first address to create a PTE for.
+ *
+ * For file mappings, the caller must check folio_ref_count_over_limit()
+ * before installing PTEs.
*/
void set_pte_range(struct vm_fault *vmf, struct folio *folio,
struct page *page, unsigned int nr, unsigned long addr)
@@ -5982,6 +6003,10 @@ vm_fault_t finish_fault(struct vm_fault *vmf)
goto fallback;
}
+ if (!is_cow && unlikely(folio_ref_count_over_limit(folio))) {
+ ret = VM_FAULT_SIGBUS;
+ goto unlock;
+ }
folio_ref_add(folio, nr_pages - 1);
set_pte_range(vmf, folio, page, nr_pages, addr);
type = is_cow ? MM_ANONPAGES : mm_counter_file(folio);
diff --git a/mm/migrate.c b/mm/migrate.c
index a5ac7192f0578..e241a594c3eed 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -385,6 +385,17 @@ static bool remove_migration_pte(struct folio *folio,
try_to_map_unused_to_zeropage(&pvmw, folio, old_pte, idx))
continue;
+ if (!folio_test_anon(folio) && !folio_test_hugetlb(folio) &&
+ unlikely(folio_ref_count_over_limit(folio))) {
+ /* File contents remain available for a later fault. */
+ pte_clear(vma->vm_mm, pvmw.address, pvmw.pte);
+ cond_install_uffd_wp_ptes(vma, pvmw.address,
+ pvmw.pte, old_pte, 1);
+ update_hiwater_rss(vma->vm_mm);
+ dec_mm_counter(vma->vm_mm, mm_counter_file(folio));
+ continue;
+ }
+
folio_get(folio);
new = folio_page(folio, idx);
pte = mk_pte(new, READ_ONCE(vma->vm_page_prot));
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 169f681f9849b..89853c157fa91 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -373,6 +373,10 @@ static int mfill_atomic_install_pte(pmd_t *dst_pmd,
goto out_unlock;
if (page_in_cache) {
+ if (unlikely(folio_ref_count_over_limit(folio))) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
folio_add_file_rmap_pte(folio, page, dst_vma);
} else {
folio_add_new_anon_rmap(folio, dst_vma, dst_addr, RMAP_EXCLUSIVE);
diff --git a/mm/util.c b/mm/util.c
index 80f6c714844f2..a5140b3b70f33 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -791,6 +791,24 @@ int folio_mc_copy(struct folio *dst, struct folio *src)
}
EXPORT_SYMBOL(folio_mc_copy);
+bool __folio_ref_count_over_limit(unsigned int ref_count)
+{
+ if (ref_count >= FOLIO_REF_COUNT_ADMIN_LIMIT)
+ return true;
+ /* Background threads must not consume admin reserves. */
+ if (current->flags & PF_KTHREAD)
+ return true;
+ /* Neither must interrupt contexts on behalf of the interrupted task. */
+ if (!in_task())
+ return true;
+ /* Capability checks are not safe with IRQs disabled. */
+ if (irqs_disabled())
+ return true;
+ /* Use the task's credentials, which filesystem overrides cannot change. */
+ return !has_capability_noaudit(current, CAP_SYS_RESOURCE);
+}
+EXPORT_SYMBOL_GPL(__folio_ref_count_over_limit);
+
int sysctl_overcommit_memory __read_mostly = OVERCOMMIT_GUESS;
static int sysctl_overcommit_ratio __read_mostly = 50;
static unsigned long sysctl_overcommit_kbytes __read_mostly;
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages
2026-11-10 20:35 ` [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages David Hildenbrand (Arm)
@ 2026-11-12 10:42 ` David Hildenbrand (Arm)
2026-11-12 12:55 ` Pedro Falcato
1 sibling, 0 replies; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-12 10:42 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: linux-mm, linux-kernel, linux-fsdevel, fuse-devel, linux-arch,
iommu, netdev, Chris Mason, stable
>
> diff --git a/mm/filemap.c b/mm/filemap.c
> index 9871b4f32a916..0f6d57432a7e5 100644
> --- a/mm/filemap.c
> +++ b/mm/filemap.c
> @@ -3968,6 +3968,11 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
> unsigned long end;
> vm_fault_t map_ret;
>
> + if (unlikely(folio_ref_count_over_limit(folio))) {
> + folio_ref_dec(folio);
Going over this once more, I'll just use folio_put() here for consistency.
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages
2026-11-10 20:35 ` [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages David Hildenbrand (Arm)
2026-11-12 10:42 ` David Hildenbrand (Arm)
@ 2026-11-12 12:55 ` Pedro Falcato
2026-11-12 13:16 ` David Hildenbrand (Arm)
1 sibling, 1 reply; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 12:55 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, Chris Mason, stable
On Tue, Nov 10, 2026 at 09:35:41PM +0100, David Hildenbrand (Arm) wrote:
> With large file folios, overflowing the folio mapcount and refcount
> became possible more easily. For anon folios it is currently not really
> an issue: each large anon folio can only be mapped once into a process; on
> 4k kernels it would be more processes than usually allowed on a system,
> and on 16k or 64k kernels the processes alone would consume hundreds of
> GiB. Something to be sorted out at some point, but it's not as urgent.
>
> Being able to overflow the refcount is not really big news, but it's about
> time that we do something about it because some modern technology might
> find ways to abuse it :)
>
> One of the enablers for that was commit ddc58f27f9ee ("mm: drop tail page
> refcounting"), however, file thp support was added later.
>
> File folios can be mapped multiple times into the same process, allowing
> for processes to hold many references to the same folio.
>
> As a short-term solution, let's introduce two folio refcount limits:
> one for ordinary consumers and one for admins. Leave some headroom
> before the refcount goes negative, which we really don't want (having
> the mapcount go negative is also not expected).
>
> Start reporting SIGBUS if we run into limits. When restoring mappings
> from migration entries, instead zap the pte/pmd entry. We allow for
> slightly exceeding the limit to keep things simpler.
>
> During fork(), prefer not copying page table entries over failing fork.
> Export __folio_ref_count_over_limit() right away as that will be needed
> from fuse code next. Use has_capability_noaudit(current, CAP_SYS_RESOURCE),
> so filesystem credential overrides do not affect access to the admin
> reserve, and to not generate auditing messages simply because we access
> optional reserves.
>
> Don't grant access to the admin reserve from kernel threads, from
> interrupt context, where "current" is just the interrupted task, or with
> IRQs disabled, as even unaudited capability checks can take locks.
>
> This is far from ideal, but better than all easy alternatives we can
> backport comfortably. For now it fixes the big problem: letting
> the refcount (+mapcount) go negative and cause trouble.
>
> This is only a temporary fix. Keep the mapping checks explicit and
> separate from reference acquisition: most are expected to disappear when
> we rework the underlying refcounting.
>
> Check order 0 folios as well for consistency: each
> folio_add_file_rmap*() and folio_dup_file_rmap*() needs a prior check.
>
> Adjust the check folio_get() to just check for invalid refcounts and
> drop folio_ref_zero_or_close_to_overflow(): we really don't want
> negative refcounts or negative mapcounts. Convert to VM_WARN_ON_FOLIO as
> we touch it.
>
> Also, just document -EHWPOISON for copy_present_ptes() while at it.
>
> Note: When zapping file migration entries, uffd-wp is preserved using PTE
> markers, but uffd-rwp protection is lost because there is no corresponding
> marker. try_to_unmap() already has the same problem when unmapping file
> folios, so we'll happily ignore it.
>
> Note2: For now we don't let migration or PTE table collapse exceed any
> limits. Admins can always refault, and if we are already operating in
> admin reserves, something is already off.
>
> Reported-by: Chris Mason <mason@kernel.org>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
> Acked-by: Zi Yan <ziy@nvidia.com>
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Mostly LGTM, with some comments:
Reviewed-by: Pedro Falcato <pfalcato@suse.de>
> ---
> include/linux/mm.h | 35 +++++++++++++++++++++++++++++++----
> mm/filemap.c | 6 ++++++
> mm/huge_memory.c | 29 +++++++++++++++++++++++++++--
> mm/memory.c | 33 +++++++++++++++++++++++++++++----
> mm/migrate.c | 11 +++++++++++
> mm/userfaultfd.c | 4 ++++
> mm/util.c | 18 ++++++++++++++++++
> 7 files changed, 126 insertions(+), 10 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index e6da76fbe8e83..6c7c29a086bd1 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -2229,9 +2229,36 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
> * back into memory.
> */
>
> -/* 127: arbitrary random number, small enough to assemble well */
> -#define folio_ref_zero_or_close_to_overflow(folio) \
> - ((unsigned int) folio_ref_count(folio) + 127u <= 127u)
> +/* Leave separate reserves for administrators and concurrent reference gains. */
> +#define FOLIO_REF_COUNT_LIMIT (INT_MAX - (1U << 26))
> +#define FOLIO_REF_COUNT_ADMIN_LIMIT (INT_MAX - (1U << 25))
> +
> +bool __folio_ref_count_over_limit(unsigned int ref_count);
> +
> +/**
> + * folio_ref_count_over_limit - Check whether to refuse more folio references.
> + * @folio: The folio.
> + *
> + * Check before adding page table mappings or other significant refcount users
> + * to pagecache folios (!hugetlb), either before acquiring references or
> + * afterwards with rollback if the operation is refused.
> + *
> + * Checking other folios does no harm.
> + *
> + * This check is an approximation; we leave some headroom such that concurrent
> + * increments, batches, temporary references, or other bounded refcount
> + * consumers are not a problem.
> + *
> + * Return: True if the caller should refuse the operation, false otherwise.
> + */
> +static inline bool folio_ref_count_over_limit(const struct folio *folio)
> +{
> + const unsigned int ref_count = folio_ref_count(folio);
> +
> + if (likely(ref_count < FOLIO_REF_COUNT_LIMIT))
> + return false;
> + return __folio_ref_count_over_limit(ref_count);
> +}
>
> /**
> * folio_get - Increment the reference count on a folio.
> @@ -2243,7 +2270,7 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
> */
> static inline void folio_get(struct folio *folio)
> {
> - VM_BUG_ON_FOLIO(folio_ref_zero_or_close_to_overflow(folio), folio);
> + VM_WARN_ON_FOLIO(folio_ref_count(folio) <= 0, folio);
> folio_ref_inc(folio);
> }
>
> diff --git a/mm/filemap.c b/mm/filemap.c
> index 9871b4f32a916..0f6d57432a7e5 100644
> --- a/mm/filemap.c
> +++ b/mm/filemap.c
> @@ -3968,6 +3968,11 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
> unsigned long end;
> vm_fault_t map_ret;
>
> + if (unlikely(folio_ref_count_over_limit(folio))) {
> + folio_ref_dec(folio);
> + goto next;
> + }
> +
It's a bit odd that you take a ref, only to check later if we're over the
limit, and ditch the folio. Could you move this to next_uptodate_folio()?
> addr += (xas.xa_index - last_pgoff) << PAGE_SHIFT;
> vmf->pte += xas.xa_index - last_pgoff;
> last_pgoff = xas.xa_index;
> @@ -4009,6 +4014,7 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
> mmap_miss - 1);
> }
>
> +next:
> folio_unlock(folio);
> } while ((folio = next_uptodate_folio(&xas, mapping, end_pgoff)) != NULL);
> add_mm_counter(vma->vm_mm, folio_type, rss);
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 4608540e40d46..439cfe446cea4 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -1626,6 +1626,7 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
> {
> struct mm_struct *mm = vma->vm_mm;
> pgtable_t pgtable = NULL;
> + vm_fault_t ret = VM_FAULT_NOPAGE;
> spinlock_t *ptl;
> pmd_t entry;
>
> @@ -1662,6 +1663,10 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
> if (is_huge_zero_folio(fop.folio)) {
> entry = pmd_mkspecial(entry);
> } else {
> + if (unlikely(folio_ref_count_over_limit(fop.folio))) {
> + ret = VM_FAULT_SIGBUS;
> + goto out_unlock;
> + }
> folio_get(fop.folio);
I see you use this pattern often. Would it make sense to have a
bool folio_get_unless_overflow()?
> folio_add_file_rmap_pmd(fop.folio, &fop.folio->page, vma);
> add_mm_counter(mm, mm_counter_file(fop.folio), HPAGE_PMD_NR);
> @@ -1688,7 +1693,7 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
> spin_unlock(ptl);
> if (pgtable)
> pte_free(mm, pgtable);
> - return VM_FAULT_NOPAGE;
> + return ret;
> }
>
> /**
> @@ -1758,6 +1763,7 @@ static vm_fault_t insert_pud(struct vm_area_struct *vma, unsigned long addr,
> pud_t *pud, struct folio_or_pfn fop, pgprot_t prot, bool write)
> {
> struct mm_struct *mm = vma->vm_mm;
> + vm_fault_t ret = VM_FAULT_NOPAGE;
> spinlock_t *ptl;
> pud_t entry;
>
> @@ -1783,6 +1789,10 @@ static vm_fault_t insert_pud(struct vm_area_struct *vma, unsigned long addr,
> if (fop.is_folio) {
> entry = folio_mk_pud(fop.folio, vma->vm_page_prot);
>
> + if (unlikely(folio_ref_count_over_limit(fop.folio))) {
> + ret = VM_FAULT_SIGBUS;
> + goto out_unlock;
> + }
> folio_get(fop.folio);
> folio_add_file_rmap_pud(fop.folio, &fop.folio->page, vma);
> add_mm_counter(mm, mm_counter_file(fop.folio), HPAGE_PUD_NR);
> @@ -1798,7 +1808,7 @@ static vm_fault_t insert_pud(struct vm_area_struct *vma, unsigned long addr,
> update_mmu_cache_pud(vma, addr, pud);
> out_unlock:
> spin_unlock(ptl);
> - return VM_FAULT_NOPAGE;
> + return ret;
> }
>
> /**
> @@ -2093,6 +2103,11 @@ int copy_huge_pud(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma
> if (page) {
> struct folio *folio = page_folio(page);
>
> + if (unlikely(folio_ref_count_over_limit(folio))) {
> + /* Skip this PUD; it can be faulted in later. */
> + ret = (dst_vma->vm_flags & VM_MIXEDMAP) ? -ENOMEM : 0;
A comment as to what's going on here with VM_MIXEDMAP would be useful. I guess
the idea is that faulting on a mixedmap later will not necessarily hand you the
exact same page fault result?
> + goto out_unlock;
> + }
> folio_get(folio);
> /*
> * TODO: once we support anonymous pages, use
> @@ -5223,6 +5238,16 @@ void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct folio *folio
> if (!(pvmw->pmd && !pvmw->pte))
> return;
>
> + if (!folio_test_anon(folio) && unlikely(folio_ref_count_over_limit(folio))) {
> + /* Leave the file mapping absent so it can be faulted in. */
> + pmd_clear(pvmw->pmd);
> + if (arch_needs_pgtable_deposit())
> + zap_deposited_table(mm, pvmw->pmd);
> + update_hiwater_rss(mm);
> + add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR);
> + return;
> + }
> +
> entry = softleaf_from_pmd(*pvmw->pmd);
> folio_get(folio);
> pmde = folio_mk_pmd(folio, READ_ONCE(vma->vm_page_prot));
> diff --git a/mm/memory.c b/mm/memory.c
> index c9d64170e47ab..977e037388963 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -1168,7 +1168,10 @@ static __always_inline void __copy_present_ptes(struct vm_area_struct *dst_vma,
> * consecutive pages of the same folio by copying them as well.
> *
> * Returns -EAGAIN if one preallocated page is required to copy the next PTE.
> - * Otherwise, returns the number of copied PTEs (at least 1).
> + * Returns -ENOMEM if the file folio refcount is too high and the PTE must
> + * be copied.
> + * Returns -EHWPOISON if copying a page fails.
> + * Otherwise, returns the number of processed PTEs (at least 1).
> */
> static inline int
> copy_present_ptes(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
> @@ -1186,6 +1189,16 @@ copy_present_ptes(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma
>
> folio = page_folio(page);
>
> + if (!folio_test_anon(folio) && unlikely(folio_ref_count_over_limit(folio))) {
> + if (dst_vma->vm_flags & VM_MIXEDMAP)
> + return -ENOMEM;
> + if (userfaultfd_pte_rwp(dst_vma, pte))
> + return -ENOMEM;
> + /* Preserve uffd-wp protection when faulting the page back in. */
> + cond_install_uffd_wp_ptes(dst_vma, addr, dst_pte, pte, 1);
> + return 1;
> + }
> +
> /*
> * If we likely have to copy, just don't bother with batching. Make
> * sure that the common "small folio" case is as fast as possible
> @@ -1367,9 +1380,9 @@ copy_pte_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
> /*
> * If we need a pre-allocated page for this pte, drop the
> * locks, allocate, and try again.
> - * If copy failed due to hwpoison in source page, break out.
> + * Other errors abort the copy after releasing the page table locks.
> */
> - if (unlikely(ret == -EAGAIN || ret == -EHWPOISON))
> + if (unlikely(ret < 0))
> break;
> if (unlikely(prealloc)) {
> /*
> @@ -1399,7 +1412,7 @@ copy_pte_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
> goto out;
> }
> entry.val = 0;
> - } else if (ret == -EBUSY || unlikely(ret == -EHWPOISON)) {
> + } else if (ret == -EBUSY || ret == -ENOMEM || unlikely(ret == -EHWPOISON)) {
> goto out;
> } else if (ret == -EAGAIN) {
> prealloc = folio_prealloc(src_mm, src_vma, addr, false);
> @@ -2474,6 +2487,8 @@ static int insert_page_into_pte_locked(struct vm_area_struct *vma, pte_t *pte,
> if (unlikely(is_zero_folio(folio))) {
> pteval = pte_mkspecial(pteval);
> } else {
> + if (unlikely(folio_ref_count_over_limit(folio)))
> + return -EOVERFLOW;
> folio_get(folio);
> pteval = mk_pte(page, prot);
> if (mkwrite) {
> @@ -5754,6 +5769,9 @@ vm_fault_t do_set_pmd(struct vm_fault *vmf, struct folio *folio, struct page *pa
> if (unlikely(folio_test_has_hwpoisoned(folio)))
> return ret;
>
> + if (unlikely(folio_ref_count_over_limit(folio)))
> + return VM_FAULT_SIGBUS;
> +
> /*
> * Archs like ppc64 need additional space to store information
> * related to pte entry. Use the preallocated table for that.
> @@ -5816,6 +5834,9 @@ vm_fault_t do_set_pmd(struct vm_fault *vmf, struct folio *folio, struct page *pa
> * @page: The first page to create a PTE for.
> * @nr: The number of PTEs to create.
> * @addr: The first address to create a PTE for.
> + *
> + * For file mappings, the caller must check folio_ref_count_over_limit()
> + * before installing PTEs.
> */
> void set_pte_range(struct vm_fault *vmf, struct folio *folio,
> struct page *page, unsigned int nr, unsigned long addr)
> @@ -5982,6 +6003,10 @@ vm_fault_t finish_fault(struct vm_fault *vmf)
> goto fallback;
> }
>
> + if (!is_cow && unlikely(folio_ref_count_over_limit(folio))) {
> + ret = VM_FAULT_SIGBUS;
> + goto unlock;
> + }
> folio_ref_add(folio, nr_pages - 1);
> set_pte_range(vmf, folio, page, nr_pages, addr);
> type = is_cow ? MM_ANONPAGES : mm_counter_file(folio);
> diff --git a/mm/migrate.c b/mm/migrate.c
> index a5ac7192f0578..e241a594c3eed 100644
> --- a/mm/migrate.c
> +++ b/mm/migrate.c
> @@ -385,6 +385,17 @@ static bool remove_migration_pte(struct folio *folio,
> try_to_map_unused_to_zeropage(&pvmw, folio, old_pte, idx))
> continue;
>
> + if (!folio_test_anon(folio) && !folio_test_hugetlb(folio) &&
> + unlikely(folio_ref_count_over_limit(folio))) {
> + /* File contents remain available for a later fault. */
> + pte_clear(vma->vm_mm, pvmw.address, pvmw.pte);
> + cond_install_uffd_wp_ptes(vma, pvmw.address,
> + pvmw.pte, old_pte, 1);
> + update_hiwater_rss(vma->vm_mm);
> + dec_mm_counter(vma->vm_mm, mm_counter_file(folio));
> + continue;
> + }
> +
> folio_get(folio);
> new = folio_page(folio, idx);
> pte = mk_pte(new, READ_ONCE(vma->vm_page_prot));
> diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
> index 169f681f9849b..89853c157fa91 100644
> --- a/mm/userfaultfd.c
> +++ b/mm/userfaultfd.c
> @@ -373,6 +373,10 @@ static int mfill_atomic_install_pte(pmd_t *dst_pmd,
> goto out_unlock;
>
> if (page_in_cache) {
> + if (unlikely(folio_ref_count_over_limit(folio))) {
> + ret = -ENOMEM;
> + goto out_unlock;
> + }
> folio_add_file_rmap_pte(folio, page, dst_vma);
> } else {
> folio_add_new_anon_rmap(folio, dst_vma, dst_addr, RMAP_EXCLUSIVE);
> diff --git a/mm/util.c b/mm/util.c
> index 80f6c714844f2..a5140b3b70f33 100644
> --- a/mm/util.c
> +++ b/mm/util.c
> @@ -791,6 +791,24 @@ int folio_mc_copy(struct folio *dst, struct folio *src)
> }
> EXPORT_SYMBOL(folio_mc_copy);
>
> +bool __folio_ref_count_over_limit(unsigned int ref_count)
> +{
> + if (ref_count >= FOLIO_REF_COUNT_ADMIN_LIMIT)
> + return true;
> + /* Background threads must not consume admin reserves. */
> + if (current->flags & PF_KTHREAD)
> + return true;
> + /* Neither must interrupt contexts on behalf of the interrupted task. */
> + if (!in_task())
> + return true;
> + /* Capability checks are not safe with IRQs disabled. */
> + if (irqs_disabled())
> + return true;
> + /* Use the task's credentials, which filesystem overrides cannot change. */
> + return !has_capability_noaudit(current, CAP_SYS_RESOURCE);
> +}
In case this function ever returns over limit, we probably want to log this
in the kernel log. If this function gets the folio you could even print
the problematic file offset/inode/filesystem/etc. Ratelimited of course.
> +EXPORT_SYMBOL_GPL(__folio_ref_count_over_limit);
I'm fairly sure this will break the build for a number of non-GPL drivers
when calling folio_get() with CONFIG_DEBUG_VM=y.
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages
2026-11-12 12:55 ` Pedro Falcato
@ 2026-11-12 13:16 ` David Hildenbrand (Arm)
2026-11-12 14:06 ` Pedro Falcato
0 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-12 13:16 UTC (permalink / raw)
To: Pedro Falcato
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, Chris Mason, stable
> Mostly LGTM, with some comments:
>
> Reviewed-by: Pedro Falcato <pfalcato@suse.de>
Thanks for the review, Pedro!
>
>> ---
>> include/linux/mm.h | 35 +++++++++++++++++++++++++++++++----
>> mm/filemap.c | 6 ++++++
>> mm/huge_memory.c | 29 +++++++++++++++++++++++++++--
>> mm/memory.c | 33 +++++++++++++++++++++++++++++----
>> mm/migrate.c | 11 +++++++++++
>> mm/userfaultfd.c | 4 ++++
>> mm/util.c | 18 ++++++++++++++++++
>> 7 files changed, 126 insertions(+), 10 deletions(-)
>>
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index e6da76fbe8e83..6c7c29a086bd1 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -2229,9 +2229,36 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
>> * back into memory.
>> */
>>
>> -/* 127: arbitrary random number, small enough to assemble well */
>> -#define folio_ref_zero_or_close_to_overflow(folio) \
>> - ((unsigned int) folio_ref_count(folio) + 127u <= 127u)
>> +/* Leave separate reserves for administrators and concurrent reference gains. */
>> +#define FOLIO_REF_COUNT_LIMIT (INT_MAX - (1U << 26))
>> +#define FOLIO_REF_COUNT_ADMIN_LIMIT (INT_MAX - (1U << 25))
>> +
>> +bool __folio_ref_count_over_limit(unsigned int ref_count);
>> +
>> +/**
>> + * folio_ref_count_over_limit - Check whether to refuse more folio references.
>> + * @folio: The folio.
>> + *
>> + * Check before adding page table mappings or other significant refcount users
>> + * to pagecache folios (!hugetlb), either before acquiring references or
>> + * afterwards with rollback if the operation is refused.
>> + *
>> + * Checking other folios does no harm.
>> + *
>> + * This check is an approximation; we leave some headroom such that concurrent
>> + * increments, batches, temporary references, or other bounded refcount
>> + * consumers are not a problem.
>> + *
>> + * Return: True if the caller should refuse the operation, false otherwise.
>> + */
>> +static inline bool folio_ref_count_over_limit(const struct folio *folio)
>> +{
>> + const unsigned int ref_count = folio_ref_count(folio);
>> +
>> + if (likely(ref_count < FOLIO_REF_COUNT_LIMIT))
>> + return false;
>> + return __folio_ref_count_over_limit(ref_count);
>> +}
>>
>> /**
>> * folio_get - Increment the reference count on a folio.
>> @@ -2243,7 +2270,7 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
>> */
>> static inline void folio_get(struct folio *folio)
>> {
>> - VM_BUG_ON_FOLIO(folio_ref_zero_or_close_to_overflow(folio), folio);
>> + VM_WARN_ON_FOLIO(folio_ref_count(folio) <= 0, folio);
>> folio_ref_inc(folio);
>> }
>>
>> diff --git a/mm/filemap.c b/mm/filemap.c
>> index 9871b4f32a916..0f6d57432a7e5 100644
>> --- a/mm/filemap.c
>> +++ b/mm/filemap.c
>> @@ -3968,6 +3968,11 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
>> unsigned long end;
>> vm_fault_t map_ret;
>>
>> + if (unlikely(folio_ref_count_over_limit(folio))) {
>> + folio_ref_dec(folio);
>> + goto next;
>> + }
>> +
>
> It's a bit odd that you take a ref, only to check later if we're over the
> limit, and ditch the folio. Could you move this to next_uptodate_folio()?
That might be even better (and avoids one conflict with Andrew's tree). Let me
take a look.
>
>> addr += (xas.xa_index - last_pgoff) << PAGE_SHIFT;
>> vmf->pte += xas.xa_index - last_pgoff;
>> last_pgoff = xas.xa_index;
>> @@ -4009,6 +4014,7 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
>> mmap_miss - 1);
>> }
>>
>> +next:
>> folio_unlock(folio);
>> } while ((folio = next_uptodate_folio(&xas, mapping, end_pgoff)) != NULL);
>> add_mm_counter(vma->vm_mm, folio_type, rss);
>> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
>> index 4608540e40d46..439cfe446cea4 100644
>> --- a/mm/huge_memory.c
>> +++ b/mm/huge_memory.c
>> @@ -1626,6 +1626,7 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
>> {
>> struct mm_struct *mm = vma->vm_mm;
>> pgtable_t pgtable = NULL;
>> + vm_fault_t ret = VM_FAULT_NOPAGE;
>> spinlock_t *ptl;
>> pmd_t entry;
>>
>> @@ -1662,6 +1663,10 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
>> if (is_huge_zero_folio(fop.folio)) {
>> entry = pmd_mkspecial(entry);
>> } else {
>> + if (unlikely(folio_ref_count_over_limit(fop.folio))) {
>> + ret = VM_FAULT_SIGBUS;
>> + goto out_unlock;
>> + }
>> folio_get(fop.folio);
>
> I see you use this pattern often. Would it make sense to have a
> bool folio_get_unless_overflow()?
I commented somewhere (in v2 I think) that I played with using
folio_get_checked() from #2, but didn't quite like it so I discarded it as
(a) it cannot be used everywhere here, as I'd need more functions to add
multiple references. next_uptodate_folio() also could use it as I would need yet
another temporary helper for a try_get().
(b) these changes here are going to get reverted fairly soon once I change the
way how mappings are refcounted (which is, unfortunately, not suitable for
stable, otherwise I wouldn't be throwing in this temporary change in the first
place)
So everything that is expected to stay in later patches uses the proper helper.
[...]
>>
>> + if (unlikely(folio_ref_count_over_limit(folio))) {
>> + /* Skip this PUD; it can be faulted in later. */
>> + ret = (dst_vma->vm_flags & VM_MIXEDMAP) ? -ENOMEM : 0;
>
> A comment as to what's going on here with VM_MIXEDMAP would be useful. I guess
> the idea is that faulting on a mixedmap later will not necessarily hand you the
> exact same page fault result?
I can throw in a comment, yes.
[...]
>> +bool __folio_ref_count_over_limit(unsigned int ref_count)
>> +{
>> + if (ref_count >= FOLIO_REF_COUNT_ADMIN_LIMIT)
>> + return true;
>> + /* Background threads must not consume admin reserves. */
>> + if (current->flags & PF_KTHREAD)
>> + return true;
>> + /* Neither must interrupt contexts on behalf of the interrupted task. */
>> + if (!in_task())
>> + return true;
>> + /* Capability checks are not safe with IRQs disabled. */
>> + if (irqs_disabled())
>> + return true;
>> + /* Use the task's credentials, which filesystem overrides cannot change. */
>> + return !has_capability_noaudit(current, CAP_SYS_RESOURCE);
>> +}
>
> In case this function ever returns over limit, we probably want to log this
> in the kernel log. If this function gets the folio you could even print
> the problematic file offset/inode/filesystem/etc. Ratelimited of course.
You mean, if this function returns "true", right? Is printing always fine?
(e.g., irqs disabled?)
>
>> +EXPORT_SYMBOL_GPL(__folio_ref_count_over_limit);
>
> I'm fairly sure this will break the build for a number of non-GPL drivers
> when calling folio_get() with CONFIG_DEBUG_VM=y.
You mean, breaking OOT drivers, right? I can switch to EXPORT_SYMBOL if that's a
concern.
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages
2026-11-12 13:16 ` David Hildenbrand (Arm)
@ 2026-11-12 14:06 ` Pedro Falcato
2026-11-12 14:16 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 14:06 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, Chris Mason, stable
On Thu, Nov 12, 2026 at 02:16:33PM +0100, David Hildenbrand (Arm) wrote:
> > Mostly LGTM, with some comments:
> >
> > Reviewed-by: Pedro Falcato <pfalcato@suse.de>
>
> Thanks for the review, Pedro!
>
> >
> >> ---
> >> include/linux/mm.h | 35 +++++++++++++++++++++++++++++++----
> >> mm/filemap.c | 6 ++++++
> >> mm/huge_memory.c | 29 +++++++++++++++++++++++++++--
> >> mm/memory.c | 33 +++++++++++++++++++++++++++++----
> >> mm/migrate.c | 11 +++++++++++
> >> mm/userfaultfd.c | 4 ++++
> >> mm/util.c | 18 ++++++++++++++++++
> >> 7 files changed, 126 insertions(+), 10 deletions(-)
> >>
> >> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> >> index 4608540e40d46..439cfe446cea4 100644
> >> --- a/mm/huge_memory.c
> >> +++ b/mm/huge_memory.c
> >> @@ -1626,6 +1626,7 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
> >> {
> >> struct mm_struct *mm = vma->vm_mm;
> >> pgtable_t pgtable = NULL;
> >> + vm_fault_t ret = VM_FAULT_NOPAGE;
> >> spinlock_t *ptl;
> >> pmd_t entry;
> >>
> >> @@ -1662,6 +1663,10 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
> >> if (is_huge_zero_folio(fop.folio)) {
> >> entry = pmd_mkspecial(entry);
> >> } else {
> >> + if (unlikely(folio_ref_count_over_limit(fop.folio))) {
> >> + ret = VM_FAULT_SIGBUS;
> >> + goto out_unlock;
> >> + }
> >> folio_get(fop.folio);
> >
> > I see you use this pattern often. Would it make sense to have a
> > bool folio_get_unless_overflow()?
>
> I commented somewhere (in v2 I think) that I played with using
> folio_get_checked() from #2, but didn't quite like it so I discarded it as
>
> (a) it cannot be used everywhere here, as I'd need more functions to add
> multiple references. next_uptodate_folio() also could use it as I would need yet
> another temporary helper for a try_get().
>
> (b) these changes here are going to get reverted fairly soon once I change the
> way how mappings are refcounted (which is, unfortunately, not suitable for
> stable, otherwise I wouldn't be throwing in this temporary change in the first
> place)
>
> So everything that is expected to stay in later patches uses the proper helper.
Alright.
>
> [...]
>
> >>
> >> + if (unlikely(folio_ref_count_over_limit(folio))) {
> >> + /* Skip this PUD; it can be faulted in later. */
> >> + ret = (dst_vma->vm_flags & VM_MIXEDMAP) ? -ENOMEM : 0;
> >
> > A comment as to what's going on here with VM_MIXEDMAP would be useful. I guess
> > the idea is that faulting on a mixedmap later will not necessarily hand you the
> > exact same page fault result?
>
> I can throw in a comment, yes.
>
> [...]
>
> >> +bool __folio_ref_count_over_limit(unsigned int ref_count)
> >> +{
> >> + if (ref_count >= FOLIO_REF_COUNT_ADMIN_LIMIT)
> >> + return true;
> >> + /* Background threads must not consume admin reserves. */
> >> + if (current->flags & PF_KTHREAD)
> >> + return true;
> >> + /* Neither must interrupt contexts on behalf of the interrupted task. */
> >> + if (!in_task())
> >> + return true;
> >> + /* Capability checks are not safe with IRQs disabled. */
> >> + if (irqs_disabled())
> >> + return true;
> >> + /* Use the task's credentials, which filesystem overrides cannot change. */
> >> + return !has_capability_noaudit(current, CAP_SYS_RESOURCE);
> >> +}
> >
> > In case this function ever returns over limit, we probably want to log this
> > in the kernel log. If this function gets the folio you could even print
> > the problematic file offset/inode/filesystem/etc. Ratelimited of course.
>
> You mean, if this function returns "true", right? Is printing always fine?
> (e.g., irqs disabled?)
Yes. As far as I know, it's always fine, even under NMI. But definitely fine
with IRQs disabled.
>
> >
> >> +EXPORT_SYMBOL_GPL(__folio_ref_count_over_limit);
> >
> > I'm fairly sure this will break the build for a number of non-GPL drivers
> > when calling folio_get() with CONFIG_DEBUG_VM=y.
>
> You mean, breaking OOT drivers, right? I can switch to EXPORT_SYMBOL if that's a
> concern.
I had the impression we had some stuff in-tree that isn't marked as GPL. But a quick
grep shows that everything is either GPL or dual-licensed.
In any case, yes, this will quite possibly break debug builds for people OOT.
I think folio_get() might be a bit too ubiquitous to do this to.
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages
2026-11-12 14:06 ` Pedro Falcato
@ 2026-11-12 14:16 ` David Hildenbrand (Arm)
2026-11-12 14:26 ` Pedro Falcato
0 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-12 14:16 UTC (permalink / raw)
To: Pedro Falcato
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, Chris Mason, stable
On 11/12/26 15:06, Pedro Falcato wrote:
> On Thu, Nov 12, 2026 at 02:16:33PM +0100, David Hildenbrand (Arm) wrote:
>>> Mostly LGTM, with some comments:
>>>
>>> Reviewed-by: Pedro Falcato <pfalcato@suse.de>
>>
>> Thanks for the review, Pedro!
>>
>>>
>>>
>>> I see you use this pattern often. Would it make sense to have a
>>> bool folio_get_unless_overflow()?
>>
>> I commented somewhere (in v2 I think) that I played with using
>> folio_get_checked() from #2, but didn't quite like it so I discarded it as
>>
>> (a) it cannot be used everywhere here, as I'd need more functions to add
>> multiple references. next_uptodate_folio() also could use it as I would need yet
>> another temporary helper for a try_get().
>>
>> (b) these changes here are going to get reverted fairly soon once I change the
>> way how mappings are refcounted (which is, unfortunately, not suitable for
>> stable, otherwise I wouldn't be throwing in this temporary change in the first
>> place)
>>
>> So everything that is expected to stay in later patches uses the proper helper.
>
> Alright.
>
>>
>> [...]
>>
>>>
>>> A comment as to what's going on here with VM_MIXEDMAP would be useful. I guess
>>> the idea is that faulting on a mixedmap later will not necessarily hand you the
>>> exact same page fault result?
>>
>> I can throw in a comment, yes.
>>
>> [...]
>>
>>>
>>> In case this function ever returns over limit, we probably want to log this
>>> in the kernel log. If this function gets the folio you could even print
>>> the problematic file offset/inode/filesystem/etc. Ratelimited of course.
>>
>> You mean, if this function returns "true", right? Is printing always fine?
>> (e.g., irqs disabled?)
>
> Yes. As far as I know, it's always fine, even under NMI. But definitely fine
> with IRQs disabled.
>
>>
>>>
>>>
>>> I'm fairly sure this will break the build for a number of non-GPL drivers
>>> when calling folio_get() with CONFIG_DEBUG_VM=y.
>>
>> You mean, breaking OOT drivers, right? I can switch to EXPORT_SYMBOL if that's a
>> concern.
>
> I had the impression we had some stuff in-tree that isn't marked as GPL. But a quick
> grep shows that everything is either GPL or dual-licensed.
>
> In any case, yes, this will quite possibly break debug builds for people OOT.
> I think folio_get() might be a bit too ubiquitous to do this to.
Thinking again, folio_get() should not be affected, it rightfully only checks
for a refcount underflow (when we exceed the limit + headroom). So only
try_get_page() is affected, which OOT very likely doesn't use.
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages
2026-11-12 14:16 ` David Hildenbrand (Arm)
@ 2026-11-12 14:26 ` Pedro Falcato
2026-11-12 14:38 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 14:26 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, Chris Mason, stable
On Thu, Nov 12, 2026 at 03:16:00PM +0100, David Hildenbrand (Arm) wrote:
> On 11/12/26 15:06, Pedro Falcato wrote:
> > On Thu, Nov 12, 2026 at 02:16:33PM +0100, David Hildenbrand (Arm) wrote:
> >>> Mostly LGTM, with some comments:
> >>>
> >>> Reviewed-by: Pedro Falcato <pfalcato@suse.de>
> >>
> >> Thanks for the review, Pedro!
> >>
> >>>
> >>>
> >>> I see you use this pattern often. Would it make sense to have a
> >>> bool folio_get_unless_overflow()?
> >>
> >> I commented somewhere (in v2 I think) that I played with using
> >> folio_get_checked() from #2, but didn't quite like it so I discarded it as
> >>
> >> (a) it cannot be used everywhere here, as I'd need more functions to add
> >> multiple references. next_uptodate_folio() also could use it as I would need yet
> >> another temporary helper for a try_get().
> >>
> >> (b) these changes here are going to get reverted fairly soon once I change the
> >> way how mappings are refcounted (which is, unfortunately, not suitable for
> >> stable, otherwise I wouldn't be throwing in this temporary change in the first
> >> place)
> >>
> >> So everything that is expected to stay in later patches uses the proper helper.
> >
> > Alright.
> >
> >>
> >> [...]
> >>
> >>>
> >>> A comment as to what's going on here with VM_MIXEDMAP would be useful. I guess
> >>> the idea is that faulting on a mixedmap later will not necessarily hand you the
> >>> exact same page fault result?
> >>
> >> I can throw in a comment, yes.
> >>
> >> [...]
> >>
> >>>
> >>> In case this function ever returns over limit, we probably want to log this
> >>> in the kernel log. If this function gets the folio you could even print
> >>> the problematic file offset/inode/filesystem/etc. Ratelimited of course.
> >>
> >> You mean, if this function returns "true", right? Is printing always fine?
> >> (e.g., irqs disabled?)
> >
> > Yes. As far as I know, it's always fine, even under NMI. But definitely fine
> > with IRQs disabled.
> >
> >>
> >>>
> >>>
> >>> I'm fairly sure this will break the build for a number of non-GPL drivers
> >>> when calling folio_get() with CONFIG_DEBUG_VM=y.
> >>
> >> You mean, breaking OOT drivers, right? I can switch to EXPORT_SYMBOL if that's a
> >> concern.
> >
> > I had the impression we had some stuff in-tree that isn't marked as GPL. But a quick
> > grep shows that everything is either GPL or dual-licensed.
> >
> > In any case, yes, this will quite possibly break debug builds for people OOT.
> > I think folio_get() might be a bit too ubiquitous to do this to.
>
> Thinking again, folio_get() should not be affected, it rightfully only checks
> for a refcount underflow (when we exceed the limit + headroom). So only
> try_get_page() is affected, which OOT very likely doesn't use.
Aha, yes, you're right. I mixed up your folio_get() change. Would it make sense
to VM_WARN_ON() on this as well, or does the <= 0 check cover us already?
>
> --
> Cheers,
>
> David
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages
2026-11-12 14:26 ` Pedro Falcato
@ 2026-11-12 14:38 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-12 14:38 UTC (permalink / raw)
To: Pedro Falcato
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, Chris Mason, stable
On 11/12/26 15:26, Pedro Falcato wrote:
> On Thu, Nov 12, 2026 at 03:16:00PM +0100, David Hildenbrand (Arm) wrote:
>> On 11/12/26 15:06, Pedro Falcato wrote:
>>>
>>> Alright.
>>>
>>>
>>> Yes. As far as I know, it's always fine, even under NMI. But definitely fine
>>> with IRQs disabled.
>>>
>>>
>>> I had the impression we had some stuff in-tree that isn't marked as GPL. But a quick
>>> grep shows that everything is either GPL or dual-licensed.
>>>
>>> In any case, yes, this will quite possibly break debug builds for people OOT.
>>> I think folio_get() might be a bit too ubiquitous to do this to.
>>
>> Thinking again, folio_get() should not be affected, it rightfully only checks
>> for a refcount underflow (when we exceed the limit + headroom). So only
>> try_get_page() is affected, which OOT very likely doesn't use.
>
> Aha, yes, you're right. I mixed up your folio_get() change. Would it make sense
> to VM_WARN_ON() on this as well, or does the <= 0 check cover us already?
<=0 is sufficient for folio_get(). There are valid cases where we can exceed the
limit into the rather large headroom (e.g., reaching the limit through page
table mappings, then performing harmless read/write that take bounded references
for a short time).
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-10 21:34 ` Jose A. Perez de Azpillaga
2026-11-12 13:09 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 3/9] mm/mmu_gather: flush pending references early before the folio limit David Hildenbrand (Arm)
` (8 subsequent siblings)
10 siblings, 2 replies; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, stable
Similar to mapping pagecache folios into page tables, let's reject
additional references when splicing folios into a pipe if the refcount
is at its limit, similar to how we handle it during GUP.
This will prevent more cases of possible folio refcount overflows with
large pagecache folios.
Introduce folio_get_checked() for checked reference acquisition when
the caller already holds a reference. It warns and fails on a non-positive
refcount and rejects additional references at the limit.
Use folio_get_checked() in splice_folio_into_pipe() and
fuse_ref_folio(), returning -EFAULT on failure, like we already do for
pipe-buffer duplication. Similarly, make try_get_page() use it instead.
Likely try_get_page() should be renamed (or removed) later, as it doesn't
match the folio_try_get() semantics and is confusing.
If we hit the limit after splicing some data, return the number of bytes
already spliced. Teach the callers to handle errors, and stop
filemap_splice_read() if we only spliced part of a folio, so we don't
skip ahead to the next folio.
The check in try_get_page() also covers pipe-buffer duplication by tee()
and partial pipe-to-pipe splicing.
Note that we deliberately will not fall back to the slow path of copying
the folio, particularly as the folio might have an invalid refcount. If
it is ever really required, we can revisit.
Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
fs/fuse/dev.c | 4 +++-
include/linux/mm.h | 26 ++++++++++++++++++++++----
mm/filemap.c | 26 ++++++++++++++++----------
mm/internal.h | 4 ++--
mm/shmem.c | 7 +++++--
5 files changed, 48 insertions(+), 19 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 3641abb90d6bb..50f41bb287832 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1230,7 +1230,9 @@ static int fuse_ref_folio(struct fuse_copy_state *cs, struct folio *folio,
if (cs->nr_segs >= cs->pipe->max_usage)
return -EIO;
- folio_get(folio);
+ if (!folio_get_checked(folio))
+ return -EFAULT;
+
err = unlock_request(cs->req);
if (err) {
folio_put(folio);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 6c7c29a086bd1..33bc57342590c 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2284,15 +2284,33 @@ static inline void get_page(struct page *page)
folio_get(folio);
}
-static inline __must_check bool try_get_page(struct page *page)
+/**
+ * folio_get_checked - Try to acquire a reference to a live folio.
+ * @folio: The folio.
+ *
+ * The caller must already hold a reference to @folio. Warn and fail on a
+ * non-positive refcount, and refuse additional references at the limit.
+ *
+ * Use folio_try_get() for speculative reference acquisition where the
+ * refcount can legitimately be zero.
+ *
+ * Return: True if the refcount was incremented, false otherwise.
+ */
+static inline __must_check bool folio_get_checked(struct folio *folio)
{
- page = compound_head(page);
- if (WARN_ON_ONCE(page_ref_count(page) <= 0))
+ if (WARN_ON_ONCE(folio_ref_count(folio) <= 0))
return false;
- page_ref_inc(page);
+ if (unlikely(folio_ref_count_over_limit(folio)))
+ return false;
+ folio_ref_inc(folio);
return true;
}
+static inline __must_check bool try_get_page(struct page *page)
+{
+ return folio_get_checked(page_folio(page));
+}
+
/**
* folio_put - Decrement the reference count on a folio.
* @folio: The folio.
diff --git a/mm/filemap.c b/mm/filemap.c
index 0f6d57432a7e5..e6e73e37f3ea4 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -3047,10 +3047,11 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
EXPORT_SYMBOL(generic_file_read_iter);
/*
- * Splice subpages from a folio into a pipe.
+ * Splice subpages from a folio into a pipe. Return the number of bytes spliced,
+ * or -EFAULT if we cannot splice from the folio.
*/
-size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
- struct folio *folio, loff_t fpos, size_t size)
+ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
+ struct folio *folio, loff_t fpos, size_t size)
{
struct page *page;
size_t spliced = 0, offset = offset_in_folio(folio, fpos);
@@ -3063,13 +3064,15 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
struct pipe_buffer *buf = pipe_head_buf(pipe);
size_t part = min_t(size_t, PAGE_SIZE - offset, size - spliced);
+ if (!folio_get_checked(folio))
+ return spliced ? spliced : -EFAULT;
+
*buf = (struct pipe_buffer) {
.ops = &page_cache_pipe_buf_ops,
.page = page,
.offset = offset,
.len = part,
};
- folio_get(folio);
pipe->head++;
page++;
spliced += part;
@@ -3094,9 +3097,10 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
* Return: On success, the number of bytes read will be returned and *@ppos
* will be updated if appropriate; 0 will be returned if there is no more data
* to be read; -EAGAIN will be returned if the pipe had no space, and some
- * other negative error code will be returned on error. A short read may occur
- * if the pipe has insufficient space, we reach the end of the data or we hit a
- * hole.
+ * other negative error code will be returned on error; -EFAULT will be returned
+ * if acquiring a folio reference fails and no data has been spliced.
+ * A short read may occur if the pipe has insufficient space, we reach the
+ * end of the data, hit a hole or hit a folio refcount limit.
*/
ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe,
@@ -3154,7 +3158,7 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
for (i = 0; i < folio_batch_count(&fbatch); i++) {
struct folio *folio = fbatch.folios[i];
- size_t n;
+ ssize_t n;
if (folio_pos(folio) >= end_offset)
goto out;
@@ -3170,13 +3174,15 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
n = min_t(loff_t, len, isize - *ppos);
n = splice_folio_into_pipe(pipe, folio, *ppos, n);
- if (!n)
+ if (n <= 0) {
+ error = n;
goto out;
+ }
len -= n;
total_spliced += n;
*ppos += n;
in->f_ra.prev_pos = *ppos;
- if (pipe_is_full(pipe))
+ if (pipe_is_full(pipe) || *ppos < folio_next_pos(folio))
goto out;
}
diff --git a/mm/internal.h b/mm/internal.h
index b129510b92e2a..08e259758937b 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1276,8 +1276,8 @@ struct migration_target_control {
/*
* mm/filemap.c
*/
-size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
- struct folio *folio, loff_t fpos, size_t size);
+ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
+ struct folio *folio, loff_t fpos, size_t size);
static inline bool vma_is_single_threaded_private(struct vm_area_struct *vma)
{
diff --git a/mm/shmem.c b/mm/shmem.c
index be4ab228cf67c..8b94877c36c18 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -3751,7 +3751,8 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
struct inode *inode = file_inode(in);
struct address_space *mapping = inode->i_mapping;
struct folio *folio = NULL;
- size_t total_spliced = 0, used, npages, n, part;
+ size_t total_spliced = 0, used, npages, part;
+ ssize_t n;
loff_t isize;
int error = 0;
@@ -3837,8 +3838,10 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
n = splice_zeropage_into_pipe(pipe, *ppos, part);
}
- if (!n)
+ if (n <= 0) {
+ error = n;
break;
+ }
len -= n;
total_spliced += n;
*ppos += n;
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes
2026-11-10 20:35 ` [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes David Hildenbrand (Arm)
@ 2026-11-10 21:34 ` Jose A. Perez de Azpillaga
2026-11-12 13:09 ` Pedro Falcato
1 sibling, 0 replies; 31+ messages in thread
From: Jose A. Perez de Azpillaga @ 2026-11-10 21:34 UTC (permalink / raw)
To: David Hildenbrand (Arm), Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Matthew Wilcox (Oracle), Jan Kara, Andrew Morton, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Usama Arif, Kiryl Shutsemau, Matthew Brost,
Joshua Hahn, Byungchul Park, Gregory Price, Ying Huang,
Alistair Popple, Peter Xu, Johannes Weiner, Song Liu,
Rik van Riel, Miklos Szeredi, Alexander Viro, Christian Brauner,
Hugh Dickins, Will Deacon, Aneesh Kumar K.V, Nick Piggin,
Peter Zijlstra, Arnd Bergmann, Jason Gunthorpe, Kevin Tian,
Joerg Roedel (AMD), Robin Murphy, Steve Sistare, John Hubbard,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: linux-mm, linux-kernel, linux-fsdevel, fuse-devel, linux-arch,
iommu, netdev, stable
On 11/10/26 9:35 PM, David Hildenbrand (Arm) wrote:
> Similar to mapping pagecache folios into page tables, let's reject
> additional references when splicing folios into a pipe if the refcount
> is at its limit, similar to how we handle it during GUP.
>
> This will prevent more cases of possible folio refcount overflows with
> large pagecache folios.
>
> Introduce folio_get_checked() for checked reference acquisition when
> the caller already holds a reference. It warns and fails on a non-positive
> refcount and rejects additional references at the limit.
>
> Use folio_get_checked() in splice_folio_into_pipe() and
> fuse_ref_folio(), returning -EFAULT on failure, like we already do for
> pipe-buffer duplication. Similarly, make try_get_page() use it instead.
>
> Likely try_get_page() should be renamed (or removed) later, as it doesn't
> match the folio_try_get() semantics and is confusing.
>
> If we hit the limit after splicing some data, return the number of bytes
> already spliced. Teach the callers to handle errors, and stop
> filemap_splice_read() if we only spliced part of a folio, so we don't
> skip ahead to the next folio.
>
> The check in try_get_page() also covers pipe-buffer duplication by tee()
> and partial pipe-to-pipe splicing.
>
> Note that we deliberately will not fall back to the slow path of copying
> the folio, particularly as the folio might have an invalid refcount. If
> it is ever really required, we can revisit.
>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
LGTM :)
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
--
cheers,
jose a. p-a
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes
2026-11-10 20:35 ` [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes David Hildenbrand (Arm)
2026-11-10 21:34 ` Jose A. Perez de Azpillaga
@ 2026-11-12 13:09 ` Pedro Falcato
2026-11-12 13:24 ` David Hildenbrand (Arm)
1 sibling, 1 reply; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 13:09 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On Tue, Nov 10, 2026 at 09:35:42PM +0100, David Hildenbrand (Arm) wrote:
> Similar to mapping pagecache folios into page tables, let's reject
> additional references when splicing folios into a pipe if the refcount
> is at its limit, similar to how we handle it during GUP.
>
> This will prevent more cases of possible folio refcount overflows with
> large pagecache folios.
>
> Introduce folio_get_checked() for checked reference acquisition when
> the caller already holds a reference. It warns and fails on a non-positive
> refcount and rejects additional references at the limit.
>
> Use folio_get_checked() in splice_folio_into_pipe() and
> fuse_ref_folio(), returning -EFAULT on failure, like we already do for
> pipe-buffer duplication. Similarly, make try_get_page() use it instead.
>
> Likely try_get_page() should be renamed (or removed) later, as it doesn't
> match the folio_try_get() semantics and is confusing.
>
> If we hit the limit after splicing some data, return the number of bytes
> already spliced. Teach the callers to handle errors, and stop
> filemap_splice_read() if we only spliced part of a folio, so we don't
> skip ahead to the next folio.
>
> The check in try_get_page() also covers pipe-buffer duplication by tee()
> and partial pipe-to-pipe splicing.
>
> Note that we deliberately will not fall back to the slow path of copying
> the folio, particularly as the folio might have an invalid refcount. If
> it is ever really required, we can revisit.
>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
> ---
> fs/fuse/dev.c | 4 +++-
> include/linux/mm.h | 26 ++++++++++++++++++++++----
> mm/filemap.c | 26 ++++++++++++++++----------
> mm/internal.h | 4 ++--
> mm/shmem.c | 7 +++++--
> 5 files changed, 48 insertions(+), 19 deletions(-)
>
> diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
> index 3641abb90d6bb..50f41bb287832 100644
> --- a/fs/fuse/dev.c
> +++ b/fs/fuse/dev.c
> @@ -1230,7 +1230,9 @@ static int fuse_ref_folio(struct fuse_copy_state *cs, struct folio *folio,
> if (cs->nr_segs >= cs->pipe->max_usage)
> return -EIO;
>
> - folio_get(folio);
> + if (!folio_get_checked(folio))
> + return -EFAULT;
> +
> err = unlock_request(cs->req);
> if (err) {
> folio_put(folio);
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 6c7c29a086bd1..33bc57342590c 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -2284,15 +2284,33 @@ static inline void get_page(struct page *page)
> folio_get(folio);
> }
>
> -static inline __must_check bool try_get_page(struct page *page)
> +/**
> + * folio_get_checked - Try to acquire a reference to a live folio.
> + * @folio: The folio.
> + *
> + * The caller must already hold a reference to @folio. Warn and fail on a
> + * non-positive refcount, and refuse additional references at the limit.
> + *
> + * Use folio_try_get() for speculative reference acquisition where the
> + * refcount can legitimately be zero.
> + *
> + * Return: True if the refcount was incremented, false otherwise.
> + */
> +static inline __must_check bool folio_get_checked(struct folio *folio)
> {
> - page = compound_head(page);
> - if (WARN_ON_ONCE(page_ref_count(page) <= 0))
> + if (WARN_ON_ONCE(folio_ref_count(folio) <= 0))
> return false;
> - page_ref_inc(page);
> + if (unlikely(folio_ref_count_over_limit(folio)))
> + return false;
Ah nice, the helper I wanted :)
Nit: Having these two functions called back-to-back will result in two
separate loads on the refcount (folio_ref_count() distills into a READ_ONCE).
> + folio_ref_inc(folio);
> return true;
> }
>
> +static inline __must_check bool try_get_page(struct page *page)
> +{
> + return folio_get_checked(page_folio(page));
> +
God I hate this. I never realized try_get_page() isn't atomic, but
folio_try_get() is.
> +
> /**
> * folio_put - Decrement the reference count on a folio.
> * @folio: The folio.
> diff --git a/mm/filemap.c b/mm/filemap.c
> index 0f6d57432a7e5..e6e73e37f3ea4 100644
> --- a/mm/filemap.c
> +++ b/mm/filemap.c
> @@ -3047,10 +3047,11 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
> EXPORT_SYMBOL(generic_file_read_iter);
>
> /*
> - * Splice subpages from a folio into a pipe.
> + * Splice subpages from a folio into a pipe. Return the number of bytes spliced,
> + * or -EFAULT if we cannot splice from the folio.
-EFAULT is a weird error code to return from splice, et al.
> */
> -size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
> - struct folio *folio, loff_t fpos, size_t size)
> +ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
> + struct folio *folio, loff_t fpos, size_t size)
> {
> struct page *page;
> size_t spliced = 0, offset = offset_in_folio(folio, fpos);
> @@ -3063,13 +3064,15 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
> struct pipe_buffer *buf = pipe_head_buf(pipe);
> size_t part = min_t(size_t, PAGE_SIZE - offset, size - spliced);
>
> + if (!folio_get_checked(folio))
> + return spliced ? spliced : -EFAULT;
return spliced ?: -EFAULT;
I don't know if there's much kernel code out there doing this, but I like
the trick :)
> +
> *buf = (struct pipe_buffer) {
> .ops = &page_cache_pipe_buf_ops,
> .page = page,
> .offset = offset,
> .len = part,
> };
> - folio_get(folio);
> pipe->head++;
> page++;
> spliced += part;
> @@ -3094,9 +3097,10 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
> * Return: On success, the number of bytes read will be returned and *@ppos
> * will be updated if appropriate; 0 will be returned if there is no more data
> * to be read; -EAGAIN will be returned if the pipe had no space, and some
> - * other negative error code will be returned on error. A short read may occur
> - * if the pipe has insufficient space, we reach the end of the data or we hit a
> - * hole.
> + * other negative error code will be returned on error; -EFAULT will be returned
> + * if acquiring a folio reference fails and no data has been spliced.
> + * A short read may occur if the pipe has insufficient space, we reach the
> + * end of the data, hit a hole or hit a folio refcount limit.
> */
> ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
> struct pipe_inode_info *pipe,
> @@ -3154,7 +3158,7 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
>
> for (i = 0; i < folio_batch_count(&fbatch); i++) {
> struct folio *folio = fbatch.folios[i];
> - size_t n;
> + ssize_t n;
>
> if (folio_pos(folio) >= end_offset)
> goto out;
> @@ -3170,13 +3174,15 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
>
> n = min_t(loff_t, len, isize - *ppos);
> n = splice_folio_into_pipe(pipe, folio, *ppos, n);
> - if (!n)
> + if (n <= 0) {
> + error = n;
> goto out;
> + }
> len -= n;
> total_spliced += n;
> *ppos += n;
> in->f_ra.prev_pos = *ppos;
> - if (pipe_is_full(pipe))
> + if (pipe_is_full(pipe) || *ppos < folio_next_pos(folio))
I'm confused. Why does this need to be done here?
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes
2026-11-12 13:09 ` Pedro Falcato
@ 2026-11-12 13:24 ` David Hildenbrand (Arm)
2026-11-12 14:00 ` Pedro Falcato
0 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-12 13:24 UTC (permalink / raw)
To: Pedro Falcato
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On 11/12/26 14:09, Pedro Falcato wrote:
> On Tue, Nov 10, 2026 at 09:35:42PM +0100, David Hildenbrand (Arm) wrote:
>> Similar to mapping pagecache folios into page tables, let's reject
>> additional references when splicing folios into a pipe if the refcount
>> is at its limit, similar to how we handle it during GUP.
>>
>> This will prevent more cases of possible folio refcount overflows with
>> large pagecache folios.
>>
>> Introduce folio_get_checked() for checked reference acquisition when
>> the caller already holds a reference. It warns and fails on a non-positive
>> refcount and rejects additional references at the limit.
>>
>> Use folio_get_checked() in splice_folio_into_pipe() and
>> fuse_ref_folio(), returning -EFAULT on failure, like we already do for
>> pipe-buffer duplication. Similarly, make try_get_page() use it instead.
>>
>> Likely try_get_page() should be renamed (or removed) later, as it doesn't
>> match the folio_try_get() semantics and is confusing.
>>
>> If we hit the limit after splicing some data, return the number of bytes
>> already spliced. Teach the callers to handle errors, and stop
>> filemap_splice_read() if we only spliced part of a folio, so we don't
>> skip ahead to the next folio.
>>
>> The check in try_get_page() also covers pipe-buffer duplication by tee()
>> and partial pipe-to-pipe splicing.
>>
>> Note that we deliberately will not fall back to the slow path of copying
>> the folio, particularly as the folio might have an invalid refcount. If
>> it is ever really required, we can revisit.
>>
>> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
>> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
>> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
>> ---
[...]
>> +static inline __must_check bool folio_get_checked(struct folio *folio)
>> {
>> - page = compound_head(page);
>> - if (WARN_ON_ONCE(page_ref_count(page) <= 0))
>> + if (WARN_ON_ONCE(folio_ref_count(folio) <= 0))
>> return false;
>> - page_ref_inc(page);
>> + if (unlikely(folio_ref_count_over_limit(folio)))
>> + return false;
>
> Ah nice, the helper I wanted :)
>
> Nit: Having these two functions called back-to-back will result in two
> separate loads on the refcount (folio_ref_count() distills into a READ_ONCE).
Ack, something to optimize. I'd likely leave that for future work, as we'd need
yet another helper.
Unless I can convince myself that it is easy to do.
>
>> + folio_ref_inc(folio);
>> return true;
>> }
>>
>> +static inline __must_check bool try_get_page(struct page *page)
>> +{
>> + return folio_get_checked(page_folio(page));
>> +
>
> God I hate this. I never realized try_get_page() isn't atomic, but
> folio_try_get() is.
Yeah, but that's for another purpose and more expensive.
>
>> +
>> /**
>> * folio_put - Decrement the reference count on a folio.
>> * @folio: The folio.
>> diff --git a/mm/filemap.c b/mm/filemap.c
>> index 0f6d57432a7e5..e6e73e37f3ea4 100644
>> --- a/mm/filemap.c
>> +++ b/mm/filemap.c
>> @@ -3047,10 +3047,11 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
>> EXPORT_SYMBOL(generic_file_read_iter);
>>
>> /*
>> - * Splice subpages from a folio into a pipe.
>> + * Splice subpages from a folio into a pipe. Return the number of bytes spliced,
>> + * or -EFAULT if we cannot splice from the folio.
>
> -EFAULT is a weird error code to return from splice, et al.
We might be able to use ETOOMANYREFS (have to check if that could confuse
callers), but IMHO EFAULT is good enough.
What would be your suggestion?
>
>> */
>> -size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>> - struct folio *folio, loff_t fpos, size_t size)
>> +ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>> + struct folio *folio, loff_t fpos, size_t size)
>> {
>> struct page *page;
>> size_t spliced = 0, offset = offset_in_folio(folio, fpos);
>> @@ -3063,13 +3064,15 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>> struct pipe_buffer *buf = pipe_head_buf(pipe);
>> size_t part = min_t(size_t, PAGE_SIZE - offset, size - spliced);
>>
>> + if (!folio_get_checked(folio))
>> + return spliced ? spliced : -EFAULT;
> return spliced ?: -EFAULT;
>
> I don't know if there's much kernel code out there doing this, but I like
> the trick :)
There are a couple of thousands, but yes, one doesn't encounter it that often.
>
>> +
>> *buf = (struct pipe_buffer) {
>> .ops = &page_cache_pipe_buf_ops,
>> .page = page,
>> .offset = offset,
>> .len = part,
>> };
>> - folio_get(folio);
>> pipe->head++;
>> page++;
>> spliced += part;
>> @@ -3094,9 +3097,10 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>> * Return: On success, the number of bytes read will be returned and *@ppos
>> * will be updated if appropriate; 0 will be returned if there is no more data
>> * to be read; -EAGAIN will be returned if the pipe had no space, and some
>> - * other negative error code will be returned on error. A short read may occur
>> - * if the pipe has insufficient space, we reach the end of the data or we hit a
>> - * hole.
>> + * other negative error code will be returned on error; -EFAULT will be returned
>> + * if acquiring a folio reference fails and no data has been spliced.
>> + * A short read may occur if the pipe has insufficient space, we reach the
>> + * end of the data, hit a hole or hit a folio refcount limit.
>> */
>> ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
>> struct pipe_inode_info *pipe,
>> @@ -3154,7 +3158,7 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
>>
>> for (i = 0; i < folio_batch_count(&fbatch); i++) {
>> struct folio *folio = fbatch.folios[i];
>> - size_t n;
>> + ssize_t n;
>>
>> if (folio_pos(folio) >= end_offset)
>> goto out;
>> @@ -3170,13 +3174,15 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
>>
>> n = min_t(loff_t, len, isize - *ppos);
>> n = splice_folio_into_pipe(pipe, folio, *ppos, n);
>> - if (!n)
>> + if (n <= 0) {
>> + error = n;
>> goto out;
>> + }
>> len -= n;
>> total_spliced += n;
>> *ppos += n;
>> in->f_ra.prev_pos = *ppos;
>> - if (pipe_is_full(pipe))
>> + if (pipe_is_full(pipe) || *ppos < folio_next_pos(folio))
>
> I'm confused. Why does this need to be done here?
>
I have above:
"stop filemap_splice_read() if we only spliced part of a folio, so we don't
skip ahead to the next folio"
Thanks a lot for the review!
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes
2026-11-12 13:24 ` David Hildenbrand (Arm)
@ 2026-11-12 14:00 ` Pedro Falcato
2026-11-12 14:31 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 14:00 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On Thu, Nov 12, 2026 at 02:24:09PM +0100, David Hildenbrand (Arm) wrote:
> On 11/12/26 14:09, Pedro Falcato wrote:
> > On Tue, Nov 10, 2026 at 09:35:42PM +0100, David Hildenbrand (Arm) wrote:
> >> Similar to mapping pagecache folios into page tables, let's reject
> >> additional references when splicing folios into a pipe if the refcount
> >> is at its limit, similar to how we handle it during GUP.
> >>
> >> This will prevent more cases of possible folio refcount overflows with
> >> large pagecache folios.
> >>
> >> Introduce folio_get_checked() for checked reference acquisition when
> >> the caller already holds a reference. It warns and fails on a non-positive
> >> refcount and rejects additional references at the limit.
> >>
> >> Use folio_get_checked() in splice_folio_into_pipe() and
> >> fuse_ref_folio(), returning -EFAULT on failure, like we already do for
> >> pipe-buffer duplication. Similarly, make try_get_page() use it instead.
> >>
> >> Likely try_get_page() should be renamed (or removed) later, as it doesn't
> >> match the folio_try_get() semantics and is confusing.
> >>
> >> If we hit the limit after splicing some data, return the number of bytes
> >> already spliced. Teach the callers to handle errors, and stop
> >> filemap_splice_read() if we only spliced part of a folio, so we don't
> >> skip ahead to the next folio.
> >>
> >> The check in try_get_page() also covers pipe-buffer duplication by tee()
> >> and partial pipe-to-pipe splicing.
> >>
> >> Note that we deliberately will not fall back to the slow path of copying
> >> the folio, particularly as the folio might have an invalid refcount. If
> >> it is ever really required, we can revisit.
> >>
> >> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> >> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> >> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> >> Cc: stable@vger.kernel.org
> >> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
> >> ---
>
> [...]
> >> index 0f6d57432a7e5..e6e73e37f3ea4 100644
> >> --- a/mm/filemap.c
> >> +++ b/mm/filemap.c
> >> @@ -3047,10 +3047,11 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
> >> EXPORT_SYMBOL(generic_file_read_iter);
> >>
> >> /*
> >> - * Splice subpages from a folio into a pipe.
> >> + * Splice subpages from a folio into a pipe. Return the number of bytes spliced,
> >> + * or -EFAULT if we cannot splice from the folio.
> >
> > -EFAULT is a weird error code to return from splice, et al.
>
> We might be able to use ETOOMANYREFS (have to check if that could confuse
> callers), but IMHO EFAULT is good enough.
>
> What would be your suggestion?
ETOOMANYREFS I think is good, also EBUSY, EMLINK (though this would be weird)
or even a generic -EIO.
>
> >
> >> */
> >> -size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
> >> - struct folio *folio, loff_t fpos, size_t size)
> >> +ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
> >> + struct folio *folio, loff_t fpos, size_t size)
> >> {
> >> struct page *page;
> >> size_t spliced = 0, offset = offset_in_folio(folio, fpos);
> >> @@ -3063,13 +3064,15 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
> >> struct pipe_buffer *buf = pipe_head_buf(pipe);
> >> size_t part = min_t(size_t, PAGE_SIZE - offset, size - spliced);
> >>
> >> + if (!folio_get_checked(folio))
> >> + return spliced ? spliced : -EFAULT;
> > return spliced ?: -EFAULT;
> >
> > I don't know if there's much kernel code out there doing this, but I like
> > the trick :)
>
> There are a couple of thousands, but yes, one doesn't encounter it that often.
>
> >
> >> +
> >> *buf = (struct pipe_buffer) {
> >> .ops = &page_cache_pipe_buf_ops,
> >> .page = page,
> >> .offset = offset,
> >> .len = part,
> >> };
> >> - folio_get(folio);
> >> pipe->head++;
> >> page++;
> >> spliced += part;
> >> @@ -3094,9 +3097,10 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
> >> * Return: On success, the number of bytes read will be returned and *@ppos
> >> * will be updated if appropriate; 0 will be returned if there is no more data
> >> * to be read; -EAGAIN will be returned if the pipe had no space, and some
> >> - * other negative error code will be returned on error. A short read may occur
> >> - * if the pipe has insufficient space, we reach the end of the data or we hit a
> >> - * hole.
> >> + * other negative error code will be returned on error; -EFAULT will be returned
> >> + * if acquiring a folio reference fails and no data has been spliced.
> >> + * A short read may occur if the pipe has insufficient space, we reach the
> >> + * end of the data, hit a hole or hit a folio refcount limit.
> >> */
> >> ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
> >> struct pipe_inode_info *pipe,
> >> @@ -3154,7 +3158,7 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
> >>
> >> for (i = 0; i < folio_batch_count(&fbatch); i++) {
> >> struct folio *folio = fbatch.folios[i];
> >> - size_t n;
> >> + ssize_t n;
> >>
> >> if (folio_pos(folio) >= end_offset)
> >> goto out;
> >> @@ -3170,13 +3174,15 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
> >>
> >> n = min_t(loff_t, len, isize - *ppos);
> >> n = splice_folio_into_pipe(pipe, folio, *ppos, n);
> >> - if (!n)
> >> + if (n <= 0) {
> >> + error = n;
> >> goto out;
> >> + }
> >> len -= n;
> >> total_spliced += n;
> >> *ppos += n;
> >> in->f_ra.prev_pos = *ppos;
> >> - if (pipe_is_full(pipe))
> >> + if (pipe_is_full(pipe) || *ppos < folio_next_pos(folio))
> >
> > I'm confused. Why does this need to be done here?
> >
>
> I have above:
>
> "stop filemap_splice_read() if we only spliced part of a folio, so we don't
> skip ahead to the next folio"
Yes, but is this a separate fix? A folio could be partially spliced previously
as well, no?
>
> Thanks a lot for the review!
You're welcome! :)
FWIW, you can add my
Reviewed-by: Pedro Falcato <pfalcato@suse.de>
to whatever comes out of these nits :)
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes
2026-11-12 14:00 ` Pedro Falcato
@ 2026-11-12 14:31 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-12 14:31 UTC (permalink / raw)
To: Pedro Falcato
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On 11/12/26 15:00, Pedro Falcato wrote:
> On Thu, Nov 12, 2026 at 02:24:09PM +0100, David Hildenbrand (Arm) wrote:
>> On 11/12/26 14:09, Pedro Falcato wrote:
>>
>> [...]
>>>
>>> -EFAULT is a weird error code to return from splice, et al.
>>
>> We might be able to use ETOOMANYREFS (have to check if that could confuse
>> callers), but IMHO EFAULT is good enough.
>>
>> What would be your suggestion?
>
> ETOOMANYREFS I think is good, also EBUSY, EMLINK (though this would be weird)
> or even a generic -EIO.
So I used EFAULT because that's also what we currently return when
generic_pipe_buf_get() fails through pipe_buf_get() in splice_pipe_to_pipe() and
link_pipe().
If we want to return ETOOMANYREFS we'd likely have to special-case "<= 0" ref
count and things get more complicated, as we'd have to return two different
error cases.
And then we'd still have the inconsistency with the above cases.
I'll mention that I do not expect that we can trigger overflows in any
reasonable setup once we changed the way page table mappings are refcounted.
So for consistency with the existing checks I'd leave it at -EFAULT for now.
>
>>
>>>
>>> return spliced ?: -EFAULT;
>>>
>>> I don't know if there's much kernel code out there doing this, but I like
>>> the trick :)
>>
>> There are a couple of thousands, but yes, one doesn't encounter it that often.
>>
>>>
>>>
>>> I'm confused. Why does this need to be done here?
>>>
>>
>> I have above:
>>
>> "stop filemap_splice_read() if we only spliced part of a folio, so we don't
>> skip ahead to the next folio"
>
> Yes, but is this a separate fix? A folio could be partially spliced previously
> as well, no?
I think that wasn't possible because splice_folio_to_pipe() never returned
early. So it's only possible now once we fail to splice halfway through.
>
>>
>> Thanks a lot for the review!
>
> You're welcome! :)
>
> FWIW, you can add my
>
> Reviewed-by: Pedro Falcato <pfalcato@suse.de>
>
> to whatever comes out of these nits :)
Thanks! :)
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 3/9] mm/mmu_gather: flush pending references early before the folio limit
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-12 13:10 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 4/9] iommufd: fix cleanup when adding memfd folio pins fails David Hildenbrand (Arm)
` (7 subsequent siblings)
10 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, Chris Mason, stable
When a task zaps mappings (e.g., MADV_DONTNEED), it can drop the PT lock
and defer dropping the folio references. In the meantime, page faults
can refault pagecache folios, creating more references for the same
folios. These deferred references can build up over multiple VMAs.
Let's just request a flush after enqueueing a pagecache folio whose
refcount reached a certain threshold. Start flushing well before new
references are refused, regardless of the caller's privileges.
When returning "true" from __tlb_remove_folio_pages_size(), PTE zapping
will flush the TLB before dropping the PT lock, but drop the references
afterwards. Refaults can still race with that, but flushing early reduces
the risk of letting others hit the limit.
This is a temporary measure until we have something better.
Reported-by: Chris Mason <mason@kernel.org>
Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
Cc: stable@vger.kernel.org
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
include/asm-generic/tlb.h | 4 ++--
include/linux/mm.h | 1 +
mm/memory.c | 4 ++--
mm/mmu_gather.c | 10 ++++++----
4 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h
index 3f4a180b48f3c..4bba02fd5d543 100644
--- a/include/asm-generic/tlb.h
+++ b/include/asm-generic/tlb.h
@@ -75,8 +75,8 @@
* - __tlb_remove_folio_pages_size()
*
* __tlb_remove_folio_pages_size() is the basic primitive that queues pages
- * for freeing. It will return a boolean indicating if the queue is (now)
- * full and a call to tlb_flush_mmu() is required.
+ * for freeing. It returns true when a call to tlb_flush_mmu() is required,
+ * either because the queue is full or a folio is near its refcount limit.
*
* tlb_remove_page() and tlb_remove_page_size() imply the call to
* tlb_flush_mmu() when required and has no return value.
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 33bc57342590c..a36ff83589c61 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2232,6 +2232,7 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
/* Leave separate reserves for administrators and concurrent reference gains. */
#define FOLIO_REF_COUNT_LIMIT (INT_MAX - (1U << 26))
#define FOLIO_REF_COUNT_ADMIN_LIMIT (INT_MAX - (1U << 25))
+#define FOLIO_REF_COUNT_FLUSH_LIMIT (INT_MAX - (1U << 27))
bool __folio_ref_count_over_limit(unsigned int ref_count);
diff --git a/mm/memory.c b/mm/memory.c
index 977e037388963..a1a9ba2430f83 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2072,8 +2072,8 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
pte_unmap_unlock(start_pte, ptl);
/*
- * If we forced a TLB flush (either due to running out of
- * batch buffers or because we needed to flush dirty TLB
+ * If we forced a TLB flush (either to release batched
+ * references or because we needed to flush dirty TLB
* entries before releasing the ptl), free the batched
* memory too. Come back again if we didn't do everything.
*/
diff --git a/mm/mmu_gather.c b/mm/mmu_gather.c
index 9f353f0e2ef4d..e98397ac577c4 100644
--- a/mm/mmu_gather.c
+++ b/mm/mmu_gather.c
@@ -179,10 +179,7 @@ static bool __tlb_remove_folio_pages_size(struct mmu_gather *tlb,
#endif
batch = tlb->active;
- /*
- * Add the page and check if we are full. If so
- * force a flush.
- */
+ /* Add the pages before deciding whether we need to flush. */
if (likely(nr_pages == 1)) {
batch->encoded_pages[batch->nr++] = encode_page(page, flags);
} else {
@@ -190,6 +187,11 @@ static bool __tlb_remove_folio_pages_size(struct mmu_gather *tlb,
batch->encoded_pages[batch->nr++] = encode_page(page, flags);
batch->encoded_pages[batch->nr++] = encode_nr_pages(nr_pages);
}
+ /* Flush pending references well before new references are refused. */
+ if (unlikely((unsigned int)folio_ref_count(page_folio(page)) >=
+ FOLIO_REF_COUNT_FLUSH_LIMIT))
+ return true;
+
/*
* Make sure that we can always add another "page" + "nr_pages",
* requiring two entries instead of only a single one.
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH v3 3/9] mm/mmu_gather: flush pending references early before the folio limit
2026-11-10 20:35 ` [PATCH v3 3/9] mm/mmu_gather: flush pending references early before the folio limit David Hildenbrand (Arm)
@ 2026-11-12 13:10 ` Pedro Falcato
0 siblings, 0 replies; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 13:10 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, Chris Mason, stable
On Tue, Nov 10, 2026 at 09:35:43PM +0100, David Hildenbrand (Arm) wrote:
> When a task zaps mappings (e.g., MADV_DONTNEED), it can drop the PT lock
> and defer dropping the folio references. In the meantime, page faults
> can refault pagecache folios, creating more references for the same
> folios. These deferred references can build up over multiple VMAs.
>
> Let's just request a flush after enqueueing a pagecache folio whose
> refcount reached a certain threshold. Start flushing well before new
> references are refused, regardless of the caller's privileges.
>
> When returning "true" from __tlb_remove_folio_pages_size(), PTE zapping
> will flush the TLB before dropping the PT lock, but drop the references
> afterwards. Refaults can still race with that, but flushing early reduces
> the risk of letting others hit the limit.
>
> This is a temporary measure until we have something better.
>
> Reported-by: Chris Mason <mason@kernel.org>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Pedro Falcato <pfalcato@suse.de>
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 4/9] iommufd: fix cleanup when adding memfd folio pins fails
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
` (2 preceding siblings ...)
2026-11-10 20:35 ` [PATCH v3 3/9] mm/mmu_gather: flush pending references early before the folio limit David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 5/9] mm/gup: limit pagecache folio refcount when grabbing references David Hildenbrand (Arm)
` (6 subsequent siblings)
10 siblings, 0 replies; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, stable, Jason Gunthorpe
folio_add_pins() can fail after memfd_pin_folios() succeeded pinning a
folio. Upcoming folio refcount limit checks will make this easier to
trigger.
Let's fix three problems in the error handling:
1. pfn_reader_release_pins() uses the allocated array size instead of the
number of folios returned by memfd_pin_folios(). With large folios,
cleanup can unpin uninitialized entries. Remember the number of returned
folios and use that during cleanup.
2. pin_memfd_pages() returns on failure without releasing the pins acquired
by the current call. Release the per-page pins for folios already
expanded into the page array, and the single pins for the remaining
folios.
3. pfn_reader_next() returns on failure before updating batch_end_index,
even if the batch already contains successfully expanded folios. Update
it before propagating the error so cleanup releases those pins as well.
Fixes: 92687c793644 ("iommufd: pfn_reader for file mappings")
Cc: stable@vger.kernel.org
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
drivers/iommu/iommufd/pages.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 2f6153108add7..fcdbde007a9ab 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -781,6 +781,7 @@ struct pfn_reader_user {
struct file *file;
struct folio **ufolios;
size_t ufolios_len;
+ unsigned long ufolios_count;
unsigned long ufolios_offset;
struct folio **ufolios_next;
};
@@ -800,6 +801,7 @@ static void pfn_reader_user_init(struct pfn_reader_user *user,
user->file = (pages->type == IOPT_ADDRESS_FILE) ? pages->file : NULL;
user->ufolios = NULL;
user->ufolios_len = 0;
+ user->ufolios_count = 0;
user->ufolios_next = NULL;
user->ufolios_offset = 0;
}
@@ -843,6 +845,7 @@ static long pin_memfd_pages(struct pfn_reader_user *user, unsigned long start,
offset >>= PAGE_SHIFT;
user->ufolios_next = user->ufolios;
+ user->ufolios_count = nfolios;
user->ufolios_offset = offset;
for (i = 0; i < nfolios; i++) {
@@ -859,8 +862,12 @@ static long pin_memfd_pages(struct pfn_reader_user *user, unsigned long start,
} else {
int rc = folio_add_pins(folio, npin - 1);
- if (rc)
+ if (rc) {
+ unpin_user_pages(user->upages,
+ upages - user->upages);
+ unpin_folios(user->ufolios + i, nfolios - i);
return rc;
+ }
while (npin--)
*upages++ = folio_page(folio, offset++);
@@ -1251,14 +1258,14 @@ static int pfn_reader_next(struct pfn_reader *pfns)
return -EINVAL;
rc = pfn_reader_fill_span(pfns);
+ pfns->batch_end_index = pfns->batch_start_index +
+ pfns->batch.total_pfns;
if (rc)
return rc;
if (WARN_ON(!pfns->batch.total_pfns))
return -EINVAL;
- pfns->batch_end_index =
- pfns->batch_start_index + pfns->batch.total_pfns;
if (pfns->batch_end_index == pfns->span.last_used + 1)
interval_tree_double_span_iter_next(&pfns->span);
@@ -1320,7 +1327,7 @@ static void pfn_reader_release_pins(struct pfn_reader *pfns)
if (!user->file) {
unpin_user_pages(user->upages + start_index, npages);
} else {
- long n = user->ufolios_len / sizeof(*user->ufolios);
+ const unsigned long n = user->ufolios_count;
unpin_folios(user->ufolios_next,
user->ufolios + n - user->ufolios_next);
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* [PATCH v3 5/9] mm/gup: limit pagecache folio refcount when grabbing references
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
` (3 preceding siblings ...)
2026-11-10 20:35 ` [PATCH v3 4/9] iommufd: fix cleanup when adding memfd folio pins fails David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-12 13:12 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 6/9] RFC: net: limit folio refcount when copying skb frag references David Hildenbrand (Arm)
` (5 subsequent siblings)
10 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, stable
Let's add a folio_ref_count_over_limit() check in fast and slow GUP
before acquiring additional references or pins.
Keep it simple and use the check for all folio types.
Also propagate try_grab_folio() errors when grabbing additional
references in __get_user_pages() and in memfd_pin_folios(), so hitting
the limit returns -ENOMEM instead of -EFAULT and -EINVAL, respectively.
Keep the existing invalid-refcount warnings.
Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
Cc: stable@vger.kernel.org
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
include/linux/mm.h | 11 +++++------
mm/gup.c | 24 +++++++++++++++---------
2 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index a36ff83589c61..9b11af88de2b9 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2244,6 +2244,9 @@ bool __folio_ref_count_over_limit(unsigned int ref_count);
* to pagecache folios (!hugetlb), either before acquiring references or
* afterwards with rollback if the operation is refused.
*
+ * Also required when pinning folios without a dedicated pincount that use
+ * GUP_PIN_COUNTING_BIAS.
+ *
* Checking other folios does no harm.
*
* This check is an approximation; we leave some headroom such that concurrent
@@ -2951,12 +2954,8 @@ static inline bool folio_maybe_dma_pinned(struct folio *folio)
return atomic_read(&folio->_pincount) > 0;
/*
- * folio_ref_count() is signed. If that refcount overflows, then
- * folio_ref_count() returns a negative value, and callers will avoid
- * further incrementing the refcount.
- *
- * Here, for that overflow case, use the sign bit to count a little
- * bit higher via unsigned math, and thus still get an accurate result.
+ * Use unsigned math so even an unexpected negative refcount is
+ * treated as possibly pinned.
*/
return ((unsigned int)folio_ref_count(folio)) >=
GUP_PIN_COUNTING_BIAS;
diff --git a/mm/gup.c b/mm/gup.c
index ebbfee09b7278..531ef2469228e 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -79,6 +79,8 @@ static inline struct folio *try_get_folio(struct page *page, int refs)
folio = page_folio(page);
if (WARN_ON_ONCE(folio_ref_count(folio) < 0))
return NULL;
+ if (unlikely(folio_ref_count_over_limit(folio)))
+ return NULL;
if (unlikely(!folio_ref_try_add(folio, refs)))
return NULL;
@@ -143,6 +145,10 @@ int __must_check try_grab_folio(struct folio *folio, int refs,
if (WARN_ON_ONCE(folio_ref_count(folio) <= 0))
return -ENOMEM;
+ if ((flags & (FOLL_GET | FOLL_PIN)) &&
+ unlikely(folio_ref_count_over_limit(folio)))
+ return -ENOMEM;
+
if (unlikely(!(flags & FOLL_PCI_P2PDMA) && folio_is_pci_p2pdma(folio)))
return -EREMOTEIO;
@@ -1482,17 +1488,17 @@ static long __get_user_pages(struct mm_struct *mm,
struct folio *folio = page_folio(page);
/*
- * Since we already hold refcount on the
- * large folio, this should never fail.
+ * Grabbing more references can fail once we
+ * hit folio refcount limits.
*/
- if (try_grab_folio(folio, page_increm - 1,
- gup_flags)) {
+ ret = try_grab_folio(folio, page_increm - 1,
+ gup_flags);
+ if (ret) {
/*
- * Release the 1st page ref if the
- * folio is problematic, fail hard.
+ * Release the folio reference we
+ * already took.
*/
gup_put_folio(folio, 1, gup_flags);
- ret = -EFAULT;
goto out;
}
}
@@ -3541,10 +3547,10 @@ long memfd_pin_folios(struct file *memfd, loff_t start, loff_t end,
break;
}
- if (try_grab_folio(folio, 1, FOLL_PIN)) {
+ ret = try_grab_folio(folio, 1, FOLL_PIN);
+ if (ret) {
folio_unlock(folio);
folio_batch_release(&fbatch);
- ret = -EINVAL;
goto err;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH v3 5/9] mm/gup: limit pagecache folio refcount when grabbing references
2026-11-10 20:35 ` [PATCH v3 5/9] mm/gup: limit pagecache folio refcount when grabbing references David Hildenbrand (Arm)
@ 2026-11-12 13:12 ` Pedro Falcato
2026-11-12 14:33 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 13:12 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On Tue, Nov 10, 2026 at 09:35:45PM +0100, David Hildenbrand (Arm) wrote:
> Let's add a folio_ref_count_over_limit() check in fast and slow GUP
> before acquiring additional references or pins.
>
> Keep it simple and use the check for all folio types.
>
> Also propagate try_grab_folio() errors when grabbing additional
> references in __get_user_pages() and in memfd_pin_folios(), so hitting
> the limit returns -ENOMEM instead of -EFAULT and -EINVAL, respectively.
>
> Keep the existing invalid-refcount warnings.
>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Same points about funny weird inefficiency applies, in any case:
Reviewed-by: Pedro Falcato <pfalcato@suse.de>
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: [PATCH v3 5/9] mm/gup: limit pagecache folio refcount when grabbing references
2026-11-12 13:12 ` Pedro Falcato
@ 2026-11-12 14:33 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-12 14:33 UTC (permalink / raw)
To: Pedro Falcato
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On 11/12/26 14:12, Pedro Falcato wrote:
> On Tue, Nov 10, 2026 at 09:35:45PM +0100, David Hildenbrand (Arm) wrote:
>> Let's add a folio_ref_count_over_limit() check in fast and slow GUP
>> before acquiring additional references or pins.
>>
>> Keep it simple and use the check for all folio types.
>>
>> Also propagate try_grab_folio() errors when grabbing additional
>> references in __get_user_pages() and in memfd_pin_folios(), so hitting
>> the limit returns -ENOMEM instead of -EFAULT and -EINVAL, respectively.
>>
>> Keep the existing invalid-refcount warnings.
>>
>> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
>> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
>> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
>> Cc: stable@vger.kernel.org
>> Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
>> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
>
> Same points about funny weird inefficiency applies, in any case:
Yes, thanks. Adding a second function that consumes folio+refcount was easy, so
I did that in patch #2.
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 6/9] RFC: net: limit folio refcount when copying skb frag references
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
` (4 preceding siblings ...)
2026-11-10 20:35 ` [PATCH v3 5/9] mm/gup: limit pagecache folio refcount when grabbing references David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-12 16:05 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 7/9] RFC: net: limit folio refcount when rearranging skb frags David Hildenbrand (Arm)
` (4 subsequent siblings)
10 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, stable
Various networking features copy frag references of an skb, for example
when unsharing a cloned skb (AF_PACKET trimming a clone), when copying an
skb (nft dup) or when building NFQUEUE messages. This is an effective
way for multiplying folio references held by an skb, allowing for
exceeding folio refcount limits.
This is not specific to large pagecache folios, but with them it is a
lot easier to reach the refcount limit, as page table mappings can cheaply
create many references.
Let's introduce skb_frag_try_ref() and skb_frags_try_ref(), which use
try_get_page(), and use them in pskb_expand_head(), __pskb_copy_fclone(),
skb_zerocopy() and pskb_carve_inside_*(). All of these can already fail,
and their callers handle that.
Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
include/linux/skbuff_ref.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++
net/core/skbuff.c | 35 +++++++++++++++++++-----------
2 files changed, 76 insertions(+), 12 deletions(-)
diff --git a/include/linux/skbuff_ref.h b/include/linux/skbuff_ref.h
index b6f6da556ee90..560ddc863bbec 100644
--- a/include/linux/skbuff_ref.h
+++ b/include/linux/skbuff_ref.h
@@ -44,6 +44,33 @@ static __always_inline void skb_frag_ref(struct sk_buff *skb, int f)
skb->pp_recycle);
}
+/**
+ * skb_frag_try_ref - try to acquire a reference on a paged fragment of an skb
+ * @skb: the buffer
+ * @f: the fragment offset
+ *
+ * Like skb_frag_ref(), but acquires non-page_pool page references using
+ * try_get_page(), refusing additional references to a page whose refcount is
+ * at its limit.
+ *
+ * Return: True if a reference was acquired, otherwise false.
+ */
+static __always_inline __must_check bool skb_frag_try_ref(struct sk_buff *skb,
+ int f)
+{
+ netmem_ref netmem = skb_frag_netmem(&skb_shinfo(skb)->frags[f]);
+
+#ifdef CONFIG_PAGE_POOL
+ if (skb->pp_recycle && napi_pp_get_page(netmem))
+ return true;
+#endif
+ if (netmem_is_net_iov(netmem)) {
+ net_iov_inc_ref(netmem_to_net_iov(netmem));
+ return true;
+ }
+ return try_get_page(netmem_to_page(netmem));
+}
+
bool napi_pp_put_page(netmem_ref netmem);
/**
@@ -83,4 +110,30 @@ static __always_inline void skb_frag_unref(struct sk_buff *skb, int f)
skb->pp_recycle);
}
+/**
+ * skb_frags_try_ref - try to acquire references on all paged fragments
+ * @skb: the buffer
+ *
+ * Try to acquire a reference on each paged fragment of @skb using
+ * skb_frag_try_ref(). On failure, acquired references are dropped.
+ *
+ * Must not be used on skbs with managed frag references.
+ *
+ * Return: True if all references were acquired, otherwise false.
+ */
+static inline __must_check bool skb_frags_try_ref(struct sk_buff *skb)
+{
+ int i;
+
+ DEBUG_NET_WARN_ON_ONCE(skb_zcopy_managed(skb));
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ if (unlikely(!skb_frag_try_ref(skb, i))) {
+ while (i--)
+ skb_frag_unref(skb, i);
+ return false;
+ }
+ }
+ return true;
+}
+
#endif /* _LINUX_SKBUFF_REF_H */
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 8da53d5b76fc2..1d44e9a4152e5 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2254,7 +2254,13 @@ struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom,
n->pp_recycle = skb->pp_recycle;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
- skb_frag_ref(n, i);
+ if (!skb_frag_try_ref(n, i)) {
+ /* Drop the references acquired so far. */
+ skb_shinfo(n)->nr_frags = i;
+ kfree_skb(n);
+ n = NULL;
+ goto out;
+ }
}
skb_shinfo(n)->nr_frags = i;
skb_shinfo(n)->flags |= skb_shinfo(skb)->flags & SKBFL_SHARED_FRAG;
@@ -2298,7 +2304,6 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
unsigned int size = osize + nhead + ntail;
long off;
u8 *data;
- int i;
if (unlikely(nhead < 0)) {
DEBUG_NET_WARN_ON_ONCE(1);
@@ -2335,12 +2340,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
* be since all we did is relocate the values
*/
if (skb_cloned(skb)) {
- if (skb_orphan_frags(skb, gfp_mask))
+ if (skb_orphan_frags(skb, gfp_mask) || !skb_frags_try_ref(skb))
goto nofrags;
if (skb_zcopy(skb))
net_zcopy_get(skb_uarg(skb));
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- skb_frag_ref(skb, i);
if (skb_has_frag_list(skb))
skb_clone_fraglist(skb);
@@ -3896,7 +3899,8 @@ EXPORT_SYMBOL_GPL(skb_zerocopy_headlen);
*
* Return value:
* 0: everything is OK
- * -ENOMEM: couldn't orphan frags of @from due to lack of memory
+ * -ENOMEM: couldn't orphan frags of @from due to lack of memory, or
+ * couldn't take additional references to the frags of @from
* -EFAULT: skb_copy_bits() found some problem with skb geometry, or readable head
* payload would be mixed with unreadable frags.
*/
@@ -3961,7 +3965,11 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen)
len);
skb_frag_size_set(&skb_shinfo(to)->frags[j], size);
len -= size;
- skb_frag_ref(to, j);
+ if (!skb_frag_try_ref(to, j)) {
+ /* The caller frees @to, dropping the acquired refs. */
+ skb_shinfo(to)->nr_frags = j;
+ return -ENOMEM;
+ }
j++;
}
skb_shinfo(to)->nr_frags = j;
@@ -6966,7 +6974,6 @@ static void skb_carve_reset_headers(struct sk_buff *skb)
static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
const int headlen, gfp_t gfp_mask)
{
- int i;
unsigned int size = skb_end_offset(skb);
int new_hlen = headlen - off;
u8 *data;
@@ -6994,14 +7001,12 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
frags[skb_shinfo(skb)->nr_frags]));
if (skb_cloned(skb)) {
/* drop the old head gracefully */
- if (skb_orphan_frags(skb, gfp_mask)) {
+ if (skb_orphan_frags(skb, gfp_mask) || !skb_frags_try_ref(skb)) {
skb_kfree_head(data);
return -ENOMEM;
}
if (skb_zcopy(skb))
net_zcopy_get(skb_uarg(skb));
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- skb_frag_ref(skb, i);
if (skb_has_frag_list(skb))
skb_clone_fraglist(skb);
skb_release_data(skb, SKB_CONSUMED);
@@ -7131,7 +7136,13 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
skb_frag_off_add(&shinfo->frags[0], off - pos);
skb_frag_size_sub(&shinfo->frags[0], off - pos);
}
- skb_frag_ref(skb, i);
+ if (!skb_frag_try_ref(skb, i)) {
+ /* We referenced frags i - k ... i - 1 so far. */
+ while (k--)
+ skb_frag_unref(skb, --i);
+ skb_kfree_head(data);
+ return -ENOMEM;
+ }
k++;
}
pos += fsize;
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH v3 6/9] RFC: net: limit folio refcount when copying skb frag references
2026-11-10 20:35 ` [PATCH v3 6/9] RFC: net: limit folio refcount when copying skb frag references David Hildenbrand (Arm)
@ 2026-11-12 16:05 ` Pedro Falcato
0 siblings, 0 replies; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 16:05 UTC (permalink / raw)
To: David Hildenbrand
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On Tue, Nov 10, 2026 at 09:35:46PM +0100, David Hildenbrand (Arm) wrote:
> Various networking features copy frag references of an skb, for example
> when unsharing a cloned skb (AF_PACKET trimming a clone), when copying an
> skb (nft dup) or when building NFQUEUE messages. This is an effective
> way for multiplying folio references held by an skb, allowing for
> exceeding folio refcount limits.
>
> This is not specific to large pagecache folios, but with them it is a
> lot easier to reach the refcount limit, as page table mappings can cheaply
> create many references.
>
> Let's introduce skb_frag_try_ref() and skb_frags_try_ref(), which use
> try_get_page(), and use them in pskb_expand_head(), __pskb_copy_fclone(),
> skb_zerocopy() and pskb_carve_inside_*(). All of these can already fail,
> and their callers handle that.
>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Pedro Falcato <pfalcato@suse.de>
For what it's worth, I tried thinking up a way to avoid all of this stuff.
I know there are severe recursion limits that (should) avoid a large copy
chain, but the issue is that, say you have a copy chain limit of 8, you can
still create 2^31/8 skbs (each with their own reference), then clone each 8
times such that you get an overflow. Annoying.
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 7/9] RFC: net: limit folio refcount when rearranging skb frags
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
` (5 preceding siblings ...)
2026-11-10 20:35 ` [PATCH v3 6/9] RFC: net: limit folio refcount when copying skb frag references David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-12 16:08 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 8/9] RFC: net: limit folio refcount when splicing pages into skbs David Hildenbrand (Arm)
` (3 subsequent siblings)
10 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, stable
Various networking features take additional references to frag pages
that are still referenced elsewhere when rearranging skb frags, for
example software GSO segmentation, TCP receive coalescing of clones, SACK
shifting and MTU probing. This is an effective way for multiplying folio
references held by an skb, allowing for exceeding folio refcount limits.
This is not specific to large pagecache folios, but with them it is a
lot easier to reach the refcount limit, as page table mappings can cheaply
create many references.
Let's use skb_frag_try_ref() and skb_frags_try_ref() in skb_segment(),
skb_try_coalesce(), skb_shift() and tcp_clone_payload(). All of these can
already fail, and their callers handle that.
skb_split() can still add one reference when splitting a frag. But each
split also allocates a new skb that is charged to the socket, reaching
the refcount limit that way would require a lot of socket memory, and the
references go away once the data was acked, so it's not that relevant in
practice. Making skb_split() fail is more involved, so let's leave it alone
for now.
Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
net/core/skbuff.c | 32 +++++++++++++++++++++-----------
net/ipv4/tcp_output.c | 8 +++++---
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 1d44e9a4152e5..f473c74956b8b 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4326,7 +4326,8 @@ static int skb_prepare_for_shift(struct sk_buff *skb)
* the length of the skb, from skb to tgt. Returns number bytes shifted.
* It's up to caller to free skb if everything was shifted.
*
- * If @tgt runs out of frags, the whole operation is aborted.
+ * If @tgt runs out of frags, or we cannot take an additional frag reference,
+ * the whole operation is aborted.
*
* Skb cannot include anything else but paged data while tgt is allowed
* to have non-paged data as well.
@@ -4409,7 +4410,9 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen)
} else {
skb_frag_page_copy(fragto, fragfrom);
- skb_frag_ref(tgt, to);
+ /* Nothing has been committed yet. */
+ if (!skb_frag_try_ref(tgt, to))
+ return 0;
skb_frag_off_copy(fragto, fragfrom);
skb_frag_size_set(fragto, todo);
@@ -5148,7 +5151,10 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
if (!skb_shinfo(nskb)->nr_frags)
nskb->pp_recycle = frag_skb->pp_recycle;
*nskb_frag = (i < 0) ? skb_head_frag_to_page_desc(frag_skb) : *frag;
- skb_frag_ref(nskb, skb_shinfo(nskb)->nr_frags);
+ if (!skb_frag_try_ref(nskb, skb_shinfo(nskb)->nr_frags)) {
+ err = -ENOMEM;
+ goto err;
+ }
size = skb_frag_size(nskb_frag);
if (pos < offset) {
@@ -6289,7 +6295,8 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
bool *fragstolen, int *delta_truesize)
{
struct skb_shared_info *to_shinfo, *from_shinfo;
- int i, delta, len = from->len;
+ int delta, len = from->len;
+ bool cloned;
*fragstolen = false;
@@ -6362,6 +6369,15 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
WARN_ON_ONCE(delta < len);
+ /*
+ * A clone keeps referencing its frags, so we need our own references.
+ * Nothing was added to @to yet: we only steal the head of @from above
+ * if it is not locked, and the head of a clone is always locked.
+ */
+ cloned = skb_cloned(from);
+ if (cloned && !skb_frags_try_ref(from))
+ return false;
+
memcpy(to_shinfo->frags + to_shinfo->nr_frags,
from_shinfo->frags,
from_shinfo->nr_frags * sizeof(skb_frag_t));
@@ -6369,15 +6385,9 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
if (from_shinfo->nr_frags)
to_shinfo->flags |= from_shinfo->flags & SKBFL_SHARED_FRAG;
- if (!skb_cloned(from))
+ if (!cloned)
from_shinfo->nr_frags = 0;
- /* if the skb is not cloned this does nothing
- * since we set nr_frags to 0.
- */
- for (i = 0; i < from_shinfo->nr_frags; i++)
- skb_frag_ref(from, i);
-
to->truesize += delta;
to->len += len;
to->data_len += len;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index cf3ddc055d48d..043838d996404 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2710,10 +2710,12 @@ static int tcp_clone_payload(struct sock *sk, struct sk_buff *to,
}
commit:
WARN_ON_ONCE(len != probe_size);
- for (i = 0; i < nr_frags; i++)
- skb_frag_ref(to, i);
-
skb_shinfo(to)->nr_frags = nr_frags;
+ if (!skb_frags_try_ref(to)) {
+ skb_shinfo(to)->nr_frags = 0;
+ return -ENOMEM;
+ }
+
to->truesize += probe_size;
to->len += probe_size;
to->data_len += probe_size;
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH v3 7/9] RFC: net: limit folio refcount when rearranging skb frags
2026-11-10 20:35 ` [PATCH v3 7/9] RFC: net: limit folio refcount when rearranging skb frags David Hildenbrand (Arm)
@ 2026-11-12 16:08 ` Pedro Falcato
0 siblings, 0 replies; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 16:08 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On Tue, Nov 10, 2026 at 09:35:47PM +0100, David Hildenbrand (Arm) wrote:
> Various networking features take additional references to frag pages
> that are still referenced elsewhere when rearranging skb frags, for
> example software GSO segmentation, TCP receive coalescing of clones, SACK
> shifting and MTU probing. This is an effective way for multiplying folio
> references held by an skb, allowing for exceeding folio refcount limits.
>
> This is not specific to large pagecache folios, but with them it is a
> lot easier to reach the refcount limit, as page table mappings can cheaply
> create many references.
>
> Let's use skb_frag_try_ref() and skb_frags_try_ref() in skb_segment(),
> skb_try_coalesce(), skb_shift() and tcp_clone_payload(). All of these can
> already fail, and their callers handle that.
>
> skb_split() can still add one reference when splitting a frag. But each
> split also allocates a new skb that is charged to the socket, reaching
> the refcount limit that way would require a lot of socket memory, and the
> references go away once the data was acked, so it's not that relevant in
> practice. Making skb_split() fail is more involved, so let's leave it alone
> for now.
>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Pedro Falcato <pfalcato@suse.de>
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 8/9] RFC: net: limit folio refcount when splicing pages into skbs
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
` (6 preceding siblings ...)
2026-11-10 20:35 ` [PATCH v3 7/9] RFC: net: limit folio refcount when rearranging skb frags David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-12 16:09 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 9/9] RFC: net: limit folio refcount when splicing from sockets David Hildenbrand (Arm)
` (2 subsequent siblings)
10 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, stable
Various networking features use MSG_SPLICE_PAGES to add pages to skbs,
whereby skb_append_pagefrags() takes a page reference for each fragment it
adds. Pages spliced from pipes were already checked when entering the
pipe, but in-kernel users like sunrpc pass pagecache pages directly. This
allows for exceeding folio refcount limits.
This is not specific to large pagecache folios, but with them it is a
lot easier to reach the refcount limit, as page table mappings can cheaply
create many references.
Let's use try_get_page() in skb_append_pagefrags() and fail with -EFAULT.
Don't use -EMSGSIZE, as TCP would just retry with a new skb. The only
caller, skb_splice_from_iter(), handles that.
Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
net/core/skbuff.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index f473c74956b8b..5e3f910eeea0b 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4702,8 +4702,13 @@ int skb_append_pagefrags(struct sk_buff *skb, struct page *page,
if (skb_can_coalesce(skb, i, page, offset)) {
skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], size);
} else if (i < max_frags) {
+ /*
+ * Don't use -EMSGSIZE, as callers would just retry with a new
+ * skb.
+ */
+ if (!try_get_page(page))
+ return -EFAULT;
skb_zcopy_downgrade_managed(skb);
- get_page(page);
skb_fill_page_desc_noacc(skb, i, page, offset, size);
} else {
return -EMSGSIZE;
@@ -7575,8 +7580,9 @@ static void skb_splice_csum_page(struct sk_buff *skb, struct page *page,
* possible, copying them to fragments if not possible (such as if they're slab
* pages).
*
- * Returns the amount of data spliced/copied or -EMSGSIZE if there's
- * insufficient space in the buffer to transfer anything.
+ * Returns the amount of data spliced/copied, -EMSGSIZE if there's
+ * insufficient space in the buffer to transfer anything, or -EFAULT if we
+ * could not take a page reference.
*/
ssize_t skb_splice_from_iter(struct sk_buff *skb, struct iov_iter *iter,
ssize_t maxsize)
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH v3 8/9] RFC: net: limit folio refcount when splicing pages into skbs
2026-11-10 20:35 ` [PATCH v3 8/9] RFC: net: limit folio refcount when splicing pages into skbs David Hildenbrand (Arm)
@ 2026-11-12 16:09 ` Pedro Falcato
0 siblings, 0 replies; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 16:09 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On Tue, Nov 10, 2026 at 09:35:48PM +0100, David Hildenbrand (Arm) wrote:
> Various networking features use MSG_SPLICE_PAGES to add pages to skbs,
> whereby skb_append_pagefrags() takes a page reference for each fragment it
> adds. Pages spliced from pipes were already checked when entering the
> pipe, but in-kernel users like sunrpc pass pagecache pages directly. This
> allows for exceeding folio refcount limits.
>
> This is not specific to large pagecache folios, but with them it is a
> lot easier to reach the refcount limit, as page table mappings can cheaply
> create many references.
>
> Let's use try_get_page() in skb_append_pagefrags() and fail with -EFAULT.
> Don't use -EMSGSIZE, as TCP would just retry with a new skb. The only
> caller, skb_splice_from_iter(), handles that.
>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Pedro Falcato <pfalcato@suse.de>
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 9/9] RFC: net: limit folio refcount when splicing from sockets
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
` (7 preceding siblings ...)
2026-11-10 20:35 ` [PATCH v3 8/9] RFC: net: limit folio refcount when splicing pages into skbs David Hildenbrand (Arm)
@ 2026-11-10 20:35 ` David Hildenbrand (Arm)
2026-11-12 16:10 ` Pedro Falcato
2026-11-11 21:37 ` [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
2026-11-12 9:39 ` (subset) " David Hildenbrand
10 siblings, 1 reply; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-10 20:35 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: David Hildenbrand (Arm), linux-mm, linux-kernel, linux-fsdevel,
fuse-devel, linux-arch, iommu, netdev, stable
Various networking features support splicing data from sockets into
pipes (TCP, AF_UNIX, KCM, kTLS), whereby spd_fill_page() takes an
additional page reference for each fragment, while the skb keeps its own
reference until it was fully consumed. This allows for exceeding folio
refcount limits.
This is not specific to large pagecache folios, but with them it is a
lot easier to reach the refcount limit, as page table mappings can cheaply
create many references.
Let's use try_get_page() in spd_fill_page() and stop splicing with
-EFAULT if that fails. To propagate the error, let spd_fill_page(),
__splice_segment() and __skb_splice_bits() return an int instead of a
bool. If we could not splice anything, return the error from
skb_splice_bits(): returning 0 would make the callers (TCP, AF_UNIX, KCM,
kTLS) report EOF, although data is still queued. The callers already
handle errors.
Use -EFAULT, consistent with splice_folio_into_pipe().
Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
Cc: stable@vger.kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
net/core/skbuff.c | 90 +++++++++++++++++++++++++++++++------------------------
1 file changed, 51 insertions(+), 39 deletions(-)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 5e3f910eeea0b..a5965f8fa1b00 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3150,11 +3150,13 @@ static bool spd_can_coalesce(const struct splice_pipe_desc *spd,
}
/*
- * Fill page/offset/length into spd, if it can hold more pages.
+ * Fill page/offset/length into spd, if it can hold more pages and we can
+ * take a page reference. Returns non-zero to stop, or a negative error
+ * if we cannot take a page reference.
*/
-static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
- unsigned int *len, unsigned int offset, bool linear,
- struct sock *sk)
+static int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
+ unsigned int *len, unsigned int offset, bool linear,
+ struct sock *sk)
{
if (unlikely(spd->nr_pages == MAX_SKB_FRAGS))
return true;
@@ -3168,7 +3170,8 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
spd->partial[spd->nr_pages - 1].len += *len;
return false;
}
- get_page(page);
+ if (!try_get_page(page))
+ return -EFAULT;
spd->pages[spd->nr_pages] = page;
spd->partial[spd->nr_pages].len = *len;
spd->partial[spd->nr_pages].offset = offset;
@@ -3177,12 +3180,14 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
return false;
}
-static bool __splice_segment(struct page *page, unsigned int poff,
- unsigned int plen, unsigned int *off,
- unsigned int *len,
- struct splice_pipe_desc *spd, bool linear,
- struct sock *sk)
+static int __splice_segment(struct page *page, unsigned int poff,
+ unsigned int plen, unsigned int *off,
+ unsigned int *len,
+ struct splice_pipe_desc *spd, bool linear,
+ struct sock *sk)
{
+ int ret;
+
if (!*len)
return true;
@@ -3200,8 +3205,9 @@ static bool __splice_segment(struct page *page, unsigned int poff,
do {
unsigned int flen = min(*len, plen);
- if (spd_fill_page(spd, page, &flen, poff, linear, sk))
- return true;
+ ret = spd_fill_page(spd, page, &flen, poff, linear, sk);
+ if (ret)
+ return ret;
poff += flen;
plen -= flen;
*len -= flen;
@@ -3213,28 +3219,30 @@ static bool __splice_segment(struct page *page, unsigned int poff,
}
/*
- * Map linear and fragment data from the skb to spd. It reports true if the
- * pipe is full or if we already spliced the requested length.
+ * Map linear and fragment data from the skb to spd. It returns non-zero to
+ * stop: true if the pipe is full or if we already spliced the requested
+ * length, or a negative error if we cannot take a page reference.
*/
-static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
- unsigned int *offset, unsigned int *len,
- struct splice_pipe_desc *spd, struct sock *sk)
+static int __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
+ unsigned int *offset, unsigned int *len,
+ struct splice_pipe_desc *spd, struct sock *sk)
{
struct sk_buff *iter;
- int seg;
+ int seg, ret;
/* map the linear part :
* If skb->head_frag is set, this 'linear' part is backed by a
* fragment, and if the head is not shared with any clones then
* we can avoid a copy since we own the head portion of this page.
*/
- if (__splice_segment(virt_to_page(skb->data),
- (unsigned long) skb->data & (PAGE_SIZE - 1),
- skb_headlen(skb),
- offset, len, spd,
- skb_head_is_locked(skb),
- sk))
- return true;
+ ret = __splice_segment(virt_to_page(skb->data),
+ (unsigned long)skb->data & (PAGE_SIZE - 1),
+ skb_headlen(skb),
+ offset, len, spd,
+ skb_head_is_locked(skb),
+ sk);
+ if (ret)
+ return ret;
/*
* then map the fragments
@@ -3248,10 +3256,11 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
if (WARN_ON_ONCE(!skb_frag_page(f)))
return false;
- if (__splice_segment(skb_frag_page(f),
- skb_frag_off(f), skb_frag_size(f),
- offset, len, spd, false, sk))
- return true;
+ ret = __splice_segment(skb_frag_page(f),
+ skb_frag_off(f), skb_frag_size(f),
+ offset, len, spd, false, sk);
+ if (ret)
+ return ret;
}
skb_walk_frags(skb, iter) {
@@ -3260,11 +3269,12 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
continue;
}
/* __skb_splice_bits() only fails if the output has no room
- * left, so no point in going over the frag_list for the error
- * case.
+ * left or on error, so no point in going over the frag_list
+ * for the error case.
*/
- if (__skb_splice_bits(iter, pipe, offset, len, spd, sk))
- return true;
+ ret = __skb_splice_bits(iter, pipe, offset, len, spd, sk);
+ if (ret)
+ return ret;
}
return false;
@@ -3272,7 +3282,9 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
/*
* Map data from the skb to a pipe. Should handle both the linear part,
- * the fragments, and the frag list.
+ * the fragments, and the frag list. Returns the number of bytes spliced,
+ * or an error if we could not splice anything because we could not take
+ * a page reference.
*/
int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
struct pipe_inode_info *pipe, unsigned int tlen,
@@ -3287,14 +3299,14 @@ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
.ops = &nosteal_pipe_buf_ops,
.spd_release = sock_spd_release,
};
- int ret = 0;
+ int ret;
- __skb_splice_bits(skb, pipe, &offset, &tlen, &spd, sk);
+ ret = __skb_splice_bits(skb, pipe, &offset, &tlen, &spd, sk);
if (spd.nr_pages)
- ret = splice_to_pipe(pipe, &spd);
-
- return ret;
+ return splice_to_pipe(pipe, &spd);
+ /* Returning 0 would make callers report EOF. */
+ return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL_GPL(skb_splice_bits);
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread* Re: [PATCH v3 9/9] RFC: net: limit folio refcount when splicing from sockets
2026-11-10 20:35 ` [PATCH v3 9/9] RFC: net: limit folio refcount when splicing from sockets David Hildenbrand (Arm)
@ 2026-11-12 16:10 ` Pedro Falcato
0 siblings, 0 replies; 31+ messages in thread
From: Pedro Falcato @ 2026-11-12 16:10 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, linux-mm, linux-kernel, linux-fsdevel, fuse-devel,
linux-arch, iommu, netdev, stable
On Tue, Nov 10, 2026 at 09:35:49PM +0100, David Hildenbrand (Arm) wrote:
> Various networking features support splicing data from sockets into
> pipes (TCP, AF_UNIX, KCM, kTLS), whereby spd_fill_page() takes an
> additional page reference for each fragment, while the skb keeps its own
> reference until it was fully consumed. This allows for exceeding folio
> refcount limits.
>
> This is not specific to large pagecache folios, but with them it is a
> lot easier to reach the refcount limit, as page table mappings can cheaply
> create many references.
>
> Let's use try_get_page() in spd_fill_page() and stop splicing with
> -EFAULT if that fails. To propagate the error, let spd_fill_page(),
> __splice_segment() and __skb_splice_bits() return an int instead of a
> bool. If we could not splice anything, return the error from
> skb_splice_bits(): returning 0 would make the callers (TCP, AF_UNIX, KCM,
> kTLS) report EOF, although data is still queued. The callers already
> handle errors.
>
> Use -EFAULT, consistent with splice_folio_into_pipe().
>
> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Pedro Falcato <pfalcato@suse.de>
--
Pedro
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v3 0/9] mm: limit pagecache folio refcount
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
` (8 preceding siblings ...)
2026-11-10 20:35 ` [PATCH v3 9/9] RFC: net: limit folio refcount when splicing from sockets David Hildenbrand (Arm)
@ 2026-11-11 21:37 ` David Hildenbrand (Arm)
2026-11-12 9:39 ` (subset) " David Hildenbrand
10 siblings, 0 replies; 31+ messages in thread
From: David Hildenbrand (Arm) @ 2026-11-11 21:37 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds
Cc: linux-mm, linux-kernel, linux-fsdevel, fuse-devel, linux-arch,
iommu, netdev, Chris Mason, stable
On 11/10/26 21:35, David Hildenbrand (Arm) wrote:
> Note: I really would prefer doing something else right now, but this is
> rather urgent.
>
As this is really something I want to have fixed rather sooner than later, I'll
throw the first 5 patches into -next tomorrow.
In the meantime, I'd appreciate some feedback from -net folks, as that must be
sorted out as well.
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread* Re: (subset) [PATCH v3 0/9] mm: limit pagecache folio refcount
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
` (9 preceding siblings ...)
2026-11-11 21:37 ` [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
@ 2026-11-12 9:39 ` David Hildenbrand
10 siblings, 0 replies; 31+ messages in thread
From: David Hildenbrand @ 2026-11-12 9:39 UTC (permalink / raw)
To: Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Matthew Wilcox (Oracle),
Jan Kara, Andrew Morton, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Kiryl Shutsemau, Matthew Brost, Joshua Hahn, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Peter Xu,
Johannes Weiner, Song Liu, Rik van Riel, Miklos Szeredi,
Alexander Viro, Christian Brauner, Hugh Dickins, Will Deacon,
Aneesh Kumar K.V, Nick Piggin, Peter Zijlstra, Arnd Bergmann,
Jason Gunthorpe, Kevin Tian, Joerg Roedel (AMD), Robin Murphy,
Steve Sistare, John Hubbard, Jose A. Perez de Azpillaga,
Josef Bacik, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Neal Cardwell, Kuniyuki Iwashima,
Linus Torvalds, David Hildenbrand (Arm)
Cc: linux-mm, linux-kernel, linux-fsdevel, fuse-devel, linux-arch,
iommu, netdev, Chris Mason, stable, Jason Gunthorpe
On Tue, 10 Nov 2026 21:35:40 +0100, David Hildenbrand (Arm) wrote:
> Note: I really would prefer doing something else right now, but this is
> rather urgent.
>
> Users can create a lot of folio references to pagecache folios, making
> it possible to overflow the refcount of large folios.
>
> While we knew that one could in theory overflow a folio's refcount, we
> mostly assumed that bad things would happen and it wouldn't go
> unnoticed.
>
> [...]
Applied, thanks!
tree: mm/core.git
branch: for-next-fixes
Applied patches will appear soon in mm-next, followed by linux-next.
Please report any outstanding bugs that were missed during review, so we
can either drop the patch series or decide how to fix it up.
It is encouraged to provide Acked-bys, Reviewed-bys, and Tested-bys even
though the patches have already been applied. If possible, patch trailers
will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. Further, this is not a guarantee that the
patches will go upstream. If in doubt, please check the listed branch.
[1/9] mm: limit pagecache folio refcount when mapping pages
https://git.kernel.org/mm/core/c/61351dc9
[2/9] mm: limit pagecache folio refcount when splicing into pipes
https://git.kernel.org/mm/core/c/0ff9a420
[3/9] mm/mmu_gather: flush pending references early before the folio limit
https://git.kernel.org/mm/core/c/bfbf74ba
[4/9] iommufd: fix cleanup when adding memfd folio pins fails
https://git.kernel.org/mm/core/c/6d0d782f
[5/9] mm/gup: limit pagecache folio refcount when grabbing references
https://git.kernel.org/mm/core/c/6d3ff38d
--
Cheers,
David
^ permalink raw reply [flat|nested] 31+ messages in thread