public inbox for linux-mm@kvack.org
 help / color / mirror / Atom feed
From: "Lorenzo Stoakes (Oracle)" <ljs@kernel.org>
To: Johannes Weiner <hannes@cmpxchg.org>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	 David Hildenbrand <david@kernel.org>,
	Shakeel Butt <shakeel.butt@linux.dev>,
	 Yosry Ahmed <yosry.ahmed@linux.dev>, Zi Yan <ziy@nvidia.com>,
	 "Liam R. Howlett" <Liam.Howlett@oracle.com>,
	Usama Arif <usama.arif@linux.dev>,
	 Kiryl Shutsemau <kas@kernel.org>,
	Dave Chinner <david@fromorbit.com>,
	 Roman Gushchin <roman.gushchin@linux.dev>,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 7/7] mm: switch deferred split shrinker to list_lru
Date: Tue, 7 Apr 2026 10:55:09 +0100	[thread overview]
Message-ID: <adTSMX9AuaPrR4Mk@lucifer> (raw)
In-Reply-To: <adQnp7d8UJQipDSj@cmpxchg.org>

On Mon, Apr 06, 2026 at 05:37:43PM -0400, Johannes Weiner wrote:
> On Wed, Apr 01, 2026 at 06:33:04PM +0100, Lorenzo Stoakes (Oracle) wrote:
> > On Mon, Mar 30, 2026 at 12:40:22PM -0400, Johannes Weiner wrote:
> > > > > @@ -414,10 +414,9 @@ static inline int split_huge_page(struct page *page)
> > > > >  {
> > > > >  	return split_huge_page_to_list_to_order(page, NULL, 0);
> > > > >  }
> > > > > +
> > > > > +extern struct list_lru deferred_split_lru;
> > > >
> > > > It might be nice for the sake of avoiding a global to instead expose this
> > > > as a getter?
> > > >
> > > > Or actually better, since every caller outside of huge_memory.c that
> > > > references this uses folio_memcg_list_lru_alloc(), do something like:
> > > >
> > > > int folio_memcg_alloc_deferred(struct folio *folio, gfp_t gfp);
> > > >
> > > > in mm/huge_memory.c:
> > > >
> > > > /**
> > > >  * blah blah blah put on error blah
> > > >  */
> > > > int folio_memcg_alloc_deferred(struct folio *folio, gfp_t gfp)
> > > > {
> > > > 	int err;
> > > >
> > > > 	err = folio_memcg_list_lru_alloc(folio, &deferred_split_lru, gfP);
> > > > 	if (err) {
> > > > 		folio_put(folio);
> > > > 		return err;
> > > > 	}
> > > >
> > > > 	return 0;
> > > > }
> > > >
> > > > And then the callers can just invoke this, and you can make
> > > > deferred_split_lru static in mm/huge_memory.c?
> > >
> > > That sounds reasonable. Let me make this change.
> >
> > Thanks!
>
> Done. This looks much nicer. Though I kept the folio_put() in the
> caller because that's who owns the reference. It would be quite
> unexpected for this one to consume a ref on error.

Thanks :) Ack on folio_put()!

>
> > > > > @@ -939,6 +949,7 @@ static int __init thp_shrinker_init(void)
> > > > >
> > > > >  	huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero");
> > > > >  	if (!huge_zero_folio_shrinker) {
> > > > > +		list_lru_destroy(&deferred_split_lru);
> > > > >  		shrinker_free(deferred_split_shrinker);
> > > >
> > > > Presumably no probably-impossible-in-reality race on somebody entering the
> > > > shrinker and referencing the deferred_split_lru before the shrinker is freed?
> > >
> > > Ah right, I think for clarity it would indeed be better to destroy the
> > > shrinker, then the queue. Let me re-order this one.
> > >
> > > But yes, in practice, none of the above fails. If we have trouble
> > > doing a couple of small kmallocs during a subsys_initcall(), that
> > > machine is unlikely to finish booting, let alone allocate enough
> > > memory to enter the THP shrinker.
> >
> > Yeah I thought that might be the case, but seems more logical killing shrinker
> > first, thanks!
>
> Done.

Thanks!

>
> > > > > @@ -3854,34 +3761,34 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
> > > > >  	struct folio *end_folio = folio_next(folio);
> > > > >  	struct folio *new_folio, *next;
> > > > >  	int old_order = folio_order(folio);
> > > > > +	struct list_lru_one *l;
> > > >
> > > > Nit, and maybe this is a convention, but hate single letter variable names,
> > > > 'lru' or something might be nicer?
> > >
> > > Yeah I stuck with the list_lru internal naming, which uses `lru` for
> > > the struct list_lru, and `l` for struct list_lru_one. I suppose that
> > > was fine for the very domain-specific code and short functions in
> > > there, but it's grating in large, general MM functions like these.
> > >
> > > Since `lru` is taken, any preferences? llo?
> >
> > ljs? ;)
> >
> > Could be list?
>
> list is taken in some of these contexts already. I may have
> overthought this. lru works fine in those callsites, and is in line
> with what other sites are using (git grep list_lru_one).

OK that works :)

>
> > But, and I _know_ it's nitty sorry, but maybe worth expanding that comment to
> > explain that e.g. 'we must take the folio look prior to the list_lru lock to
> > avoid racing with deferred_split_scan() in accessing the folio reference count'
> > or similar?
>
> Good idea! Done.

Thanks!

>
> > > > > +	int nid = folio_nid(folio);
> > > > >  	unsigned long flags;
> > > > >  	bool unqueued = false;
> > > > >
> > > > >  	WARN_ON_ONCE(folio_ref_count(folio));
> > > > >  	WARN_ON_ONCE(!mem_cgroup_disabled() && !folio_memcg_charged(folio));
> > > > >
> > > > > -	ds_queue = folio_split_queue_lock_irqsave(folio, &flags);
> > > > > -	if (!list_empty(&folio->_deferred_list)) {
> > > > > -		ds_queue->split_queue_len--;
> > > > > +	rcu_read_lock();
> > > > > +	l = list_lru_lock_irqsave(&deferred_split_lru, nid, folio_memcg(folio), &flags);
> > > > > +	if (__list_lru_del(&deferred_split_lru, l, &folio->_deferred_list, nid)) {
> > > >
> > > > Maybe worth factoring __list_lru_del() into something that explicitly
> > > > references &folio->_deferred_list rather than open codingin both places?
> > >
> > > Hm, I wouldn't want to encode this into list_lru API, but we could do
> > > a huge_memory.c-local helper?
> > >
> > > folio_deferred_split_del(folio, l, nid)
> >
> > Well, I kind of hate how we're using the global deferred_split_lru all over the
> > place, so a helper woudl be preferable but one that also could be used for
> > khugepaged.c and memory.c also?
>
> This function is used only in huge_memory.c. I managed to make the
> deferred_list_lru static as well without making any changes to this ^
> particular function/callsite.
>
> Let me know, after looking at the delta diff below, if you'd still
> like to see changes here.

Ack will take a look!

>
> > > > > @@ -4534,64 +4438,32 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
> > > > >  		}
> > > > >  		folio_unlock(folio);
> > > > >  next:
> > > > > -		if (did_split || !folio_test_partially_mapped(folio))
> > > > > -			continue;
> > > > >  		/*
> > > > >  		 * Only add back to the queue if folio is partially mapped.
> > > > >  		 * If thp_underused returns false, or if split_folio fails
> > > > >  		 * in the case it was underused, then consider it used and
> > > > >  		 * don't add it back to split_queue.
> > > > >  		 */
> > > > > -		fqueue = folio_split_queue_lock_irqsave(folio, &flags);
> > > > > -		if (list_empty(&folio->_deferred_list)) {
> > > > > -			list_add_tail(&folio->_deferred_list, &fqueue->split_queue);
> > > > > -			fqueue->split_queue_len++;
> > > > > +		if (!did_split && folio_test_partially_mapped(folio)) {
> > > > > +			rcu_read_lock();
> > > > > +			l = list_lru_lock_irqsave(&deferred_split_lru,
> > > > > +						  folio_nid(folio),
> > > > > +						  folio_memcg(folio),
> > > > > +						  &flags);
> > > > > +			__list_lru_add(&deferred_split_lru, l,
> > > > > +				       &folio->_deferred_list,
> > > > > +				       folio_nid(folio), folio_memcg(folio));
> > > > > +			list_lru_unlock_irqrestore(l, &flags);
> > > >
> > > > Hmm this does make me think it'd be nice to have a list_lru_add() variant
> > > > for irqsave/restore then, since it's a repeating pattern!
> > >
> > > Yeah, this site calls for it the most :( I tried to balance callsite
> > > prettiness with the need to extend the list_lru api; it's just one
> > > caller. And the possible mutations and variants with these locks is
> > > seemingly endless once you open that can of worms...
> >
> > True...
> >
> > >
> > > Case in point: this is process context and we could use
> > > spin_lock_irq() here. I'm just using list_lru_lock_irqsave() because
> > > that's the common variant used by the add and del paths already.
> > >
> > > If I went with a helper, I could do list_lru_add_irq().
> > >
> > > I think it would actually nicely mirror the list_lru_shrink_walk_irq()
> > > a few lines up.
> >
> > Yeah, I mean I'm pretty sure this repeats quite a few times so is worthy of a
> > helper.
>
> It's only one callsite, actually. But I added the helper. It's churny
> on the list_lru side, but that callsite does look much better.

OK, I was possibly misremembering that then :) I am an advocate of using helpers
like this even for a single callsite if it makes the logic easier to understand,
(generally :>) compilers will do the right thing (TM) so this helps the hunam
bwangs reading the code, i.e. the difficult part of kernel development :)

>
> Anyway, I hope I got everything. Can you take a look? Will obviously
> fold this into the respective patches, but just double checking
> whether these things are what you had in mind.

Thanks, OK so I've put some pleased-sounds below under the various bits but
TL;DR is this looks to address all my concerns.

Feel free to plonk a:

Reviewed-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>

On this patch on respin! I am trusting obviously that nothing breaks and you've
(re-)tested it :>) obv. :P

Thanks, Lorenzo

>
> ---
>
> diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> index 8d801ed378db..b473605b4d7d 100644
> --- a/include/linux/huge_mm.h
> +++ b/include/linux/huge_mm.h
> @@ -415,7 +415,8 @@ static inline int split_huge_page(struct page *page)
>  	return split_huge_page_to_list_to_order(page, NULL, 0);
>  }
>
> -extern struct list_lru deferred_split_lru;
> +int folio_memcg_alloc_deferred(struct folio *folio);
> +
>  void deferred_split_folio(struct folio *folio, bool partially_mapped);
>
>  void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
> diff --git a/include/linux/list_lru.h b/include/linux/list_lru.h
> index 4bd29b61c59a..733a262b91e5 100644
> --- a/include/linux/list_lru.h
> +++ b/include/linux/list_lru.h
> @@ -83,6 +83,21 @@ int memcg_list_lru_alloc(struct mem_cgroup *memcg, struct list_lru *lru,
>  			 gfp_t gfp);
>
>  #ifdef CONFIG_MEMCG
> +/**
> + * folio_memcg_list_lru_alloc - allocate list_lru heads for shrinkable folio
> + * @folio: the newly allocated & charged folio
> + * @lru: the list_lru this might be queued on
> + * @gfp: gfp mask
> + *
> + * Allocate list_lru heads (per-memcg, per-node) needed to queue this
> + * particular folio down the line.
> + *
> + * This does memcg_list_lru_alloc(), but on the memcg that @folio is
> + * associated with. Handles folio_memcg() access rules in the fast
> + * path (list_lru heads allocated) and the allocation slowpath.
> + *
> + * Returns 0 on success, a negative error value otherwise.
> + */
>  int folio_memcg_list_lru_alloc(struct folio *folio, struct list_lru *lru,
>  			       gfp_t gfp);

LGTM, nice comment thanks!

>  #else
> @@ -118,6 +133,10 @@ struct list_lru_one *list_lru_lock(struct list_lru *lru, int nid,
>   */
>  void list_lru_unlock(struct list_lru_one *l);
>
> +struct list_lru_one *list_lru_lock_irq(struct list_lru *lru, int nid,
> +		struct mem_cgroup *memcg);
> +void list_lru_unlock_irq(struct list_lru_one *l);
> +
>  struct list_lru_one *list_lru_lock_irqsave(struct list_lru *lru, int nid,
>  		struct mem_cgroup *memcg, unsigned long *irq_flags);
>  void list_lru_unlock_irqrestore(struct list_lru_one *l,
> @@ -161,6 +180,9 @@ bool __list_lru_del(struct list_lru *lru, struct list_lru_one *l,
>  bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid,
>  		    struct mem_cgroup *memcg);
>
> +bool list_lru_add_irq(struct list_lru *lru, struct list_head *item, int nid,
> +		      struct mem_cgroup *memcg);
> +

Nice!

>  /**
>   * list_lru_add_obj: add an element to the lru list's tail
>   * @lru: the lru pointer
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index c8c6c4602cc7..a0cce6a56620 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -69,7 +69,7 @@ unsigned long transparent_hugepage_flags __read_mostly =
>  	(1<<TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG);
>
>  static struct lock_class_key deferred_split_key;
> -struct list_lru deferred_split_lru;
> +static struct list_lru deferred_split_lru;

Lovely!

>  static struct shrinker *deferred_split_shrinker;
>  static unsigned long deferred_split_count(struct shrinker *shrink,
>  					  struct shrink_control *sc);
> @@ -913,6 +913,11 @@ static inline void hugepage_exit_sysfs(struct kobject *hugepage_kobj)
>  }
>  #endif /* CONFIG_SYSFS */
>
> +int folio_memcg_alloc_deferred(struct folio *folio)
> +{
> +	return folio_memcg_list_lru_alloc(folio, &deferred_split_lru, GFP_KERNEL);
> +}
> +
>  static int __init thp_shrinker_init(void)
>  {
>  	deferred_split_shrinker = shrinker_alloc(SHRINKER_NUMA_AWARE |
> @@ -949,8 +954,8 @@ static int __init thp_shrinker_init(void)
>
>  	huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero");
>  	if (!huge_zero_folio_shrinker) {
> -		list_lru_destroy(&deferred_split_lru);
>  		shrinker_free(deferred_split_shrinker);
> +		list_lru_destroy(&deferred_split_lru);
>  		return -ENOMEM;
>  	}
>
> @@ -964,8 +969,8 @@ static int __init thp_shrinker_init(void)
>  static void __init thp_shrinker_exit(void)
>  {
>  	shrinker_free(huge_zero_folio_shrinker);
> -	list_lru_destroy(&deferred_split_lru);
>  	shrinker_free(deferred_split_shrinker);
> +	list_lru_destroy(&deferred_split_lru);
>  }
>
>  static int __init hugepage_init(void)
> @@ -1246,7 +1251,7 @@ static struct folio *vma_alloc_anon_folio_pmd(struct vm_area_struct *vma,
>  		return NULL;
>  	}
>
> -	if (folio_memcg_list_lru_alloc(folio, &deferred_split_lru, GFP_KERNEL)) {
> +	if (folio_memcg_alloc_deferred(folio)) {
>  		folio_put(folio);
>  		count_vm_event(THP_FAULT_FALLBACK);
>  		count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK);
> @@ -3761,31 +3766,37 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
>  	struct folio *end_folio = folio_next(folio);
>  	struct folio *new_folio, *next;
>  	int old_order = folio_order(folio);
> -	struct list_lru_one *l;
> +	struct list_lru_one *lru;
>  	bool dequeue_deferred;
>  	int ret = 0;
>
>  	VM_WARN_ON_ONCE(!mapping && end);
> -	/* Prevent deferred_split_scan() touching ->_refcount */
> +	/*
> +	 * If this folio can be on the deferred split queue, lock out
> +	 * the shrinker before freezing the ref. If the shrinker sees
> +	 * a 0-ref folio, it assumes it beat folio_put() to the list
> +	 * lock and must clean up the LRU state - the same dequeue we
> +	 * will do below as part of the split.
> +	 */

Great thanks!

>  	dequeue_deferred = folio_test_anon(folio) && old_order > 1;
>  	if (dequeue_deferred) {
>  		rcu_read_lock();
> -		l = list_lru_lock(&deferred_split_lru,
> -				  folio_nid(folio), folio_memcg(folio));
> +		lru = list_lru_lock(&deferred_split_lru,
> +				    folio_nid(folio), folio_memcg(folio));
>  	}
>  	if (folio_ref_freeze(folio, folio_cache_ref_count(folio) + 1)) {
>  		struct swap_cluster_info *ci = NULL;
>  		struct lruvec *lruvec;
>
>  		if (dequeue_deferred) {
> -			__list_lru_del(&deferred_split_lru, l,
> +			__list_lru_del(&deferred_split_lru, lru,
>  				       &folio->_deferred_list, folio_nid(folio));
>  			if (folio_test_partially_mapped(folio)) {
>  				folio_clear_partially_mapped(folio);
>  				mod_mthp_stat(old_order,
>  					MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
>  			}
> -			list_lru_unlock(l);
> +			list_lru_unlock(lru);
>  			rcu_read_unlock();
>  		}
>
> @@ -3890,7 +3901,7 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
>  			swap_cluster_unlock(ci);
>  	} else {
>  		if (dequeue_deferred) {
> -			list_lru_unlock(l);
> +			list_lru_unlock(lru);
>  			rcu_read_unlock();
>  		}
>  		return -EAGAIN;
> @@ -4268,7 +4279,7 @@ int split_folio_to_list(struct folio *folio, struct list_head *list)
>   */
>  bool __folio_unqueue_deferred_split(struct folio *folio)
>  {
> -	struct list_lru_one *l;
> +	struct list_lru_one *lru;
>  	int nid = folio_nid(folio);
>  	unsigned long flags;
>  	bool unqueued = false;
> @@ -4277,8 +4288,8 @@ bool __folio_unqueue_deferred_split(struct folio *folio)
>  	WARN_ON_ONCE(!mem_cgroup_disabled() && !folio_memcg_charged(folio));
>
>  	rcu_read_lock();
> -	l = list_lru_lock_irqsave(&deferred_split_lru, nid, folio_memcg(folio), &flags);
> -	if (__list_lru_del(&deferred_split_lru, l, &folio->_deferred_list, nid)) {
> +	lru = list_lru_lock_irqsave(&deferred_split_lru, nid, folio_memcg(folio), &flags);
> +	if (__list_lru_del(&deferred_split_lru, lru, &folio->_deferred_list, nid)) {
>  		if (folio_test_partially_mapped(folio)) {
>  			folio_clear_partially_mapped(folio);
>  			mod_mthp_stat(folio_order(folio),
> @@ -4286,7 +4297,7 @@ bool __folio_unqueue_deferred_split(struct folio *folio)
>  		}
>  		unqueued = true;
>  	}
> -	list_lru_unlock_irqrestore(l, &flags);
> +	list_lru_unlock_irqrestore(lru, &flags);
>  	rcu_read_unlock();
>
>  	return unqueued;	/* useful for debug warnings */
> @@ -4295,7 +4306,7 @@ bool __folio_unqueue_deferred_split(struct folio *folio)
>  /* partially_mapped=false won't clear PG_partially_mapped folio flag */
>  void deferred_split_folio(struct folio *folio, bool partially_mapped)
>  {
> -	struct list_lru_one *l;
> +	struct list_lru_one *lru;
>  	int nid;
>  	struct mem_cgroup *memcg;
>  	unsigned long flags;
> @@ -4324,7 +4335,7 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)
>
>  	rcu_read_lock();
>  	memcg = folio_memcg(folio);
> -	l = list_lru_lock_irqsave(&deferred_split_lru, nid, memcg, &flags);
> +	lru = list_lru_lock_irqsave(&deferred_split_lru, nid, memcg, &flags);
>  	if (partially_mapped) {
>  		if (!folio_test_partially_mapped(folio)) {
>  			folio_set_partially_mapped(folio);
> @@ -4337,8 +4348,8 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)
>  		/* partially mapped folios cannot become non-partially mapped */
>  		VM_WARN_ON_FOLIO(folio_test_partially_mapped(folio), folio);
>  	}
> -	__list_lru_add(&deferred_split_lru, l, &folio->_deferred_list, nid, memcg);
> -	list_lru_unlock_irqrestore(l, &flags);
> +	__list_lru_add(&deferred_split_lru, lru, &folio->_deferred_list, nid, memcg);
> +	list_lru_unlock_irqrestore(lru, &flags);
>  	rcu_read_unlock();
>  }
>
> @@ -4411,8 +4422,6 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
>  	list_for_each_entry_safe(folio, next, &dispose, _deferred_list) {
>  		bool did_split = false;
>  		bool underused = false;
> -		struct list_lru_one *l;
> -		unsigned long flags;
>
>  		list_del_init(&folio->_deferred_list);
>
> @@ -4446,14 +4455,10 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
>  		 */
>  		if (!did_split && folio_test_partially_mapped(folio)) {
>  			rcu_read_lock();
> -			l = list_lru_lock_irqsave(&deferred_split_lru,
> -						  folio_nid(folio),
> -						  folio_memcg(folio),
> -						  &flags);
> -			__list_lru_add(&deferred_split_lru, l,
> -				       &folio->_deferred_list,
> -				       folio_nid(folio), folio_memcg(folio));
> -			list_lru_unlock_irqrestore(l, &flags);
> +			list_lru_add_irq(&deferred_split_lru,
> +					 &folio->_deferred_list,
> +					 folio_nid(folio),
> +					 folio_memcg(folio));

Also nice :)

>  			rcu_read_unlock();
>  		}
>  		folio_put(folio);
> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index a81470f529e3..44a9b1350dbd 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -1121,7 +1121,7 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a
>  	if (result != SCAN_SUCCEED)
>  		goto out_nolock;
>
> -	if (folio_memcg_list_lru_alloc(folio, &deferred_split_lru, GFP_KERNEL))
> +	if (folio_memcg_alloc_deferred(folio))

Much nicer :)

>  		goto out_nolock;
>
>  	mmap_read_lock(mm);
> diff --git a/mm/list_lru.c b/mm/list_lru.c
> index 1ccdd45b1d14..23bf7c243083 100644
> --- a/mm/list_lru.c
> +++ b/mm/list_lru.c
> @@ -160,6 +160,18 @@ void list_lru_unlock(struct list_lru_one *l)
>  	unlock_list_lru(l, /*irq_off=*/false, /*irq_flags=*/NULL);
>  }
>
> +struct list_lru_one *list_lru_lock_irq(struct list_lru *lru, int nid,
> +				       struct mem_cgroup *memcg)
> +{
> +	return lock_list_lru_of_memcg(lru, nid, memcg, /*irq=*/true,
> +				      /*irq_flags=*/NULL, /*skip_empty=*/false);
> +}
> +
> +void list_lru_unlock_irq(struct list_lru_one *l)
> +{
> +	unlock_list_lru(l, /*irq_off=*/true, /*irq_flags=*/NULL);
> +}
> +
>  struct list_lru_one *list_lru_lock_irqsave(struct list_lru *lru, int nid,
>  					   struct mem_cgroup *memcg,
>  					   unsigned long *flags)
> @@ -213,6 +225,18 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid,
>  	return ret;
>  }
>
> +bool list_lru_add_irq(struct list_lru *lru, struct list_head *item,
> +		      int nid, struct mem_cgroup *memcg)
> +{
> +	struct list_lru_one *l;
> +	bool ret;
> +
> +	l = list_lru_lock_irq(lru, nid, memcg);
> +	ret = __list_lru_add(lru, l, item, nid, memcg);
> +	list_lru_unlock_irq(l);
> +	return ret;
> +}
> +
>  bool list_lru_add_obj(struct list_lru *lru, struct list_head *item)
>  {
>  	bool ret;
> diff --git a/mm/memory.c b/mm/memory.c
> index 24dd531125b4..23da4720576d 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -4658,8 +4658,7 @@ static struct folio *alloc_swap_folio(struct vm_fault *vmf)
>  			folio_put(folio);
>  			goto next;
>  		}
> -		if (order > 1 &&
> -		    folio_memcg_list_lru_alloc(folio, &deferred_split_lru, GFP_KERNEL)) {
> +		if (order > 1 && folio_memcg_alloc_deferred(folio)) {
>  			folio_put(folio);
>  			goto fallback;
>  		}
> @@ -5183,8 +5182,7 @@ static struct folio *alloc_anon_folio(struct vm_fault *vmf)
>  			folio_put(folio);
>  			goto next;
>  		}
> -		if (order > 1 &&
> -		    folio_memcg_list_lru_alloc(folio, &deferred_split_lru, GFP_KERNEL)) {
> +		if (order > 1 && folio_memcg_alloc_deferred(folio)) {

Yeah big improvements on both!

>  			folio_put(folio);
>  			goto fallback;
>  		}


  reply	other threads:[~2026-04-07  9:55 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-18 19:53 [PATCH v3 0/7] mm: switch THP shrinker to list_lru Johannes Weiner
2026-03-18 19:53 ` [PATCH v3 1/7] mm: list_lru: lock_list_lru_of_memcg() cannot return NULL if !skip_empty Johannes Weiner
2026-03-18 20:12   ` Shakeel Butt
2026-03-24 11:30   ` Lorenzo Stoakes (Oracle)
2026-03-18 19:53 ` [PATCH v3 2/7] mm: list_lru: deduplicate unlock_list_lru() Johannes Weiner
2026-03-24 11:32   ` Lorenzo Stoakes (Oracle)
2026-03-18 19:53 ` [PATCH v3 3/7] mm: list_lru: move list dead check to lock_list_lru_of_memcg() Johannes Weiner
2026-03-18 20:20   ` Shakeel Butt
2026-03-24 11:34   ` Lorenzo Stoakes (Oracle)
2026-03-18 19:53 ` [PATCH v3 4/7] mm: list_lru: deduplicate lock_list_lru() Johannes Weiner
2026-03-18 20:22   ` Shakeel Butt
2026-03-24 11:36   ` Lorenzo Stoakes (Oracle)
2026-03-18 19:53 ` [PATCH v3 5/7] mm: list_lru: introduce caller locking for additions and deletions Johannes Weiner
2026-03-18 20:51   ` Shakeel Butt
2026-03-20 16:18     ` Johannes Weiner
2026-03-24 11:55   ` Lorenzo Stoakes (Oracle)
2026-03-18 19:53 ` [PATCH v3 6/7] mm: list_lru: introduce folio_memcg_list_lru_alloc() Johannes Weiner
2026-03-18 20:52   ` Shakeel Butt
2026-03-18 21:01   ` Shakeel Butt
2026-03-24 12:01   ` Lorenzo Stoakes (Oracle)
2026-03-30 16:54     ` Johannes Weiner
2026-04-01 14:43       ` Lorenzo Stoakes (Oracle)
2026-03-18 19:53 ` [PATCH v3 7/7] mm: switch deferred split shrinker to list_lru Johannes Weiner
2026-03-18 20:26   ` David Hildenbrand (Arm)
2026-03-18 23:18   ` Shakeel Butt
2026-03-24 13:48   ` Lorenzo Stoakes (Oracle)
2026-03-30 16:40     ` Johannes Weiner
2026-04-01 17:33       ` Lorenzo Stoakes (Oracle)
2026-04-06 21:37         ` Johannes Weiner
2026-04-07  9:55           ` Lorenzo Stoakes (Oracle) [this message]
2026-03-27  7:51   ` Kairui Song
2026-03-30 16:51     ` Johannes Weiner
2026-03-30 16:37   ` [PATCH v3 7/7] mm: switch deferred split shrinker to list_lru - [s390] panic in __memcg_list_lru_alloc Mikhail Zaslonko
2026-03-30 19:03     ` Andrew Morton
2026-03-30 20:41     ` Johannes Weiner
2026-03-30 20:56       ` Johannes Weiner
2026-03-30 22:46         ` Vasily Gorbik
2026-03-31  8:04         ` Mikhail Zaslonko
2026-03-18 21:00 ` [PATCH v3 0/7] mm: switch THP shrinker to list_lru Lorenzo Stoakes (Oracle)
2026-03-18 22:31   ` Johannes Weiner
2026-03-19  8:47     ` Lorenzo Stoakes (Oracle)
2026-03-19  8:52       ` David Hildenbrand (Arm)
2026-03-19 11:45         ` Lorenzo Stoakes (Oracle)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=adTSMX9AuaPrR4Mk@lucifer \
    --to=ljs@kernel.org \
    --cc=Liam.Howlett@oracle.com \
    --cc=akpm@linux-foundation.org \
    --cc=david@fromorbit.com \
    --cc=david@kernel.org \
    --cc=hannes@cmpxchg.org \
    --cc=kas@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=roman.gushchin@linux.dev \
    --cc=shakeel.butt@linux.dev \
    --cc=usama.arif@linux.dev \
    --cc=yosry.ahmed@linux.dev \
    --cc=ziy@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox