diff mbox series

btrfs: qgroup: do not warn on record without @old_roots populated

Message ID de9535cd9d3b5b020190bbfc751c3705fee8d55d.1673334821.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: qgroup: do not warn on record without @old_roots populated | expand

Commit Message

Qu Wenruo Jan. 10, 2023, 7:14 a.m. UTC
[BUG]
There is some reports from the mailing list that since v6.1 kernel, the
WARN_ON() inside btrfs_qgroup_account_extent() get triggered during
rescan:

 ------------[ cut here ]------------
 WARNING: CPU: 3 PID: 6424 at fs/btrfs/qgroup.c:2756 btrfs_qgroup_account_extents+0x1ae/0x260 [btrfs]
 CPU: 3 PID: 6424 Comm: snapperd Tainted: P           OE      6.1.2-1-default #1 openSUSE Tumbleweed 05c7a1b1b61d5627475528f71f50444637b5aad7
 RIP: 0010:btrfs_qgroup_account_extents+0x1ae/0x260 [btrfs]
 Call Trace:
  <TASK>
 btrfs_commit_transaction+0x30c/0xb40 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
  ? start_transaction+0xc3/0x5b0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
 btrfs_qgroup_rescan+0x42/0xc0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
  btrfs_ioctl+0x1ab9/0x25c0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
  ? __rseq_handle_notify_resume+0xa9/0x4a0
  ? mntput_no_expire+0x4a/0x240
  ? __seccomp_filter+0x319/0x4d0
  __x64_sys_ioctl+0x90/0xd0
  do_syscall_64+0x5b/0x80
  ? syscall_exit_to_user_mode+0x17/0x40
  ? do_syscall_64+0x67/0x80
 entry_SYSCALL_64_after_hwframe+0x63/0xcd
 RIP: 0033:0x7fd9b790d9bf
  </TASK>
 ---[ end trace 0000000000000000 ]---

[CAUSE]
Since commit e15e9f43c7ca ("btrfs: introduce
BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING to skip qgroup accounting"), if
our qgroup is already in inconsistent status, we will no longer do the
time-consuming backref walk.

This can leave some qgroup records without a valid old_roots ulist.
Normally this is fine, as btrfs_qgroup_account_extents() would also skip
those records if we have NO_ACCOUNTING flag set.

But there is a small window, if we have NO_ACCOUNTING flag set, and
inserted some qgroup_record without a old_roots ulist, but then the user
triggered a qgroup rescan.

During btrfs_qgroup_rescan(), we firstly clear NO_ACCOUNTING flag, then
commit current transaction.

And since we have a qgroup_record with old_roots = NULL, we trigger the
WARN_ON() during btrfs_qgroup_account_extents().

[FIX]
Unfortunately due to the introduce of NO_ACCOUNTING flag, the assumption
that every qgroup_record would has its old_roots populated is no longer
correct.

So to fix the false alerts, just change the WARN_ON() to unlikely().

Reported-by: Lukas Straub <lukasstraub2@web.de>
Reported-by: HanatoK <summersnow9403@gmail.com>
Link: https://lore.kernel.org/linux-btrfs/2403c697-ddaf-58ad-3829-0335fc89df09@gmail.com/
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/qgroup.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

Comments

Filipe Manana Jan. 10, 2023, 12:02 p.m. UTC | #1
On Tue, Jan 10, 2023 at 7:18 AM Qu Wenruo <wqu@suse.com> wrote:
>
> [BUG]
> There is some reports from the mailing list that since v6.1 kernel, the
> WARN_ON() inside btrfs_qgroup_account_extent() get triggered during
> rescan:
>
>  ------------[ cut here ]------------
>  WARNING: CPU: 3 PID: 6424 at fs/btrfs/qgroup.c:2756 btrfs_qgroup_account_extents+0x1ae/0x260 [btrfs]
>  CPU: 3 PID: 6424 Comm: snapperd Tainted: P           OE      6.1.2-1-default #1 openSUSE Tumbleweed 05c7a1b1b61d5627475528f71f50444637b5aad7
>  RIP: 0010:btrfs_qgroup_account_extents+0x1ae/0x260 [btrfs]
>  Call Trace:
>   <TASK>
>  btrfs_commit_transaction+0x30c/0xb40 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
>   ? start_transaction+0xc3/0x5b0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
>  btrfs_qgroup_rescan+0x42/0xc0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
>   btrfs_ioctl+0x1ab9/0x25c0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
>   ? __rseq_handle_notify_resume+0xa9/0x4a0
>   ? mntput_no_expire+0x4a/0x240
>   ? __seccomp_filter+0x319/0x4d0
>   __x64_sys_ioctl+0x90/0xd0
>   do_syscall_64+0x5b/0x80
>   ? syscall_exit_to_user_mode+0x17/0x40
>   ? do_syscall_64+0x67/0x80
>  entry_SYSCALL_64_after_hwframe+0x63/0xcd
>  RIP: 0033:0x7fd9b790d9bf
>   </TASK>
>  ---[ end trace 0000000000000000 ]---
>
> [CAUSE]
> Since commit e15e9f43c7ca ("btrfs: introduce
> BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING to skip qgroup accounting"), if
> our qgroup is already in inconsistent status, we will no longer do the
> time-consuming backref walk.
>
> This can leave some qgroup records without a valid old_roots ulist.
> Normally this is fine, as btrfs_qgroup_account_extents() would also skip
> those records if we have NO_ACCOUNTING flag set.
>
> But there is a small window, if we have NO_ACCOUNTING flag set, and
> inserted some qgroup_record without a old_roots ulist, but then the user
> triggered a qgroup rescan.
>
> During btrfs_qgroup_rescan(), we firstly clear NO_ACCOUNTING flag, then
> commit current transaction.
>
> And since we have a qgroup_record with old_roots = NULL, we trigger the
> WARN_ON() during btrfs_qgroup_account_extents().
>
> [FIX]
> Unfortunately due to the introduce of NO_ACCOUNTING flag, the assumption
> that every qgroup_record would has its old_roots populated is no longer
> correct.
>
> So to fix the false alerts, just change the WARN_ON() to unlikely().
>
> Reported-by: Lukas Straub <lukasstraub2@web.de>
> Reported-by: HanatoK <summersnow9403@gmail.com>
> Link: https://lore.kernel.org/linux-btrfs/2403c697-ddaf-58ad-3829-0335fc89df09@gmail.com/
> Signed-off-by: Qu Wenruo <wqu@suse.com>

So missing a

Fixes: e15e9f43c7ca ("btrfs: introduce
BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING to skip qgroup accounting")

Or a

CC: stable@vger.kernel.org # 6.1+

No?

> ---
>  fs/btrfs/qgroup.c | 14 ++++++++++++--
>  1 file changed, 12 insertions(+), 2 deletions(-)
>
> diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
> index d275bf24b250..6a1aedf0dc72 100644
> --- a/fs/btrfs/qgroup.c
> +++ b/fs/btrfs/qgroup.c
> @@ -2765,9 +2765,19 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
>
>                         /*
>                          * Old roots should be searched when inserting qgroup
> -                        * extent record
> +                        * extent record.
> +                        *
> +                        * But for INCONSISTENT (NO_ACCOUNTING) -> rescan case,
> +                        * we may have some record inserted during
> +                        * NO_ACCOUNTING (thus no old_roots populated), but
> +                        * later we start rescan, which clears NO_ACCOUNTING,
> +                        * leaving some inserted records without old_roots
> +                        * populated.
> +                        *
> +                        * Those cases are rare and should not cause too much
> +                        * time spent during commit_transaction().
>                          */
> -                       if (WARN_ON(!record->old_roots)) {
> +                       if (unlikely(!record->old_roots)) {
>                                 /* Search commit root to find old_roots */
>                                 ret = btrfs_find_all_roots(&ctx, false);
>                                 if (ret < 0)
> --
> 2.39.0
>
David Sterba Jan. 10, 2023, 3:15 p.m. UTC | #2
On Tue, Jan 10, 2023 at 03:14:17PM +0800, Qu Wenruo wrote:
> [BUG]
> There is some reports from the mailing list that since v6.1 kernel, the
> WARN_ON() inside btrfs_qgroup_account_extent() get triggered during
> rescan:
> 
>  ------------[ cut here ]------------
>  WARNING: CPU: 3 PID: 6424 at fs/btrfs/qgroup.c:2756 btrfs_qgroup_account_extents+0x1ae/0x260 [btrfs]
>  CPU: 3 PID: 6424 Comm: snapperd Tainted: P           OE      6.1.2-1-default #1 openSUSE Tumbleweed 05c7a1b1b61d5627475528f71f50444637b5aad7
>  RIP: 0010:btrfs_qgroup_account_extents+0x1ae/0x260 [btrfs]
>  Call Trace:
>   <TASK>
>  btrfs_commit_transaction+0x30c/0xb40 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
>   ? start_transaction+0xc3/0x5b0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
>  btrfs_qgroup_rescan+0x42/0xc0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
>   btrfs_ioctl+0x1ab9/0x25c0 [btrfs c39c9c546c241c593f03bd6d5f39ea1b676250f6]
>   ? __rseq_handle_notify_resume+0xa9/0x4a0
>   ? mntput_no_expire+0x4a/0x240
>   ? __seccomp_filter+0x319/0x4d0
>   __x64_sys_ioctl+0x90/0xd0
>   do_syscall_64+0x5b/0x80
>   ? syscall_exit_to_user_mode+0x17/0x40
>   ? do_syscall_64+0x67/0x80
>  entry_SYSCALL_64_after_hwframe+0x63/0xcd
>  RIP: 0033:0x7fd9b790d9bf
>   </TASK>
>  ---[ end trace 0000000000000000 ]---
> 
> [CAUSE]
> Since commit e15e9f43c7ca ("btrfs: introduce
> BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING to skip qgroup accounting"), if
> our qgroup is already in inconsistent status, we will no longer do the
> time-consuming backref walk.
> 
> This can leave some qgroup records without a valid old_roots ulist.
> Normally this is fine, as btrfs_qgroup_account_extents() would also skip
> those records if we have NO_ACCOUNTING flag set.
> 
> But there is a small window, if we have NO_ACCOUNTING flag set, and
> inserted some qgroup_record without a old_roots ulist, but then the user
> triggered a qgroup rescan.
> 
> During btrfs_qgroup_rescan(), we firstly clear NO_ACCOUNTING flag, then
> commit current transaction.
> 
> And since we have a qgroup_record with old_roots = NULL, we trigger the
> WARN_ON() during btrfs_qgroup_account_extents().
> 
> [FIX]
> Unfortunately due to the introduce of NO_ACCOUNTING flag, the assumption
> that every qgroup_record would has its old_roots populated is no longer
> correct.
> 
> So to fix the false alerts, just change the WARN_ON() to unlikely().
> 
> Reported-by: Lukas Straub <lukasstraub2@web.de>
> Reported-by: HanatoK <summersnow9403@gmail.com>
> Link: https://lore.kernel.org/linux-btrfs/2403c697-ddaf-58ad-3829-0335fc89df09@gmail.com/
> Signed-off-by: Qu Wenruo <wqu@suse.com>

Added to misc-next, with the suggested Fixes and CC tags, thanks.
diff mbox series

Patch

diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index d275bf24b250..6a1aedf0dc72 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -2765,9 +2765,19 @@  int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
 
 			/*
 			 * Old roots should be searched when inserting qgroup
-			 * extent record
+			 * extent record.
+			 *
+			 * But for INCONSISTENT (NO_ACCOUNTING) -> rescan case,
+			 * we may have some record inserted during
+			 * NO_ACCOUNTING (thus no old_roots populated), but
+			 * later we start rescan, which clears NO_ACCOUNTING,
+			 * leaving some inserted records without old_roots
+			 * populated.
+			 *
+			 * Those cases are rare and should not cause too much
+			 * time spent during commit_transaction().
 			 */
-			if (WARN_ON(!record->old_roots)) {
+			if (unlikely(!record->old_roots)) {
 				/* Search commit root to find old_roots */
 				ret = btrfs_find_all_roots(&ctx, false);
 				if (ret < 0)