Re: [PATCH RFC 08/14] fs/erofs: use folio_attach/detach_private() instead of direct assignment

"Zi Yan" <[email protected]> Tue, 04 Aug 2026 22:41:23 -0400
Newsgroups org.ozlabs.lists.linux-erofs,org.kernel.vger.linux-kernel,org.kvack.linux-mm
Message-ID <[email protected]>
On Mon Aug 3, 2026 at 7:40 PM EDT, Gao Xiang wrote:
> Hi Zi,
>
> On Fri, Jul 31, 2026 at 10:13:31PM -0400, Zi Yan wrote:
>> erofs_onelinefolio_init/split/end() use folio->private without setting
>> PG_private or increase folio refcount and it works. But after PG_private=
 is
>> replaced by checking folio->private in a future commit, it can break
>> folio_expected_ref_count(), since the folio has private data without
>> elevated refcount. Change it now.
>>=20
>> It prepares for a future commit that removes PG_private.
>>=20
>> No funtional change intended.
>>=20
>> Assisted-by: Claude:claude-opus-4-8
>> Assisted-by: Codex:gpt-5
>> Signed-off-by: Zi Yan <[email protected]>
>> To: Gao Xiang <[email protected]>
>> To: Chao Yu <[email protected]>
>> Cc: Yue Hu <[email protected]>
>> Cc: Jeffle Xu <[email protected]>
>> Cc: Sandeep Dhavale <[email protected]>
>> Cc: Hongbo Li <[email protected]>
>> Cc: Chunhai Guo <[email protected]>
>> Cc: [email protected]
>> Cc: [email protected]
>
> It looks fine as long as PG_private flag will be removed in the
> follow-up patches:
>

Hi Gao,

Sashiko spot an issue in this patch[1]. Basically, ->private can be 0 if
I/O completes without any issue or being dirty and it causes
folio_detach_private() not to folio_put(). My fix is to add a bias, 1,
to the counter, so that ->private stays non NULL throughout online folio
process. The revised patch is below. Let me know your thoughts. Thanks.

[1] https://sashiko.dev/#/patchset/20260731-remove-pg_private-v1-0-142c97ba=
3562%40nvidia.com?part=3D8

From d8fadd13fee03e72c03a718af65ed41927ccbcca Mon Sep 17 00:00:00 2001
From: Zi Yan <[email protected]>
Date: Thu, 30 Jul 2026 11:04:00 -0400
Subject: [PATCH] erofs: use folio_attach/detach_private() instead of direct
 assignment

erofs_onlinefolio_init/split/end() use folio->private without setting
PG_private or increasing folio refcount and it works. But after PG_private
is replaced by checking folio->private in a future commit, it can break
folio_expected_ref_count(), since the folio has private data without
elevated refcount. Change them to use folio_attach/detach_private().

Furthermore, because folio->private is used to store in-flight I/O counter
and the counter reaches 0 when all I/O completes successfully without error
or being dirty, ->private=3D0 causes folio_detach_private() to not drop the
elevated folio refcount. Solve this issue by using bias=3D1 for the counter=
,
so that ->private stays non NULL throughout every attach-to-detach process.
Add a macro EROFS_ONLINEFOLIO_BIAS=3D1. While at it, fix the comment about
->private bit layout and add EROFS_ONLINEFOLIO_COUNT_MASK.

It prepares for a future commit that removes PG_private.

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5
Signed-off-by: Zi Yan <[email protected]>
To: Gao Xiang <[email protected]>
To: Chao Yu <[email protected]>
Cc: Yue Hu <[email protected]>
Cc: Jeffle Xu <[email protected]>
Cc: Sandeep Dhavale <[email protected]>
Cc: Hongbo Li <[email protected]>
Cc: Chunhai Guo <[email protected]>
Cc: [email protected]
Cc: [email protected]
---
 fs/erofs/data.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 9aa48c8d67d12..81e9dab247e0f 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -251,19 +251,23 @@ int erofs_map_dev(struct super_block *sb, struct erof=
s_map_dev *map)
 /*
  * bit 30: I/O error occurred on this folio
  * bit 29: CPU has dirty data in D-cache (needs aliasing handling);
- * bit 0 - 29: remaining parts to complete this folio
+ * bit 0 - 28: remaining parts to complete this folio, biased by 1 so that
+ *	       ->private stays non-NULL while the folio is attached
  */
 #define EROFS_ONLINEFOLIO_EIO		30
 #define EROFS_ONLINEFOLIO_DIRTY		29
+#define EROFS_ONLINEFOLIO_COUNT_MASK	(BIT(EROFS_ONLINEFOLIO_DIRTY) - 1)
+#define EROFS_ONLINEFOLIO_BIAS		1
=20
 void erofs_onlinefolio_init(struct folio *folio)
 {
 	union {
 		atomic_t o;
 		void *v;
-	} u =3D { .o =3D ATOMIC_INIT(1) };
+	} u =3D { .o =3D ATOMIC_INIT(1 + EROFS_ONLINEFOLIO_BIAS) };
=20
-	folio->private =3D u.v;	/* valid only if file-backed folio is locked */
+	/* valid only if file-backed folio is locked */
+	folio_attach_private(folio, u.v);
 }
=20
 void erofs_onlinefolio_split(struct folio *folio)
@@ -277,14 +281,14 @@ void erofs_onlinefolio_end(struct folio *folio, int e=
rr, bool dirty)
=20
 	do {
 		orig =3D atomic_read((atomic_t *)&folio->private);
-		DBG_BUGON(orig <=3D 0);
+		DBG_BUGON((orig & EROFS_ONLINEFOLIO_COUNT_MASK) <=3D EROFS_ONLINEFOLIO_B=
IAS);
 		v =3D dirty << EROFS_ONLINEFOLIO_DIRTY;
 		v |=3D (orig - 1) | (!!err << EROFS_ONLINEFOLIO_EIO);
 	} while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) !=3D orig);
=20
-	if (v & (BIT(EROFS_ONLINEFOLIO_DIRTY) - 1))
+	if ((v & EROFS_ONLINEFOLIO_COUNT_MASK) !=3D EROFS_ONLINEFOLIO_BIAS)
 		return;
-	folio->private =3D 0;
+	folio_detach_private(folio);
 	if (v & BIT(EROFS_ONLINEFOLIO_DIRTY))
 		flush_dcache_folio(folio);
 	folio_end_read(folio, !(v & BIT(EROFS_ONLINEFOLIO_EIO)));
--=20
2.53.0




--=20
Best Regards,
Yan, Zi