[Linux Kernel Bug] WARNING in nilfs_copy_back_pages

Jiaming Zhang posted 1 patch 3 weeks, 3 days ago
[Linux Kernel Bug] WARNING in nilfs_copy_back_pages
Posted by Jiaming Zhang 3 weeks, 3 days ago
Dear Linux kernel developers and maintainers,

We are writing to report an issue discovered in the NILFS2 subsystem.  The
issue is reproducible on the latest version of linux (v7.3-rc1, commit
cee9395acd8043be0644b25c34bfa86623f2b935).  Below is the kernel report:

NILFS (loop0): error -2 preparing GC: cannot delete virtual blocks from DAT file
------------[ cut here ]------------
folio_test_dirty(dfolio)
WARNING: fs/nilfs2/page.c:331 at nilfs_copy_back_pages+0x639/0x810
fs/nilfs2/page.c:331, CPU#0: syz-executor887/9521
Modules linked in:
CPU: 0 UID: 0 PID: 9521 Comm: syz-executor887 Not tainted 7.3.0-rc1
#37 PREEMPT(full)
Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:nilfs_copy_back_pages+0x639/0x810 fs/nilfs2/page.c:331
Code: 44 89 fe e8 39 8e 12 fe 45 84 f6 74 25 e8 9f 8a 12 fe 48 89 df
e8 67 9a 55 fe 4c 8b 7c 24 10 e9 e5 fe ff ff e8 88 8a 12 fe 90 <0f> 0b
90 e9 1d fe ff ff e8 7a 8a 12 fe 4c 8b 7c 24 10 e9 c8 fe ff
RSP: 0018:ffffc9000854f4c0 EFLAGS: 00010293
RAX: ffffffff83a8e928 RBX: ffffea00013cf3c0 RCX: ffff888026619f80
RDX: 0000000000000000 RSI: 0000000000000010 RDI: 0000000000000000
RBP: ffffc9000854f6e8 R08: ffffea00012db607 R09: 1ffffd400025b6c0
R10: dffffc0000000000 R11: fffff9400025b6c1 R12: ffffc9000854f548
R13: ffffea00012db600 R14: 0000000000000010 R15: 1ffffd4000279e79
FS:  000055558d08a380(0000) GS:ffff8880985d9000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000055558d08aca0 CR3: 0000000044f02000 CR4: 0000000000752ef0
PKRU: 55555554
Call Trace:
 <TASK>
 nilfs_mdt_restore_from_shadow_map+0x127/0x260 fs/nilfs2/mdt.c:652
 nilfs_clean_segments+0xab8/0xba0 fs/nilfs2/segment.c:2552
 nilfs_ioctl_clean_segments fs/nilfs2/ioctl.c:938 [inline]
 nilfs_ioctl+0x242a/0x2590 fs/nilfs2/ioctl.c:1368
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:597 [inline]
 __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x170/0x540 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fa00bf4d42d
Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 48 89 f8 48
89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d
01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffd36ab1678 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007fa00bf4d42d
RDX: 0000200000000300 RSI: 0000000040786e88 RDI: 0000000000000004
RBP: 0000000000000000 R08: 0000000000000000 R09: 00007ffd36ab16c0
R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffd36ab169c
R13: 00007ffd36ab16e0 R14: 000000000000000c R15: 431bde82d7b634db
 </TASK>

Following is the root cause analysis for this issue, note that the
analysis is performed with the assistance of LLM, but we try our best to
ensure the accuracy.

The WARNING triggers because a folio of the DAT metadata file is still
dirty where nilfs_copy_back_pages() expects a clean one.  That function
runs when garbage collection fails, which the reproducer causes by asking
it to free a virtual block number the DAT file does not map.  nilfs2 then
restores the DAT page cache to a snapshot taken before the collection
started: it first clears the dirty state of the folios in that cache, then
copies the snapshot back over them, assuming the first step left nothing
dirty.

The clearing step can skip a folio.  Since commit ca76bb226bf4 ("nilfs2: do
not force clear folio if buffer is referenced") nilfs_clear_folio_dirty()
returns without clearing if any buffer head of the folio is busy, to avoid
clobbering the state of a buffer somebody else is using.  A buffer head is
busy here: reading a metadata block goes through nilfs_mdt_read_block(),
which also submits read-ahead for the blocks that follow and waits only for
the first.  When the block size is smaller than the page size, several
metadata blocks share one folio, so a locked read-ahead buffer can be
adjacent to a dirty buffer and keep the whole folio dirty.

nilfs_copy_folio() then copies the snapshot over that folio and clears
BH_Dirty on all of its buffer heads, leaving the folio dirty with no dirty
buffer under it, which no later segment construction cleans up.  On a
kernel booted with panic_on_warn, the WARNING can lead to a system crash.

To fix this issue, the dirty flag of the destination folio should be
cleared before the folio is overwritten:

diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index cf4f1c6798f5..b26d9c3bda6d 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -328,7 +328,8 @@ void nilfs_copy_back_pages(struct address_space *dmap,
  dfolio = filemap_lock_folio(dmap, index);
  if (!IS_ERR(dfolio)) {
  /* overwrite existing folio in the destination cache */
- WARN_ON(folio_test_dirty(dfolio));
+ if (unlikely(folio_test_dirty(dfolio)))
+ __nilfs_clear_folio_dirty(dfolio);
  nilfs_copy_folio(dfolio, folio, false);
  folio_unlock(dfolio);
  folio_put(dfolio);

After applying the above patch, the reproducer no longer triggers the issue
on our machine.  If this solution is acceptable, we are happy to submit a
formal patch.

The kernel console output, kernel config, syzkaller reproducer, and C
reproducer are available at google drive:
https://drive.google.com/drive/folders/1R6EqqLPh5vgylKRPvO2ji5Q8Tsf0x8QY?usp=sharing

Please let us know if any further information is required.

Best Regards,
Jiaming Zhang
Re: [Linux Kernel Bug] WARNING in nilfs_copy_back_pages
Posted by Ryusuke Konishi 3 weeks, 3 days ago
On Tue, Sep 1, 2026 at 9:58 PM Jiaming Zhang wrote:
>
> Dear Linux kernel developers and maintainers,
>
> We are writing to report an issue discovered in the NILFS2 subsystem.  The
> issue is reproducible on the latest version of linux (v7.3-rc1, commit
> cee9395acd8043be0644b25c34bfa86623f2b935).  Below is the kernel report:
>
> NILFS (loop0): error -2 preparing GC: cannot delete virtual blocks from DAT file
> ------------[ cut here ]------------
> folio_test_dirty(dfolio)
> WARNING: fs/nilfs2/page.c:331 at nilfs_copy_back_pages+0x639/0x810
> fs/nilfs2/page.c:331, CPU#0: syz-executor887/9521
> Modules linked in:
> CPU: 0 UID: 0 PID: 9521 Comm: syz-executor887 Not tainted 7.3.0-rc1
> #37 PREEMPT(full)
> Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
> 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> RIP: 0010:nilfs_copy_back_pages+0x639/0x810 fs/nilfs2/page.c:331
> Code: 44 89 fe e8 39 8e 12 fe 45 84 f6 74 25 e8 9f 8a 12 fe 48 89 df
> e8 67 9a 55 fe 4c 8b 7c 24 10 e9 e5 fe ff ff e8 88 8a 12 fe 90 <0f> 0b
> 90 e9 1d fe ff ff e8 7a 8a 12 fe 4c 8b 7c 24 10 e9 c8 fe ff
> RSP: 0018:ffffc9000854f4c0 EFLAGS: 00010293
> RAX: ffffffff83a8e928 RBX: ffffea00013cf3c0 RCX: ffff888026619f80
> RDX: 0000000000000000 RSI: 0000000000000010 RDI: 0000000000000000
> RBP: ffffc9000854f6e8 R08: ffffea00012db607 R09: 1ffffd400025b6c0
> R10: dffffc0000000000 R11: fffff9400025b6c1 R12: ffffc9000854f548
> R13: ffffea00012db600 R14: 0000000000000010 R15: 1ffffd4000279e79
> FS:  000055558d08a380(0000) GS:ffff8880985d9000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 000055558d08aca0 CR3: 0000000044f02000 CR4: 0000000000752ef0
> PKRU: 55555554
> Call Trace:
>  <TASK>
>  nilfs_mdt_restore_from_shadow_map+0x127/0x260 fs/nilfs2/mdt.c:652
>  nilfs_clean_segments+0xab8/0xba0 fs/nilfs2/segment.c:2552
>  nilfs_ioctl_clean_segments fs/nilfs2/ioctl.c:938 [inline]
>  nilfs_ioctl+0x242a/0x2590 fs/nilfs2/ioctl.c:1368
>  vfs_ioctl fs/ioctl.c:51 [inline]
>  __do_sys_ioctl fs/ioctl.c:597 [inline]
>  __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
>  do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
>  do_syscall_64+0x170/0x540 arch/x86/entry/syscall_64.c:84
>  entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7fa00bf4d42d
> Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 48 89 f8 48
> 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d
> 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48
> RSP: 002b:00007ffd36ab1678 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
> RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007fa00bf4d42d
> RDX: 0000200000000300 RSI: 0000000040786e88 RDI: 0000000000000004
> RBP: 0000000000000000 R08: 0000000000000000 R09: 00007ffd36ab16c0
> R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffd36ab169c
> R13: 00007ffd36ab16e0 R14: 000000000000000c R15: 431bde82d7b634db
>  </TASK>
>
> Following is the root cause analysis for this issue, note that the
> analysis is performed with the assistance of LLM, but we try our best to
> ensure the accuracy.
>
> The WARNING triggers because a folio of the DAT metadata file is still
> dirty where nilfs_copy_back_pages() expects a clean one.  That function
> runs when garbage collection fails, which the reproducer causes by asking
> it to free a virtual block number the DAT file does not map.  nilfs2 then
> restores the DAT page cache to a snapshot taken before the collection
> started: it first clears the dirty state of the folios in that cache, then
> copies the snapshot back over them, assuming the first step left nothing
> dirty.
>
> The clearing step can skip a folio.  Since commit ca76bb226bf4 ("nilfs2: do
> not force clear folio if buffer is referenced") nilfs_clear_folio_dirty()
> returns without clearing if any buffer head of the folio is busy, to avoid
> clobbering the state of a buffer somebody else is using.  A buffer head is
> busy here: reading a metadata block goes through nilfs_mdt_read_block(),
> which also submits read-ahead for the blocks that follow and waits only for
> the first.  When the block size is smaller than the page size, several
> metadata blocks share one folio, so a locked read-ahead buffer can be
> adjacent to a dirty buffer and keep the whole folio dirty.
>
> nilfs_copy_folio() then copies the snapshot over that folio and clears
> BH_Dirty on all of its buffer heads, leaving the folio dirty with no dirty
> buffer under it, which no later segment construction cleans up.  On a
> kernel booted with panic_on_warn, the WARNING can lead to a system crash.
>
> To fix this issue, the dirty flag of the destination folio should be
> cleared before the folio is overwritten:
>
> diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
> index cf4f1c6798f5..b26d9c3bda6d 100644
> --- a/fs/nilfs2/page.c
> +++ b/fs/nilfs2/page.c
> @@ -328,7 +328,8 @@ void nilfs_copy_back_pages(struct address_space *dmap,
>   dfolio = filemap_lock_folio(dmap, index);
>   if (!IS_ERR(dfolio)) {
>   /* overwrite existing folio in the destination cache */
> - WARN_ON(folio_test_dirty(dfolio));
> + if (unlikely(folio_test_dirty(dfolio)))
> + __nilfs_clear_folio_dirty(dfolio);
>   nilfs_copy_folio(dfolio, folio, false);
>   folio_unlock(dfolio);
>   folio_put(dfolio);
>
> After applying the above patch, the reproducer no longer triggers the issue
> on our machine.  If this solution is acceptable, we are happy to submit a
> formal patch.
>
> The kernel console output, kernel config, syzkaller reproducer, and C
> reproducer are available at google drive:
> https://drive.google.com/drive/folders/1R6EqqLPh5vgylKRPvO2ji5Q8Tsf0x8QY?usp=sharing
>
> Please let us know if any further information is required.

Thanks for the report, Jiaming Zhang!

I think the issue is that commit ca76bb226bf4 changed the expected
behavior of nilfs_clear_dirty_pages(), which is called before
nilfs_copy_back_pages().

Originally, nilfs_clear_dirty_pages() was implemented for
nilfs_mdt_restore_from_shadow_map() to restore the pre-GC state from
the DAT shadow page cache. It was designed to forcefully clear dirty
pages/folios in the DAT page cache, assuming complete synchronization
with the log writer.

However, over time, it started being called to resolve page/folio
dirty states when the filesystem degrades to read-only during a
page/folio writeback request.

Since this page/folio dirty state clearing was asynchronous with the
log writer, it caused unpredictable bugs in various places after the
filesystem fell back to read-only. Commit ca76bb226bf4 is one of the
essential workarounds introduced to avoid this.

The root cause is that the same helper function is being used for
calls with two entirely different premises. I believe the solution is
to restore the original semantics of nilfs_clear_dirty_pages() only
when it is called from nilfs_mdt_restore_from_shadow_map().

Specifically, how about adding a bool force flag argument to
nilfs_clear_dirty_pages() and nilfs_clear_folio_dirty(), and having
the call from nilfs_mdt_restore_from_shadow_map() set this flag to
clear the dirty state regardless of the buffer head's reference state,
just like the original implementation?

Would it be possible for you to propose a fix using this approach?

Alternatively, since their premises differ, we could separate the
implementations entirely, but I expect that adding a boolean option to
switch modes would be simpler.

Adjusting the WARN_ON in nilfs_copy_back_pages() would break the
original design (premise) and is therefore undesirable, in my view.

Thanks,
Ryusuke Konishi

>
> Best Regards,
> Jiaming Zhang
Re: [Linux Kernel Bug] WARNING in nilfs_copy_back_pages
Posted by Jiaming Zhang 3 weeks, 3 days ago
Ryusuke Konishi <konishi.ryusuke@gmail.com> 于2026年9月1日周二 23:11写道:
>
> On Tue, Sep 1, 2026 at 9:58 PM Jiaming Zhang wrote:
> >
> > Dear Linux kernel developers and maintainers,
> >
> > We are writing to report an issue discovered in the NILFS2 subsystem.  The
> > issue is reproducible on the latest version of linux (v7.3-rc1, commit
> > cee9395acd8043be0644b25c34bfa86623f2b935).  Below is the kernel report:
> >
> > NILFS (loop0): error -2 preparing GC: cannot delete virtual blocks from DAT file
> > ------------[ cut here ]------------
> > folio_test_dirty(dfolio)
> > WARNING: fs/nilfs2/page.c:331 at nilfs_copy_back_pages+0x639/0x810
> > fs/nilfs2/page.c:331, CPU#0: syz-executor887/9521
> > Modules linked in:
> > CPU: 0 UID: 0 PID: 9521 Comm: syz-executor887 Not tainted 7.3.0-rc1
> > #37 PREEMPT(full)
> > Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
> > 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> > RIP: 0010:nilfs_copy_back_pages+0x639/0x810 fs/nilfs2/page.c:331
> > Code: 44 89 fe e8 39 8e 12 fe 45 84 f6 74 25 e8 9f 8a 12 fe 48 89 df
> > e8 67 9a 55 fe 4c 8b 7c 24 10 e9 e5 fe ff ff e8 88 8a 12 fe 90 <0f> 0b
> > 90 e9 1d fe ff ff e8 7a 8a 12 fe 4c 8b 7c 24 10 e9 c8 fe ff
> > RSP: 0018:ffffc9000854f4c0 EFLAGS: 00010293
> > RAX: ffffffff83a8e928 RBX: ffffea00013cf3c0 RCX: ffff888026619f80
> > RDX: 0000000000000000 RSI: 0000000000000010 RDI: 0000000000000000
> > RBP: ffffc9000854f6e8 R08: ffffea00012db607 R09: 1ffffd400025b6c0
> > R10: dffffc0000000000 R11: fffff9400025b6c1 R12: ffffc9000854f548
> > R13: ffffea00012db600 R14: 0000000000000010 R15: 1ffffd4000279e79
> > FS:  000055558d08a380(0000) GS:ffff8880985d9000(0000) knlGS:0000000000000000
> > CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> > CR2: 000055558d08aca0 CR3: 0000000044f02000 CR4: 0000000000752ef0
> > PKRU: 55555554
> > Call Trace:
> >  <TASK>
> >  nilfs_mdt_restore_from_shadow_map+0x127/0x260 fs/nilfs2/mdt.c:652
> >  nilfs_clean_segments+0xab8/0xba0 fs/nilfs2/segment.c:2552
> >  nilfs_ioctl_clean_segments fs/nilfs2/ioctl.c:938 [inline]
> >  nilfs_ioctl+0x242a/0x2590 fs/nilfs2/ioctl.c:1368
> >  vfs_ioctl fs/ioctl.c:51 [inline]
> >  __do_sys_ioctl fs/ioctl.c:597 [inline]
> >  __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
> >  do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
> >  do_syscall_64+0x170/0x540 arch/x86/entry/syscall_64.c:84
> >  entry_SYSCALL_64_after_hwframe+0x77/0x7f
> > RIP: 0033:0x7fa00bf4d42d
> > Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 48 89 f8 48
> > 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d
> > 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48
> > RSP: 002b:00007ffd36ab1678 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
> > RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007fa00bf4d42d
> > RDX: 0000200000000300 RSI: 0000000040786e88 RDI: 0000000000000004
> > RBP: 0000000000000000 R08: 0000000000000000 R09: 00007ffd36ab16c0
> > R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffd36ab169c
> > R13: 00007ffd36ab16e0 R14: 000000000000000c R15: 431bde82d7b634db
> >  </TASK>
> >
> > Following is the root cause analysis for this issue, note that the
> > analysis is performed with the assistance of LLM, but we try our best to
> > ensure the accuracy.
> >
> > The WARNING triggers because a folio of the DAT metadata file is still
> > dirty where nilfs_copy_back_pages() expects a clean one.  That function
> > runs when garbage collection fails, which the reproducer causes by asking
> > it to free a virtual block number the DAT file does not map.  nilfs2 then
> > restores the DAT page cache to a snapshot taken before the collection
> > started: it first clears the dirty state of the folios in that cache, then
> > copies the snapshot back over them, assuming the first step left nothing
> > dirty.
> >
> > The clearing step can skip a folio.  Since commit ca76bb226bf4 ("nilfs2: do
> > not force clear folio if buffer is referenced") nilfs_clear_folio_dirty()
> > returns without clearing if any buffer head of the folio is busy, to avoid
> > clobbering the state of a buffer somebody else is using.  A buffer head is
> > busy here: reading a metadata block goes through nilfs_mdt_read_block(),
> > which also submits read-ahead for the blocks that follow and waits only for
> > the first.  When the block size is smaller than the page size, several
> > metadata blocks share one folio, so a locked read-ahead buffer can be
> > adjacent to a dirty buffer and keep the whole folio dirty.
> >
> > nilfs_copy_folio() then copies the snapshot over that folio and clears
> > BH_Dirty on all of its buffer heads, leaving the folio dirty with no dirty
> > buffer under it, which no later segment construction cleans up.  On a
> > kernel booted with panic_on_warn, the WARNING can lead to a system crash.
> >
> > To fix this issue, the dirty flag of the destination folio should be
> > cleared before the folio is overwritten:
> >
> > diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
> > index cf4f1c6798f5..b26d9c3bda6d 100644
> > --- a/fs/nilfs2/page.c
> > +++ b/fs/nilfs2/page.c
> > @@ -328,7 +328,8 @@ void nilfs_copy_back_pages(struct address_space *dmap,
> >   dfolio = filemap_lock_folio(dmap, index);
> >   if (!IS_ERR(dfolio)) {
> >   /* overwrite existing folio in the destination cache */
> > - WARN_ON(folio_test_dirty(dfolio));
> > + if (unlikely(folio_test_dirty(dfolio)))
> > + __nilfs_clear_folio_dirty(dfolio);
> >   nilfs_copy_folio(dfolio, folio, false);
> >   folio_unlock(dfolio);
> >   folio_put(dfolio);
> >
> > After applying the above patch, the reproducer no longer triggers the issue
> > on our machine.  If this solution is acceptable, we are happy to submit a
> > formal patch.
> >
> > The kernel console output, kernel config, syzkaller reproducer, and C
> > reproducer are available at google drive:
> > https://drive.google.com/drive/folders/1R6EqqLPh5vgylKRPvO2ji5Q8Tsf0x8QY?usp=sharing
> >
> > Please let us know if any further information is required.
>
> Thanks for the report, Jiaming Zhang!
>
> I think the issue is that commit ca76bb226bf4 changed the expected
> behavior of nilfs_clear_dirty_pages(), which is called before
> nilfs_copy_back_pages().
>
> Originally, nilfs_clear_dirty_pages() was implemented for
> nilfs_mdt_restore_from_shadow_map() to restore the pre-GC state from
> the DAT shadow page cache. It was designed to forcefully clear dirty
> pages/folios in the DAT page cache, assuming complete synchronization
> with the log writer.
>
> However, over time, it started being called to resolve page/folio
> dirty states when the filesystem degrades to read-only during a
> page/folio writeback request.
>
> Since this page/folio dirty state clearing was asynchronous with the
> log writer, it caused unpredictable bugs in various places after the
> filesystem fell back to read-only. Commit ca76bb226bf4 is one of the
> essential workarounds introduced to avoid this.
>
> The root cause is that the same helper function is being used for
> calls with two entirely different premises. I believe the solution is
> to restore the original semantics of nilfs_clear_dirty_pages() only
> when it is called from nilfs_mdt_restore_from_shadow_map().
>
> Specifically, how about adding a bool force flag argument to
> nilfs_clear_dirty_pages() and nilfs_clear_folio_dirty(), and having
> the call from nilfs_mdt_restore_from_shadow_map() set this flag to
> clear the dirty state regardless of the buffer head's reference state,
> just like the original implementation?
>
> Would it be possible for you to propose a fix using this approach?

Thank you for the explanation! I agree that your proposal sounds more
reasonable. I will send v2 patch later, add a bool force argument on
nilfs_clear_dirty_pages() and nilfs_clear_folio_dirty(), it is only
set true on the two calls in nilfs_mdt_restore_from_shadow_map(), and
keep the WARN_ON().

>
> Alternatively, since their premises differ, we could separate the
> implementations entirely, but I expect that adding a boolean option to
> switch modes would be simpler.
>
> Adjusting the WARN_ON in nilfs_copy_back_pages() would break the
> original design (premise) and is therefore undesirable, in my view.
>
> Thanks,
> Ryusuke Konishi
>
> >
> > Best Regards,
> > Jiaming Zhang