]> git.hungrycats.org Git - linux/commitdiff
btrfs: stripe_alloc: charge the held stripe margin against the claimable supply
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 11 Sep 2026 08:02:21 +0000 (04:02 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 11 Sep 2026 08:02:21 +0000 (04:02 -0400)
The data admission gate is meant to refuse a write at reservation time
when the whole-stripe supply cannot cover it, so that write() returns
ENOSPC instead of writeback finding no stripe later.  It compared
bytes_may_use + bytes against bytes_stripe_claimable and left out
bytes_stripe_margin, the whole-stripe collateral every outstanding data
extent holds against the commit that closes its open run and traps the
tail.  The margin is charged against total_bytes through
btrfs_space_info_used(), but not against the supply the admitted bytes
actually draw on.

Each commit shrinks that supply by the trapped tails, with nothing
holding it, so at the fill edge a write that passed admission reached
find_free_extent() and failed: "data writeback allocation of 4096 bytes
returned ENOSPC despite reservation margin; buffered data in this range
will be dropped", followed by the cow_file_range() failures 35 seconds
later in the same fill.

Add the held margin to the admitted bytes.  This over-refuses by the
margin, which the commit rescan raising bytes_stripe_claimable and the
FLUSH_DATA ticket retry heal, and stays within phase 1's pessimistic
contract; the relocation path keeps its own whole-stripe charge.

Assisted-by: Claude:claude-fable-5
fs/btrfs/space-info.c

index 6bf657efe988c2c63149e120b2123e989692d45a..0d7ef57efb176d63e4e914b6190cb4ca330d1bf3 100644 (file)
@@ -654,6 +654,19 @@ static bool stripe_claimable_admit(struct btrfs_space_info *space_info, u64 byte
                } else {
                        /* ...and that group is out of reach for everyone else */
                        supply -= min(supply, dedicated);
+                       /*
+                        * Every outstanding data extent holds a whole-stripe
+                        * margin (bytes_stripe_margin) as collateral against
+                        * the commit that closes its open run and traps the
+                        * tail.  btrfs_space_info_used() charges that margin
+                        * against total_bytes, but what an admitted write
+                        * actually draws on is the whole-stripe supply: left
+                        * out of this bound, the stripes behind bytes admitted
+                        * earlier are lost at the next commit, and a write
+                        * that passed admission finds no stripe at writeback
+                        * (ENOSPC despite reservation margin, data dropped).
+                        */
+                       bytes += space_info->bytes_stripe_margin;
                }
                return space_info->bytes_may_use + bytes <= supply;
        }