Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: "Zi Yan" <ziy@nvidia.com>
To: <kasong@tencent.com>, <linux-mm@kvack.org>
Cc: <linux-kernel@vger.kernel.org>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"David Hildenbrand" <david@kernel.org>,
	"Lorenzo Stoakes" <ljs@kernel.org>,
	"Baolin Wang" <baolin.wang@linux.alibaba.com>,
	"Liam R. Howlett" <liam@infradead.org>,
	"Nico Pache" <nico.pache@linux.dev>,
	"Ryan Roberts" <ryan.roberts@arm.com>,
	"Dev Jain" <dev.jain@arm.com>,
	"Lance Yang" <lance.yang@linux.dev>,
	"Usama Arif" <usama.arif@linux.dev>,
	"Vlastimil Babka" <vbabka@kernel.org>,
	"Mike Rapoport" <rppt@kernel.org>,
	"Suren Baghdasaryan" <surenb@google.com>,
	"Michal Hocko" <mhocko@suse.com>, "Chris Li" <chrisl@kernel.org>,
	"Kemeng Shi" <shikemeng@huaweicloud.com>,
	"Nhat Pham" <nphamcs@gmail.com>,
	"Baoquan He" <baoquan.he@linux.dev>,
	"Barry Song" <baohua@kernel.org>,
	"Youngjun Park" <youngjun.park@lge.com>
Subject: Re: [PATCH RFC 04/13] mm/huge_memory: split the routine for splitting anon and file folio
Date: Sat, 08 Aug 2026 14:52:31 -0400	[thread overview]
Message-ID: <DKJSFCLP967N.YBR4DNK1NM2N@nvidia.com> (raw)
In-Reply-To: <20260808-swap-thp-cleanup-v1-4-689939a7ccc3@tencent.com>

On Fri Aug 7, 2026 at 5:17 PM EDT, Kairui Song via B4 Relay wrote:
> From: Kairui Song <kasong@tencent.com>
>
> No functional change intended. Before adding more logic, split
> __folio_freeze_and_split_unmapped() into an anon and a file variant so
> each path can evolve independently. The two paths shared little beyond
> the folio freeze call, the LRU locking, and the unfreeze skeleton, but
> differed in all other per-folio bookkeeping and routines.

While at it, can you rename __split_unmapped_folio() to
__split_frozen_folio() to reflect the actual folio state? It is causing
confusion and people tried to use __split_unmapped_folio() on non frozen
folios.

>
> While splitting, some cleanups become easy to apply, and helped dropping
> a few now redundant checks.
>
> Signed-off-by: Kairui Song <kasong@tencent.com>
> ---
>  mm/huge_memory.c | 121 ++++++++++++++++++++++++++++++++++---------------------
>  1 file changed, 76 insertions(+), 45 deletions(-)
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index cf8f90b94e42..56a356c30f30 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -3934,22 +3934,19 @@ static unsigned int folio_cache_ref_count(const struct folio *folio)
>  	return folio_nr_pages(folio);
>  }
>  
> -static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int new_order,
> -					     struct page *split_at, struct xa_state *xas,
> -					     struct address_space *mapping, bool do_lru,
> -					     struct list_head *list, enum split_type split_type,
> -					     pgoff_t end, int *nr_shmem_dropped)
> +static int __folio_freeze_split_unmapped_anon(struct folio *folio, unsigned int new_order,
> +					      struct page *split_at, bool do_lru,
> +					      struct list_head *list, enum split_type split_type)
>  {
>  	struct folio *end_folio = folio_next(folio);
>  	struct swap_cluster_info *ci = NULL;
> -	struct folio *new_folio, *next;
> +	struct folio *new_folio;
>  	int old_order = folio_order(folio);
>  	struct list_lru_one *lru;
>  	struct lruvec *lruvec;
>  	bool dequeue_deferred;
>  	int ret = 0;
>  
> -	VM_WARN_ON_ONCE(!mapping && end);

We no longer need mapping here, the caller already makes sure mapping is
NULL. Great!

>  	/*
>  	 * If this folio can be on the deferred split queue, lock out
>  	 * the shrinker before freezing the ref. If the shrinker sees
> @@ -3957,7 +3954,7 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
>  	 * lock and must clean up the LRU state - the same dequeue we
>  	 * will do below as part of the split.
>  	 */
> -	dequeue_deferred = folio_test_anon(folio) && old_order > 1;
> +	dequeue_deferred = old_order > 1;
>  	if (dequeue_deferred) {
>  		struct mem_cgroup *memcg;
>  
> @@ -3987,24 +3984,73 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
>  		rcu_read_unlock();
>  	}
>  
> -	if (mapping) {
> +	if (folio_test_swapcache(folio))
> +		ci = swap_cluster_get_and_lock(folio);
> +
> +	if (do_lru)
> +		lruvec = folio_lruvec_lock(folio);
> +
> +	ret = __split_unmapped_folio(folio, new_order, split_at, NULL,
> +				     NULL, split_type);
> +
> +	/*
> +	 * Unfreeze the after-split folios and put them back to the right
> +	 * place, keeping the head @folio frozen until the end. While the
> +	 * folio is in the swap cache, the sub entries must be updated with
> +	 * their after-split folios before the head is unfrozen, so a
> +	 * concurrent swap_cache_get_folio() cannot return the head folio
> +	 * for a sub entry. Keeping the head frozen throughout also stops a
> +	 * parallel folio_try_get() from observing a partially split folio.
> +	 */
> +	for (new_folio = folio_next(folio); new_folio != end_folio;
> +	     new_folio = folio_next(new_folio)) {

Please keep the existing for loop pattern by using next =
folio_next(new_folio) in the loop buddy.

Hugh pointed out an issue when I did the above for loop pattern[1].
Basically, folio_next() reads folio_nr_pages() and relies on a stable
new_folio input. In my old code, the input of folio_next() can be freed
and causing oops. In your code, that does not apply, but it can bite
people in the future the loop body changes and new_folio's lifetime ends
before the for loop finishes.

Maybe add a comment to explain why next = folio_next(new_folio) should
be used.

[1] https://lore.kernel.org/all/2fae27fe-6e2e-3587-4b68-072118d80cf8@google.com/

> +		zone_device_private_split_cb(folio, new_folio);
> +		folio_ref_unfreeze(new_folio,
> +				   folio_cache_ref_count(new_folio) + 1);
> +		if (do_lru)
> +			lru_add_split_folio(folio, new_folio, lruvec, list);
> +		if (ci)
> +			__swap_cache_replace_folio(ci, folio, new_folio);
> +	}
> +
> +	zone_device_private_split_cb(folio, NULL);
> +	folio_ref_unfreeze(folio, folio_cache_ref_count(folio) + 1);
> +
> +	if (do_lru)
> +		lruvec_unlock(lruvec);
> +	if (ci)
> +		swap_cluster_unlock(ci);
> +
> +	return ret;
> +}
> +
> +static int __folio_freeze_split_unmapped_file(struct folio *folio, unsigned int new_order,
> +					      struct page *split_at, struct xa_state *xas,
> +					      struct address_space *mapping, bool do_lru,
> +					      struct list_head *list, enum split_type split_type,
> +					      pgoff_t end, int *nr_shmem_dropped)
> +{
> +	struct folio *end_folio = folio_next(folio);
> +	struct folio *new_folio, *next;
> +	struct lruvec *lruvec;
> +	int ret;
> +
> +	if (!folio_ref_freeze(folio, folio_cache_ref_count(folio) + 1))
> +		return -EAGAIN;
> +
> +	if (folio_test_pmd_mappable(folio) &&
> +	    new_order < HPAGE_PMD_ORDER) {
>  		int nr = folio_nr_pages(folio);
>  
> -		if (folio_test_pmd_mappable(folio) &&
> -		    new_order < HPAGE_PMD_ORDER) {
> -			if (folio_test_swapbacked(folio)) {
> -				lruvec_stat_mod_folio(folio,
> -						      NR_SHMEM_THPS, -nr);
> -			} else {
> -				lruvec_stat_mod_folio(folio,
> -						      NR_FILE_THPS, -nr);
> -			}
> +		if (folio_test_swapbacked(folio)) {
> +			lruvec_stat_mod_folio(folio,
> +					      NR_SHMEM_THPS, -nr);
> +		} else {
> +			lruvec_stat_mod_folio(folio,
> +					      NR_FILE_THPS, -nr);
>  		}
>  	}
>  
> -	if (folio_test_swapcache(folio))
> -		ci = swap_cluster_get_and_lock(folio);
> -
>  	/* lock lru list/PageCompound, ref frozen by page_ref_freeze */
>  	if (do_lru)
>  		lruvec = folio_lruvec_lock(folio);
> @@ -4014,7 +4060,7 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
>  
>  	/*
>  	 * Unfreeze after-split folios and put them back to the right
> -	 * list. @folio should be kept frozon until page cache
> +	 * list. @folio should be kept frozen until page cache
>  	 * entries are updated with all the other after-split folios
>  	 * to prevent others seeing stale page cache entries.
>  	 * As a result, new_folio starts from the next folio of
> @@ -4026,27 +4072,12 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
>  
>  		next = folio_next(new_folio);
>  
> -		zone_device_private_split_cb(folio, new_folio);
> -
>  		folio_ref_unfreeze(new_folio,
>  				   folio_cache_ref_count(new_folio) + 1);
>  
>  		if (do_lru)
>  			lru_add_split_folio(folio, new_folio, lruvec, list);
>  
> -		/*
> -		 * Anonymous folio with swap cache.
> -		 * NOTE: shmem in swap cache is not supported yet.
> -		 */
> -		if (ci) {
> -			__swap_cache_replace_folio(ci, folio, new_folio);
> -			continue;
> -		}
> -
> -		/* Anonymous folio without swap cache */
> -		if (!mapping)
> -			continue;
> -
>  		/* Add the new folio to the page cache. */
>  		if (new_folio->index < end) {
>  			__xa_store(&mapping->i_pages, new_folio->index,
> @@ -4065,7 +4096,6 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
>  		folio_put_refs(new_folio, nr_pages);
>  	}
>  
> -	zone_device_private_split_cb(folio, NULL);
>  	/*
>  	 * Unfreeze @folio only after all page cache entries, which
>  	 * used to point to it, have been updated with new folios.
> @@ -4076,8 +4106,6 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
>  
>  	if (do_lru)
>  		lruvec_unlock(lruvec);
> -	if (ci)
> -		swap_cluster_unlock(ci);
>  
>  	return ret;
>  }
> @@ -4231,10 +4259,14 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
>  			ret = -EAGAIN;
>  			goto fail;
>  		}
> +		ret = __folio_freeze_split_unmapped_file(folio, new_order, split_at, &xas, mapping,
> +							 true, list, split_type, end,
> +							 &nr_shmem_dropped);
> +	} else {
> +		ret = __folio_freeze_split_unmapped_anon(folio, new_order, split_at, true,
> +							 list, split_type);
>  	}
>  
> -	ret = __folio_freeze_and_split_unmapped(folio, new_order, split_at, &xas, mapping,
> -						true, list, split_type, end, &nr_shmem_dropped);
>  fail:
>  	if (mapping)
>  		xas_unlock(&xas);
> @@ -4334,9 +4366,8 @@ int folio_split_unmapped(struct folio *folio, unsigned int new_order)
>  		return -EAGAIN;
>  
>  	local_irq_disable();
> -	ret = __folio_freeze_and_split_unmapped(folio, new_order, &folio->page, NULL,
> -						NULL, false, NULL, SPLIT_TYPE_UNIFORM,
> -						0, NULL);
> +	ret = __folio_freeze_split_unmapped_anon(folio, new_order, &folio->page,
> +						 false, NULL, SPLIT_TYPE_UNIFORM);
>  	local_irq_enable();
>  	return ret;
>  }

There are some code duplications but overall looks good to me. The lru
lock, unfreeze loop, and the last unfreeze are replicated across two
functions. I cannot think of an easy alternative. A tiny improvement
might be instead of replicating unfreeze comments, changing one to point
to the other one and asking the code should be in sync.

-- 
Best Regards,
Yan, Zi



  reply	other threads:[~2026-08-08 18:58 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-07 21:17 [PATCH RFC 00/13] mm/huge_memory: clean up folio split and lift swapcache split limits Kairui Song via B4 Relay
2026-08-07 21:17 ` [PATCH RFC 01/13] mm/swap: fix off-by-one in swap cache replace sanity check Kairui Song via B4 Relay
2026-08-08 17:07   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 02/13] mm/huge_memory: fix rejection of swap cache folios with a mapping Kairui Song via B4 Relay
2026-08-08 18:01   ` Zi Yan
2026-08-08 18:14     ` Kairui Song
2026-08-08 18:53       ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 03/13] mm/huge_memory: invert folio_ref_freeze() check to reduce indentation Kairui Song via B4 Relay
2026-08-08 18:04   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 04/13] mm/huge_memory: split the routine for splitting anon and file folio Kairui Song via B4 Relay
2026-08-08 18:52   ` Zi Yan [this message]
2026-08-08 20:19     ` Kairui Song
2026-08-07 21:17 ` [PATCH RFC 05/13] mm/huge_memory: consolidate irq and locking for folio split Kairui Song via B4 Relay
2026-08-09  1:54   ` Zi Yan
2026-08-10  3:35     ` Kairui Song
2026-08-07 21:17 ` [PATCH RFC 06/13] mm/huge_memory: move EOF trimming into the file split helper Kairui Song via B4 Relay
2026-08-09  1:59   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 07/13] mm/huge_memory: move unmap and remap into the split helpers Kairui Song via B4 Relay
2026-08-09  2:13   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 08/13] mm/huge_memory: move anon_vma and filemap management into " Kairui Song via B4 Relay
2026-08-09  2:22   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 09/13] mm/huge_memory: move memcg switch into the file split helper Kairui Song via B4 Relay
2026-08-09  2:29   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 10/13] mm/huge_memory: allow splitting mappingless swap cache folios Kairui Song via B4 Relay
2026-08-09  2:35   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 11/13] mm/huge_memory: clean up after-split folio freeing in __folio_split Kairui Song via B4 Relay
2026-08-09  2:41   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 12/13] mm/huge_memory: lift order-0 restriction for swapcache split Kairui Song via B4 Relay
2026-08-09  2:46   ` Zi Yan
2026-08-07 21:17 ` [PATCH RFC 13/13] mm/huge_memory: count only swap cache refs in anon folio split Kairui Song via B4 Relay
2026-08-09  2:49   ` Zi Yan

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=DKJSFCLP967N.YBR4DNK1NM2N@nvidia.com \
    --to=ziy@nvidia.com \
    --cc=akpm@linux-foundation.org \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=baoquan.he@linux.dev \
    --cc=chrisl@kernel.org \
    --cc=david@kernel.org \
    --cc=dev.jain@arm.com \
    --cc=kasong@tencent.com \
    --cc=lance.yang@linux.dev \
    --cc=liam@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=mhocko@suse.com \
    --cc=nico.pache@linux.dev \
    --cc=nphamcs@gmail.com \
    --cc=rppt@kernel.org \
    --cc=ryan.roberts@arm.com \
    --cc=shikemeng@huaweicloud.com \
    --cc=surenb@google.com \
    --cc=usama.arif@linux.dev \
    --cc=vbabka@kernel.org \
    --cc=youngjun.park@lge.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