[PATCH v3 05/17] btrfs: rename cache_write_mutex to dirty_bgs_update_mutex

From: Tal Zussman

Date: Thu Sep 17 2026 - 00:04:45 EST


The v1 space cache writeout is gone, but the mutex is still needed. It
keeps btrfs_remove_block_group() from deleting a block group item while
btrfs_start_dirty_block_groups() is updating it outside the commit
critical section.

Rename it to reflect what it protects, and update the comments around
the dirty block group writeout that still refer to the space cache.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Tal Zussman <tz2294@xxxxxxxxxxxx>
---
fs/btrfs/block-group.c | 46 +++++++++++++++++++++++++---------------------
fs/btrfs/transaction.c | 25 ++++++++++---------------
fs/btrfs/transaction.h | 8 ++++----
3 files changed, 39 insertions(+), 40 deletions(-)

diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index 5c2a57243de5..34f2cc0ef33b 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -1196,7 +1196,11 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,

inode = lookup_free_space_inode(block_group, path);

- mutex_lock(&trans->transaction->cache_write_mutex);
+ /*
+ * Do not delete the block group item while
+ * btrfs_start_dirty_block_groups() is updating it.
+ */
+ mutex_lock(&trans->transaction->dirty_bgs_update_mutex);
spin_lock(&trans->transaction->dirty_bgs_lock);
if (!list_empty(&block_group->dirty_list)) {
list_del_init(&block_group->dirty_list);
@@ -1204,7 +1208,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
btrfs_put_block_group(block_group);
}
spin_unlock(&trans->transaction->dirty_bgs_lock);
- mutex_unlock(&trans->transaction->cache_write_mutex);
+ mutex_unlock(&trans->transaction->dirty_bgs_update_mutex);

ret = btrfs_remove_free_space_inode(trans, inode, block_group);
if (unlikely(ret)) {
@@ -3353,15 +3357,15 @@ static int update_block_group_item(struct btrfs_trans_handle *trans,
}

/*
- * Transaction commit does final block group cache writeback during a critical
+ * Transaction commit does the final block group item updates during a critical
* section where nothing is allowed to change the FS. This is required in
- * order for the cache to actually match the block group, but can introduce a
+ * order for the items to actually match the block groups, but can introduce a
* lot of latency into the commit.
*
- * So, btrfs_start_dirty_block_groups is here to kick off block group cache IO.
- * There's a chance we'll have to redo some of it if the block group changes
- * again during the commit, but it greatly reduces the commit latency by
- * getting rid of the easy block groups while we're still allowing others to
+ * So, btrfs_start_dirty_block_groups is here to update the block group items
+ * early. There's a chance we'll have to redo some of it if the block group
+ * changes again during the commit, but it greatly reduces the commit latency
+ * by getting rid of the easy block groups while we're still allowing others to
* join the commit.
*/
int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans)
@@ -3395,11 +3399,11 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans)
}

/*
- * cache_write_mutex is here only to save us from balance or automatic
- * removal of empty block groups deleting this block group while we are
- * updating its item
+ * dirty_bgs_update_mutex is here only to save us from balance or
+ * automatic removal of empty block groups deleting this block group
+ * while we are updating its item
*/
- mutex_lock(&trans->transaction->cache_write_mutex);
+ mutex_lock(&trans->transaction->dirty_bgs_update_mutex);
while (!list_empty(&dirty)) {
bool drop_reserve = true;

@@ -3441,12 +3445,12 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans)
if (drop_reserve)
btrfs_dec_delayed_refs_rsv_bg_updates(fs_info);
/* Avoid blocking other tasks for too long. */
- mutex_unlock(&trans->transaction->cache_write_mutex);
+ mutex_unlock(&trans->transaction->dirty_bgs_update_mutex);
if (ret)
goto out;
- mutex_lock(&trans->transaction->cache_write_mutex);
+ mutex_lock(&trans->transaction->dirty_bgs_update_mutex);
}
- mutex_unlock(&trans->transaction->cache_write_mutex);
+ mutex_unlock(&trans->transaction->dirty_bgs_update_mutex);

/*
* Go through delayed refs for all the stuff we've just kicked off
@@ -3460,7 +3464,7 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans)
list_splice_init(&cur_trans->dirty_bgs, &dirty);
/*
* dirty_bgs_lock protects us from concurrent block group
- * deletes too (not just cache_write_mutex).
+ * deletes too (not just dirty_bgs_update_mutex).
*/
if (!list_empty(&dirty)) {
spin_unlock(&cur_trans->dirty_bgs_lock);
@@ -3556,10 +3560,10 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans,
factor = btrfs_bg_type_to_factor(cache->flags);

/*
- * If this block group has free space cache written out, we need to make
- * sure to load it if we are removing space. This is because we need
- * the unpinning stage to actually add the space back to the block group,
- * otherwise we will leak space.
+ * Make sure the free space of this block group is loaded if we are
+ * removing space. This is because we need the unpinning stage to
+ * actually add the space back to the block group, otherwise we will
+ * leak space.
*/
if (!alloc && !btrfs_block_group_done(cache))
btrfs_cache_block_group(cache, true);
@@ -3615,7 +3619,7 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans,
/*
* No longer have used bytes in this block group, queue it for deletion.
* We do this after adding the block group to the dirty list to avoid
- * races between cleaner kthread and space cache writeout.
+ * races between the cleaner kthread and the dirty block group writeout.
*/
if (!alloc && old_val == 0) {
if (!btrfs_test_opt(info, DISCARD_ASYNC))
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 6a36d29406c3..6a7f33ac87f2 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -380,7 +380,7 @@ static noinline int join_transaction(struct btrfs_fs_info *fs_info,
INIT_LIST_HEAD(&cur_trans->switch_commits);
INIT_LIST_HEAD(&cur_trans->dirty_bgs);
INIT_LIST_HEAD(&cur_trans->dropped_roots);
- mutex_init(&cur_trans->cache_write_mutex);
+ mutex_init(&cur_trans->dirty_bgs_update_mutex);
spin_lock_init(&cur_trans->dirty_bgs_lock);
INIT_LIST_HEAD(&cur_trans->deleted_bgs);
spin_lock_init(&cur_trans->dropped_roots_lock);
@@ -2268,18 +2268,16 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
if (!test_bit(BTRFS_TRANS_DIRTY_BG_RUN, &cur_trans->flags)) {
bool run_it = false;

- /* this mutex is also taken before trying to set
- * block groups readonly. We need to make sure
- * that nobody has set a block group readonly
- * after a extents from that block group have been
- * allocated for cache files. btrfs_set_block_group_ro
- * will wait for the transaction to commit if it
- * finds BTRFS_TRANS_DIRTY_BG_RUN set.
+ /*
+ * This mutex is also taken before trying to set block groups
+ * readonly. btrfs_inc_block_group_ro() will wait for the
+ * transaction to commit if it finds BTRFS_TRANS_DIRTY_BG_RUN
+ * set.
*
* The BTRFS_TRANS_DIRTY_BG_RUN flag is also used to make sure
- * only one process starts all the block group IO. It wouldn't
- * hurt to have more than one go through, but there's no
- * real advantage to it either.
+ * only one process starts all the block group item updates. It
+ * wouldn't hurt to have more than one go through, but there's
+ * no real advantage to it either.
*/
mutex_lock(&fs_info->ro_block_group_mutex);
if (!test_and_set_bit(BTRFS_TRANS_DIRTY_BG_RUN,
@@ -2513,10 +2511,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
if (unlikely(ret))
goto unlock_reloc;

- /*
- * The tasks which save the space cache and inode cache may also
- * update ->aborted, check it.
- */
+ /* Other tasks may also have updated ->aborted, check it. */
if (TRANS_ABORTED(cur_trans)) {
ret = cur_trans->aborted;
goto unlock_reloc;
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 8245a58587c0..33a1884c5765 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -81,11 +81,11 @@ struct btrfs_transaction {
struct extent_io_tree pinned_extents;

/*
- * we need to make sure block group deletion doesn't race with
- * free space cache writeout. This mutex keeps them from stomping
- * on each other
+ * We need to make sure block group deletion doesn't race with the
+ * dirty block group item updates done outside the commit critical
+ * section. This mutex keeps them from stomping on each other.
*/
- struct mutex cache_write_mutex;
+ struct mutex dirty_bgs_update_mutex;
spinlock_t dirty_bgs_lock;
/* Protected by spin lock fs_info->unused_bgs_lock. */
struct list_head deleted_bgs;

--
2.39.5