All of lore.kernel.org
 help / color / mirror / Atom feed
* [bug report] btrfs: refactor __btrfs_run_delayed_refs loop
@ 2026-05-21 12:53 Dan Carpenter
  2026-05-21 14:44 ` Filipe Manana
  0 siblings, 1 reply; 2+ messages in thread
From: Dan Carpenter @ 2026-05-21 12:53 UTC (permalink / raw)
  To: Nikolay Borisov; +Cc: linux-btrfs

[ 8 year old code and no one has complained...  It's surprising no
  one has hit this in real life. - dan ]

Hello Nikolay Borisov,

Commit 0110a4c43451 ("btrfs: refactor __btrfs_run_delayed_refs loop")
from Aug 15, 2018 (linux-next), leads to the following Smatch static
checker warning:

	fs/btrfs/extent-tree.c:2131 __btrfs_run_delayed_refs()
	error: 'locked_ref' dereferencing possible ERR_PTR()

fs/btrfs/extent-tree.c
    2084 static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
    2085                                              u64 min_bytes)
    2086 {
    2087         struct btrfs_fs_info *fs_info = trans->fs_info;
    2088         struct btrfs_delayed_ref_root *delayed_refs;
    2089         struct btrfs_delayed_ref_head *locked_ref = NULL;
    2090         int ret;
    2091         unsigned long count = 0;
    2092         unsigned long max_count = 0;
    2093         u64 bytes_processed = 0;
    2094 
    2095         delayed_refs = &trans->transaction->delayed_refs;
    2096         if (min_bytes == 0) {
    2097                 /*
    2098                  * We may be subject to a harmless race if some task is
    2099                  * concurrently adding or removing a delayed ref, so silence
    2100                  * KCSAN and similar tools.
    2101                  */
    2102                 max_count = data_race(delayed_refs->num_heads_ready);
    2103                 min_bytes = U64_MAX;
    2104         }
    2105 
    2106         do {
    2107                 if (!locked_ref) {
    2108                         locked_ref = btrfs_select_ref_head(fs_info, delayed_refs);
    2109                         if (IS_ERR_OR_NULL(locked_ref)) {
    2110                                 if (PTR_ERR(locked_ref) == -EAGAIN) {
    2111                                         continue;

In the old we set "locked_ref = NULL" on this path and I think we
probably should restore that line.  Now it looks like a crash.

    2112                                 } else {
    2113                                         break;
    2114                                 }
    2115                         }
    2116                         count++;
    2117                 }
    2118                 /*
    2119                  * We need to try and merge add/drops of the same ref since we
    2120                  * can run into issues with relocate dropping the implicit ref
    2121                  * and then it being added back again before the drop can
    2122                  * finish.  If we merged anything we need to re-loop so we can
    2123                  * get a good ref.
    2124                  * Or we can get node references of the same type that weren't
    2125                  * merged when created due to bumps in the tree mod seq, and
    2126                  * we need to merge them to prevent adding an inline extent
    2127                  * backref before dropping it (triggering a BUG_ON at
    2128                  * insert_inline_extent_backref()).
    2129                  */
    2130                 spin_lock(&locked_ref->lock);
                                    ^^^^^^^^^^^^^^^^
On the next iteration &locked_ref will be ERR_PTR(-EAGAIN).

--> 2131                 btrfs_merge_delayed_refs(fs_info, delayed_refs, locked_ref);
    2132 
    2133                 ret = btrfs_run_delayed_refs_for_head(trans, locked_ref, &bytes_processed);
    2134                 if (ret < 0 && ret != -EAGAIN) {
    2135                         /*
    2136                          * Error, btrfs_run_delayed_refs_for_head already
    2137                          * unlocked everything so just bail out
    2138                          */
    2139                         return ret;
    2140                 } else if (!ret) {
    2141                         /*
    2142                          * Success, perform the usual cleanup of a processed
    2143                          * head
    2144                          */
    2145                         ret = cleanup_ref_head(trans, locked_ref, &bytes_processed);
    2146                         if (ret > 0 ) {
    2147                                 /* We dropped our lock, we need to loop. */
    2148                                 ret = 0;
    2149                                 continue;
    2150                         } else if (ret) {
    2151                                 return ret;
    2152                         }
    2153                 }
    2154 
    2155                 /*
    2156                  * Either success case or btrfs_run_delayed_refs_for_head
    2157                  * returned -EAGAIN, meaning we need to select another head
    2158                  */
    2159 
    2160                 locked_ref = NULL;
    2161                 cond_resched();
    2162         } while ((min_bytes != U64_MAX && bytes_processed < min_bytes) ||
    2163                  (max_count > 0 && count < max_count) ||
    2164                  locked_ref);
                          ^^^^^^^^^^
We continue down to here and loop back since locked_ref is -EAGAIN.

    2165 
    2166         return 0;
    2167 }

This email is a free service from the Smatch-CI project [smatch.sf.net].

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [bug report] btrfs: refactor __btrfs_run_delayed_refs loop
  2026-05-21 12:53 [bug report] btrfs: refactor __btrfs_run_delayed_refs loop Dan Carpenter
@ 2026-05-21 14:44 ` Filipe Manana
  0 siblings, 0 replies; 2+ messages in thread
From: Filipe Manana @ 2026-05-21 14:44 UTC (permalink / raw)
  To: Dan Carpenter; +Cc: Nikolay Borisov, linux-btrfs

On Thu, May 21, 2026 at 2:02 PM Dan Carpenter <error27@gmail.com> wrote:
>
> [ 8 year old code and no one has complained...  It's surprising no
>   one has hit this in real life. - dan ]
>
> Hello Nikolay Borisov,
>
> Commit 0110a4c43451 ("btrfs: refactor __btrfs_run_delayed_refs loop")
> from Aug 15, 2018 (linux-next), leads to the following Smatch static
> checker warning:
>
>         fs/btrfs/extent-tree.c:2131 __btrfs_run_delayed_refs()
>         error: 'locked_ref' dereferencing possible ERR_PTR()
>
> fs/btrfs/extent-tree.c
>     2084 static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
>     2085                                              u64 min_bytes)
>     2086 {
>     2087         struct btrfs_fs_info *fs_info = trans->fs_info;
>     2088         struct btrfs_delayed_ref_root *delayed_refs;
>     2089         struct btrfs_delayed_ref_head *locked_ref = NULL;
>     2090         int ret;
>     2091         unsigned long count = 0;
>     2092         unsigned long max_count = 0;
>     2093         u64 bytes_processed = 0;
>     2094
>     2095         delayed_refs = &trans->transaction->delayed_refs;
>     2096         if (min_bytes == 0) {
>     2097                 /*
>     2098                  * We may be subject to a harmless race if some task is
>     2099                  * concurrently adding or removing a delayed ref, so silence
>     2100                  * KCSAN and similar tools.
>     2101                  */
>     2102                 max_count = data_race(delayed_refs->num_heads_ready);
>     2103                 min_bytes = U64_MAX;
>     2104         }
>     2105
>     2106         do {
>     2107                 if (!locked_ref) {
>     2108                         locked_ref = btrfs_select_ref_head(fs_info, delayed_refs);
>     2109                         if (IS_ERR_OR_NULL(locked_ref)) {
>     2110                                 if (PTR_ERR(locked_ref) == -EAGAIN) {
>     2111                                         continue;
>
> In the old we set "locked_ref = NULL" on this path and I think we
> probably should restore that line.  Now it looks like a crash.
>
>     2112                                 } else {
>     2113                                         break;
>     2114                                 }
>     2115                         }
>     2116                         count++;
>     2117                 }
>     2118                 /*
>     2119                  * We need to try and merge add/drops of the same ref since we
>     2120                  * can run into issues with relocate dropping the implicit ref
>     2121                  * and then it being added back again before the drop can
>     2122                  * finish.  If we merged anything we need to re-loop so we can
>     2123                  * get a good ref.
>     2124                  * Or we can get node references of the same type that weren't
>     2125                  * merged when created due to bumps in the tree mod seq, and
>     2126                  * we need to merge them to prevent adding an inline extent
>     2127                  * backref before dropping it (triggering a BUG_ON at
>     2128                  * insert_inline_extent_backref()).
>     2129                  */
>     2130                 spin_lock(&locked_ref->lock);
>                                     ^^^^^^^^^^^^^^^^
> On the next iteration &locked_ref will be ERR_PTR(-EAGAIN).

Indeed.

Nikolay is no longer active in btrfs, so I've sent a fix for this:

https://lore.kernel.org/linux-btrfs/f33f040f3ced4b3813dba4ddb0b5377ab5aeac29.1779374308.git.fdmanana@suse.com/

Thanks.

>
> --> 2131                 btrfs_merge_delayed_refs(fs_info, delayed_refs, locked_ref);
>     2132
>     2133                 ret = btrfs_run_delayed_refs_for_head(trans, locked_ref, &bytes_processed);
>     2134                 if (ret < 0 && ret != -EAGAIN) {
>     2135                         /*
>     2136                          * Error, btrfs_run_delayed_refs_for_head already
>     2137                          * unlocked everything so just bail out
>     2138                          */
>     2139                         return ret;
>     2140                 } else if (!ret) {
>     2141                         /*
>     2142                          * Success, perform the usual cleanup of a processed
>     2143                          * head
>     2144                          */
>     2145                         ret = cleanup_ref_head(trans, locked_ref, &bytes_processed);
>     2146                         if (ret > 0 ) {
>     2147                                 /* We dropped our lock, we need to loop. */
>     2148                                 ret = 0;
>     2149                                 continue;
>     2150                         } else if (ret) {
>     2151                                 return ret;
>     2152                         }
>     2153                 }
>     2154
>     2155                 /*
>     2156                  * Either success case or btrfs_run_delayed_refs_for_head
>     2157                  * returned -EAGAIN, meaning we need to select another head
>     2158                  */
>     2159
>     2160                 locked_ref = NULL;
>     2161                 cond_resched();
>     2162         } while ((min_bytes != U64_MAX && bytes_processed < min_bytes) ||
>     2163                  (max_count > 0 && count < max_count) ||
>     2164                  locked_ref);
>                           ^^^^^^^^^^
> We continue down to here and loop back since locked_ref is -EAGAIN.
>
>     2165
>     2166         return 0;
>     2167 }
>
> This email is a free service from the Smatch-CI project [smatch.sf.net].
>
> regards,
> dan carpenter
>

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-05-21 14:45 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-21 12:53 [bug report] btrfs: refactor __btrfs_run_delayed_refs loop Dan Carpenter
2026-05-21 14:44 ` Filipe Manana

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.