From: arjan@linux.intel.com
To: linux-btrfs@vger.kernel.org
Cc: syzbot+768687d6d7c96a124e24@syzkaller.appspotmail.com,
clm@fb.com, dsterba@suse.com, linux-kernel@vger.kernel.org,
syzkaller-bugs@googlegroups.com,
Arjan van de Ven <arjan@linux.intel.com>
Subject: Re: [syzbot] [btrfs?] WARNING in __btrfs_update_delayed_inode (4)
Date: Fri, 24 Apr 2026 07:13:28 -0700 [thread overview]
Message-ID: <20260424141331.331289-1-arjan@linux.intel.com> (raw)
In-Reply-To: <69eb21b8.a00a0220.17a17.0056.GAE@google.com>
From: Arjan van de Ven <arjan@linux.intel.com>
On Fri, 24 Apr 2026 00:54:32 -0700
syzbot <syzbot+768687d6d7c96a124e24@syzkaller.appspotmail.com> wrote:
> WARNING: fs/btrfs/delayed-inode.c:1027 at
> __btrfs_update_delayed_inode+0xe3d/0x1070
Analyzed this WARNING; here are the findings.
Decoded backtrace source listings
----------------------------------
--- __btrfs_update_delayed_inode (crash site, fs/btrfs/delayed-inode.c:1027) ---
996 static int __btrfs_update_delayed_inode(struct btrfs_trans_handle *trans,
997 struct btrfs_root *root,
998 struct btrfs_path *path,
999 struct btrfs_delayed_node *node)
1000 {
1001 struct btrfs_fs_info *fs_info = root->fs_info;
1002 struct btrfs_key key;
1003 struct btrfs_inode_item *inode_item;
1004 struct extent_buffer *leaf;
1005 int mod;
1006 int ret;
...
1012 if (test_bit(BTRFS_DELAYED_NODE_DEL_IREF, &node->flags))
1013 mod = -1;
1014 else
1015 mod = 1;
1016
1017 ret = btrfs_lookup_inode(trans, root, path, &key, mod); // returns -28 (-ENOSPC)
1018 if (ret > 0)
1019 ret = -ENOENT;
1020 if (ret < 0) {
1021 /*
1022 * If we fail to update the delayed inode we need to abort the
1023 * transaction, because we could leave the inode with the
1024 * improper counts behind.
1025 */
1026 if (unlikely(ret != -ENOENT))
1027 btrfs_abort_transaction(trans, ret); // <- WARNING fires here
1028 goto out;
1029 }
...
1088 }
--- btrfs_update_delayed_inode (inlined, fs/btrfs/delayed-inode.c:1103) ---
1090 static inline int btrfs_update_delayed_inode(struct btrfs_trans_handle *trans,
1091 struct btrfs_root *root,
1092 struct btrfs_path *path,
1093 struct btrfs_delayed_node *node)
1094 {
1095 int ret;
1096
1097 mutex_lock(&node->mutex);
1098 if (!test_bit(BTRFS_DELAYED_NODE_INODE_DIRTY, &node->flags)) {
1099 mutex_unlock(&node->mutex);
1100 return 0;
1101 }
1102
1103 ret = __btrfs_update_delayed_inode(trans, root, path, node); // <- call here
1104 mutex_unlock(&node->mutex);
1105 return ret;
1106 }
--- __btrfs_commit_inode_delayed_items (fs/btrfs/delayed-inode.c:1127) ---
1108 static inline int
1109 __btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
1110 struct btrfs_path *path,
1111 struct btrfs_delayed_node *node)
1112 {
1113 int ret;
1114
1115 ret = btrfs_insert_delayed_items(trans, path, node->root, node);
1116 if (ret)
1117 return ret;
1118
1119 ret = btrfs_delete_delayed_items(trans, path, node->root, node);
1120 if (ret)
1121 return ret;
1122
1123 ret = btrfs_record_root_in_trans(trans, node->root);
1124 if (ret)
1125 return ret;
1126
1127 return btrfs_update_delayed_inode(trans, node->root, path, node); // <- call here
1128 }
--- __btrfs_run_delayed_items (fs/btrfs/delayed-inode.c:1158) ---
1136 static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr)
1137 {
1138 struct btrfs_fs_info *fs_info = trans->fs_info;
...
1141 struct btrfs_path *path;
1142 struct btrfs_block_rsv *block_rsv;
...
1149 path = btrfs_alloc_path();
...
1153 block_rsv = trans->block_rsv;
1154 trans->block_rsv = &fs_info->delayed_block_rsv; // RSV switched to delayed_block_rsv
1155
1156 curr_node = btrfs_first_delayed_node(fs_info, &curr_delayed_node_tracker);
1157 while (curr_node && (!count || nr--)) {
1158 ret = __btrfs_commit_inode_delayed_items(trans, path, // <- call here
1159 curr_node);
1160 if (unlikely(ret)) {
1161 btrfs_abort_transaction(trans, ret);
1162 break;
1163 }
...
The WARN() fires inside the btrfs_abort_transaction macro when
btrfs_lookup_inode() returns -ENOSPC (-28) in
__btrfs_update_delayed_inode. The macro calls
btrfs_abort_should_print_stack(-28), which returns true because -ENOSPC
is not in the exception list (-EIO, -EROFS, -ENOMEM). This causes the
WARN() to fire and print the stack trace.
The root cause is a circular ENOSPC condition: the async data-space
reclaim path commits a transaction to free data space, but that
transaction commit needs metadata space for B-tree copy-on-write
operations (allocated from delayed_block_rsv). When both data and
metadata space are critically low, btrfs_search_slot fails with
-ENOSPC even for the deletion COW path.
The transaction abort is correct behavior for this failure. The issue
is that -ENOSPC is not an exceptional/unexpected error in this context
— it is a predictable outcome under severe space pressure — yet
btrfs_abort_should_print_stack() treats it as unexpected and fires
WARN().
Proposed fix: add -ENOSPC to the exception list in
btrfs_abort_should_print_stack() in fs/btrfs/transaction.h:
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -236,6 +236,7 @@ static inline bool btrfs_abort_should_print_stack(int error)
switch (error) {
case -EIO:
case -EROFS:
+ case -ENOSPC:
case -ENOMEM:
return false;
}
This matches the intent of the list (errors that are expected/normal
filesystem conditions, not programming errors). The transaction is
still aborted correctly via __btrfs_abort_transaction(); only the
spurious stack-printing WARN() is suppressed.
A deeper fix would ensure delayed_block_rsv has sufficient reservation
to avoid the ENOSPC in the first place, but that is a harder problem
touching btrfs space-reservation accounting.
Full analysis at:
http://oops.fenrus.org/reports/lkml/69eb21b8.a00a0220.17a17.0056.GAE_google.com/report.md
Arjan van de Ven
prev parent reply other threads:[~2026-04-24 14:12 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-24 7:54 [syzbot] [btrfs?] WARNING in __btrfs_update_delayed_inode (4) syzbot
2026-04-24 14:13 ` arjan [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260424141331.331289-1-arjan@linux.intel.com \
--to=arjan@linux.intel.com \
--cc=clm@fb.com \
--cc=dsterba@suse.com \
--cc=linux-btrfs@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=syzbot+768687d6d7c96a124e24@syzkaller.appspotmail.com \
--cc=syzkaller-bugs@googlegroups.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.