From 235aa75ab48653557589a102fbbfb8a9d06abd69 Mon Sep 17 00:00:00 2001 From: Zygo Blaxell Date: Sun, 9 Aug 2026 16:04:23 -0400 Subject: [PATCH] btrfs: stripe_meta: allocate tree blocks by whole stripes Metadata gets the placement rule data already has: tree blocks are handed out from runs of fully free stripes, and a stripe is never revisited once the transaction that filled it has written it. No separate mount option: the chunk profile already decides it. raid56 metadata has the same write hole as raid56 data, so it gets the same protection at the same time, and metadata on any other profile has no write hole to close -- the policy's RAID56_MASK test simply does not match it, so nothing changes there. It does cost a stranded stripe tail per commit, and a metadata allocation that fails aborts the transaction rather than failing one write. Those are the price of covering raid56 metadata at all rather than a reason to make it opt-in separately: a filesystem unwilling to pay them does not want raid56 metadata, and converting it to raid1c3/raid1c4 removes both the cost and the write hole. Only system chunks are left uncovered: btrfs_is_stripe_meta_bg() tests the METADATA flag and a system chunk does not carry it, so a raid56 system chunk keeps the legacy read-modify-write. The mount-time warning is narrowed to say exactly that instead of claiming metadata is uncovered, which it no longer is. The machinery is the data machinery, including the per-sector liveness map the previous patch gave it. Metadata needed three things of its own: - A completion report. A run drains when every byte allocated from it has been accounted for, and metadata had no equivalent of ordered extent completion, so its runs could never drain -- the deadlock that mixed block groups hit. end_bbio_meta_write() now reports, and btrfs_open_stripe_write_abandoned() covers a tree block freed before it was ever written (COWed twice in one transaction, say). - A two-phase closure. btrfs_retire_open_stripes() runs before the commit writes tree blocks, so it skips metadata runs; they are closed and drained afterwards by btrfs_retire_meta_stripes(), once every block is on disk. Closing earlier would strand blocks the commit has not allocated yet, draining earlier would wait for writes it has not issued. Metadata runs are also freed there rather than when their counter reaches zero: a stripe's columns can be written by separate rbios, and freeing on the first completion leaves the second with no run to pad or merge against. - Whole stripes per writeback pass. The opportunistic writeback passes do not know about stripes and will happily write three tree blocks now and five later; btrfs_defer_stripe_meta_write() leaves the blocks dirty for the commit (or a WB_SYNC_ALL waiter) instead, the same shape as the zoned write-pointer deferral. Two free paths bypass the usual completion reporting and must report the block abandoned themselves: btrfs_free_tree_block() can send a same-transaction unwritten block through the delayed-ref machinery when check_ref_cleanup() finds its ref head already processed, and clean_log_buffer() discards unwritten log tree blocks via btrfs_pin_reserved_extent() without ever visiting btrfs_free_tree_block(). Either miss leaves the block counted as inflight forever: the run never drains, the block group reference never drops, and unmount asserts. Both were found by the dm-log-writes replay matrix, whose crash-recovery mounts exercise both paths routinely. Assisted-by: Claude:claude-fable-5 --- fs/btrfs/block-group.c | 327 ++++++++++++++++++++++++++++++++++-- fs/btrfs/block-group.h | 13 ++ fs/btrfs/extent-tree.c | 72 +++++++- fs/btrfs/extent_io.c | 18 ++ fs/btrfs/free-space-cache.c | 2 +- fs/btrfs/transaction.c | 19 +++ fs/btrfs/tree-log.c | 22 +++ 7 files changed, 457 insertions(+), 16 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 70d88469454a9..bb34b93b5fe3e 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -501,6 +501,7 @@ struct btrfs_open_stripe_run { u64 offset; /* next unallocated byte */ u64 inflight_bytes; /* reserved bytes with data IO pending */ u64 open_seq; /* fs_info->stripe_retire_seq at open */ + u64 transid; /* fs_info->generation at open */ u64 owner; /* LOG class: btrfs_ino of the owner */ enum btrfs_stripe_run_class class; bool open; /* accepting allocations */ @@ -521,6 +522,60 @@ struct btrfs_open_stripe_run { u32 live_nbits; /* sectors @live covers; caps growth */ }; +static void btrfs_dump_open_meta_runs(struct btrfs_block_group *bg) +{ + struct btrfs_open_stripe_run *run; + unsigned long flags; + + spin_lock_irqsave(&bg->stripe_run_lock, flags); + list_for_each_entry(run, &bg->open_stripe_runs, list) { + if (run->class != BTRFS_STRIPE_RUN_META) + continue; + btrfs_warn(bg->fs_info, +" run %llu..%llu frontier %llu open %d inflight %llu transid %llu", + run->start, run->end, run->offset, run->open, + run->inflight_bytes, run->transid); + } + spin_unlock_irqrestore(&bg->stripe_run_lock, flags); +} + +static bool bg_has_undrained_meta_runs(struct btrfs_block_group *bg, + u64 transid) +{ + struct btrfs_open_stripe_run *run; + unsigned long flags; + bool ret = false; + + spin_lock_irqsave(&bg->stripe_run_lock, flags); + list_for_each_entry(run, &bg->open_stripe_runs, list) { + if (run->class != BTRFS_STRIPE_RUN_META) + continue; + /* + * An open run here belongs to the running transaction: the + * retirer closed every run it is responsible for before its + * wait, so this one was opened while it waited, and drains + * when that transaction's own commit writes its tree blocks. + * Waiting on it would be waiting for writes this commit never + * issued. + */ + if (run->open) + continue; + /* + * Likewise a run the running transaction opened and a band + * collision or eviction closed early: its blocks are written, + * and its stripes settled, by that transaction's own commit. + */ + if (run->transid > transid) + continue; + if (run->inflight_bytes) { + ret = true; + break; + } + } + spin_unlock_irqrestore(&bg->stripe_run_lock, flags); + return ret; +} + static bool open_stripe_run_drained(const struct btrfs_open_stripe_run *run) { return !run->open && run->inflight_bytes == 0; @@ -606,7 +661,22 @@ static void run_live_set(struct btrfs_open_stripe_run *run, u64 start, u64 len, bitmap_clear(run->live, first, nr); } -/* Free a drained run. Caller holds bg->stripe_run_lock. */ +/* + * A drained run may still be needed. + * + * Metadata writeback issues one rbio per contiguous run of tree blocks, so + * a stripe's columns can be submitted separately. If the run were freed as + * soon as its counter hit zero -- which happens when the first column's + * completions arrive -- the second column would find no run covering its + * stripe, and with no run there is nothing to pad against and nothing to + * merge with. Metadata runs are therefore freed by + * btrfs_retire_meta_stripes(), which runs once every tree block is on disk. + */ +static bool run_freed_on_drain(const struct btrfs_open_stripe_run *run) +{ + return run->class != BTRFS_STRIPE_RUN_META; +} + static void free_open_stripe_run(struct btrfs_block_group *bg, struct btrfs_open_stripe_run *run) { @@ -617,6 +687,27 @@ static void free_open_stripe_run(struct btrfs_block_group *bg, wake_up_var(&bg->open_stripe_runs); } +/* + * Report a run's transition to drained. Freeing wakes the retire waiters as + * a side effect of free_open_stripe_run(); a metadata run instead stays + * behind for padding and merging until btrfs_retire_meta_stripes() collects + * it, but that same retirer may already be asleep waiting for exactly this + * drain -- completion reports normally all precede its wait, but a report + * can arrive outside the writeback window (a tree block freed before it was + * written, e.g. an unwritten log tree block). Wake it, or it sleeps out + * its whole timeout. + */ +static void open_stripe_run_drain_reported(struct btrfs_block_group *bg, + struct btrfs_open_stripe_run *run) +{ + if (!open_stripe_run_drained(run)) + return; + if (run_freed_on_drain(run)) + free_open_stripe_run(bg, run); + else + wake_up_var(&bg->open_stripe_runs); +} + /* * Close a run: no further allocation may join it, and its unallocated tail * must be returned to the free space cache by the caller (after dropping the @@ -725,6 +816,10 @@ int btrfs_alloc_from_open_stripe(struct btrfs_block_group *bg, u64 num_bytes, u64 *ret_offset, u64 *available) { struct btrfs_fs_info *fs_info = bg->fs_info; + const u64 transid = btrfs_get_fs_generation(fs_info); + u64 evict_start[BTRFS_STRIPE_RUN_NR_BANDS]; + u64 evict_len[BTRFS_STRIPE_RUN_NR_BANDS]; + int nr_evict = 0; const u64 fsl = bg->full_stripe_len; struct btrfs_open_stripe_run *new_run; struct btrfs_open_stripe_run *run; @@ -752,6 +847,24 @@ int btrfs_alloc_from_open_stripe(struct btrfs_block_group *bg, u64 num_bytes, if (!r) continue; + /* + * Metadata runs are closed after the next transaction is + * already running (btrfs_retire_meta_stripes()), so a slot can + * still hold the previous transaction's run. Joining it would + * put two transactions' tree blocks in the same stripes, and + * the later one's writeback would read-modify-write parity + * over the earlier one's committed blocks: the write hole. + * Close it; its tail returns to the free space cache below. + */ + if (class == BTRFS_STRIPE_RUN_META && r->transid != transid) { + bg->open_stripe[class][band] = NULL; + evict_len[nr_evict] = + close_open_stripe_run(bg, r, + &evict_start[nr_evict]); + if (evict_len[nr_evict]) + nr_evict++; + continue; + } rem = r->end - r->offset; if (rem > *available) *available = rem; @@ -773,10 +886,16 @@ int btrfs_alloc_from_open_stripe(struct btrfs_block_group *bg, u64 num_bytes, spin_unlock_irqrestore(&bg->stripe_run_lock, flags); if (tail_len) btrfs_add_free_space(bg, tail_start, tail_len); + for (int i = 0; i < nr_evict; i++) + btrfs_add_free_space(bg, evict_start[i], evict_len[i]); stripe_open_remainder_sync(bg); return 0; } spin_unlock_irqrestore(&bg->stripe_run_lock, flags); + for (int i = 0; i < nr_evict; i++) + btrfs_add_free_space(bg, evict_start[i], evict_len[i]); + if (nr_evict) + stripe_open_remainder_sync(bg); /* * Zeroed: the error paths below reach out: before the liveness map is @@ -856,6 +975,9 @@ int btrfs_alloc_from_open_stripe(struct btrfs_block_group *bg, u64 num_bytes, if (!r || r->end != start) continue; + /* Never grow another transaction's run (see the scan above). */ + if (class == BTRFS_STRIPE_RUN_META && r->transid != transid) + continue; /* * Only grow as far as the run's liveness map reaches; it * cannot be reallocated here (see alloc_run_live_map()). @@ -894,6 +1016,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->owner = 0; + new_run->transid = transid; new_run->start = start; new_run->end = start + len; new_run->offset = start + num_bytes; @@ -2035,8 +2158,7 @@ void btrfs_open_stripe_write_abandoned(struct btrfs_block_group *bg, u64 start, ASSERT(run->inflight_bytes >= num_bytes); run_live_set(run, start, num_bytes, false); run->inflight_bytes -= num_bytes; - if (open_stripe_run_drained(run)) - free_open_stripe_run(bg, run); + open_stripe_run_drain_reported(bg, run); break; } } @@ -2055,8 +2177,7 @@ void btrfs_open_stripe_write_done(struct btrfs_block_group *bg, u64 start, ASSERT(start + num_bytes <= run->offset); ASSERT(run->inflight_bytes >= num_bytes); run->inflight_bytes -= num_bytes; - if (open_stripe_run_drained(run)) - free_open_stripe_run(bg, run); + open_stripe_run_drain_reported(bg, run); break; } } @@ -2131,8 +2252,7 @@ void btrfs_open_stripe_write_done_run(struct btrfs_open_stripe_run *run, spin_lock_irqsave(&bg->stripe_run_lock, flags); ASSERT(run->inflight_bytes >= num_bytes); run->inflight_bytes -= num_bytes; - if (open_stripe_run_drained(run)) - free_open_stripe_run(bg, run); + open_stripe_run_drain_reported(bg, run); spin_unlock_irqrestore(&bg->stripe_run_lock, flags); } @@ -2168,6 +2288,13 @@ restart: */ if (run->class == BTRFS_STRIPE_RUN_NOCOW && seq != U64_MAX) continue; + /* + * Metadata runs are still being filled at this point in the + * commit and their tree blocks are not written until later; + * btrfs_retire_meta_stripes() closes them once they are. + */ + if (run->class == BTRFS_STRIPE_RUN_META && seq != U64_MAX) + continue; for (class = 0; class < BTRFS_STRIPE_RUN_NR_CLASSES; class++) for (band = 0; band < BTRFS_STRIPE_RUN_NR_BANDS; band++) if (bg->open_stripe[class][band] == run) @@ -2209,6 +2336,9 @@ static bool bg_open_stripes_settled(struct btrfs_block_group *bg, u64 seq) /* Persistent nocow runs are not part of any commit window. */ if (run->class == BTRFS_STRIPE_RUN_NOCOW && seq != U64_MAX) continue; + /* Metadata drains after tree writeback, not here. */ + if (run->class == BTRFS_STRIPE_RUN_META && seq != U64_MAX) + continue; if (run->open_seq < seq) { ret = false; break; @@ -2245,6 +2375,165 @@ void btrfs_retire_block_group_stripes(struct btrfs_block_group *bg) * Calls are serialized by the transaction commit; @trans may be NULL for * the final cleanup call. */ +/* + * Close and drain the metadata stripe runs, once every tree block this + * transaction allocated has reached the disk. + * + * This is the second half of a two-phase closure. The first half happens + * implicitly: btrfs_retire_open_stripes() runs earlier in the commit and + * skips these runs, so they stay open for allocation right through the + * commit-time tree building that follows it. Here the allocation side is + * finished and the writes are complete, so the runs can be closed and their + * unused tails returned. + * + * Ordering matters in both directions. Closing earlier would strand tree + * blocks the commit has not allocated yet; draining earlier would wait for + * writes the commit has not issued yet, which is precisely the deadlock + * mixed block groups used to hit. + */ +void btrfs_retire_meta_stripes(struct btrfs_fs_info *fs_info, u64 transid) +{ + struct btrfs_block_group *bg; + LIST_HEAD(retire_list); + unsigned long flags; + + if (!btrfs_test_opt(fs_info, STRIPE_ALLOC)) + return; + + spin_lock(&fs_info->open_stripe_lock); + list_for_each_entry(bg, &fs_info->open_stripe_bgs, open_stripe_bg_list) + list_add_tail(&bg->open_stripe_retire_list, &retire_list); + spin_unlock(&fs_info->open_stripe_lock); + + while (!list_empty(&retire_list)) { + struct btrfs_open_stripe_run *run; + bool again = true; + + bg = list_first_entry(&retire_list, struct btrfs_block_group, + open_stripe_retire_list); + list_del_init(&bg->open_stripe_retire_list); + if (!(bg->flags & BTRFS_BLOCK_GROUP_METADATA)) + continue; + + while (again) { + u64 tail_start; + u64 tail_len = 0; + + again = false; + spin_lock_irqsave(&bg->stripe_run_lock, flags); + list_for_each_entry(run, &bg->open_stripe_runs, list) { + if (run->class != BTRFS_STRIPE_RUN_META || + !run->open) + continue; + /* + * The running transaction's runs are not ours + * to close: their tree blocks are written, and + * their stripes settled, by that transaction's + * own commit. + */ + if (run->transid > transid) + continue; + for (int band = 0; + band < BTRFS_STRIPE_RUN_NR_BANDS; band++) + if (bg->open_stripe[BTRFS_STRIPE_RUN_META][band] == run) + bg->open_stripe[BTRFS_STRIPE_RUN_META][band] = NULL; + tail_len = close_open_stripe_run(bg, run, &tail_start); + again = true; + break; + } + spin_unlock_irqrestore(&bg->stripe_run_lock, flags); + if (tail_len) + btrfs_add_free_space(bg, tail_start, tail_len); + } + + /* + * Every tree block is on disk, so end_bbio_meta_write() has + * reported all of them and this settles without waiting. + * + * Bounded rather than indefinite on purpose. A run drains + * only when every byte allocated from it has been reported, + * by writeback or by whatever freed the block before it was + * written; a path that frees a tree block without reporting + * would otherwise wedge the commit with no clue as to which + * one. Time out, say so, and carry on: the stripe is then + * left open, which costs space, where hanging costs the + * filesystem. + */ + if (!wait_var_event_timeout(&bg->open_stripe_runs, + !bg_has_undrained_meta_runs(bg, transid), + 60 * HZ)) { + btrfs_warn(fs_info, +"metadata stripe runs in block group %llu did not drain; a tree block was freed without reporting to its run", + bg->start); + btrfs_dump_open_meta_runs(bg); + } + + /* Drained and no longer needed for padding or merging. */ + spin_lock_irqsave(&bg->stripe_run_lock, flags); + while (true) { + struct btrfs_open_stripe_run *next = NULL; + + list_for_each_entry(run, &bg->open_stripe_runs, list) { + if (run->class == BTRFS_STRIPE_RUN_META && + open_stripe_run_drained(run)) { + next = run; + break; + } + } + if (!next) + break; + free_open_stripe_run(bg, next); + } + spin_unlock_irqrestore(&bg->stripe_run_lock, flags); + } +} + +/* + * Should this tree block be left dirty for the transaction commit to write? + * + * A stripe filled by one transaction is written in one piece only if all of + * its tree blocks go out in the same writeback pass. The opportunistic + * passes -- memory reclaim, the periodic writeback timer -- do not know + * about stripes and will happily write three tree blocks now and the other + * five later, which turns a full-stripe write into two read-modify-writes. + * + * Zoned mode has the same problem for a different reason (a tree block that + * is not at its zone's write pointer cannot be written yet) and solves it + * the same way: btrfs_check_meta_write_pointer() declines the block and the + * writeback loop leaves it dirty. This declines on stripe grounds instead. + * + * WB_SYNC_ALL is never deferred. That is what keeps this from deadlocking: + * a reclaimer that must have these pages written waits on WB_SYNC_ALL, which + * always proceeds, so dirty metadata pinned in an incomplete stripe can + * always be flushed by the caller who needs the memory. Only the + * best-effort passes are turned away, and turning one away asks for a commit + * rather than leaving the pages for the periodic writeback to find again. + */ +bool btrfs_defer_stripe_meta_write(struct btrfs_fs_info *fs_info, + struct extent_buffer *eb, + const struct writeback_control *wbc) +{ + struct btrfs_block_group *bg; + bool defer; + + if (!btrfs_test_opt(fs_info, STRIPE_ALLOC)) + return false; + if (wbc->sync_mode == WB_SYNC_ALL) + return false; + + bg = btrfs_lookup_block_group(fs_info, eb->start); + if (!bg) + return false; + defer = btrfs_is_stripe_meta_bg(bg); + btrfs_put_block_group(bg); + + if (defer) { + set_bit(BTRFS_FS_NEED_TRANS_COMMIT, &fs_info->flags); + wake_up_process(fs_info->transaction_kthread); + } + return defer; +} + void btrfs_retire_open_stripes(struct btrfs_fs_info *fs_info, struct btrfs_transaction *trans) { @@ -2416,7 +2705,14 @@ void btrfs_stripe_check_write(struct btrfs_fs_info *fs_info, bg = btrfs_lookup_block_group(fs_info, full_stripe_start); if (!bg) return; - if (!btrfs_is_stripe_alloc_bg(bg) || + /* + * Metadata is checked on the same terms once stripe_meta places it: + * its runs stay open across the commit's tree building but are closed + * and drained before the commit ends, so a tree block write outside + * every live run means a stripe was revisited after the transaction + * that filled it finished -- the same violation as for data. + */ + if ((!btrfs_is_stripe_alloc_bg(bg) && !btrfs_is_stripe_meta_bg(bg)) || test_bit(BLOCK_GROUP_FLAG_STRIPE_RELOC_USED, &bg->runtime_flags) || test_bit(BLOCK_GROUP_FLAG_STRIPE_NOCOW_USED, &bg->runtime_flags)) goto out; @@ -4708,15 +5004,20 @@ static int fill_dummy_bgs(struct btrfs_fs_info *fs_info) */ static void warn_stripe_alloc_uncovered(struct btrfs_fs_info *info) { - const u64 meta_bits = info->avail_metadata_alloc_bits | - info->avail_system_alloc_bits; - if (!btrfs_test_opt(info, STRIPE_ALLOC)) return; - if (meta_bits & BTRFS_BLOCK_GROUP_RAID56_MASK) + /* + * raid56 metadata is covered now that the policy follows the chunk + * profile. System chunks are not: btrfs_is_stripe_meta_bg() tests + * the METADATA flag, and a system chunk does not carry it, so a + * raid56 system chunk keeps the legacy read-modify-write and its + * write hole. They are small and rewritten rarely, but say so + * rather than let the earlier "covered" message imply otherwise. + */ + if (info->avail_system_alloc_bits & BTRFS_BLOCK_GROUP_RAID56_MASK) btrfs_warn(info, -"stripe_alloc does not cover raid56 metadata: the write hole remains for metadata, consider converting it to raid1c3/raid1c4"); +"stripe_alloc does not cover raid56 system chunks: the write hole remains for them, consider converting to raid1c3/raid1c4"); } int btrfs_read_block_groups(struct btrfs_fs_info *info) diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h index 2f3091b848317..bacabac29e351 100644 --- a/fs/btrfs/block-group.h +++ b/fs/btrfs/block-group.h @@ -46,6 +46,14 @@ enum btrfs_stripe_run_class { * blast radius to the owning file alone. */ BTRFS_STRIPE_RUN_NOCOW, + /* + * Tree blocks. Kept out of the commit-time drain that settles the + * classes above, because metadata writeback happens later in the same + * commit than that drain runs: these are sealed against further + * allocation and drained separately once the tree blocks are on disk + * (see btrfs_retire_meta_stripes()). + */ + BTRFS_STRIPE_RUN_META, BTRFS_STRIPE_RUN_NR_CLASSES, }; @@ -462,8 +470,12 @@ struct btrfs_open_stripe_run *btrfs_get_open_stripe_run( struct btrfs_fs_info *fs_info, u64 bytenr, u64 num_bytes, u64 *open_seq); void btrfs_close_bg_open_stripes(struct btrfs_block_group *bg); +void btrfs_retire_meta_stripes(struct btrfs_fs_info *fs_info, u64 transid); void btrfs_retire_block_group_stripes(struct btrfs_block_group *bg); bool btrfs_stripe_in_open_run(struct btrfs_fs_info *fs_info, u64 logical); +bool btrfs_defer_stripe_meta_write(struct btrfs_fs_info *fs_info, + struct extent_buffer *eb, + const struct writeback_control *wbc); bool btrfs_stripe_open_run_class(struct btrfs_fs_info *fs_info, u64 logical, enum btrfs_stripe_run_class *class); bool btrfs_stripe_run_range_usable(struct btrfs_block_group *bg, @@ -510,6 +522,7 @@ void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg); void btrfs_release_data_reloc_bg(struct btrfs_fs_info *fs_info); bool btrfs_stripe_alloc_forces_cow(struct btrfs_fs_info *fs_info, u64 bytenr); bool btrfs_is_stripe_alloc_bg(const struct btrfs_block_group *bg); +bool btrfs_is_stripe_meta_bg(const struct btrfs_block_group *bg); void btrfs_scan_stripe_unusable(struct btrfs_fs_info *fs_info, bool force); #ifdef CONFIG_BTRFS_DEBUG void btrfs_stripe_check_write(struct btrfs_fs_info *fs_info, diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 1a9a6f05815fe..225a7cdd084aa 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3658,6 +3658,24 @@ int btrfs_free_tree_block(struct btrfs_trans_handle *trans, if (btrfs_header_generation(buf) != trans->transid) return 0; + /* + * Last ref to a tree block this transaction allocated and never + * wrote: no writeback completion will ever report it to its stripe + * run. Report it here, before the free paths diverge. When the + * delayed ref head was already processed (truncate and friends run + * delayed refs mid-transaction for throttling) check_ref_cleanup() + * below returns 0 and the free goes through __btrfs_free_extent() + * and the pin/unpin machinery, none of which know about the run -- + * the run's inflight count would never drain. + */ + if (!btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) { + bg = btrfs_lookup_block_group(fs_info, buf->start); + if (btrfs_is_stripe_meta_bg(bg)) + btrfs_open_stripe_write_abandoned(bg, buf->start, + buf->len); + btrfs_put_block_group(bg); + } + if (root_id != BTRFS_TREE_LOG_OBJECTID) { ret = check_ref_cleanup(trans, buf->start); if (!ret) @@ -4214,6 +4232,30 @@ bool btrfs_is_stripe_alloc_bg(const struct btrfs_block_group *bg) (bg->flags & BTRFS_BLOCK_GROUP_RAID56_MASK); } +/* + * Should this block group allocate tree blocks by whole stripes? + * + * The same rule data follows: only fully free stripes are claimed, and the + * partial stripe left at the end of a commit is padded out rather than + * revisited, so no tree block write ever lands in a stripe holding earlier + * committed data. Not separately gated: the chunk profile already decides + * it. raid56 metadata has the same write hole as raid56 data and gets the + * same protection at the same time; metadata on any other profile has no + * write hole to close, and the RAID56_MASK test below means the policy + * simply does not apply to it. The costs metadata brings -- a stranded + * stripe tail per commit, and an allocation failure that aborts the + * transaction rather than failing one write -- are the price of covering + * raid56 metadata at all, and a filesystem that does not want them wants + * raid1c3/raid1c4 metadata instead, which is the better answer anyway. + */ +bool btrfs_is_stripe_meta_bg(const struct btrfs_block_group *bg) +{ + return btrfs_test_opt(bg->fs_info, STRIPE_ALLOC) && + (bg->flags & BTRFS_BLOCK_GROUP_METADATA) && + !(bg->flags & BTRFS_BLOCK_GROUP_DATA) && + (bg->flags & BTRFS_BLOCK_GROUP_RAID56_MASK); +} + static int do_allocation_stripe(struct btrfs_block_group *block_group, struct find_free_extent_ctl *ffe_ctl, struct btrfs_block_group **bg_ret) @@ -4225,6 +4267,30 @@ static int do_allocation_stripe(struct btrfs_block_group *block_group, bool skip = false; int ret; + /* + * Tree blocks take the same path as data -- whole free stripes, a + * frontier that padding can complete -- in their own class, and none + * of the steering below applies to them: there is no inode to steer + * by, metadata is always COW, and relocation moves tree blocks + * through the normal metadata allocator. + */ + if (btrfs_is_stripe_meta_bg(block_group)) { + ret = btrfs_alloc_from_open_stripe(block_group, + ffe_ctl->num_bytes, + BTRFS_STRIPE_RUN_META, + &offset, &available); + if (ret == -ENOMEM) + return ret; + if (ret) { + if (available > ffe_ctl->max_extent_size) + ffe_ctl->max_extent_size = available; + return 1; + } + ffe_ctl->found_offset = offset; + ffe_ctl->search_start = offset; + return 0; + } + /* * Soft dedication of one block group to relocation, mirroring the * zoned data_reloc_bg rule: it keeps relocation's prealloc-then- @@ -4312,7 +4378,8 @@ static int do_allocation_stripe(struct btrfs_block_group *block_group, static void undo_stripe_allocation(struct btrfs_block_group *block_group, struct find_free_extent_ctl *ffe_ctl) { - if (btrfs_is_stripe_alloc_bg(block_group)) + if (btrfs_is_stripe_alloc_bg(block_group) || + btrfs_is_stripe_meta_bg(block_group)) btrfs_open_stripe_write_abandoned(block_group, ffe_ctl->found_offset, ffe_ctl->num_bytes); @@ -4324,7 +4391,8 @@ static int do_allocation(struct btrfs_block_group *block_group, { switch (ffe_ctl->policy) { case BTRFS_EXTENT_ALLOC_CLUSTERED: - if (btrfs_is_stripe_alloc_bg(block_group)) + if (btrfs_is_stripe_alloc_bg(block_group) || + btrfs_is_stripe_meta_bg(block_group)) return do_allocation_stripe(block_group, ffe_ctl, bg_ret); return do_allocation_clustered(block_group, ffe_ctl, bg_ret); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 55e9144d47595..b4fd33de1a615 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2392,6 +2392,16 @@ static void end_bbio_meta_write(struct btrfs_bio *bbio) if (bbio->bio.bi_status != BLK_STS_OK) set_btree_ioerr(eb); + /* + * Tell the stripe run this tree block is written. Data reports the + * same thing from ordered extent completion; metadata has no ordered + * extent, so without this the run's inflight bytes would never come + * back down and nothing waiting on it could ever proceed. Reported + * on error too: the bytes will not be written again, and a run that + * cannot drain is worse than one that drains after a failed write. + */ + btrfs_open_stripe_write_done_bytenr(eb->fs_info, eb->start, eb->len); + /* * Clear writeback on the buffer's own folios. The bio may carry the * shared zero page instead (EXTENT_BUFFER_ZONED_ZEROOUT), so iterate @@ -2653,6 +2663,14 @@ retry: while ((eb = eb_batch_next(&batch)) != NULL) { ctx.eb = eb; + /* + * Leave it dirty for the commit, so its stripe goes + * out in one piece instead of once per writeback + * pass. + */ + if (btrfs_defer_stripe_meta_write(fs_info, eb, wbc)) + continue; + ret = write_meta_extent_buffer(&ctx, wbc); if (ret == -EBUSY) { ret = 0; diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index dbda642a1d45d..075c00ea55b38 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2103,7 +2103,7 @@ void btrfs_block_group_init_stripe_unusable(struct btrfs_block_group *bg) u64 trapped, claimable, nstripes; u32 *freep; - if (!btrfs_is_stripe_alloc_bg(bg)) + if (!btrfs_is_stripe_alloc_bg(bg) && !btrfs_is_stripe_meta_bg(bg)) return; if (READ_ONCE(bg->stripe_unusable_ready)) return; diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 0b3961bcba313..3bff08a7ebfcf 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -23,6 +23,8 @@ #include "dev-replace.h" #include "qgroup.h" #include "block-group.h" +#include "free-space-cache.h" +#include "raid56.h" #include "space-info.h" #include "fs.h" #include "accessors.h" @@ -1305,6 +1307,13 @@ static int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans) blk_start_plug(&plug); ret = btrfs_write_marked_extents(fs_info, dirty_pages, EXTENT_DIRTY); blk_finish_plug(&plug); + /* + * Everything this commit is going to submit has been submitted, so any + * rbio still parked waiting for a sibling is waiting for one that will + * never come. Kick them down now rather than let the wait below sit + * out their park deadline. + */ + btrfs_flush_parked_rbios(fs_info, 0, U64_MAX, true); ret2 = btrfs_wait_extents(fs_info, dirty_pages); if (ret) @@ -2615,6 +2624,16 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) goto scrub_continue; } + /* + * Every tree block this transaction allocated is now on disk, so its + * metadata stripe runs can be closed and drained. This is the point + * the earlier btrfs_retire_open_stripes() deliberately left them + * alone for: closing before here would strand blocks the commit had + * not allocated yet, draining before here would wait on writes it had + * not issued. + */ + btrfs_retire_meta_stripes(fs_info, cur_trans->transid); + ret = write_all_supers(trans); /* * the super is written, we can safely allow the tree-loggers diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index e9b688024aed6..28a1439f0416b 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2936,6 +2936,28 @@ static int clean_log_buffer(struct btrfs_trans_handle *trans, wait_on_extent_buffer_writeback(eb); btrfs_tree_unlock(eb); + /* + * A log tree block discarded before it was ever written has no + * writeback completion coming: nothing else will report it to its + * stripe run, and an unreported allocation keeps the run's inflight + * count from ever draining. Report it abandoned here. Written + * blocks were already reported by their write endio (the writeback + * wait above means no submission is still in flight), and log blocks + * from a previous mount walked during log replay carry the WRITTEN + * flag on disk, so both are excluded. + */ + if (!btrfs_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN)) { + struct btrfs_block_group *stripe_bg; + + stripe_bg = btrfs_lookup_block_group(fs_info, eb->start); + if (stripe_bg) { + if (btrfs_is_stripe_meta_bg(stripe_bg)) + btrfs_open_stripe_write_abandoned(stripe_bg, + eb->start, eb->len); + btrfs_put_block_group(stripe_bg); + } + } + if (trans) { int ret; -- 2.53.0