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

From: Gao Xiang

Date: Wed Aug 05 2026 - 00:18:14 EST


Hi Zi,

On Tue, Aug 04, 2026 at 10:41:23PM -0400, Zi Yan wrote:
> 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.
> >>
> >> It prepares for a future commit that removes PG_private.
> >>
> >> No funtional change intended.
> >>
> >> 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
> >
> > 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.
>

Yes, that is a valid issue: ->private can be decreased to 0 without
folio_detach_private() for a short period so a +1 bias is indeed
a solution.

The following diff looks good to me, you could use it in your next
version.

Thanks,
Gao Xiang

> [1] https://sashiko.dev/#/patchset/20260731-remove-pg_private-v1-0-142c97ba3562%40nvidia.com?part=8
>
> >From d8fadd13fee03e72c03a718af65ed41927ccbcca Mon Sep 17 00:00:00 2001
> From: Zi Yan <ziy@xxxxxxxxxx>
> 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=0 causes folio_detach_private() to not drop the
> elevated folio refcount. Solve this issue by using bias=1 for the counter,
> so that ->private stays non NULL throughout every attach-to-detach process.
> Add a macro EROFS_ONLINEFOLIO_BIAS=1. 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 erofs_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
>
> void erofs_onlinefolio_init(struct folio *folio)
> {
> union {
> atomic_t o;
> void *v;
> - } u = { .o = ATOMIC_INIT(1) };
> + } u = { .o = ATOMIC_INIT(1 + EROFS_ONLINEFOLIO_BIAS) };
>
> - folio->private = u.v; /* valid only if file-backed folio is locked */
> + /* valid only if file-backed folio is locked */
> + folio_attach_private(folio, u.v);
> }
>
> void erofs_onlinefolio_split(struct folio *folio)
> @@ -277,14 +281,14 @@ void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty)
>
> do {
> orig = atomic_read((atomic_t *)&folio->private);
> - DBG_BUGON(orig <= 0);
> + DBG_BUGON((orig & EROFS_ONLINEFOLIO_COUNT_MASK) <= EROFS_ONLINEFOLIO_BIAS);
> v = dirty << EROFS_ONLINEFOLIO_DIRTY;
> v |= (orig - 1) | (!!err << EROFS_ONLINEFOLIO_EIO);
> } while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
>
> - if (v & (BIT(EROFS_ONLINEFOLIO_DIRTY) - 1))
> + if ((v & EROFS_ONLINEFOLIO_COUNT_MASK) != EROFS_ONLINEFOLIO_BIAS)
> return;
> - folio->private = 0;
> + folio_detach_private(folio);
> if (v & BIT(EROFS_ONLINEFOLIO_DIRTY))
> flush_dcache_folio(folio);
> folio_end_read(folio, !(v & BIT(EROFS_ONLINEFOLIO_EIO)));
> --
> 2.53.0
>
>
>
>
> --
> Best Regards,
> Yan, Zi
>