]> git.hungrycats.org Git - linux/commitdiff
wip: cand41 = cand40 + nocow private runs only while in-place writes are allowed...
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 11 Sep 2026 13:23:47 +0000 (09:23 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 11 Sep 2026 13:23:47 +0000 (09:23 -0400)
fs/btrfs/block-group.c
fs/btrfs/extent-tree.c

index 4ad20f68b933a328322aac49626bb938d51748d8..9b6ddd8ae8b03444778f7cf9c0304ab29697031c 100644 (file)
@@ -477,6 +477,7 @@ struct btrfs_open_stripe_run {
        u64 owner;                      /* LOG class: btrfs_ino of the owner */
        enum btrfs_stripe_run_class class;
        bool open;                      /* accepting allocations */
+       bool persistent;                /* survives commits; see close_block_group_stripe_runs() */
        /*
         * One bit per sector of the run: set while that sector holds an
         * allocation someone still intends to write, clear otherwise.
@@ -1031,6 +1032,7 @@ int btrfs_alloc_from_open_stripe(struct btrfs_block_group *bg, u64 num_bytes,
        }
        new_run->bg = bg;
        new_run->class = class;
+       new_run->persistent = false;
        new_run->owner = 0;
        new_run->transid = transid;
        new_run->start = start;
@@ -1215,6 +1217,17 @@ install:
        }
        new_run->bg = bg;
        new_run->class = class;
+       /*
+        * A NOCOW run outlives commits only while the stripe_alloc_allow_rmw
+        * policy lets nocow data be written in place at all (see
+        * close_block_group_stripe_runs()).  Decided once, here: the commit
+        * path's close and settle predicates must agree with each other for
+        * the run's whole life, whatever the policy does meanwhile.
+        */
+       new_run->persistent = class == BTRFS_STRIPE_RUN_NOCOW &&
+                             (btrfs_stripe_allow_rmw(fs_info) &
+                              (BTRFS_STRIPE_RMW_NODATACOW |
+                               BTRFS_STRIPE_RMW_PREALLOC));
        new_run->owner = ino;
        new_run->start = start;
        new_run->end = start + len;
@@ -2358,14 +2371,19 @@ restart:
                        continue;
                /*
                 * A nodatacow inode's private run survives transaction
-                * commits: its stripes hold only that inode's expendable
-                * (write-hole-waived) data, so invariant I2 has nothing to
-                * protect there, and closing would burn a stripe per commit
-                * for a slowly appended nocow file.  Forced quiescing
+                * commits while the stripe_alloc_allow_rmw policy lets its
+                * data be written in place: its stripes then hold only that
+                * inode's expendable (write-hole-waived) data, so invariant
+                * I2 has nothing to protect there, and closing would burn a
+                * stripe per commit for a slowly appended nocow file.  With
+                * nothing waived the run holds ordinary COWed extents (or
+                * unwritten preallocation) and closes like a COW run: kept
+                * open, it only strands the whole stripes freed inside its
+                * range until the inode is evicted.  Forced quiescing
                 * (read-only, removal, unmount: seq == U64_MAX) still closes
-                * them, as does the owning inode's eviction.
+                * persistent runs, as does the owning inode's eviction.
                 */
-               if (run->class == BTRFS_STRIPE_RUN_NOCOW && seq != U64_MAX)
+               if (run->persistent && seq != U64_MAX)
                        continue;
                /*
                 * Metadata runs are still being filled at this point in the
@@ -2413,7 +2431,7 @@ static bool bg_open_stripes_settled(struct btrfs_block_group *bg, u64 seq)
        spin_lock_irqsave(&bg->stripe_run_lock, flags);
        list_for_each_entry(run, &bg->open_stripe_runs, list) {
                /* Persistent nocow runs are not part of any commit window. */
-               if (run->class == BTRFS_STRIPE_RUN_NOCOW && seq != U64_MAX)
+               if (run->persistent && seq != U64_MAX)
                        continue;
                /* Metadata drains after tree writeback, not here. */
                if (run->class == BTRFS_STRIPE_RUN_META && seq != U64_MAX)
index 94e27d6e0ad4180fb360b14cf6dfd073695144d8..758bece18ff3ff440dfdc41ab62b8a495e3ad77f 100644 (file)
@@ -4928,13 +4928,22 @@ int btrfs_reserve_extent(struct btrfs_root *root, struct btrfs_inode *inode,
         * seeded with the run's location as the search hint.  NOCOW class
         * for preallocated extents and nodatacow files' extents (candidates
         * for future write-in-place, which must never share a stripe with
-        * any other file's data); LOG class for log-active inodes (see
-        * BTRFS_INODE_LOG_ALLOC).  Purely placement: any allocation that
-        * cannot be served from a private run falls back to the shared runs.
+        * any other file's data) -- but only while the stripe_alloc_allow_rmw
+        * policy lets that kind of extent be written in place: otherwise
+        * every write to it is COWed like anyone else's, and a private run
+        * (which outlives commits) would only strand the whole stripes freed
+        * inside its range until the inode is evicted; LOG class for
+        * log-active inodes (see BTRFS_INODE_LOG_ALLOC).  Purely placement:
+        * any allocation that cannot be served from a private run falls back
+        * to the shared runs.
         */
        if (inode && is_data && !for_data_reloc &&
            btrfs_test_opt(fs_info, STRIPE_ALLOC)) {
-               if (for_prealloc || (inode->flags & BTRFS_INODE_NODATACOW)) {
+               const u32 rmw = btrfs_stripe_allow_rmw(fs_info);
+
+               if ((for_prealloc && (rmw & BTRFS_STRIPE_RMW_PREALLOC)) ||
+                   (!for_prealloc && (inode->flags & BTRFS_INODE_NODATACOW) &&
+                    (rmw & BTRFS_STRIPE_RMW_NODATACOW))) {
                        steer_inode = inode;
                        steer_class = BTRFS_STRIPE_RUN_NOCOW;
                } else if (test_bit(BTRFS_INODE_LOG_ALLOC,