diff options
| author | Mark Brown <broonie@kernel.org> | 2026-11-13 13:24:56 +0000 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-11-13 13:24:56 +0000 |
| commit | e07a73680dfe9877a1cfae7df3f28614f62766ec (patch) | |
| tree | b97537018c13bc2806d522086f0da286842e869a /include/linux | |
| parent | 7e1392a42ea288d70a266f6553fe06f57f8647ea (diff) | |
| parent | 3d91660a7bacb14d6837b796bd8357b2fa1d9328 (diff) | |
| download | linux-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.h | 15 | ||||
| -rw-r--r-- | include/linux/maple_tree.h | 48 | ||||
| -rw-r--r-- | include/linux/mm.h | 112 | ||||
| -rw-r--r-- | include/linux/pgtable.h | 24 | ||||
| -rw-r--r-- | include/linux/userfaultfd_k.h | 5 |
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; |
