FAILED: patch "[PATCH] mm: slub: avoid wake up kswapd in set_track_prepare" failed to apply to 6.1-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x 850470a8413a8a78e772c4f6bd9fe81ec6bd5b0f
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025090635-affluent-reputable-419b@gregkh' --subject-prefix 'PATCH 6.1.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 850470a8413a8a78e772c4f6bd9fe81ec6bd5b0f Mon Sep 17 00:00:00 2001
From: yangshiguang <[email protected]>
Date: Sat, 30 Aug 2025 10:09:46 +0800
Subject: [PATCH] mm: slub: avoid wake up kswapd in set_track_prepare
set_track_prepare() can incur lock recursion.
The issue is that it is called from hrtimer_start_range_ns holding the per_cpu(hrtimer_bases)[n].lock, but when enabled CONFIG_DEBUG_OBJECTS_TIMERS, may wake up kswapd in set_track_prepare, and try to hold the per_cpu(hrtimer_bases)[n].lock.
Avoid deadlock caused by implicitly waking up kswapd by passing in allocation flags, which do not contain __GFP_KSWAPD_RECLAIM in the debug_objects_fill_pool() case. Inside stack depot they are processed by gfp_nested_mask().
Since ___slab_alloc() has preemption disabled, we mask out __GFP_DIRECT_RECLAIM from the flags there.
The oops looks something like:
BUG: spinlock recursion on CPU#3, swapper/3/0 lock: 0xffffff8a4bf29c80, .magic: dead4ead, .owner: swapper/3/0, .owner_cpu: 3 Hardware name: Qualcomm Technologies, Inc. Popsicle based on SM8850 (DT) Call trace: spin_bug+0x0 _raw_spin_lock_irqsave+0x80 hrtimer_try_to_cancel+0x94 task_contending+0x10c enqueue_dl_entity+0x2a4 dl_server_start+0x74 enqueue_task_fair+0x568 enqueue_task+0xac do_activate_task+0x14c ttwu_do_activate+0xcc try_to_wake_up+0x6c8 default_wake_function+0x20 autoremove_wake_function+0x1c __wake_up+0xac wakeup_kswapd+0x19c wake_all_kswapds+0x78 __alloc_pages_slowpath+0x1ac __alloc_pages_noprof+0x298 stack_depot_save_flags+0x6b0 stack_depot_save+0x14 set_track_prepare+0x5c ___slab_alloc+0xccc __kmalloc_cache_noprof+0x470 __set_page_owner+0x2bc post_alloc_hook[jt]+0x1b8 prep_new_page+0x28 get_page_from_freelist+0x1edc __alloc_pages_noprof+0x13c alloc_slab_page+0x244 allocate_slab+0x7c ___slab_alloc+0x8e8 kmem_cache_alloc_noprof+0x450 debug_objects_fill_pool+0x22c debug_object_activate+0x40 enqueue_hrtimer[jt]+0xdc hrtimer_start_range_ns+0x5f8
...
mm/slub.c +20 -10
@@ -962,19 +962,19 @@ static struct track *get_track(struct kmem_cache *s, void *object, }#ifdef CONFIG_STACKDEPOT -static noinline depot_stack_handle_t set_track_prepare(void) +static noinline depot_stack_handle_t set_track_prepare(gfp_t gfp_flags) {depot_stack_handle_t handle;unsigned long entries[TRACK_ADDRS_COUNT ];unsigned int nr_entries; nr_entries =stack_trace_save (entries,ARRAY_SIZE (entries),3 ); - handle =stack_depot_save (entries, nr_entries,GFP_NOWAIT ); + handle =stack_depot_save (entries, nr_entries, gfp_flags);return handle; }#else -static inline depot_stack_handle_t set_track_prepare (void ) +static inline depot_stack_handle_t set_track_prepare (gfp_t gfp_flags) {return 0 ; }
@@ -996,9 +996,9 @@ static void set_track_update(struct kmem_cache *s, void *object, } static __always_inline void set_track(struct kmem_cache *s, void *object, -enum track_item alloc,unsigned long addr) +enum track_item alloc,unsigned long addr,gfp_t gfp_flags) { -depot_stack_handle_t handle =set_track_prepare (); +depot_stack_handle_t handle =set_track_prepare (gfp_flags);set_track_update (s, object, alloc, addr, handle); }
@@ -1926,9 +1926,9 @@ static inline bool free_debug_processing(struct kmem_cache *s,static inline void slab_pad_check (struct kmem_cache *s,struct slab *slab) {}static inline int check_object (struct kmem_cache *s,struct slab *slab,void *object,u8 val) {return 1 ; } -static inline depot_stack_handle_t set_track_prepare (void ) {return 0 ; } +static inline depot_stack_handle_t set_track_prepare (gfp_t gfp_flags) {return 0 ; }static inline void set_track (struct kmem_cache *s,void *object, -enum track_item alloc,unsigned long addr) {} +enum track_item alloc,unsigned long addr,gfp_t gfp_flags) {}static inline void add_full (struct kmem_cache *s,struct kmem_cache_node *n,struct slab *slab) {} static inline void remove_full(struct kmem_cache *s, struct kmem_cache_node *n,
@@ -3881,9 +3881,14 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,* For debug caches here we had to go through * alloc_single_from_partial() so just store the * tracking info and return the object. +* +* Due to disabled preemption we need to disallow +* blocking. The flags are further adjusted by +* gfp_nested_mask() in stack_depot itself. */ if (s->flags &SLAB_STORE_USER ) -set_track (s, freelist,TRACK_ALLOC , addr); +set_track (s, freelist,TRACK_ALLOC , addr, + gfpflags & ~(__GFP_DIRECT_RECLAIM));return freelist; }
@@ -3915,7 +3920,8 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, goto new_objects;if (s->flags &SLAB_STORE_USER ) -set_track (s, freelist,TRACK_ALLOC , addr); +set_track (s, freelist,TRACK_ALLOC , addr, + gfpflags & ~(__GFP_DIRECT_RECLAIM));return freelist; }
@@ -4426,8 +4432,12 @@ static noinline void free_to_partial_list(unsigned long flags;depot_stack_handle_t handle =0 ; +/* +* We cannot use GFP_NOWAIT as there are callsites where waking up +* kswapd could deadlock +*/ if (s->flags &SLAB_STORE_USER ) - handle =set_track_prepare (); + handle =set_track_prepare (__GFP_NOWARN);spin_lock_irqsave (&n->list_lock, flags);
Sasha Levin
maintainerPATCH 1/3 mm, slub: refactor free debug processing
From: Vlastimil Babka <[email protected]>
[ Upstream commit fa9b88e459d710cadf3b01e8a64eda00cc91cdd6 ]
Since commit c7323a5ad078 ("mm/slub: restrict sysfs validation to debug caches and make it safe"), caches with debugging enabled use the free_debug_processing() function to do both freeing checks and actual freeing to partial list under list_lock, bypassing the fast paths.
We will want to use the same path for CONFIG_SLUB_TINY, but without the debugging checks, so refactor the code so that free_debug_processing() does only the checks, while the freeing is handled by a new function free_to_partial_list().
For consistency, change return parameter alloc_debug_processing() from int to bool and correct the !SLUB_DEBUG variant to return true and not false. This didn't matter until now, but will in the following changes.
Stable-dep-of: 850470a8413a ("mm: slub: avoid wake up kswapd in set_track_prepare")
--- mm/slub.c | 154 +++++++++++++++++++++++++++++------------------------- 1 file changed, 83 insertions(+), 71 deletions(-)
mm/slub.c +83 -71
@@ -1363,7 +1363,7 @@ static inline int alloc_consistency_checks(struct kmem_cache *s,return 1 ; } -static noinline int alloc_debug_processing(struct kmem_cache *s, +static noinline bool alloc_debug_processing(struct kmem_cache *s,struct slab *slab,void *object,int orig_size) {if (s->flags &SLAB_CONSISTENCY_CHECKS ) {
@@ -1375,7 +1375,7 @@ static noinline int alloc_debug_processing(struct kmem_cache *s,trace (s, slab, object,1 );set_orig_size (s, object, orig_size);init_object (s, object,SLUB_RED_ACTIVE ); -return 1 ; +return true; bad:if (folio_test_slab (slab_folio (slab))) {
@@ -1388,7 +1388,7 @@ static noinline int alloc_debug_processing(struct kmem_cache *s, slab->inuse = slab->objects; slab->freelist =NULL ; } -return 0 ; +return false; } static inline int free_consistency_checks(struct kmem_cache *s,
@@ -1641,17 +1641,17 @@ static inline void setup_object_debug(struct kmem_cache *s, void *object) {}static inline void setup_slab_debug (struct kmem_cache *s,struct slab *slab,void *addr) {} -static inline int alloc_debug_processing (struct kmem_cache *s, -struct slab *slab,void *object,int orig_size) {return 0 ; } +static inline bool alloc_debug_processing (struct kmem_cache *s, +struct slab *slab,void *object,int orig_size) {return true; } -static inline void free_debug_processing ( -struct kmem_cache *s,struct slab *slab, -void *head,void *tail,int bulk_cnt, -unsigned long addr) {} +static inline bool free_debug_processing (struct kmem_cache *s, +struct slab *slab,void *head,void *tail,int *bulk_cnt, +unsigned long addr,depot_stack_handle_t handle) {return true; }static inline void slab_pad_check (struct kmem_cache *s,struct slab *slab) {}static inline int check_object (struct kmem_cache *s,struct slab *slab,void *object,u8 val) {return 1 ; } +static inline depot_stack_handle_t set_track_prepare (void ) {return 0 ; }static inline void set_track (struct kmem_cache *s,void *object,enum track_item alloc,unsigned long addr) {} static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n,
@@ -2828,38 +2828,28 @@ static inline unsigned long node_nr_objs(struct kmem_cache_node *n) }/* Supports checking bulk free of a constructed freelist */ -static noinline void free_debug_processing( -struct kmem_cache *s,struct slab *slab, -void *head,void *tail,int bulk_cnt, -unsigned long addr) +static inline bool free_debug_processing (struct kmem_cache *s, +struct slab *slab,void *head,void *tail,int *bulk_cnt, +unsigned long addr,depot_stack_handle_t handle) { -struct kmem_cache_node *n =get_node (s,slab_nid (slab)); -struct slab *slab_free =NULL ; +bool checks_ok = false;void *object = head;int cnt =0 ; -unsigned long flags; -bool checks_ok = false; -depot_stack_handle_t handle =0 ; - -if (s->flags &SLAB_STORE_USER ) - handle =set_track_prepare (); - -spin_lock_irqsave (&n->list_lock, flags);if (s->flags &SLAB_CONSISTENCY_CHECKS ) {if (!check_slab (s, slab)) goto out; } -if (slab->inuse < bulk_cnt) { +if (slab->inuse < *bulk_cnt) {slab_err (s, slab,"Slab has %d allocated objects but %d are to be freed\n" , - slab->inuse, bulk_cnt); + slab->inuse, *bulk_cnt); goto out; } next_object: -if (++cnt > bulk_cnt) +if (++cnt > *bulk_cnt) goto out_cnt;if (s->flags &SLAB_CONSISTENCY_CHECKS ) {
@@ -2881,57 +2871,18 @@ static noinline void free_debug_processing( checks_ok = true; out_cnt: -if (cnt != bulk_cnt) +if (cnt != *bulk_cnt) {slab_err (s, slab,"Bulk free expected %d objects but found %d\n" , - bulk_cnt, cnt); - -out: -if (checks_ok) { -void *prior = slab->freelist; - -/* Perform the actual freeing while we still hold the locks */ - slab->inuse -= cnt; -set_freepointer (s, tail, prior); - slab->freelist = head; - -/* -* If the slab is empty, and node's partial list is full, -* it should be discarded anyway no matter it's on full or -* partial list. -*/ -if (slab->inuse ==0 && n->nr_partial >= s->min_partial) - slab_free = slab; - -if (!prior) { -/* was on full list */ -remove_full (s, n, slab); -if (!slab_free) { -add_partial (n, slab,DEACTIVATE_TO_TAIL ); -stat (s,FREE_ADD_PARTIAL ); - } - }else if (slab_free) { -remove_partial (n, slab); -stat (s,FREE_REMOVE_PARTIAL ); - } + *bulk_cnt, cnt); + *bulk_cnt = cnt; } -if (slab_free) { -/* -* Update the counters while still holding n->list_lock to -* prevent spurious validation warnings -*/ -dec_slabs_node (s,slab_nid (slab_free), slab_free->objects); - } - -spin_unlock_irqrestore (&n->list_lock, flags); +out:if (!checks_ok)slab_fix (s,"Object at 0x%p not freed" , object); -if (slab_free) { -stat (s,FREE_SLAB ); -free_slab (s, slab_free); - } + return checks_ok; }#endif /* CONFIG_SLUB_DEBUG */
@@ -3448,6 +3399,67 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node) }EXPORT_SYMBOL (kmem_cache_alloc_node); +static noinline void free_to_partial_list( +struct kmem_cache *s,struct slab *slab, +void *head,void *tail,int bulk_cnt, +unsigned long addr) +{ +struct kmem_cache_node *n =get_node (s,slab_nid (slab)); +struct slab *slab_free =NULL ; +int cnt = bulk_cnt; +unsigned long flags; +depot_stack_handle_t handle =0 ; + +if (s->flags &SLAB_STORE_USER ) + handle =set_track_prepare (); + +spin_lock_irqsave (&n->list_lock, flags); + +if (free_debug_processing (s, slab, head, tail, &cnt, addr, handle)) { +void *prior = slab->freelist; + +/* Perform the actual freeing while we still hold the locks */ + slab->inuse -= cnt; +set_freepointer (s, tail, prior); + slab->freelist = head; + +/* +* If the slab is empty, and node's partial list is full, +* it should be discarded anyway no matter it's on full or +* partial list. +*/ +if (slab->inuse ==0 && n->nr_partial >= s->min_partial) + slab_free = slab; + +if (!prior) { +/* was on full list */ +remove_full (s, n, slab); +if (!slab_free) { +add_partial (n, slab,DEACTIVATE_TO_TAIL ); +stat (s,FREE_ADD_PARTIAL ); + } + }else if (slab_free) { +remove_partial (n, slab); +stat (s,FREE_REMOVE_PARTIAL ); + } + } + +if (slab_free) { +/* +* Update the counters while still holding n->list_lock to +* prevent spurious validation warnings +*/ +dec_slabs_node (s,slab_nid (slab_free), slab_free->objects); + } + +spin_unlock_irqrestore (&n->list_lock, flags); + +if (slab_free) { +stat (s,FREE_SLAB ); +free_slab (s, slab_free); + } +} +/* * Slow path handling. This may still be called frequently since objects * have a longer lifetime than the cpu slabs in most processing loads.
@@ -3474,7 +3486,7 @@ static void __slab_free(struct kmem_cache *s, struct slab *slab,return ;if (kmem_cache_debug (s)) { -free_debug_processing (s, slab, head, tail, cnt, addr); +free_to_partial_list (s, slab, head, tail, cnt, addr);return ; }
Signature · 1 line
2.51.0
Sasha Levin
maintainerPATCH 2/3 slub: Reflow ___slab_alloc()
From: Chengming Zhou <[email protected]>
[ Upstream commit 24c6a097b5a270e05c6e99a99da66b91be81fd7d ]
The get_partial() interface used in ___slab_alloc() may return a single object in the "kmem_cache_debug(s)" case, in which we will just return the "freelist" object.
Move this handling up to prepare for later changes.
And the "pfmemalloc_match()" part is not needed for node partial slab, since we already check this in the get_partial_node().
Stable-dep-of: 850470a8413a ("mm: slub: avoid wake up kswapd in set_track_prepare")
--- mm/slub.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-)
mm/slub.c +15 -16
@@ -3124,8 +3124,21 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, pc.slab = &slab; pc.orig_size = orig_size; freelist =get_partial (s, node, &pc); -if (freelist) - goto check_new_slab; +if (freelist) { +if (kmem_cache_debug (s)) { +/* +* For debug caches here we had to go through +* alloc_single_from_partial() so just store the +* tracking info and return the object. +*/ +if (s->flags &SLAB_STORE_USER ) +set_track (s, freelist,TRACK_ALLOC , addr); + +return freelist; + } + + goto retry_load_slab; + }slub_put_cpu_ptr (s->cpu_slab); slab =new_slab (s, gfpflags, node);
@@ -3161,20 +3174,6 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,inc_slabs_node (s,slab_nid (slab), slab->objects); -check_new_slab: - -if (kmem_cache_debug (s)) { -/* -* For debug caches here we had to go through -* alloc_single_from_partial() so just store the tracking info -* and return the object -*/ -if (s->flags &SLAB_STORE_USER ) -set_track (s, freelist,TRACK_ALLOC , addr); - -return freelist; - } -if (unlikely (!pfmemalloc_match (slab, gfpflags))) {/* * For !pfmemalloc_match() case we don't load freelist so that
Signature · 1 line
2.51.0
Sasha Levin
maintainerPATCH 3/3 mm: slub: avoid wake up kswapd in set_track_prepare
In reply to Sasha LevinFrom: yangshiguang <[email protected]>
[ Upstream commit 850470a8413a8a78e772c4f6bd9fe81ec6bd5b0f ]
set_track_prepare() can incur lock recursion.
The issue is that it is called from hrtimer_start_range_ns holding the per_cpu(hrtimer_bases)[n].lock, but when enabled CONFIG_DEBUG_OBJECTS_TIMERS, may wake up kswapd in set_track_prepare, and try to hold the per_cpu(hrtimer_bases)[n].lock.
Avoid deadlock caused by implicitly waking up kswapd by passing in allocation flags, which do not contain __GFP_KSWAPD_RECLAIM in the debug_objects_fill_pool() case. Inside stack depot they are processed by gfp_nested_mask().
Since ___slab_alloc() has preemption disabled, we mask out __GFP_DIRECT_RECLAIM from the flags there.
The oops looks something like:
BUG: spinlock recursion on CPU#3, swapper/3/0 lock: 0xffffff8a4bf29c80, .magic: dead4ead, .owner: swapper/3/0, .owner_cpu: 3 Hardware name: Qualcomm Technologies, Inc. Popsicle based on SM8850 (DT) Call trace: spin_bug+0x0 _raw_spin_lock_irqsave+0x80 hrtimer_try_to_cancel+0x94 task_contending+0x10c enqueue_dl_entity+0x2a4 dl_server_start+0x74 enqueue_task_fair+0x568 enqueue_task+0xac do_activate_task+0x14c ttwu_do_activate+0xcc try_to_wake_up+0x6c8 default_wake_function+0x20 autoremove_wake_function+0x1c __wake_up+0xac wakeup_kswapd+0x19c wake_all_kswapds+0x78 __alloc_pages_slowpath+0x1ac __alloc_pages_noprof+0x298 stack_depot_save_flags+0x6b0 stack_depot_save+0x14 set_track_prepare+0x5c ___slab_alloc+0xccc __kmalloc_cache_noprof+0x470 __set_page_owner+0x2bc post_alloc_hook[jt]+0x1b8 prep_new_page+0x28 get_page_from_freelist+0x1edc __alloc_pages_noprof+0x13c alloc_slab_page+0x244 allocate_slab+0x7c ___slab_alloc+0x8e8 kmem_cache_alloc_noprof+0x450 debug_objects_fill_pool+0x22c debug_object_activate+0x40 enqueue_hrtimer[jt]+0xdc hrtimer_start_range_ns+0x5f8
...
--- mm/slub.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-)
mm/slub.c +20 -10
@@ -726,19 +726,19 @@ static struct track *get_track(struct kmem_cache *s, void *object, }#ifdef CONFIG_STACKDEPOT -static noinline depot_stack_handle_t set_track_prepare(void) +static noinline depot_stack_handle_t set_track_prepare(gfp_t gfp_flags) {depot_stack_handle_t handle;unsigned long entries[TRACK_ADDRS_COUNT ];unsigned int nr_entries; nr_entries =stack_trace_save (entries,ARRAY_SIZE (entries),3 ); - handle =stack_depot_save (entries, nr_entries,GFP_NOWAIT ); + handle =stack_depot_save (entries, nr_entries, gfp_flags);return handle; }#else -static inline depot_stack_handle_t set_track_prepare (void ) +static inline depot_stack_handle_t set_track_prepare (gfp_t gfp_flags) {return 0 ; }
@@ -760,9 +760,9 @@ static void set_track_update(struct kmem_cache *s, void *object, } static __always_inline void set_track(struct kmem_cache *s, void *object, -enum track_item alloc,unsigned long addr) +enum track_item alloc,unsigned long addr,gfp_t gfp_flags) { -depot_stack_handle_t handle =set_track_prepare (); +depot_stack_handle_t handle =set_track_prepare (gfp_flags);set_track_update (s, object, alloc, addr, handle); }
@@ -1651,9 +1651,9 @@ static inline bool free_debug_processing(struct kmem_cache *s,static inline void slab_pad_check (struct kmem_cache *s,struct slab *slab) {}static inline int check_object (struct kmem_cache *s,struct slab *slab,void *object,u8 val) {return 1 ; } -static inline depot_stack_handle_t set_track_prepare (void ) {return 0 ; } +static inline depot_stack_handle_t set_track_prepare (gfp_t gfp_flags) {return 0 ; }static inline void set_track (struct kmem_cache *s,void *object, -enum track_item alloc,unsigned long addr) {} +enum track_item alloc,unsigned long addr,gfp_t gfp_flags) {}static inline void add_full (struct kmem_cache *s,struct kmem_cache_node *n,struct slab *slab) {} static inline void remove_full(struct kmem_cache *s, struct kmem_cache_node *n,
@@ -3130,9 +3130,14 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,* For debug caches here we had to go through * alloc_single_from_partial() so just store the * tracking info and return the object. +* +* Due to disabled preemption we need to disallow +* blocking. The flags are further adjusted by +* gfp_nested_mask() in stack_depot itself. */ if (s->flags &SLAB_STORE_USER ) -set_track (s, freelist,TRACK_ALLOC , addr); +set_track (s, freelist,TRACK_ALLOC , addr, + gfpflags & ~(__GFP_DIRECT_RECLAIM));return freelist; }
@@ -3158,7 +3163,8 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, goto new_objects;if (s->flags &SLAB_STORE_USER ) -set_track (s, freelist,TRACK_ALLOC , addr); +set_track (s, freelist,TRACK_ALLOC , addr, + gfpflags & ~(__GFP_DIRECT_RECLAIM));return freelist; }
@@ -3409,8 +3415,12 @@ static noinline void free_to_partial_list(unsigned long flags;depot_stack_handle_t handle =0 ; +/* +* We cannot use GFP_NOWAIT as there are callsites where waking up +* kswapd could deadlock +*/ if (s->flags &SLAB_STORE_USER ) - handle =set_track_prepare (); + handle =set_track_prepare (__GFP_NOWARN);spin_lock_irqsave (&n->list_lock, flags);
Signature · 1 line
2.51.0