summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-11-13 13:24:56 +0000
committerMark Brown <broonie@kernel.org>2026-11-13 13:24:56 +0000
commite07a73680dfe9877a1cfae7df3f28614f62766ec (patch)
treeb97537018c13bc2806d522086f0da286842e869a /include/linux
parent7e1392a42ea288d70a266f6553fe06f57f8647ea (diff)
parent3d91660a7bacb14d6837b796bd8357b2fa1d9328 (diff)
downloadlinux-next-e07a73680dfe9877a1cfae7df3f28614f62766ec.tar.gz
linux-next-e07a73680dfe9877a1cfae7df3f28614f62766ec.zip
Merge branch 'for-next-fixes' of https://git.kernel.org/pub/scm/linux/kernel/git/mm/linux.git
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/kasan.h15
-rw-r--r--include/linux/maple_tree.h48
-rw-r--r--include/linux/mm.h112
-rw-r--r--include/linux/pgtable.h24
-rw-r--r--include/linux/userfaultfd_k.h5
5 files changed, 143 insertions, 61 deletions
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index bf233bde68c7..c1ac7971601e 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -180,10 +180,12 @@ static __always_inline void * __must_check kasan_init_slab_obj(
}
bool __kasan_slab_pre_free(struct kmem_cache *s, void *object,
- unsigned long ip);
+ unsigned long ip, bool after_rcu_delay);
/**
* kasan_slab_pre_free - Check whether freeing a slab object is safe.
+ * @s: Cache the object belongs to.
* @object: Object to be freed.
+ * @after_rcu_delay: Whether the free follows an RCU grace period.
*
* This function checks whether freeing the given object is safe. It may
* check for double-free and invalid-free bugs and report them.
@@ -193,10 +195,12 @@ bool __kasan_slab_pre_free(struct kmem_cache *s, void *object,
* @Return true if freeing the object is unsafe; false otherwise.
*/
static __always_inline bool kasan_slab_pre_free(struct kmem_cache *s,
- void *object)
+ void *object,
+ bool after_rcu_delay)
{
if (kasan_enabled())
- return __kasan_slab_pre_free(s, object, _RET_IP_);
+ return __kasan_slab_pre_free(s, object, _RET_IP_,
+ after_rcu_delay);
return false;
}
@@ -424,7 +428,8 @@ static inline void *kasan_init_slab_obj(struct kmem_cache *cache,
return (void *)object;
}
-static inline bool kasan_slab_pre_free(struct kmem_cache *s, void *object)
+static inline bool kasan_slab_pre_free(struct kmem_cache *s, void *object,
+ bool after_rcu_delay)
{
return false;
}
@@ -495,6 +500,7 @@ void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,
void kasan_cache_shrink(struct kmem_cache *cache);
void kasan_cache_shutdown(struct kmem_cache *cache);
void kasan_record_aux_stack(void *ptr);
+void kasan_mark_rcu_free_pending(void *ptr);
#else /* CONFIG_KASAN_GENERIC */
@@ -512,6 +518,7 @@ static inline void kasan_cache_create(struct kmem_cache *cache,
static inline void kasan_cache_shrink(struct kmem_cache *cache) {}
static inline void kasan_cache_shutdown(struct kmem_cache *cache) {}
static inline void kasan_record_aux_stack(void *ptr) {}
+static inline void kasan_mark_rcu_free_pending(void *ptr) {}
#endif /* CONFIG_KASAN_GENERIC */
diff --git a/include/linux/maple_tree.h b/include/linux/maple_tree.h
index 691f6012daea..3892201d7efd 100644
--- a/include/linux/maple_tree.h
+++ b/include/linux/maple_tree.h
@@ -684,7 +684,7 @@ void mt_cache_shrink(void);
pr_info("Pass: %u Run:%u\n", \
atomic_read(&maple_tree_tests_passed), \
atomic_read(&maple_tree_tests_run)); \
- dump_stack(); \
+ BUG_ON(1); \
} else { \
atomic_inc(&maple_tree_tests_passed); \
} \
@@ -700,7 +700,7 @@ void mt_cache_shrink(void);
pr_info("Pass: %u Run:%u\n", \
atomic_read(&maple_tree_tests_passed), \
atomic_read(&maple_tree_tests_run)); \
- dump_stack(); \
+ BUG_ON(1); \
} else { \
atomic_inc(&maple_tree_tests_passed); \
} \
@@ -717,7 +717,7 @@ void mt_cache_shrink(void);
pr_info("Pass: %u Run:%u\n", \
atomic_read(&maple_tree_tests_passed), \
atomic_read(&maple_tree_tests_run)); \
- dump_stack(); \
+ BUG_ON(1); \
} else { \
atomic_inc(&maple_tree_tests_passed); \
} \
@@ -726,16 +726,16 @@ void mt_cache_shrink(void);
#define MT_WARN_ON(__tree, __x) ({ \
int ret = !!(__x); \
atomic_inc(&maple_tree_tests_run); \
- if (ret) { \
- pr_info("WARN at %s:%d (%u)\n", \
- __func__, __LINE__, __x); \
+ if (!ret) { \
+ atomic_inc(&maple_tree_tests_passed); \
+ } else if (__ONCE_LITE_IF(ret)) { \
+ pr_info("WARN at %s:%d\n", \
+ __func__, __LINE__); \
mt_dump(__tree, mt_dump_hex); \
pr_info("Pass: %u Run:%u\n", \
atomic_read(&maple_tree_tests_passed), \
atomic_read(&maple_tree_tests_run)); \
- dump_stack(); \
- } else { \
- atomic_inc(&maple_tree_tests_passed); \
+ WARN_ON(1); \
} \
unlikely(ret); \
})
@@ -743,17 +743,17 @@ void mt_cache_shrink(void);
#define MAS_WARN_ON(__mas, __x) ({ \
int ret = !!(__x); \
atomic_inc(&maple_tree_tests_run); \
- if (ret) { \
- pr_info("WARN at %s:%d (%u)\n", \
- __func__, __LINE__, __x); \
+ if (!ret) { \
+ atomic_inc(&maple_tree_tests_passed); \
+ } else if (__ONCE_LITE_IF(ret)) { \
+ pr_info("WARN at %s:%d\n", \
+ __func__, __LINE__); \
mas_dump(__mas); \
mt_dump((__mas)->tree, mt_dump_hex); \
pr_info("Pass: %u Run:%u\n", \
atomic_read(&maple_tree_tests_passed), \
atomic_read(&maple_tree_tests_run)); \
- dump_stack(); \
- } else { \
- atomic_inc(&maple_tree_tests_passed); \
+ WARN_ON(1); \
} \
unlikely(ret); \
})
@@ -761,18 +761,18 @@ void mt_cache_shrink(void);
#define MAS_WR_WARN_ON(__wrmas, __x) ({ \
int ret = !!(__x); \
atomic_inc(&maple_tree_tests_run); \
- if (ret) { \
- pr_info("WARN at %s:%d (%u)\n", \
- __func__, __LINE__, __x); \
+ if (!ret) { \
+ atomic_inc(&maple_tree_tests_passed); \
+ } else if (__ONCE_LITE_IF(ret)) { \
+ pr_info("WARN at %s:%d\n", \
+ __func__, __LINE__); \
mas_wr_dump(__wrmas); \
mas_dump((__wrmas)->mas); \
mt_dump((__wrmas)->mas->tree, mt_dump_hex); \
pr_info("Pass: %u Run:%u\n", \
atomic_read(&maple_tree_tests_passed), \
atomic_read(&maple_tree_tests_run)); \
- dump_stack(); \
- } else { \
- atomic_inc(&maple_tree_tests_passed); \
+ WARN_ON(1); \
} \
unlikely(ret); \
})
@@ -780,9 +780,9 @@ void mt_cache_shrink(void);
#define MT_BUG_ON(__tree, __x) BUG_ON(__x)
#define MAS_BUG_ON(__mas, __x) BUG_ON(__x)
#define MAS_WR_BUG_ON(__mas, __x) BUG_ON(__x)
-#define MT_WARN_ON(__tree, __x) WARN_ON(__x)
-#define MAS_WARN_ON(__mas, __x) WARN_ON(__x)
-#define MAS_WR_WARN_ON(__mas, __x) WARN_ON(__x)
+#define MT_WARN_ON(__tree, __x) WARN_ON_ONCE(__x)
+#define MAS_WARN_ON(__mas, __x) WARN_ON_ONCE(__x)
+#define MAS_WR_WARN_ON(__mas, __x) WARN_ON_ONCE(__x)
#endif /* CONFIG_DEBUG_MAPLE_TREE */
/**
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 6af3b7008f1a..dce5a35189c8 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2249,9 +2249,40 @@ 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))
+#define FOLIO_REF_COUNT_FLUSH_LIMIT (INT_MAX - (1U << 27))
+
+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.
+ *
+ * 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
+ * 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.
@@ -2263,7 +2294,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);
}
@@ -2277,15 +2308,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;
+ if (unlikely(folio_ref_count_over_limit(folio)))
return false;
- page_ref_inc(page);
+ 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.
@@ -2925,12 +2974,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;
@@ -3971,23 +4016,6 @@ static inline void pagetable_free_kernel(struct ptdesc *pt)
__pagetable_free(pt);
}
#endif
-/**
- * pagetable_free - Free pagetables
- * @pt: The page table descriptor
- *
- * pagetable_free frees the memory of all page tables described by a page
- * table descriptor and the memory for the descriptor itself.
- */
-static inline void pagetable_free(struct ptdesc *pt)
-{
- if (ptdesc_test_kernel(pt)) {
- ptdesc_clear_kernel(pt);
- pagetable_free_kernel(pt);
- } else {
- __pagetable_free(pt);
- }
-}
-
#if defined(CONFIG_SPLIT_PTE_PTLOCKS)
#if ALLOC_SPLIT_PTLOCKS
void __init ptlock_cache_init(void);
@@ -4080,6 +4108,26 @@ static inline void pagetable_dtor(struct ptdesc *ptdesc)
lruvec_stat_sub_folio(folio, NR_PAGETABLE);
}
+/**
+ * pagetable_free - Free pagetables
+ * @pt: The page table descriptor
+ *
+ * pagetable_free frees the memory of all page tables described by a page
+ * table descriptor and the memory for the descriptor itself.
+ */
+static inline void pagetable_free(struct ptdesc *pt)
+{
+ if (PageTable(ptdesc_page(pt)))
+ pagetable_dtor(pt);
+
+ if (ptdesc_test_kernel(pt)) {
+ ptdesc_clear_kernel(pt);
+ pagetable_free_kernel(pt);
+ } else {
+ __pagetable_free(pt);
+ }
+}
+
static inline void pagetable_dtor_free(struct ptdesc *ptdesc)
{
pagetable_dtor(ptdesc);
@@ -4367,9 +4415,7 @@ anon_rmap_tree_iter_first(struct anon_vma *anon_vma,
struct anon_vma_chain *
anon_rmap_tree_iter_next(struct anon_vma_chain *avc,
pgoff_t pgoff_start, pgoff_t pgoff_last);
-#ifdef CONFIG_DEBUG_VM_RB
void anon_rmap_tree_verify(struct anon_vma_chain *avc);
-#endif
#define anon_rmap_tree_foreach(avc, anon_vma, pgoff_start, pgoff_last) \
for (avc = anon_rmap_tree_iter_first(anon_vma, pgoff_start, pgoff_last); \
diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h
index 780fe849ff8b..66d369870a74 100644
--- a/include/linux/pgtable.h
+++ b/include/linux/pgtable.h
@@ -12,6 +12,7 @@
#ifdef CONFIG_MMU
#include <linux/mm_types.h>
+#include <linux/minmax.h>
#include <linux/bug.h>
#include <linux/errno.h>
#include <asm-generic/pgtable_uffd.h>
@@ -1518,6 +1519,29 @@ static inline void arch_swap_restore(swp_entry_t entry, struct folio *folio)
})
#endif
+/**
+ * pte_table_clamped_end() - Limit a range to a single PTE table.
+ * @addr: Start address.
+ * @end: Exclusive end address.
+ *
+ * Only use pmd_addr_end() when traversing page tables, in a pattern like::
+ *
+ * next = pmd_addr_end(addr, end);
+ *
+ * Higher levels already bound the range when PMDs are folded. Use this
+ * helper elsewhere to clamp the range regardless of PMD folding.
+ *
+ * Return: The exclusive end of the PTE table containing @addr, capped at @end.
+ */
+static inline unsigned long pte_table_clamped_end(unsigned long addr,
+ unsigned long end)
+{
+ const unsigned long pmd_end = (addr & PMD_MASK) + PMD_SIZE;
+
+ /* Compare inclusive ends to avoid wraparounds. */
+ return min(pmd_end - 1, end - 1) + 1;
+}
+
/*
* When walking page tables, we usually want to skip any p?d_none entries;
* and any p?d_bad entries - reporting the error before resetting to none.
diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h
index dff9a5c9a0df..5768a3902c7c 100644
--- a/include/linux/userfaultfd_k.h
+++ b/include/linux/userfaultfd_k.h
@@ -277,6 +277,7 @@ extern int userfaultfd_unmap_prep(struct vm_area_struct *vma,
unsigned long start, unsigned long end, struct list_head *uf);
extern void userfaultfd_unmap_complete(struct mm_struct *mm,
struct list_head *uf);
+void userfaultfd_unmap_abort(struct list_head *uf);
extern bool userfaultfd_wp_unpopulated(struct vm_area_struct *vma);
extern bool userfaultfd_wp_async(struct vm_area_struct *vma);
extern bool userfaultfd_rwp_async(struct vm_area_struct *vma);
@@ -443,6 +444,10 @@ static inline void userfaultfd_unmap_complete(struct mm_struct *mm,
{
}
+static inline void userfaultfd_unmap_abort(struct list_head *uf)
+{
+}
+
static inline bool uffd_disable_fault_around(struct vm_area_struct *vma)
{
return false;