[PATCH] erofs: use folio_attach/detach_private() instead of direct assignment

From: Zi Yan

Date: Thu Jul 30 2026 - 11:04:00 EST


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 <ziy@xxxxxxxxxx>
To: Gao Xiang <xiang@xxxxxxxxxx>
To: Chao Yu <chao@xxxxxxxxxx>
Cc: Yue Hu <zbestahu@xxxxxxxxx>
Cc: Jeffle Xu <jefflexu@xxxxxxxxxxxxxxxxx>
Cc: Sandeep Dhavale <dhavale@xxxxxxxxxx>
Cc: Hongbo Li <hongbohbli@xxxxxxxxxxx>
Cc: Chunhai Guo <guochunhai@xxxxxxxx>
Cc: linux-erofs@xxxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
---
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