From: Zygo Blaxell Date: Fri, 11 Sep 2026 13:23:47 +0000 (-0400) Subject: wip: cand41 = cand40 + nocow private runs only while in-place writes are allowed... X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91ea32c5359bc0e339669727c447dbf87a1762bc;p=linux wip: cand41 = cand40 + nocow private runs only while in-place writes are allowed (B1) --- diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 4ad20f68b933a..9b6ddd8ae8b03 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -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) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 94e27d6e0ad41..758bece18ff3f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -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,