Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: "Zi Yan" <ziy@nvidia.com>
To: "David Hildenbrand" <david@kernel.org>,
	"Matthew Wilcox (Oracle)" <willy@infradead.org>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Muchun Song" <muchun.song@linux.dev>,
	"Lorenzo Stoakes" <ljs@kernel.org>,
	"Liam R. Howlett" <liam@infradead.org>,
	"Vlastimil Babka" <vbabka@kernel.org>,
	"Mike Rapoport" <rppt@kernel.org>,
	"Suren Baghdasaryan" <surenb@google.com>,
	"Michal Hocko" <mhocko@suse.com>,
	"Baolin Wang" <baolin.wang@linux.alibaba.com>,
	"Nico Pache" <nico.pache@linux.dev>,
	"Ryan Roberts" <ryan.roberts@arm.com>,
	"Dev Jain" <dev.jain@arm.com>, "Barry Song" <baohua@kernel.org>,
	"Lance Yang" <lance.yang@linux.dev>,
	"Usama Arif" <usama.arif@linux.dev>,
	"Gregory Price" <gourry@gourry.net>,
	"Ying Huang" <ying.huang@linux.alibaba.com>,
	"Alistair Popple" <apopple@nvidia.com>,
	"Johannes Weiner" <hannes@cmpxchg.org>,
	"Qi Zheng" <qi.zheng@linux.dev>,
	"Shakeel Butt" <shakeel.butt@linux.dev>,
	"Kairui Song" <kasong@tencent.com>
Cc: <linux-mm@kvack.org>, <linux-kernel@vger.kernel.org>,
	"Zi Yan" <ziy@nvidia.com>, "Steven Rostedt" <rostedt@goodmis.org>,
	"Masami Hiramatsu" <mhiramat@kernel.org>,
	"Jan Kara" <jack@suse.cz>,
	"Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>,
	"Matthew Brost" <matthew.brost@intel.com>,
	"Joshua Hahn" <joshua.hahnjy@gmail.com>,
	"Rakie Kim" <rakie.kim@sk.com>,
	"Byungchul Park" <byungchul@sk.com>,
	"Axel Rasmussen" <axelrasmussen@google.com>,
	"Yuanchu Xie" <yuanchu@google.com>, "Wei Xu" <weixugc@google.com>,
	<linux-fsdevel@vger.kernel.org>,
	<linux-trace-kernel@vger.kernel.org>
Subject: Re: [PATCH v2 09/14] mm/page-flags: check page/folio->private instead of PG_private
Date: Mon, 31 Aug 2026 22:11:45 -0400	[thread overview]
Message-ID: <DL3M668E4MQ7.1OEAAHUPN9S6S@nvidia.com> (raw)
In-Reply-To: <20260831-remove-pg_private-v2-9-3668159cd9e8@nvidia.com>

On Mon Aug 31, 2026 at 3:25 PM EDT, Zi Yan wrote:
> After the changes of the prior commits, page/folio->private != NULL is now
> equivalent to checking PG_private.
>
> Stop checking PG_private on pages and folios and use page/folio->private
> instead, except swapcache and hugetlb folios, because the former uses a
> field (swp_entry_t swap) overlapping with ->private and the latter sets its
> flags in ->private. Exclude swapcache and hugetlb when the code is meant to
> check PG_private only.
>
> folio_expected_ref_count() can be called without folio lock, so annotate
> folio_test_private() with data_race() to avoid triggering race condition
> checks. While at it, annotate folio->mapping too.
>
> folio_set/clear_private() and Set/ClearPagePrivate() become no-ops.
> PG_private is no longer checked at page free time.
>
> Remove KPF_PRIVATE since PG_private is no longer used.
>
<snip>

> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index dd09c438fa23e..786f8a47cea6d 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3022,9 +3022,9 @@ static inline bool folio_maybe_mapped_shared(struct folio *folio)
>   * @folio: the folio
>   *
>   * Calculate the expected folio refcount, taking references from the pagecache,
> - * swapcache, PG_private and page table mappings into account. Useful in
> - * combination with folio_ref_count() to detect unexpected references (e.g.,
> - * GUP or other temporary references).
> + * swapcache, private data (folio->private != NULL) and page table mappings into
> + * account. Useful in combination with folio_ref_count() to detect unexpected
> + * references (e.g., GUP or other temporary references).
>   *
>   * Does currently not consider references from the LRU cache. If the folio
>   * was isolated from the LRU (which is the case during migration or split),
> @@ -3062,10 +3062,18 @@ static inline int folio_expected_ref_count(const struct folio *folio)
>  	ref_count += folio_test_swapcache(folio) << order;
>  
>  	if (!folio_test_anon(folio)) {
> -		/* One reference per page from the pagecache. */
> -		ref_count += !!folio->mapping << order;
> -		/* One reference from PG_private. */
> -		ref_count += folio_test_private(folio);
> +		/*
> +		 * One reference per page from the pagecache.
> +		 * Use data_race() since folio might not be locked.
> +		 */
> +		ref_count += !!data_race(folio->mapping) << order;
> +		/*
> +		 * One reference from filesystem private data.
> +		 * Use data_race() since folio might not be locked.
> +		 */
> +		ref_count += data_race(folio_test_private(folio)) &&
> +			     !folio_test_hugetlb(folio) &&
> +			     !folio_test_swapcache(folio);

Sashiko said [Severity: High]:

Could this lockless evaluation of folio->private and PG_swapcache lead to a
TOCTOU race for shmem folios during swap cache removal?

During __delete_from_swap_cache, folio->swap.val (which aliases
folio->private) is cleared before PG_swapcache. Without memory barriers, a
lockless reader like memfd_tag_pins calling folio_expected_ref_count could
observe the stale non-zero folio->private and the newly cleared PG_swapcache.

This would evaluate the condition above as true, falsely inflating the
expected refcount by 1. If the folio has exactly one extra GUP pin, the
inflated expected refcount would match the actual refcount, bypassing the
F_SEAL_WRITE protections.

Answer:

Yes, it is a problem, since folio->swap.val and PG_swapcache cannot be
read as a whole, when __swap_cache_do_del_folio() (was
__delete_from_swap_cache()) clears folio->swap.val first then
PG_swapcache.

Fortunately, PG_swapbacked is stable during the process. So the code can
exclude swapcache folios by checking PG_swapbacked instead.

In the next patch, folio_test_fs_private() will be changed to replace the
new check: folio_test_private() && !folio_test_hugetlb(folio) &&
!folio_test_swapbacked().

<snip>

> diff --git a/include/trace/events/pagemap.h b/include/trace/events/pagemap.h
> index 36c3a90f0acca..fb9abec40ec79 100644
> --- a/include/trace/events/pagemap.h
> +++ b/include/trace/events/pagemap.h
> @@ -22,7 +22,9 @@
>  	(folio_test_swapcache(folio)	? PAGEMAP_SWAPCACHE  : 0) | \
>  	(folio_test_swapbacked(folio)	? PAGEMAP_SWAPBACKED : 0) | \
>  	(folio_test_mappedtodisk(folio)	? PAGEMAP_MAPPEDDISK : 0) | \
> -	(folio_test_private(folio)	? PAGEMAP_BUFFERS    : 0) \
> +	(folio_test_private(folio) && \
> +	 !folio_test_swapcache(folio) && \
> +	 !folio_test_hugetlb(folio)	? PAGEMAP_BUFFERS    : 0) \

Sashiko said:
hould this lockless read of folio->private be annotated with data_race() or
READ_ONCE()?

Since tracepoints like TRACE_EVENT(mm_lru_insertion) are often executed
without the folio lock, concurrent modifications to folio->private could
trigger KCSAN data race warnings here.

Answer:

Yes, will annotate with data_race() here.

>  	)
>  
>  TRACE_EVENT(mm_lru_insertion,
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index ced400f72d43a..546b37ccce37f 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -4810,7 +4810,9 @@ static int split_huge_pages_pid(int pid, unsigned long vaddr_start,
>  		 * will try to drop it before split and then check if the folio
>  		 * can be split or not. So skip the check here.
>  		 */
> -		if (!folio_test_private(folio) &&
> +		if (!(folio_test_private(folio) &&
> +		      !folio_test_swapcache(folio) &&
> +		      !folio_test_hugetlb(folio)) &&
>  		    folio_expected_ref_count(folio) != folio_ref_count(folio))
>  			goto next;

This is another lockless check and data_race() annotation is needed.


>  
> diff --git a/mm/migrate.c b/mm/migrate.c
> index 15b45832bcfa7..f14e7bfee14bd 100644
> --- a/mm/migrate.c
> +++ b/mm/migrate.c
> @@ -1327,7 +1327,8 @@ static int migrate_folio_unmap(new_folio_t get_new_folio,
>  	 * free the metadata, so the page can be freed.
>  	 */
>  	if (!src->mapping) {
> -		if (folio_test_private(src)) {
> +		if (folio_test_private(src) && !folio_test_swapcache(src) &&
> +		    !folio_test_hugetlb(src)) {
>  			try_to_free_buffers(src);
>  			goto out;
>  		}

Sashiko said [Severity: High]:
This is a pre-existing issue, but does this generic migration code blindly
assume all private data on an orphaned folio corresponds to buffer heads?

If a folio is orphaned after truncation (src->mapping is NULL) but retains
private data because an earlier release failed, try_to_free_buffers is
unconditionally called. For filesystems like iomap, folio->private contains
custom structures (like iomap_folio_state), not a linked list of buffer
heads. Calling try_to_free_buffers here could lead to type confusion and a
kernel panic when accessing what it expects to be bh->b_this_page.


Answer:

Not an issue. With the help of gpt-5.6-sol, this issue only affects
buffer heads. Folios using iomap always clears folio->private before
folio->mapping is cleared.

> diff --git a/mm/page-writeback.c b/mm/page-writeback.c
> index eeab25d6ce364..4022d6c381896 100644
> --- a/mm/page-writeback.c
> +++ b/mm/page-writeback.c
> @@ -2705,7 +2705,10 @@ bool filemap_dirty_folio(struct address_space *mapping, struct folio *folio)
>  	if (folio_test_set_dirty(folio))
>  		return false;
>  
> -	__folio_mark_dirty(folio, mapping, !folio_test_private(folio));
> +	__folio_mark_dirty(folio, mapping,
> +			   !(folio_test_private(folio) &&
> +			     !folio_test_swapcache(folio) &&
> +			     !folio_test_hugetlb(folio)));
>  

Another place needs data_race() annotation since filemap_dirty_folio()
can be called locklessly (e.g., zap_pte_range()).


-- 
Best Regards,
Yan, Zi



  reply	other threads:[~2026-09-01  2:12 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-31 19:25 [PATCH v2 00/14] Remove PG_private by using page/folio->private checks instead Zi Yan
2026-08-31 19:25 ` [PATCH v2 01/14] mm/zsmalloc: replace PG_private with pointer comparison Zi Yan
2026-08-31 19:25 ` [PATCH v2 02/14] perf/ring_buffer: stop using PG_private as AUX page high-order marker Zi Yan
2026-08-31 19:25 ` [PATCH v2 03/14] xen/grant-table: stop setting PG_private on pages for grant mapping Zi Yan
2026-08-31 19:25 ` [PATCH v2 04/14] fscrypt: stop setting PG_private on bounce page Zi Yan
2026-08-31 19:25 ` [PATCH v2 05/14] mm/hugetlb: use direct assignment instead of folio_change_private() Zi Yan
2026-09-03  0:05   ` Gregory Price
2026-08-31 19:25 ` [PATCH v2 06/14] f2fs: stop using PG_private Zi Yan
2026-09-03 15:37   ` Jaegeuk Kim
2026-08-31 19:25 ` [PATCH v2 07/14] erofs: mm/pagemap: add readahead_folio_last() to avoid folio->private Zi Yan
2026-08-31 19:25 ` [PATCH v2 08/14] erofs: use folio_attach/detach_private() instead of direct assignment Zi Yan
2026-08-31 19:25 ` [PATCH v2 09/14] mm/page-flags: check page/folio->private instead of PG_private Zi Yan
2026-09-01  2:11   ` Zi Yan [this message]
2026-08-31 19:25 ` [PATCH v2 10/14] mm/page-flags: introduce folio_test_fs_private() Zi Yan
2026-08-31 19:25 ` [PATCH v2 11/14] treewide: remove folio_set/clear_private() Zi Yan
2026-08-31 19:25 ` [PATCH v2 12/14] treewide: replace PagePrivate() with page_private() Zi Yan
2026-08-31 19:25 ` [PATCH v2 13/14] treewide: adjust comments on PagePrivate and PG_private Zi Yan
2026-08-31 19:25 ` [PATCH v2 14/14] mm/page-flags: remove PG_private Zi Yan
2026-09-01  2:17   ` Zi Yan
2026-09-01 15:55   ` Steven Rostedt
2026-09-01 16:01     ` Zi Yan
2026-09-01 17:50       ` Steven Rostedt
2026-09-02 17:09   ` Usama Arif
2026-09-02 17:57     ` Zi Yan
2026-09-03 16:10 ` [f2fs-dev] [PATCH v2 00/14] Remove PG_private by using page/folio->private checks instead patchwork-bot+f2fs

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=DL3M668E4MQ7.1OEAAHUPN9S6S@nvidia.com \
    --to=ziy@nvidia.com \
    --cc=akpm@linux-foundation.org \
    --cc=apopple@nvidia.com \
    --cc=axelrasmussen@google.com \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=byungchul@sk.com \
    --cc=david@kernel.org \
    --cc=dev.jain@arm.com \
    --cc=gourry@gourry.net \
    --cc=hannes@cmpxchg.org \
    --cc=jack@suse.cz \
    --cc=joshua.hahnjy@gmail.com \
    --cc=kasong@tencent.com \
    --cc=lance.yang@linux.dev \
    --cc=liam@infradead.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=ljs@kernel.org \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=matthew.brost@intel.com \
    --cc=mhiramat@kernel.org \
    --cc=mhocko@suse.com \
    --cc=muchun.song@linux.dev \
    --cc=nico.pache@linux.dev \
    --cc=qi.zheng@linux.dev \
    --cc=rakie.kim@sk.com \
    --cc=rostedt@goodmis.org \
    --cc=rppt@kernel.org \
    --cc=ryan.roberts@arm.com \
    --cc=shakeel.butt@linux.dev \
    --cc=surenb@google.com \
    --cc=usama.arif@linux.dev \
    --cc=vbabka@kernel.org \
    --cc=weixugc@google.com \
    --cc=willy@infradead.org \
    --cc=ying.huang@linux.alibaba.com \
    --cc=yuanchu@google.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