diff mbox series

btrfs: remove folio order ASSERT()s in super block writeback path

Message ID 92a474470c6230241d7ebff3673c3d624c38ae6a.1743110853.git.wqu@suse.com (mailing list archive)
State New
Headers show
Series btrfs: remove folio order ASSERT()s in super block writeback path | expand

Commit Message

Qu Wenruo March 27, 2025, 9:29 p.m. UTC
[BUG]
There is a syzbot report that the ASSERT() inside write_dev_supers() got
triggered:

  assertion failed: folio_order(folio) == 0, in fs/btrfs/disk-io.c:3858
  ------------[ cut here ]------------
  kernel BUG at fs/btrfs/disk-io.c:3858!
  Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
  CPU: 0 UID: 0 PID: 6730 Comm: syz-executor378 Not tainted 6.14.0-syzkaller-03565-gf6e0150b2003 #0 PREEMPT(full)
  Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014
  RIP: 0010:write_dev_supers fs/btrfs/disk-io.c:3858 [inline]
  RIP: 0010:write_all_supers+0x400f/0x4090 fs/btrfs/disk-io.c:4155
  Call Trace:
   <TASK>
   btrfs_commit_transaction+0x1eda/0x3750 fs/btrfs/transaction.c:2528
   btrfs_quota_enable+0xfcc/0x21a0 fs/btrfs/qgroup.c:1226
   btrfs_ioctl_quota_ctl+0x144/0x1c0 fs/btrfs/ioctl.c:3677
   vfs_ioctl fs/ioctl.c:51 [inline]
   __do_sys_ioctl fs/ioctl.c:906 [inline]
   __se_sys_ioctl+0xf1/0x160 fs/ioctl.c:892
   do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
   do_syscall_64+0xf3/0x230 arch/x86/entry/syscall_64.c:94
   entry_SYSCALL_64_after_hwframe+0x77/0x7f
  RIP: 0033:0x7f5ad1f20289
   </TASK>
  ---[ end trace 0000000000000000 ]---

[CAUSE]
Since commit f93ee0df5139 ("btrfs: convert super block writes to folio
in write_dev_supers()") and commit c94b7349b859 ("btrfs: convert super
block writes to folio in wait_dev_supers()"), the super block writeback
path is converted to use folio.

Since the original code is using page based interfaces, we have an
"ASSERT(folio_order(folio) == 0);" added to make sure everything is not
changed.

But the folio here is not from any btrfs inode, but from the block
device, and we have no control on the folio order in bdev, the device
can choose whatever folio size they want/need.

E.g. the bdev may even have a block size of multiple pages.

So the ASSERT() is triggered.

[FIX]
The super block writeback path has taken larger folios into
consideration, so there is no need for the ASSERT().

And since commit bc00965dbff7 ("btrfs: count super block write errors in
device instead of tracking folio error state"), the wait path no longer
checks the folio status but only wait for the folio writeback to finish,
there is nothing requiring the ASSERT() either.

So we can remove both ASSERT()s safely now.

Reported-by: syzbot+34122898a11ab689518a@syzkaller.appspotmail.com
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
Our super block read path is still using page interfaces, and I believe
it's better to migrate to the folio interfaces with large folio support.
---
 fs/btrfs/disk-io.c | 2 --
 1 file changed, 2 deletions(-)

Comments

David Sterba March 28, 2025, 1:46 a.m. UTC | #1
On Fri, Mar 28, 2025 at 07:59:12AM +1030, Qu Wenruo wrote:
> [BUG]
> There is a syzbot report that the ASSERT() inside write_dev_supers() got
> triggered:
> 
>   assertion failed: folio_order(folio) == 0, in fs/btrfs/disk-io.c:3858
>   ------------[ cut here ]------------
>   kernel BUG at fs/btrfs/disk-io.c:3858!
>   Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
>   CPU: 0 UID: 0 PID: 6730 Comm: syz-executor378 Not tainted 6.14.0-syzkaller-03565-gf6e0150b2003 #0 PREEMPT(full)
>   Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014
>   RIP: 0010:write_dev_supers fs/btrfs/disk-io.c:3858 [inline]
>   RIP: 0010:write_all_supers+0x400f/0x4090 fs/btrfs/disk-io.c:4155
>   Call Trace:
>    <TASK>
>    btrfs_commit_transaction+0x1eda/0x3750 fs/btrfs/transaction.c:2528
>    btrfs_quota_enable+0xfcc/0x21a0 fs/btrfs/qgroup.c:1226
>    btrfs_ioctl_quota_ctl+0x144/0x1c0 fs/btrfs/ioctl.c:3677
>    vfs_ioctl fs/ioctl.c:51 [inline]
>    __do_sys_ioctl fs/ioctl.c:906 [inline]
>    __se_sys_ioctl+0xf1/0x160 fs/ioctl.c:892
>    do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
>    do_syscall_64+0xf3/0x230 arch/x86/entry/syscall_64.c:94
>    entry_SYSCALL_64_after_hwframe+0x77/0x7f
>   RIP: 0033:0x7f5ad1f20289
>    </TASK>
>   ---[ end trace 0000000000000000 ]---
> 
> [CAUSE]
> Since commit f93ee0df5139 ("btrfs: convert super block writes to folio
> in write_dev_supers()") and commit c94b7349b859 ("btrfs: convert super
> block writes to folio in wait_dev_supers()"), the super block writeback
> path is converted to use folio.
> 
> Since the original code is using page based interfaces, we have an
> "ASSERT(folio_order(folio) == 0);" added to make sure everything is not
> changed.
> 
> But the folio here is not from any btrfs inode, but from the block
> device, and we have no control on the folio order in bdev, the device
> can choose whatever folio size they want/need.
> 
> E.g. the bdev may even have a block size of multiple pages.
> 
> So the ASSERT() is triggered.
> 
> [FIX]
> The super block writeback path has taken larger folios into
> consideration, so there is no need for the ASSERT().
> 
> And since commit bc00965dbff7 ("btrfs: count super block write errors in
> device instead of tracking folio error state"), the wait path no longer
> checks the folio status but only wait for the folio writeback to finish,
> there is nothing requiring the ASSERT() either.
> 
> So we can remove both ASSERT()s safely now.
> 
> Reported-by: syzbot+34122898a11ab689518a@syzkaller.appspotmail.com
> Signed-off-by: Qu Wenruo <wqu@suse.com>

Reviewed-by: David Sterba <dsterba@suse.com>

> ---
> Our super block read path is still using page interfaces, and I believe
> it's better to migrate to the folio interfaces with large folio support.

Yeah, it's the page API wrapping folios anyway.
diff mbox series

Patch

diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 1a916716cefe..d0da9988ea48 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -3853,7 +3853,6 @@  static int write_dev_supers(struct btrfs_device *device,
 			atomic_inc(&device->sb_write_errors);
 			continue;
 		}
-		ASSERT(folio_order(folio) == 0);
 
 		offset = offset_in_folio(folio, bytenr);
 		disk_super = folio_address(folio) + offset;
@@ -3926,7 +3925,6 @@  static int wait_dev_supers(struct btrfs_device *device, int max_mirrors)
 		/* If the folio has been removed, then we know it completed. */
 		if (IS_ERR(folio))
 			continue;
-		ASSERT(folio_order(folio) == 0);
 
 		/* Folio will be unlocked once the write completes. */
 		folio_wait_locked(folio);