From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-ot1-f71.google.com (mail-ot1-f71.google.com [209.85.210.71]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 115632F7EFB for ; Tue, 19 May 2026 09:00:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.71 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779181246; cv=none; b=iZtBhMFCvBojfMAExNFLLrcS7AG21GJYo/tdNCApcF5EXaL9pzTmF/Ybv920lbkEYNOEWsBEBtIgdXp+9iB4M7vNYuXGGfgvU6Za/1k6/t0C+mhqG/V412qvRVoiklTWX2xp3n6Ppg6c7z0rWSwX5nRlZmNdfhIFIW2eWyz0Pwg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779181246; c=relaxed/simple; bh=fXLDSsFkBVISihdqaEBZvbW6VHhCJJHE1den4t+y924=; h=MIME-Version:Date:In-Reply-To:Message-ID:Subject:From:To: Content-Type; b=GHky9Vkrpc6JzHDB6zMNQBffjEzEsPPCX0CRtg/cDbqDWk5Ymylh45F7DJOUwfL7kG/Sf+LDW8qQsFfxRBtEwRMhilEh06ZJ6auvo+O7dD5HMs9yI0uim90lbFIOtG0xB88+mC6WdnYeQFLPrO/4JKAlOuQTXVV8NpAS+aqi88o= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com; arc=none smtp.client-ip=209.85.210.71 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com Received: by mail-ot1-f71.google.com with SMTP id 46e09a7af769-7e3a338673eso10457357a34.0 for ; Tue, 19 May 2026 02:00:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779181244; x=1779786044; h=content-transfer-encoding:to:from:subject:message-id:in-reply-to :date:mime-version:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=N3qA4BtuFPxNvPBHM07EQTY+swf9v+FCQsYRtmAIXeE=; b=nj6Gm9wnC7LE6LXJMdfrFlzeqxqgYXNeeV8BvD8FAfpq/5GaOzfvkVR7PYhT+pJsfn OT/S5eV/kprvlp4F9tKGvxwLy47JwCgIoEaT6tzs5tJ8dnv4Az174uxI0n8tU0rZlkH7 wA0oz1onZet1CzpSeW5xz6iIvNPNhj7uDp1qeO7wTC0kn4hCx+bHUZdlxY3Eg3sTBqFL uPjmy5MmArtosKpPKrydoqk3PnSSgNedWT9qYgQpk8m0eoX0Xs4j5tP3clGA3RTRe05j z1zmYCahXQWQdv4CG8jVt04DM/B/tLvs1KNVhDPnUNdsogtrrk+yiPDx0NwXMoo7av3C n37w== X-Gm-Message-State: AOJu0YzG4dvYtF/8sALJRewefEvXowvH9HZRj1KudnZAgbKRVYduloLC ikSHKKnZrJ33g4tnTmGDaE+QaUMVt9pszCgJcuEUWkrCc36hXhg0l8uW07CIYg7d9A7YAfJ6ZCW cLvr2Eq51onqVmJByZgdXhLTvw2urpzaKMHJahwJRgfHPmvVBIrmKUYyoQss= Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Received: by 2002:a05:6820:163:b0:696:2cfa:5d85 with SMTP id 006d021491bc7-69b8803d652mr8894745eaf.13.1779181243770; Tue, 19 May 2026 02:00:43 -0700 (PDT) Date: Tue, 19 May 2026 02:00:43 -0700 In-Reply-To: <000000000000172fc905f8a19ab5@google.com> X-Google-Appengine-App-Id: s~syzkaller X-Google-Appengine-App-Id-Alias: syzkaller Message-ID: <6a0c26bb.a70a0220.2f1cd9.0003.GAE@google.com> Subject: Forwarded: Re: [PATCH RFC v3] btrfs: fix delayed transaction aborts From: syzbot To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable For archival purposes, forwarding an incoming command email to linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com. *** Subject: Re: [PATCH RFC v3] btrfs: fix delayed transaction aborts Author: nogikh@google.com #syz reject On Sun, May 17, 2026 at 1:42=E2=80=AFAM 'syzbot' via syzkaller-upstream-moderation wrote: > > A transaction abort with error -28 (-ENOSPC) can trigger a WARN_ON in > cleanup_transaction(). The stack trace is somewhat misleading because > the transaction abort is delayed until the cleanup phase of > btrfs_commit_transaction(), hiding the actual function that ran out of > space. > > When a highly crafted, extremely small BTRFS image is mounted and a > BTRFS_IOC_BALANCE_V2 ioctl is issued, the balance operation joins a > transaction and immediately commits it. During > btrfs_commit_transaction(), the filesystem needs to update various > trees. To update these trees, BTRFS must COW their root nodes, which > eventually calls btrfs_alloc_tree_block() to allocate a new physical > extent. Because the crafted image is tiny and has no free physical space > left, btrfs_reserve_extent() fails and returns -ENOSPC. > > The -ENOSPC error propagates up the call stack to commit_cowonly_roots() > or commit_fs_roots(). Crucially, when these functions receive this > error, they simply return it to btrfs_commit_transaction() without > calling btrfs_abort_transaction() themselves. The error is caught in > btrfs_commit_transaction() and execution jumps to the cleanup labels. > Inside cleanup_transaction(), btrfs_abort_transaction() is finally > called. Because the failing functions neglected to abort the transaction > when the error actually occurred, this call inside cleanup_transaction() > is the first abort, completely hiding the true source of the -ENOSPC. > > To fix this and ensure developers get accurate stack traces for > transaction aborts, explicitly call btrfs_abort_transaction() before > returning errors in functions that can fail with fatal errors during the > commit critical section (commit_cowonly_roots(), commit_fs_roots(), and > btrfs_qgroup_account_extents()). > > Also, replace the WARN() macro in btrfs_abort_transaction() with > dump_stack() to prevent transaction aborts from triggering kernel > warnings, while still printing the stack trace. > > Additionally, use lockdep maps for workqueue allocations to address > lockdep key exhaustion. > > Fixes: 49b25e0540904be0bf558b84475c69d72e4de66e ("btrfs: enhance transact= ion abort infrastructure") > Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview > Reported-by: syzbot+dafbca0e20fbc5946925@syzkaller.appspotmail.com > Link: https://syzkaller.appspot.com/bug?extid=3Ddafbca0e20fbc5946925 > Link: https://syzkaller.appspot.com/ai_job?id=3D96c50581-2f5c-4140-ad4e-b= f2cccae111e > To: > To: > To: > Cc: > > --- > v3: > - Dropped btrfs_abort_transaction() calls in create_pending_snapshot() to= reduce patch size. > - Replaced WARN() with dump_stack() in btrfs_abort_transaction() instead = of adding -ENOSPC to btrfs_abort_should_print_stack(). > - Restored workqueue changes addressing lockdep key exhaustion. > - Moved btrfs_abort_transaction() call in btrfs_qgroup_account_extents() = back to the error site. > > v2: > - Consolidated btrfs_abort_transaction() calls at the end of functions un= der cleanup/fail/out labels instead of calling them at each error site. > - Dropped unrelated workqueue and super.c changes addressing lockdep key = exhaustion. > https://lore.kernel.org/all/54bfa402-a284-4649-94d3-a25a88a6c475@mail.ker= nel.org/T/ > > v1: > https://lore.kernel.org/all/f77b5e84-af17-41f8-9357-e8f4cd4b8438@mail.ker= nel.org/T/ > --- > diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c > index e6f33d094..634b7b578 100644 > --- a/fs/btrfs/async-thread.c > +++ b/fs/btrfs/async-thread.c > @@ -81,9 +81,10 @@ static void btrfs_init_workqueue(struct btrfs_workqueu= e *wq, > spin_lock_init(&wq->thres_lock); > } > > -struct btrfs_workqueue *btrfs_alloc_workqueue(struct btrfs_fs_info *fs_i= nfo, > - const char *name, unsigned = int flags, > - int limit_active, int thres= h) > +struct btrfs_workqueue * > +__btrfs_alloc_workqueue(struct btrfs_fs_info *fs_info, const char *name, > + unsigned int flags, int limit_active, int thresh, > + struct lock_class_key *key, struct lockdep_map *m= ap) > { > struct btrfs_workqueue *ret =3D kzalloc_obj(*ret); > > @@ -109,8 +110,14 @@ struct btrfs_workqueue *btrfs_alloc_workqueue(struct= btrfs_fs_info *fs_info, > ret->thresh =3D thresh; > } > > +#ifdef CONFIG_LOCKDEP > + lockdep_init_map(map, name, key, 0); > + ret->normal_wq =3D alloc_workqueue_lockdep_map( > + "btrfs-%s", flags, ret->current_active, map, name); > +#else > ret->normal_wq =3D alloc_workqueue("btrfs-%s", flags, ret->curren= t_active, > name); > +#endif > if (!ret->normal_wq) { > kfree(ret); > return NULL; > @@ -120,9 +127,10 @@ struct btrfs_workqueue *btrfs_alloc_workqueue(struct= btrfs_fs_info *fs_info, > return ret; > } > > -struct btrfs_workqueue *btrfs_alloc_ordered_workqueue( > - struct btrfs_fs_info *fs_info, const char= *name, > - unsigned int flags) > +struct btrfs_workqueue * > +__btrfs_alloc_ordered_workqueue(struct btrfs_fs_info *fs_info, const cha= r *name, > + unsigned int flags, struct lock_class_key= *key, > + struct lockdep_map *map) > { > struct btrfs_workqueue *ret; > > @@ -137,7 +145,13 @@ struct btrfs_workqueue *btrfs_alloc_ordered_workqueu= e( > ret->current_active =3D 1; > ret->thresh =3D NO_THRESHOLD; > > +#ifdef CONFIG_LOCKDEP > + lockdep_init_map(map, name, key, 0); > + ret->normal_wq =3D alloc_ordered_workqueue_lockdep_map("btrfs-%s"= , flags, > + map, name); > +#else > ret->normal_wq =3D alloc_ordered_workqueue("btrfs-%s", flags, nam= e); > +#endif > if (!ret->normal_wq) { > kfree(ret); > return NULL; > diff --git a/fs/btrfs/async-thread.h b/fs/btrfs/async-thread.h > index 04c2f3175..f43f5feb9 100644 > --- a/fs/btrfs/async-thread.h > +++ b/fs/btrfs/async-thread.h > @@ -29,14 +29,40 @@ struct btrfs_work { > unsigned long flags; > }; > > -struct btrfs_workqueue *btrfs_alloc_workqueue(struct btrfs_fs_info *fs_i= nfo, > - const char *name, > - unsigned int flags, > - int limit_active, > - int thresh); > -struct btrfs_workqueue *btrfs_alloc_ordered_workqueue( > - struct btrfs_fs_info *fs_info, const char= *name, > - unsigned int flags); > +struct btrfs_workqueue * > +__btrfs_alloc_workqueue(struct btrfs_fs_info *fs_info, const char *name, > + unsigned int flags, int limit_active, int thresh, > + struct lock_class_key *key, struct lockdep_map *m= ap); > +#ifdef CONFIG_LOCKDEP > +#define btrfs_alloc_workqueue(fs_info, name, flags, limit_active, thresh= ) \ > + ({ = \ > + static struct lock_class_key __key; = \ > + static struct lockdep_map __map; = \ > + __btrfs_alloc_workqueue(fs_info, name, flags, limit_activ= e, \ > + thresh, &__key, &__map); = \ > + }) > +#else > +#define btrfs_alloc_workqueue(fs_info, name, flags, limit_active, thresh= ) \ > + __btrfs_alloc_workqueue(fs_info, name, flags, limit_active, thres= h, \ > + NULL, NULL) > +#endif > + > +struct btrfs_workqueue * > +__btrfs_alloc_ordered_workqueue(struct btrfs_fs_info *fs_info, const cha= r *name, > + unsigned int flags, struct lock_class_key= *key, > + struct lockdep_map *map); > +#ifdef CONFIG_LOCKDEP > +#define btrfs_alloc_ordered_workqueue(fs_info, name, flags) = \ > + ({ = \ > + static struct lock_class_key __key; = \ > + static struct lockdep_map __map; = \ > + __btrfs_alloc_ordered_workqueue(fs_info, name, flags, &__= key, \ > + &__map); = \ > + }) > +#else > +#define btrfs_alloc_ordered_workqueue(fs_info, name, flags) \ > + __btrfs_alloc_ordered_workqueue(fs_info, name, flags, NULL, NULL) > +#endif > void btrfs_init_work(struct btrfs_work *work, btrfs_func_t func, > btrfs_ordered_func_t ordered_func); > void btrfs_queue_work(struct btrfs_workqueue *wq, > diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c > index cdf736d3a..f9acfab14 100644 > --- a/fs/btrfs/qgroup.c > +++ b/fs/btrfs/qgroup.c > @@ -3076,6 +3076,10 @@ int btrfs_qgroup_account_extents(struct btrfs_tran= s_handle *trans) > record->num_byt= es, > record->old_roo= ts, > new_roots); > + if (ret < 0) { > + btrfs_abort_transaction(trans, ret); > + goto cleanup; > + } > record->old_roots =3D NULL; > new_roots =3D NULL; > } > diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c > index 248adb785..aa0ce0c59 100644 > --- a/fs/btrfs/transaction.c > +++ b/fs/btrfs/transaction.c > @@ -1372,21 +1372,23 @@ static noinline int commit_cowonly_roots(struct b= trfs_trans_handle *trans) > free_extent_buffer(eb); > > if (ret) > - return ret; > + goto out; > > ret =3D btrfs_run_dev_stats(trans); > if (ret) > - return ret; > + goto out; > + > ret =3D btrfs_run_dev_replace(trans); > if (ret) > - return ret; > + goto out; > + > ret =3D btrfs_run_qgroups(trans); > if (ret) > - return ret; > + goto out; > > ret =3D btrfs_setup_space_cache(trans); > if (ret) > - return ret; > + goto out; > > again: > while (!list_empty(&fs_info->dirty_cowonly_roots)) { > @@ -1400,18 +1402,18 @@ static noinline int commit_cowonly_roots(struct b= trfs_trans_handle *trans) > > ret =3D update_cowonly_root(trans, root); > if (ret) > - return ret; > + goto out; > } > > /* Now flush any delayed refs generated by updating all of the ro= ots */ > ret =3D btrfs_run_delayed_refs(trans, U64_MAX); > if (ret) > - return ret; > + goto out; > > while (!list_empty(dirty_bgs) || !list_empty(io_bgs)) { > ret =3D btrfs_write_dirty_block_groups(trans); > if (ret) > - return ret; > + goto out; > > /* > * We're writing the dirty block groups, which could gene= rate > @@ -1421,7 +1423,7 @@ static noinline int commit_cowonly_roots(struct btr= fs_trans_handle *trans) > */ > ret =3D btrfs_run_delayed_refs(trans, U64_MAX); > if (ret) > - return ret; > + goto out; > } > > if (!list_empty(&fs_info->dirty_cowonly_roots)) > @@ -1431,7 +1433,10 @@ static noinline int commit_cowonly_roots(struct bt= rfs_trans_handle *trans) > fs_info->dev_replace.committed_cursor_left =3D > fs_info->dev_replace.cursor_left_last_write_of_item; > > - return 0; > +out: > + if (ret) > + btrfs_abort_transaction(trans, ret); > + return ret; > } > > /* > @@ -1492,6 +1497,7 @@ static noinline int commit_fs_roots(struct btrfs_tr= ans_handle *trans) > struct btrfs_root *gang[8]; > int i; > int ret; > + int err =3D 0; > > /* > * At this point no one can be using this transaction to modify a= ny tree > @@ -1510,7 +1516,6 @@ static noinline int commit_fs_roots(struct btrfs_tr= ans_handle *trans) > break; > for (i =3D 0; i < ret; i++) { > struct btrfs_root *root =3D gang[i]; > - int ret2; > > /* > * At this point we can neither have tasks loggin= g inodes > @@ -1533,9 +1538,9 @@ static noinline int commit_fs_roots(struct btrfs_tr= ans_handle *trans) > spin_unlock(&fs_info->fs_roots_radix_lock); > > btrfs_free_log(trans, root); > - ret2 =3D btrfs_update_reloc_root(trans, root); > - if (unlikely(ret2)) > - return ret2; > + err =3D btrfs_update_reloc_root(trans, root); > + if (unlikely(err)) > + goto out; > > /* see comments in should_cow_block() */ > clear_bit(BTRFS_ROOT_FORCE_COW, &root->state); > @@ -1548,16 +1553,19 @@ static noinline int commit_fs_roots(struct btrfs_= trans_handle *trans) > root->node); > } > > - ret2 =3D btrfs_update_root(trans, fs_info->tree_r= oot, > + err =3D btrfs_update_root(trans, fs_info->tree_ro= ot, > &root->root_key, > &root->root_item); > - if (unlikely(ret2)) > - return ret2; > + if (unlikely(err)) > + goto out; > spin_lock(&fs_info->fs_roots_radix_lock); > } > } > spin_unlock(&fs_info->fs_roots_radix_lock); > - return 0; > +out: > + if (err) > + btrfs_abort_transaction(trans, err); > + return err; > } > > /* > diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h > index 7d70fe486..f816e62f3 100644 > --- a/fs/btrfs/transaction.h > +++ b/fs/btrfs/transaction.h > @@ -246,27 +246,21 @@ static inline bool btrfs_abort_should_print_stack(i= nt error) > * Call btrfs_abort_transaction as early as possible when an error condi= tion is > * detected, that way the exact stack trace is reported for some errors. > */ > -#define btrfs_abort_transaction(trans, error) \ > -do { \ > - bool __first =3D false; \ > - /* Report first abort since mount */ \ > - if (!test_and_set_bit(BTRFS_FS_STATE_TRANS_ABORTED, \ > - &((trans)->fs_info->fs_state))) { \ > - __first =3D true; \ > - if (WARN(btrfs_abort_should_print_stack(error), \ > - KERN_ERR \ > - "BTRFS: Transaction aborted (error %d)\n", \ > - (error))) { \ > - /* Stack trace printed. */ \ > - } else { \ > - btrfs_err((trans)->fs_info, \ > - "Transaction aborted (error %d)", \ > - (error)); \ > - } \ > - } \ > - __btrfs_abort_transaction((trans), __func__, \ > - __LINE__, (error), __first); \ > -} while (0) > +#define btrfs_abort_transaction(trans, error) = \ > + do { = \ > + bool __first =3D false; = \ > + /* Report first abort since mount */ = \ > + if (!test_and_set_bit(BTRFS_FS_STATE_TRANS_ABORTED, = \ > + &((trans)->fs_info->fs_state))) { = \ > + __first =3D true; = \ > + btrfs_err((trans)->fs_info, = \ > + "Transaction aborted (error %d)", (erro= r)); \ > + if (btrfs_abort_should_print_stack(error)) = \ > + dump_stack(); = \ > + } = \ > + __btrfs_abort_transaction((trans), __func__, __LINE__, = \ > + (error), __first); = \ > + } while (0) > > int btrfs_end_transaction(struct btrfs_trans_handle *trans); > struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *ro= ot, > > > base-commit: 7fd2df204f342fc17d1a0bfcd474b24232fb0f32 > -- > This is an AI-generated patch subject to moderation. > Reply with '#syz upstream' to send it to the mailing list. > Reply with '#syz reject' to reject it. > > See for more information. > > -- > You received this message because you are subscribed to the Google Groups= "syzkaller-upstream-moderation" group. > To unsubscribe from this group and stop receiving emails from it, send an= email to syzkaller-upstream-moderation+unsubscribe@googlegroups.com. > To view this discussion visit https://groups.google.com/d/msgid/syzkaller= -upstream-moderation/d56161b7-1f00-4e88-94fe-9e6d4db29ae0%40mail.kernel.org= . --=20 You received this message because you are subscribed to the Google Groups "= syzkaller-upstream-moderation" group. To unsubscribe from this group and stop receiving emails from it, send an e= mail to syzkaller-upstream-moderation+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/syzkaller-u= pstream-moderation/CANp29Y4f6VyeHoGyWhmmp%2B_CgqVWj4Lbif07goqkYb23H_j8EA%40= mail.gmail.com.