mm/page-flags: check page/folio->private instead of PG_private

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.  PG_swapcache and folio->swap.val cannot
be set/clear as a whole, so excluding swapcache with
folio_test_swapcache() is not reliable.  Instead, use
folio_test_swapbacked(), since PG_swapbacked is stable when a folio is
added to/removed from swapcache.  Add a helper,
folio_has_attached_private(), for this check.

folio_test_private() and PagePrivate() now read folio/page->private
plainly instead of an atomic read of PG_private bit, so KCSAN complains
about possible data races.  Annotate them with data_race().

folio_expected_ref_count() can be called without the folio lock, so
annotate folio->mapping with data_race() while at it.

folio_set/clear_private() and Set/ClearPagePrivate() become no-ops.
PG_private is no longer checked at page free time. They will be removed in
an upcoming commit.

Remove KPF_PRIVATE since PG_private is no longer used.

Link: https://lore.kernel.org/20260920-remove-pg_private-v5-10-bb68b6a21869@nvidia.com
Signed-off-by: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Assisted-by: LLM
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: "Liam R. Howlett" <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Nico Pache <nico.pache@linux.dev>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Usama Arif <usama.arif@linux.dev>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Rakie Kim <rakie.kim@sk.com>
Cc: Byungchul Park <byungchul@sk.com>
Cc: Gregory Price <gourry@gourry.net>
Cc: Ying Huang <ying.huang@linux.alibaba.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Qi Zheng <qi.zheng@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Kairui Song <kasong@tencent.com>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Wei Xu <weixugc@google.com>
This commit is contained in:
Zi Yan 2026-09-20 22:28:06 -04:00 committed by Andrew Morton
parent 96fd9017e3
commit e01f7ddcb1
10 changed files with 68 additions and 22 deletions

View File

@ -232,7 +232,6 @@ u64 stable_page_flags(const struct page *page)
u |= kpf_copy_bit(k, KPF_RESERVED, PG_reserved);
u |= kpf_copy_bit(k, KPF_OWNER_2, PG_owner_2);
u |= kpf_copy_bit(k, KPF_PRIVATE, PG_private);
u |= kpf_copy_bit(k, KPF_PRIVATE_2, PG_private_2);
u |= kpf_copy_bit(k, KPF_OWNER_PRIVATE, PG_owner_priv_1);
u |= kpf_copy_bit(k, KPF_ARCH, PG_arch_1);

View File

@ -11,7 +11,6 @@
#define KPF_RESERVED 32
#define KPF_MLOCKED 33
#define KPF_OWNER_2 34
#define KPF_PRIVATE 35
#define KPF_PRIVATE_2 36
#define KPF_OWNER_PRIVATE 37
#define KPF_ARCH 38

View File

@ -3005,9 +3005,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),
@ -3045,10 +3045,15 @@ 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.
*/
ref_count += folio_has_attached_private(folio);
}
/* One reference per page table mapping. */

View File

@ -575,9 +575,31 @@ FOLIO_FLAG(swapbacked, FOLIO_HEAD_PAGE)
/*
* Private page markings that may be used by the filesystem that owns the page
* for its own purposes.
* - PG_private and PG_private_2 cause release_folio() and co to be invoked
* - folio->private and PG_private_2 cause release_folio() and co to be invoked
*/
PAGEFLAG(Private, private, PF_ANY)
static __always_inline bool folio_test_private(const struct folio *folio)
{
/*
* data_race() is added for readers without holding the folio lock.
* Only the NULL/non-NULL answer is used and both are valid while
* private is being attached or detached, so the race is benign.
*/
return data_race(folio->private);
}
static __always_inline int PagePrivate(const struct page *page)
{
/* See folio_test_private() for data_race() use */
return !!data_race(page->private);
}
/* no-ops during transition */
static __always_inline void folio_set_private(struct folio *folio) { }
static __always_inline void folio_clear_private(struct folio *folio) { }
static __always_inline void SetPagePrivate(struct page *page) { }
static __always_inline void ClearPagePrivate(struct page *page) { }
FOLIO_FLAG(private_2, FOLIO_HEAD_PAGE)
/* owner_2 can be set on tail pages for anon memory */
@ -1169,7 +1191,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
*/
#define PAGE_FLAGS_CHECK_AT_FREE \
(1UL << PG_lru | 1UL << PG_locked | \
1UL << PG_private | 1UL << PG_private_2 | \
1UL << PG_private_2 | \
1UL << PG_writeback | 1UL << PG_reserved | \
1UL << PG_active | \
1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK)
@ -1193,8 +1215,31 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
(0xffUL /* order */ | 1UL << PG_has_hwpoisoned | \
1UL << PG_large_rmappable | 1UL << PG_partially_mapped)
#define PAGE_FLAGS_PRIVATE \
(1UL << PG_private | 1UL << PG_private_2)
/**
* folio_has_attached_private - check if the folio has private data attached
* @folio: The folio to check.
*
* Use this in code that may encounter swapcache or hugetlb folios but only
* wants to detect attached private data.
*
* Return: true if the folio has private data attached.
*/
static inline bool folio_has_attached_private(const struct folio *folio)
{
/*
* Swapcache stores swp_entry_t in folio->swap, a union with
* folio->private, and hugetlb stores its own flags in folio->private;
* both are excluded.
*
* NOTE: For swapcache, folio->swap.val PG_swapcache are not set as
* a whole, so folio_test_swapcache() is not reliable to exclude
* swapcache. Use folio_test_swapbacked() instead, since it remains set
* when a folio is added to/removed from swapcache.
*/
return folio_test_private(folio) && !folio_test_swapbacked(folio) &&
!folio_test_hugetlb(folio);
}
/**
* folio_has_private - Determine if folio has private stuff
* @folio: The folio to be checked
@ -1204,7 +1249,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
*/
static inline int folio_has_private(const struct folio *folio)
{
return !!(folio->flags.f & PAGE_FLAGS_PRIVATE);
return folio_has_attached_private(folio) || folio_test_private_2(folio);
}
#undef PF_ANY

View File

@ -22,7 +22,7 @@
(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_has_attached_private(folio) ? PAGEMAP_BUFFERS : 0) \
)
TRACE_EVENT(mm_lru_insertion,

View File

@ -4845,7 +4845,7 @@ 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_has_attached_private(folio) &&
folio_expected_ref_count(folio) != folio_ref_count(folio))
goto next;

View File

@ -1327,7 +1327,7 @@ 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_has_attached_private(src)) {
try_to_free_buffers(src);
goto out;
}

View File

@ -2705,7 +2705,7 @@ 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_has_attached_private(folio));
if (mapping->host) {
/* !PageAnon && !swapper_space */

View File

@ -1029,7 +1029,7 @@ static void folio_check_dirty_writeback(struct folio *folio,
*writeback = folio_test_writeback(folio);
/* Verify dirty/writeback state if the filesystem supports it */
if (!folio_test_private(folio))
if (!folio_has_attached_private(folio))
return;
mapping = folio_mapping(folio);

View File

@ -73,7 +73,6 @@
#define KPF_RESERVED 32
#define KPF_MLOCKED 33
#define KPF_OWNER_2 34
#define KPF_PRIVATE 35
#define KPF_PRIVATE_2 36
#define KPF_OWNER_PRIVATE 37
#define KPF_ARCH 38
@ -131,7 +130,6 @@ static const char * const page_flag_names[] = {
[KPF_RESERVED] = "r:reserved",
[KPF_MLOCKED] = "m:mlocked",
[KPF_OWNER_2] = "d:owner_2",
[KPF_PRIVATE] = "P:private",
[KPF_PRIVATE_2] = "p:private_2",
[KPF_OWNER_PRIVATE] = "O:owner_private",
[KPF_ARCH] = "h:arch",