* [PATCH v5 08/17] erofs: mm/pagemap: add readahead_folio_last() to avoid folio->private
2026-09-21 2:27 [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead Zi Yan
@ 2026-09-21 2:28 ` Zi Yan
2026-09-21 8:20 ` Lance Yang
2026-09-21 2:28 ` [PATCH v5 10/17] mm/page-flags: check page/folio->private instead of PG_private Zi Yan
` (4 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Zi Yan @ 2026-09-21 2:28 UTC (permalink / raw)
To: David Hildenbrand, Matthew Wilcox (Oracle), Andrew Morton,
Muchun Song, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Gregory Price, Ying Huang, Alistair Popple,
Johannes Weiner, Qi Zheng, Shakeel Butt, Kairui Song
Cc: linux-mm, linux-kernel, Zi Yan, Gao Xiang, Chao Yu, Jan Kara,
Yue Hu, Jeffle Xu, Sandeep Dhavale, Hongbo Li, Chunhai Guo,
linux-erofs, linux-fsdevel
erofs needs to traverse readahead folios in reverse order to achieve
maximum performance by
1. reading all folios from readahead_folio();
2. storing the prior folio pointer in folio->private;
3. traverse from the last folio to the first one.
Add readahead_folio_last() to achieve the same function without using
folio->private. __readahead_advance() helper shares readahead_control
adjustment code among __readahead_folio(), readahead_folio_last(), and
__readahead_batch() by checking new private member, _forward, of
readahead_control.
It prepares for a future commit that replaces PG_private checks with
!folio->private checks. After switching the checks, erofs's use of
folio->private without bumping folio refcount can cause unexpected
outcomes, e.g., in filemap_release_folio(), try_to_free_buffers() becomes
reachable.
No functional change intended.
Assisted-by: LLM
To: Gao Xiang <xiang@kernel.org>
To: Chao Yu <chao@kernel.org>
To: "Matthew Wilcox (Oracle)" <willy@infradead.org>
To: Jan Kara <jack@suse.cz>
Cc: Yue Hu <zbestahu@gmail.com>
Cc: Jeffle Xu <jefflexu@linux.alibaba.com>
Cc: Sandeep Dhavale <dhavale@google.com>
Cc: Hongbo Li <hongbohbli@tencent.com>
Cc: Chunhai Guo <guochunhai@vivo.com>
Cc: linux-erofs@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-mm@kvack.org
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
fs/erofs/zdata.c | 13 +++--------
include/linux/pagemap.h | 60 ++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 55 insertions(+), 18 deletions(-)
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index e1e25ca0d1904..78fd7d980e957 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -1898,21 +1898,14 @@ static void z_erofs_readahead(struct readahead_control *rac)
struct inode *realinode = erofs_real_inode(sharedinode, &need_iput);
Z_EROFS_DEFINE_FRONTEND(f, realinode, sharedinode, readahead_pos(rac));
unsigned int nrpages = readahead_count(rac);
- struct folio *head = NULL, *folio;
+ struct folio *folio;
int err;
trace_erofs_readahead(realinode, readahead_index(rac), nrpages, false);
z_erofs_pcluster_readmore(&f, rac, true);
- while ((folio = readahead_folio(rac))) {
- folio->private = head;
- head = folio;
- }
-
- /* traverse in reverse order for best metadata I/O performance */
- while (head) {
- folio = head;
- head = folio_get_private(folio);
+ /* traverse from last to first for best metadata I/O performance */
+ while ((folio = readahead_folio_last(rac))) {
err = z_erofs_scan_folio(&f, folio, true);
if (err && err != -EINTR)
erofs_err(realinode->i_sb, "readahead error at folio %lu @ nid %llu",
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 939f3a5e973f6..1e3462357aaa4 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -1415,6 +1415,7 @@ struct readahead_control {
bool dropbehind;
bool _workingset;
unsigned long _pflags;
+ bool _forward;
};
#define DEFINE_READAHEAD(ractl, f, r, m, i) \
@@ -1479,18 +1480,29 @@ void page_cache_async_readahead(struct address_space *mapping,
page_cache_async_ra(&ractl, folio, req_count);
}
+/*
+ * Adjust readahead_control to ensure next folio comes from
+ * [_index, _index + _nr_pages) afterwards and reset _batch_count.
+ */
+static inline void __readahead_advance(struct readahead_control *rac)
+{
+ if (rac->_forward)
+ rac->_index += rac->_batch_count;
+
+ rac->_nr_pages -= rac->_batch_count;
+ rac->_batch_count = 0;
+}
+
static inline struct folio *__readahead_folio(struct readahead_control *ractl)
{
struct folio *folio;
BUG_ON(ractl->_batch_count > ractl->_nr_pages);
- ractl->_nr_pages -= ractl->_batch_count;
- ractl->_index += ractl->_batch_count;
+ __readahead_advance(ractl);
+ ractl->_forward = true;
- if (!ractl->_nr_pages) {
- ractl->_batch_count = 0;
+ if (!ractl->_nr_pages)
return NULL;
- }
folio = xa_load(&ractl->mapping->i_pages, ractl->_index);
VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
@@ -1516,6 +1528,39 @@ static inline struct folio *readahead_folio(struct readahead_control *ractl)
return folio;
}
+/**
+ * readahead_folio_last - Get the next folio to read, from the tail.
+ * @ractl: The current readahead request.
+ *
+ * Like readahead_folio(), but walks the range back-to-front. The folio is
+ * returned locked with its refcount dropped; the caller unlocks it once I/O
+ * completes. Compound folios are returned once, at their head index.
+ *
+ * Context: The folio is locked.
+ * Return: A pointer to the next folio, or %NULL when done.
+ */
+static inline struct folio *readahead_folio_last(struct readahead_control *ractl)
+{
+ struct folio *folio;
+
+ /* Drop the previously returned batch from the remaining range. */
+ __readahead_advance(ractl);
+ ractl->_forward = false;
+
+ if (!ractl->_nr_pages)
+ return NULL;
+
+ /* xa_load() follows sibling entries, so a tail index returns the head */
+ folio = xa_load(&ractl->mapping->i_pages,
+ ractl->_index + ractl->_nr_pages - 1);
+ VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
+
+ ractl->_batch_count = folio_nr_pages(folio);
+
+ folio_put(folio);
+ return folio;
+}
+
static inline unsigned int __readahead_batch(struct readahead_control *rac,
struct page **array, unsigned int array_sz)
{
@@ -1524,9 +1569,8 @@ static inline unsigned int __readahead_batch(struct readahead_control *rac,
struct folio *folio;
BUG_ON(rac->_batch_count > rac->_nr_pages);
- rac->_nr_pages -= rac->_batch_count;
- rac->_index += rac->_batch_count;
- rac->_batch_count = 0;
+ __readahead_advance(rac);
+ rac->_forward = true;
xas_set(&xas, rac->_index);
rcu_read_lock();
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v5 08/17] erofs: mm/pagemap: add readahead_folio_last() to avoid folio->private
2026-09-21 2:28 ` [PATCH v5 08/17] erofs: mm/pagemap: add readahead_folio_last() to avoid folio->private Zi Yan
@ 2026-09-21 8:20 ` Lance Yang
0 siblings, 0 replies; 12+ messages in thread
From: Lance Yang @ 2026-09-21 8:20 UTC (permalink / raw)
To: Zi Yan
Cc: linux-mm, David Hildenbrand, Dev Jain, Usama Arif, Gregory Price,
linux-kernel, Muchun Song, Gao Xiang, Chao Yu, Ryan Roberts,
Matthew Wilcox (Oracle), Jan Kara, Mike Rapoport, Nico Pache,
Baolin Wang, Lorenzo Stoakes, Michal Hocko, Qi Zheng, Yue Hu,
Jeffle Xu, Sandeep Dhavale, Shakeel Butt, Alistair Popple,
Johannes Weiner, Hongbo Li, Kairui Song, Liam R. Howlett,
Chunhai Guo, linux-erofs, linux-fsdevel, Ying Huang, Barry Song,
Suren Baghdasaryan, Vlastimil Babka, Andrew Morton
On 2026/9/21 10:28, Zi Yan wrote:
> erofs needs to traverse readahead folios in reverse order to achieve
> maximum performance by
> 1. reading all folios from readahead_folio();
> 2. storing the prior folio pointer in folio->private;
> 3. traverse from the last folio to the first one.
>
> Add readahead_folio_last() to achieve the same function without using
> folio->private. __readahead_advance() helper shares readahead_control
> adjustment code among __readahead_folio(), readahead_folio_last(), and
> __readahead_batch() by checking new private member, _forward, of
> readahead_control.
>
> It prepares for a future commit that replaces PG_private checks with
> !folio->private checks. After switching the checks, erofs's use of
> folio->private without bumping folio refcount can cause unexpected
> outcomes, e.g., in filemap_release_folio(), try_to_free_buffers() becomes
> reachable.
>
> No functional change intended.
>
> Assisted-by: LLM
> To: Gao Xiang <xiang@kernel.org>
> To: Chao Yu <chao@kernel.org>
> To: "Matthew Wilcox (Oracle)" <willy@infradead.org>
> To: Jan Kara <jack@suse.cz>
> Cc: Yue Hu <zbestahu@gmail.com>
> Cc: Jeffle Xu <jefflexu@linux.alibaba.com>
> Cc: Sandeep Dhavale <dhavale@google.com>
> Cc: Hongbo Li <hongbohbli@tencent.com>
> Cc: Chunhai Guo <guochunhai@vivo.com>
> Cc: linux-erofs@lists.ozlabs.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-fsdevel@vger.kernel.org
> Cc: linux-mm@kvack.org
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---
Nothing jumped out at me, feel free to add:
Reviewed-by: Lance Yang <lance.yang@linux.dev>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v5 10/17] mm/page-flags: check page/folio->private instead of PG_private
2026-09-21 2:27 [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead Zi Yan
2026-09-21 2:28 ` [PATCH v5 08/17] erofs: mm/pagemap: add readahead_folio_last() to avoid folio->private Zi Yan
@ 2026-09-21 2:28 ` Zi Yan
2026-09-21 9:22 ` David Hildenbrand (Arm)
2026-09-21 2:28 ` [PATCH v5 11/17] treewide: remove folio_set/clear_private() usage Zi Yan
` (3 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Zi Yan @ 2026-09-21 2:28 UTC (permalink / raw)
To: David Hildenbrand, Matthew Wilcox (Oracle), Andrew Morton,
Muchun Song, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Gregory Price, Ying Huang, Alistair Popple,
Johannes Weiner, Qi Zheng, Shakeel Butt, Kairui Song
Cc: linux-mm, linux-kernel, Zi Yan, Steven Rostedt, Masami Hiramatsu,
Jan Kara, Mathieu Desnoyers, Matthew Brost, Joshua Hahn,
Rakie Kim, Byungchul Park, Axel Rasmussen, Yuanchu Xie, Wei Xu,
linux-fsdevel, linux-trace-kernel
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.
Assisted-by: LLM
Signed-off-by: Zi Yan <ziy@nvidia.com>
To: Andrew Morton <akpm@linux-foundation.org>
To: David Hildenbrand <david@kernel.org>
To: Steven Rostedt <rostedt@goodmis.org>
To: Masami Hiramatsu <mhiramat@kernel.org>
To: Lorenzo Stoakes <ljs@kernel.org>
To: "Matthew Wilcox (Oracle)" <willy@infradead.org>
To: Jan Kara <jack@suse.cz>
To: 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: Zi Yan <ziy@nvidia.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>
Cc: linux-kernel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: linux-trace-kernel@vger.kernel.org
---
fs/proc/page.c | 1 -
include/linux/kernel-page-flags.h | 1 -
include/linux/mm.h | 19 ++++++++-----
include/linux/page-flags.h | 57 ++++++++++++++++++++++++++++++++++-----
include/trace/events/pagemap.h | 2 +-
mm/huge_memory.c | 2 +-
mm/migrate.c | 2 +-
mm/page-writeback.c | 2 +-
mm/vmscan.c | 2 +-
tools/mm/page-types.c | 2 --
10 files changed, 68 insertions(+), 22 deletions(-)
diff --git a/fs/proc/page.c b/fs/proc/page.c
index 260772b20bd99..f90e1030825e9 100644
--- a/fs/proc/page.c
+++ b/fs/proc/page.c
@@ -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);
diff --git a/include/linux/kernel-page-flags.h b/include/linux/kernel-page-flags.h
index 196778a087c4d..fe5ab6e50bd70 100644
--- a/include/linux/kernel-page-flags.h
+++ b/include/linux/kernel-page-flags.h
@@ -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
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 66d384da4433b..d9392ac8dff9f 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3004,9 +3004,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),
@@ -3044,10 +3044,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. */
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 7080a6a1a79e7..6d839f50bdcb7 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -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
diff --git a/include/trace/events/pagemap.h b/include/trace/events/pagemap.h
index 36c3a90f0acca..5d47b774633a4 100644
--- a/include/trace/events/pagemap.h
+++ b/include/trace/events/pagemap.h
@@ -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,
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 30b7c63b0e359..8aa2daba37391 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4846,7 +4846,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;
diff --git a/mm/migrate.c b/mm/migrate.c
index a369d0c95c386..b7b92925a28c3 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -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;
}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index eeab25d6ce364..499a35473e4f3 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -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 */
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 80041e2b8049c..dd6261c862794 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -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);
diff --git a/tools/mm/page-types.c b/tools/mm/page-types.c
index 7fc5a8be5997f..47e4781c5fc38 100644
--- a/tools/mm/page-types.c
+++ b/tools/mm/page-types.c
@@ -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",
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v5 10/17] mm/page-flags: check page/folio->private instead of PG_private
2026-09-21 2:28 ` [PATCH v5 10/17] mm/page-flags: check page/folio->private instead of PG_private Zi Yan
@ 2026-09-21 9:22 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 12+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-21 9:22 UTC (permalink / raw)
To: Zi Yan, Matthew Wilcox (Oracle), Andrew Morton, Muchun Song,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Gregory Price, Ying Huang, Alistair Popple, Johannes Weiner,
Qi Zheng, Shakeel Butt, Kairui Song
Cc: linux-mm, linux-kernel, Steven Rostedt, Masami Hiramatsu,
Jan Kara, Mathieu Desnoyers, Matthew Brost, Joshua Hahn,
Rakie Kim, Byungchul Park, Axel Rasmussen, Yuanchu Xie, Wei Xu,
linux-fsdevel, linux-trace-kernel
On 9/21/26 04:28, 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. 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.
>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v5 11/17] treewide: remove folio_set/clear_private() usage
2026-09-21 2:27 [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead Zi Yan
2026-09-21 2:28 ` [PATCH v5 08/17] erofs: mm/pagemap: add readahead_folio_last() to avoid folio->private Zi Yan
2026-09-21 2:28 ` [PATCH v5 10/17] mm/page-flags: check page/folio->private instead of PG_private Zi Yan
@ 2026-09-21 2:28 ` Zi Yan
2026-09-21 2:28 ` [PATCH v5 17/17] mm/page-flags: remove PG_private Zi Yan
` (2 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Zi Yan @ 2026-09-21 2:28 UTC (permalink / raw)
To: David Hildenbrand, Matthew Wilcox (Oracle), Andrew Morton,
Muchun Song, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Gregory Price, Ying Huang, Alistair Popple,
Johannes Weiner, Qi Zheng, Shakeel Butt, Kairui Song
Cc: linux-mm, linux-kernel, Zi Yan, Trond Myklebust, Anna Schumaker,
Jan Kara, Matthew Brost, Joshua Hahn, Rakie Kim, Byungchul Park,
linux-nfs, linux-fsdevel
They are no-ops now. Remove them.
Assisted-by: LLM
To: Trond Myklebust <trondmy@kernel.org>
To: Anna Schumaker <anna@kernel.org>
To: "Matthew Wilcox (Oracle)" <willy@infradead.org>
To: Jan Kara <jack@suse.cz>
To: Andrew Morton <akpm@linux-foundation.org>
To: David Hildenbrand <david@kernel.org>
Cc: Zi Yan <ziy@nvidia.com>
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: linux-nfs@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-mm@kvack.org
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
fs/nfs/write.c | 2 --
include/linux/pagemap.h | 4 +---
mm/migrate.c | 1 -
3 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 623e7ef1f73d5..b6967b5286691 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -717,7 +717,6 @@ static void nfs_inode_add_request(struct nfs_page *req)
nfs_lock_request(req);
spin_lock(&mapping->i_private_lock);
set_bit(PG_MAPPED, &req->wb_flags);
- folio_set_private(folio);
folio->private = req;
spin_unlock(&mapping->i_private_lock);
atomic_long_inc(&nfsi->nrequests);
@@ -745,7 +744,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)
spin_lock(&mapping->i_private_lock);
folio->private = NULL;
- folio_clear_private(folio);
clear_bit(PG_MAPPED, &req->wb_head->wb_flags);
spin_unlock(&mapping->i_private_lock);
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 1e3462357aaa4..bcbb0afe1a681 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -594,7 +594,6 @@ static inline void folio_attach_private(struct folio *folio, void *data)
{
folio_get(folio);
folio->private = data;
- folio_set_private(folio);
}
/**
@@ -629,9 +628,8 @@ static inline void *folio_detach_private(struct folio *folio)
{
void *data = folio_get_private(folio);
- if (!folio_test_private(folio))
+ if (!data)
return NULL;
- folio_clear_private(folio);
folio->private = NULL;
folio_put(folio);
diff --git a/mm/migrate.c b/mm/migrate.c
index b7b92925a28c3..7e3a81f069744 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -835,7 +835,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
*/
if (folio_test_swapcache(folio))
folio_clear_swapcache(folio);
- folio_clear_private(folio);
/* page->private contains hugetlb specific flags */
if (!folio_test_hugetlb(folio))
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v5 17/17] mm/page-flags: remove PG_private
2026-09-21 2:27 [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead Zi Yan
` (2 preceding siblings ...)
2026-09-21 2:28 ` [PATCH v5 11/17] treewide: remove folio_set/clear_private() usage Zi Yan
@ 2026-09-21 2:28 ` Zi Yan
2026-09-21 2:55 ` sashiko-bot
2026-09-21 4:08 ` [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead Andrew Morton
2026-09-22 4:16 ` Nanzhe Zhao
5 siblings, 1 reply; 12+ messages in thread
From: Zi Yan @ 2026-09-21 2:28 UTC (permalink / raw)
To: David Hildenbrand, Matthew Wilcox (Oracle), Andrew Morton,
Muchun Song, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Gregory Price, Ying Huang, Alistair Popple,
Johannes Weiner, Qi Zheng, Shakeel Butt, Kairui Song
Cc: linux-mm, linux-kernel, Zi Yan, Baoquan He, Pasha Tatashin,
Pratyush Yadav, Jonathan Corbet, Jan Kara, Steven Rostedt,
Masami Hiramatsu, Dave Young, Shuah Khan, Mathieu Desnoyers,
kexec, linux-doc, linux-fsdevel, linux-trace-kernel
folio->private != NULL indicates a folio carries private data, replacing
PG_private. All PG_private users are converted. Remove PG_private and
reserve the space as PG_folio for future use. Unused PG_private functions
are removed too.
Assisted-by: LLM
To: Andrew Morton <akpm@linux-foundation.org>
To: Baoquan He <baoquan.he@linux.dev>
To: Mike Rapoport <rppt@kernel.org>
To: Pasha Tatashin <pasha.tatashin@soleen.com>
To: Pratyush Yadav <pratyush@kernel.org>
To: Jonathan Corbet <corbet@lwn.net>
To: "Matthew Wilcox (Oracle)" <willy@infradead.org>
To: Jan Kara <jack@suse.cz>
To: David Hildenbrand <david@kernel.org>
To: Steven Rostedt <rostedt@goodmis.org>
To: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Dave Young <ruirui.yang@linux.dev>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: "Liam R. Howlett" <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: kexec@lists.infradead.org
Cc: linux-doc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: linux-trace-kernel@vger.kernel.org
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
include/linux/page-flags.h | 18 +-----------------
include/trace/events/mmflags.h | 2 +-
kernel/vmcore_info.c | 1 -
3 files changed, 2 insertions(+), 19 deletions(-)
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 6d839f50bdcb7..b0ddc652e76cc 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -44,10 +44,6 @@
* Consequently, PG_reserved for a page mapped into user space can indicate
* the zero page, the vDSO, MMIO pages or device memory.
*
- * The PG_private bitflag is set on pagecache pages if they contain filesystem
- * specific data (which is normally at page->private). It can be used by
- * private allocations for its own usage.
- *
* During initiation of disk I/O, PG_locked is set. This bit is set before I/O
* and cleared when writeback _starts_ or when read _completes_. PG_writeback
* is set before writeback starts and cleared when it finishes.
@@ -105,7 +101,7 @@ enum pageflags {
PG_owner_2, /* Owner use. If pagecache, fs may use */
PG_arch_1,
PG_reserved,
- PG_private, /* If pagecache, has fs-private data */
+ PG_folio, /* Do not use: reserved for folio identification */
PG_private_2, /* If pagecache, has fs aux data */
PG_reclaim, /* To be reclaimed asap */
PG_swapbacked, /* Page is backed by RAM/swap */
@@ -588,18 +584,6 @@ static __always_inline bool folio_test_private(const struct folio *folio)
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 */
diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
index ef9aa388b84f7..3c153b3ad8450 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -144,7 +144,7 @@ TRACE_DEFINE_ENUM(___GFP_LAST_BIT);
DEF_PAGEFLAG_NAME(owner_2), \
DEF_PAGEFLAG_NAME(arch_1), \
DEF_PAGEFLAG_NAME(reserved), \
- DEF_PAGEFLAG_NAME(private), \
+ DEF_PAGEFLAG_NAME(folio), \
DEF_PAGEFLAG_NAME(private_2), \
DEF_PAGEFLAG_NAME(writeback), \
DEF_PAGEFLAG_NAME(head), \
diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c
index 8614430ca212a..5a417f8a922ab 100644
--- a/kernel/vmcore_info.c
+++ b/kernel/vmcore_info.c
@@ -216,7 +216,6 @@ static int __init crash_save_vmcoreinfo_init(void)
VMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES);
VMCOREINFO_NUMBER(NR_FREE_PAGES);
VMCOREINFO_NUMBER(PG_lru);
- VMCOREINFO_NUMBER(PG_private);
VMCOREINFO_NUMBER(PG_swapcache);
VMCOREINFO_NUMBER(PG_swapbacked);
#define PAGE_SLAB_MAPCOUNT_VALUE (PGTY_slab << 24)
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v5 17/17] mm/page-flags: remove PG_private
2026-09-21 2:28 ` [PATCH v5 17/17] mm/page-flags: remove PG_private Zi Yan
@ 2026-09-21 2:55 ` sashiko-bot
2026-09-21 15:36 ` Zi Yan
0 siblings, 1 reply; 12+ messages in thread
From: sashiko-bot @ 2026-09-21 2:55 UTC (permalink / raw)
To: Shakeel Butt, Muchun Song, Liam R. Howlett,
Matthew Wilcox (Oracle), Suren Baghdasaryan, Nico Pache,
Johannes Weiner, Gregory Price, Kairui Song, David Hildenbrand,
Michal Hocko, Alistair Popple, Baolin Wang, Vlastimil Babka,
Zi Yan, Lance Yang, Lorenzo Stoakes, Andrew Morton, Barry Song,
Ryan Roberts, Qi Zheng, Dev Jain, Ying Huang, Usama Arif,
Mike Rapoport
Cc: Jan Kara, Jonathan Corbet, Pasha Tatashin, linux-mm,
Mathieu Desnoyers, kexec, Masami Hiramatsu, Pratyush Yadav,
linux-doc, linux-trace-kernel, Baoquan He, linux-kernel,
Dave Young, Steven Rostedt, Shuah Khan, linux-fsdevel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] mm: silent memory leak masking due to PG_private removal
--
commit aee12ed3446e69cb6eee57384ca09e768dab36dd
Author: Zi Yan <ziy@nvidia.com>
mm/page-flags: remove PG_private
This commit removes the PG_private page flag and replaces its usage with
checking if folio->private != NULL. The space left by PG_private is
reserved as PG_folio for future use.
> diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
> index 7080a6a1a79e7..b0ddc652e76cc 100644
> --- a/include/linux/page-flags.h
> +++ b/include/linux/page-flags.h
[ ... ]
> @@ -1169,7 +1175,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 | \
[Severity: Medium]
Since PG_private is removed from PAGE_FLAGS_CHECK_AT_FREE, does this bypass
the safety check that ensures a page doesn't have uncleared private data?
Looking at __free_pages_prepare() in mm/page_alloc.c:
page_cpupid_reset_last(page);
page->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
page->private = 0;
reset_page_owner(page, order);
The head page's private field is silently zeroed here without any prior
verification that it was NULL. Previously, free_page_is_bad() would catch
and report leaked data via a bad_page() warning using
PAGE_FLAGS_CHECK_AT_FREE.
Now, if a buggy filesystem fails to detach and free folio->private data
before freeing the page, will this result in silent memory leaks?
> 1UL << PG_writeback | 1UL << PG_reserved | \
> 1UL << PG_active | \
> 1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260920-remove-pg_private-v5-0-bb68b6a21869@nvidia.com?part=17
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v5 17/17] mm/page-flags: remove PG_private
2026-09-21 2:55 ` sashiko-bot
@ 2026-09-21 15:36 ` Zi Yan
0 siblings, 0 replies; 12+ messages in thread
From: Zi Yan @ 2026-09-21 15:36 UTC (permalink / raw)
To: sashiko-reviews
Cc: Shakeel Butt, Muchun Song, Liam R. Howlett, Matthew Wilcox,
Suren Baghdasaryan, Nico Pache, Johannes Weiner, Gregory Price,
Kairui Song, David Hildenbrand, Michal Hocko, Alistair Popple,
Baolin Wang, Vlastimil Babka, Lance Yang, Lorenzo Stoakes,
Andrew Morton, Barry Song, Ryan Roberts, Qi Zheng, Dev Jain,
Ying Huang, Usama Arif, Mike Rapoport, Jan Kara, Jonathan Corbet,
Pasha Tatashin, linux-mm, Mathieu Desnoyers, kexec,
Masami Hiramatsu, Pratyush Yadav, linux-doc, linux-trace-kernel,
Baoquan He, linux-kernel, Dave Young, Steven Rostedt, Shuah Khan,
linux-fsdevel
On 20 Sep 2026, at 22:55, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] mm: silent memory leak masking due to PG_private removal
> --
>
> commit aee12ed3446e69cb6eee57384ca09e768dab36dd
> Author: Zi Yan <ziy@nvidia.com>
>
> mm/page-flags: remove PG_private
>
> This commit removes the PG_private page flag and replaces its usage with
> checking if folio->private != NULL. The space left by PG_private is
> reserved as PG_folio for future use.
>
>> diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
>> index 7080a6a1a79e7..b0ddc652e76cc 100644
>> --- a/include/linux/page-flags.h
>> +++ b/include/linux/page-flags.h
> [ ... ]
>> @@ -1169,7 +1175,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 | \
>
> [Severity: Medium]
> Since PG_private is removed from PAGE_FLAGS_CHECK_AT_FREE, does this bypass
> the safety check that ensures a page doesn't have uncleared private data?
>
> Looking at __free_pages_prepare() in mm/page_alloc.c:
>
> page_cpupid_reset_last(page);
> page->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
> page->private = 0;
> reset_page_owner(page, order);
>
> The head page's private field is silently zeroed here without any prior
> verification that it was NULL. Previously, free_page_is_bad() would catch
> and report leaked data via a bad_page() warning using
> PAGE_FLAGS_CHECK_AT_FREE.
>
> Now, if a buggy filesystem fails to detach and free folio->private data
> before freeing the page, will this result in silent memory leaks?
Answer copied from RFC:
After the conversion, folio/page should have an elevated refcount whenever
->private is set. That would help detect leaked private data. I tried to
enforce ->private needs to be NULL at page free time[1], but that might
cause trouble for certain use cases.
[1] https://lore.kernel.org/all/20260223032641.1859381-1-ziy@nvidia.com/
>
>> 1UL << PG_writeback | 1UL << PG_reserved | \
>> 1UL << PG_active | \
>> 1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK)
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260920-remove-pg_private-v5-0-bb68b6a21869@nvidia.com?part=17
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead
2026-09-21 2:27 [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead Zi Yan
` (3 preceding siblings ...)
2026-09-21 2:28 ` [PATCH v5 17/17] mm/page-flags: remove PG_private Zi Yan
@ 2026-09-21 4:08 ` Andrew Morton
2026-09-22 4:16 ` Nanzhe Zhao
5 siblings, 0 replies; 12+ messages in thread
From: Andrew Morton @ 2026-09-21 4:08 UTC (permalink / raw)
To: Zi Yan
Cc: David Hildenbrand, Matthew Wilcox (Oracle), Muchun Song,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Usama Arif,
Gregory Price, Ying Huang, Alistair Popple, Johannes Weiner,
Qi Zheng, Shakeel Butt, Kairui Song, linux-mm, linux-kernel,
Minchan Kim, Sergey Senozhatsky, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Namhyung Kim, Thomas Gleixner,
Borislav Petkov, Dave Hansen, x86, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, H. Peter Anvin, linux-perf-users, Juergen Gross,
Stefano Stabellini, Oleksandr Tyshchenko, xen-devel, Eric Biggers,
Theodore Y. Ts'o, Jaegeuk Kim, linux-fscrypt, Oscar Salvador,
Chao Yu, linux-f2fs-devel, Tal Zussman, Gao Xiang, Jan Kara,
Yue Hu, Jeffle Xu, Sandeep Dhavale, Hongbo Li, Chunhai Guo,
linux-erofs, linux-fsdevel, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Matthew Brost, Joshua Hahn, Rakie Kim,
Byungchul Park, Axel Rasmussen, Yuanchu Xie, Wei Xu,
linux-trace-kernel, Trond Myklebust, Anna Schumaker, linux-nfs,
Ilya Dryomov, Alex Markuze, Viacheslav Dubeyko, ceph-devel,
Richard Weinberger, Zhihao Cheng, linux-mtd, Baoquan He,
Pasha Tatashin, Pratyush Yadav, Jonathan Corbet, Dave Young,
Shuah Khan, kexec, linux-doc
On Sun, 20 Sep 2026 22:27:56 -0400 Zi Yan <ziy@nvidia.com> wrote:
> Hi all,
>
> This patchset removes PG_private to make space for upcoming PG_folio for
> identifying pages from a folio (more details in Note below). Instead of
> checking PG_private, all code is changed to check page/folio->private !=
> NULL instead.
Thanks, I updated mm-unstable to this version.
> Changes in v5:
> 1. replaced md patches (patch 13 and 14 in v4) with Matthew Wilcox's
> version (see Matthew's replies to v4).
> 2. used data_race() inside folio_test_private() and PagePrivate(), so that
> the new versions can be used without KCSAN warnings while not holding
> folio lock like before.
> 3. moved folio_has_attached_private() implementation detail comment next to
> the code.
Here's how v5 altered mm.git:
drivers/md/md-bitmap.c | 17 ++++++++---------
fs/buffer.c | 8 --------
include/linux/buffer_head.h | 2 +-
include/linux/mm.h | 3 +--
include/linux/page-flags.h | 30 +++++++++++++++++++-----------
include/trace/events/pagemap.h | 3 +--
mm/huge_memory.c | 3 +--
mm/page-writeback.c | 3 +--
8 files changed, 32 insertions(+), 37 deletions(-)
--- a/drivers/md/md-bitmap.c~b
+++ a/drivers/md/md-bitmap.c
@@ -516,7 +516,8 @@ static void end_bitmap_write(struct bio
static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)
{
- struct buffer_head *bh = (struct buffer_head *)page_private(page);
+ struct folio *folio = page_folio(page);
+ struct buffer_head *bh = folio_buffers(folio);
while (bh && bh->b_blocknr) {
atomic_inc(&bitmap->pending_writes);
@@ -533,18 +534,15 @@ static void write_file_page(struct bitma
static void free_buffers(struct page *page)
{
- struct buffer_head *bh = (struct buffer_head *)page_private(page);
-
- if (!bh)
- return;
+ struct folio *folio = page_folio(page);
+ struct buffer_head *bh = folio_detach_private(folio);
while (bh) {
struct buffer_head *next = bh->b_this_page;
free_buffer_head(bh);
bh = next;
}
- detach_page_private(page);
- put_page(page);
+ folio_put(folio);
}
/* read a page from a file.
@@ -559,6 +557,7 @@ static int read_file_page(struct file *f
{
int ret = 0;
struct inode *inode = file_inode(file);
+ struct folio *folio = page_folio(page);
struct buffer_head *bh;
sector_t block, blk_cur;
unsigned long blocksize = i_blocksize(inode);
@@ -566,12 +565,12 @@ static int read_file_page(struct file *f
pr_debug("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
(unsigned long long)index << PAGE_SHIFT);
- bh = alloc_page_buffers(page, blocksize);
+ bh = folio_alloc_buffers(folio, blocksize, GFP_NOFS | __GFP_ACCOUNT);
if (!bh) {
ret = -ENOMEM;
goto out;
}
- attach_page_private(page, bh);
+ folio_attach_private(folio, bh);
blk_cur = index << (PAGE_SHIFT - inode->i_blkbits);
while (bh) {
block = blk_cur;
--- a/fs/buffer.c~b
+++ a/fs/buffer.c
@@ -773,14 +773,6 @@ no_grow:
}
EXPORT_SYMBOL_GPL(folio_alloc_buffers);
-struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size)
-{
- gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT;
-
- return folio_alloc_buffers(page_folio(page), size, gfp);
-}
-EXPORT_SYMBOL_GPL(alloc_page_buffers);
-
static inline void link_dev_buffers(struct folio *folio,
struct buffer_head *head)
{
--- a/include/linux/buffer_head.h~b
+++ a/include/linux/buffer_head.h
@@ -175,6 +175,7 @@ static inline unsigned long bh_offset(co
return (unsigned long)(bh)->b_data & (page_size(bh->b_page) - 1);
}
+/* If we *know* folio->private refers to buffer_heads */
#define folio_buffers(folio) folio_get_private(folio)
void buffer_check_dirty_writeback(struct folio *folio,
@@ -191,7 +192,6 @@ void folio_set_bh(struct buffer_head *bh
unsigned long offset);
struct buffer_head *folio_alloc_buffers(struct folio *folio, unsigned long size,
gfp_t gfp);
-struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size);
struct buffer_head *create_empty_buffers(struct folio *folio,
unsigned long blocksize, unsigned long b_state);
void end_buffer_read_sync(struct buffer_head *bh, int uptodate);
--- a/include/linux/mm.h~b
+++ a/include/linux/mm.h
@@ -3052,9 +3052,8 @@ static inline int folio_expected_ref_cou
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_has_attached_private(folio));
+ ref_count += folio_has_attached_private(folio);
}
/* One reference per page table mapping. */
--- a/include/linux/page-flags.h~b
+++ a/include/linux/page-flags.h
@@ -576,7 +576,12 @@ FOLIO_FLAG(swapbacked, FOLIO_HEAD_PAGE)
static __always_inline bool folio_test_private(const struct folio *folio)
{
- return folio->private;
+ /*
+ * 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);
}
FOLIO_FLAG(private_2, FOLIO_HEAD_PAGE)
@@ -1199,20 +1204,23 @@ static __always_inline void __ClearPageA
* @folio: The folio to check.
*
* Use this in code that may encounter swapcache or hugetlb folios but only
- * wants to detect attached private data. 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.
+ * wants to detect attached private data.
*
- * Return: true if folio->private is set and the folio is neither swapcache
- * nor hugetlb.
+ * 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);
}
--- a/include/trace/events/pagemap.h~b
+++ a/include/trace/events/pagemap.h
@@ -22,8 +22,7 @@
(folio_test_swapcache(folio) ? PAGEMAP_SWAPCACHE : 0) | \
(folio_test_swapbacked(folio) ? PAGEMAP_SWAPBACKED : 0) | \
(folio_test_mappedtodisk(folio) ? PAGEMAP_MAPPEDDISK : 0) | \
- /* data_race() is used to read attached private locklessly */ \
- (data_race(folio_has_attached_private(folio)) ? PAGEMAP_BUFFERS : 0) \
+ (folio_has_attached_private(folio) ? PAGEMAP_BUFFERS : 0) \
)
TRACE_EVENT(mm_lru_insertion,
--- a/mm/huge_memory.c~b
+++ a/mm/huge_memory.c
@@ -4845,9 +4845,8 @@ static int split_huge_pages_pid(int pid,
* For folios with private, split_huge_page_to_list_to_order()
* will try to drop it before split and then check if the folio
* can be split or not. So skip the check here.
- * data_race() is used to read attached private locklessly.
*/
- if (!data_race(folio_has_attached_private(folio)) &&
+ if (!folio_has_attached_private(folio) &&
folio_expected_ref_count(folio) != folio_ref_count(folio))
goto next;
--- a/mm/page-writeback.c~b
+++ a/mm/page-writeback.c
@@ -2705,8 +2705,7 @@ bool filemap_dirty_folio(struct address_
if (folio_test_set_dirty(folio))
return false;
- /* data_race() is used to read attached private locklessly */
- __folio_mark_dirty(folio, mapping, !data_race(folio_has_attached_private(folio)));
+ __folio_mark_dirty(folio, mapping, !folio_has_attached_private(folio));
if (mapping->host) {
/* !PageAnon && !swapper_space */
_
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead
2026-09-21 2:27 [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead Zi Yan
` (4 preceding siblings ...)
2026-09-21 4:08 ` [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead Andrew Morton
@ 2026-09-22 4:16 ` Nanzhe Zhao
2026-09-22 15:31 ` [f2fs-dev] " Jaegeuk Kim
5 siblings, 1 reply; 12+ messages in thread
From: Nanzhe Zhao @ 2026-09-22 4:16 UTC (permalink / raw)
To: Zi Yan
Cc: David Hildenbrand, Matthew Wilcox (Oracle), Andrew Morton,
Muchun Song, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Gregory Price, Ying Huang, Alistair Popple,
Johannes Weiner, Qi Zheng, Shakeel Butt, Kairui Song,
Mark Rutland, Ian Rogers, Jan Kara, linux-doc, Alex Markuze,
Peter Zijlstra, kexec, Dave Hansen, Dave Young, Adrian Hunter,
linux-mm, Hongbo Li, H. Peter Anvin, Chunhai Guo, Shuah Khan,
Tal Zussman, Baoquan He, Matthew Brost, Anna Schumaker,
Stefano Stabellini, Yue Hu, Rakie Kim, Minchan Kim,
Richard Weinberger, x86, ceph-devel, Eric Biggers,
Alexander Shishkin, Ingo Molnar, Viacheslav Dubeyko, Wei Xu,
xen-devel, Gao Xiang, Masami Hiramatsu, Joshua Hahn,
Byungchul Park, James Clark, Arnaldo Carvalho de Melo,
linux-fscrypt, Borislav Petkov, Steven Rostedt, linux-mtd,
Axel Rasmussen, Jeffle Xu, Namhyung Kim, Jaegeuk Kim, Yuanchu Xie,
Ilya Dryomov, Oscar Salvador, Juergen Gross, Pratyush Yadav,
linux-nfs, Theodore Y. Ts'o, Oleksandr Tyshchenko,
Jonathan Corbet, Pasha Tatashin, linux-kernel, linux-f2fs-devel,
linux-perf-users, Sergey Senozhatsky, Thomas Gleixner, Jiri Olsa,
linux-fsdevel, Mathieu Desnoyers, linux-trace-kernel, linux-erofs,
Trond Myklebust, Chao Yu, Daeho Jeong
Hi Zi Yan,
Thanks for the series. The cleanup is a nice help for the f2fs
large-folio work -- it removes the page-based private-flag plumbing that
my series would otherwise have to carry itself.
My large-folio series v2 [1] can be rebased on top of this series. The
only overlap is the PAGE_PRIVATE_* flag helpers, which I can re-express
on top of the F2FS_FOLIO_PRIVATE_* names while keeping the
f2fs_folio_state indirection.
Jaegeuk, do you have a preference on the ordering here? My personal
suggestion would be to review and merge this series first, and I'll
rebase on top of it.
[1] https://lore.kernel.org/linux-f2fs-devel/20260915041909.2903887-1-zhaonanzhe@xiaomi.com/
Thanks,
Nanzhe
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [f2fs-dev] [PATCH v5 00/17] Remove PG_private by using page/folio->private checks instead
2026-09-22 4:16 ` Nanzhe Zhao
@ 2026-09-22 15:31 ` Jaegeuk Kim
0 siblings, 0 replies; 12+ messages in thread
From: Jaegeuk Kim @ 2026-09-22 15:31 UTC (permalink / raw)
To: Nanzhe Zhao
Cc: Zi Yan, Qi Zheng, Matthew Brost, Alistair Popple, ceph-devel,
Eric Biggers, xen-devel, Gregory Price, Arnaldo Carvalho de Melo,
linux-fscrypt, Shuah Khan, David Hildenbrand, Suren Baghdasaryan,
linux-kernel, Nico Pache, linux-perf-users, Oleksandr Tyshchenko,
Masami Hiramatsu, Jiri Olsa, linux-fsdevel, Andrew Morton,
Trond Myklebust, Mark Rutland, linux-doc, Dave Hansen, Hongbo Li,
Ying Huang, Stefano Stabellini, Vlastimil Babka,
Sergey Senozhatsky, Byungchul Park, linux-trace-kernel,
Lorenzo Stoakes, Joshua Hahn, Barry Song, Kairui Song,
Theodore Y. Ts'o, Muchun Song, linux-f2fs-devel, Minchan Kim,
Lance Yang, Thomas Gleixner, Anna Schumaker, Pratyush Yadav,
Alex Markuze, Alexander Shishkin, linux-mtd, Chunhai Guo,
Jonathan Corbet, Dev Jain, Matthew Wilcox (Oracle),
Viacheslav Dubeyko, Gao Xiang, Wei Xu, Baoquan He, James Clark,
Steven Rostedt, Borislav Petkov, Baolin Wang, Shakeel Butt,
Tal Zussman, Ilya Dryomov, Oscar Salvador, linux-nfs, linux-mm,
linux-erofs, Mike Rapoport, Ian Rogers, Michal Hocko, Jan Kara,
Peter Zijlstra, Dave Young, Yuanchu Xie, Liam R. Howlett,
H. Peter Anvin, Yue Hu, Rakie Kim, Richard Weinberger, x86,
Ingo Molnar, Axel Rasmussen, Ryan Roberts, Pasha Tatashin,
Usama Arif, Jeffle Xu, Namhyung Kim, Juergen Gross, kexec,
Adrian Hunter, Johannes Weiner, Mathieu Desnoyers
On 09/22, Nanzhe Zhao wrote:
> Hi Zi Yan,
>
> Thanks for the series. The cleanup is a nice help for the f2fs
> large-folio work -- it removes the page-based private-flag plumbing that
> my series would otherwise have to carry itself.
>
> My large-folio series v2 [1] can be rebased on top of this series. The
> only overlap is the PAGE_PRIVATE_* flag helpers, which I can re-express
> on top of the F2FS_FOLIO_PRIVATE_* names while keeping the
> f2fs_folio_state indirection.
>
> Jaegeuk, do you have a preference on the ordering here? My personal
> suggestion would be to review and merge this series first, and I'll
> rebase on top of it.
Let's keep working on the f2fs tree, since we should not merge this in my tree.
Later, we'd need to prepare how to address the merge conflict.
>
> [1] https://lore.kernel.org/linux-f2fs-devel/20260915041909.2903887-1-zhaonanzhe@xiaomi.com/
>
> Thanks,
> Nanzhe
>
>
> _______________________________________________
> Linux-f2fs-devel mailing list
> Linux-f2fs-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 12+ messages in thread