From cfeb5d28c0b877d1ef5fea50a2c20793c99464ae Mon Sep 17 00:00:00 2001 From: Yalagada Pavan Kumar Date: Fri, 9 Oct 2026 08:43:40 +1030 Subject: [PATCH 01/20] btrfs: fix use-after-free on quota enable allocation failure [BUG] There is a syzbot report that a use-after-free bug is triggered after a failed btrfs_quota_enable(): loop0: detected capacity change from 32768 to 0 ================================================================== BUG: KASAN: slab-use-after-free in __list_add_valid_or_report+0x4e/0x130 lib/list_debug.c:29 Read of size 8 at addr ffff888040e0a528 by task syz.0.0/5324 CPU: 0 UID: 0 PID: 5324 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description+0x55/0x1e0 mm/kasan/report.c:378 print_report+0x58/0x70 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 __list_add_valid_or_report+0x4e/0x130 lib/list_debug.c:29 __list_add_valid include/linux/list.h:104 [inline] __list_add include/linux/list.h:169 [inline] list_add include/linux/list.h:192 [inline] list_move include/linux/list.h:345 [inline] add_root_to_dirty_list+0x38b/0x470 fs/btrfs/ctree.c:232 btrfs_force_cow_block+0xf09/0x1bf0 fs/btrfs/ctree.c:553 btrfs_cow_block+0x3f1/0xaf0 fs/btrfs/ctree.c:696 btrfs_search_slot+0xd70/0x2d20 fs/btrfs/ctree.c:-1 btrfs_search_prev_slot fs/btrfs/free-space-tree.c:134 [inline] remove_free_space_extent fs/btrfs/free-space-tree.c:732 [inline] __btrfs_remove_from_free_space_tree fs/btrfs/free-space-tree.c:837 [inline] btrfs_remove_from_free_space_tree+0x4d6/0xda0 fs/btrfs/free-space-tree.c:866 alloc_reserved_extent+0x4a/0x2b0 fs/btrfs/extent-tree.c:4976 alloc_reserved_tree_block fs/btrfs/extent-tree.c:5155 [inline] run_delayed_tree_ref fs/btrfs/extent-tree.c:1815 [inline] run_one_delayed_ref fs/btrfs/extent-tree.c:1851 [inline] btrfs_run_delayed_refs_for_head fs/btrfs/extent-tree.c:2058 [inline] __btrfs_run_delayed_refs+0x18b5/0x43b0 fs/btrfs/extent-tree.c:2134 btrfs_run_delayed_refs+0xdc/0x2a0 fs/btrfs/extent-tree.c:2246 btrfs_commit_transaction+0x28a/0x3170 fs/btrfs/transaction.c:2267 sync_filesystem+0x1d2/0x240 fs/sync.c:66 btrfs_reconfigure+0x2dc/0x2100 fs/btrfs/super.c:1511 reconfigure_super+0x232/0x8f0 fs/super.c:1128 do_remount fs/namespace.c:3410 [inline] path_mount+0xd4e/0x1050 fs/namespace.c:4160 do_mount fs/namespace.c:4181 [inline] __do_sys_mount fs/namespace.c:4397 [inline] __se_sys_mount+0x31d/0x420 fs/namespace.c:4374 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f9c8dd9f3ca Allocated by task 5323: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:409 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:426 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x321/0x600 mm/slub.c:5563 _kmalloc_noprof include/linux/slab.h:991 [inline] _kzalloc_noprof include/linux/slab.h:1312 [inline] btrfs_alloc_root+0x75/0x840 fs/btrfs/disk-io.c:638 btrfs_create_tree+0xa8/0x5c0 fs/btrfs/disk-io.c:832 btrfs_quota_enable+0x3d6/0x1e20 fs/btrfs/qgroup.c:1074 btrfs_ioctl_quota_ctl+0x186/0x1f0 fs/btrfs/ioctl.c:3581 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 5323: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:264 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:296 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2748 [inline] slab_free mm/slub.c:6508 [inline] kfree+0x1c5/0x650 mm/slub.c:6801 btrfs_quota_enable+0x1208/0x1e20 fs/btrfs/qgroup.c:1292 btrfs_ioctl_quota_ctl+0x186/0x1f0 fs/btrfs/ioctl.c:3581 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f [CAUSE] During btrfs_quota_enable(), a temporary quota_root is created, then qgroup items are created for each subvolume and inserted into that root. If there are enough subvolumes, the level of quota_root will be bumped, and add_root_to_dirty_list() will be called, adding quota_root->dirty_list into fs_info->dirty_cowonly_roots. But if a problem is hit during btrfs_quota_enable() later, that quota_root will be released through btrfs_put_root(), causing the whole quota_root structure to be released, while leaving fs_info->dirty_cowonly_roots still pointing to the released quota_root->dirty_list. Later access through fs_info->dirty_cowonly_roots will trigger a use-after-free bug. [FIX] We can just call list_del_init(&root->dirty_list) when btrfs_quota_enable() failed. Also to follow the existing error handling behavior, abort the transaction when the memory preallocation failed. Reported-by: syzbot+947286c775f432b073a8@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/6ac5a5e1.9ad129c8.3ce5b3.0010.GAE@google.com/ Closes: https://syzkaller.appspot.com/bug?extid=947286c775f432b073a8 Signed-off-by: Yalagada Pavan Kumar Reviewed-by: Boris Burkov Reviewed-by: Qu Wenruo Reviewed-by: Filipe Manana [ Change the commit message to include a proper analysis. ] Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/qgroup.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index cf8dfd5c692b..a0651fbfe778 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1207,6 +1207,7 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, prealloc = kzalloc_obj(*prealloc, GFP_NOFS); if (!prealloc) { ret = -ENOMEM; + btrfs_abort_transaction(trans, ret); goto out; } qgroup = add_qgroup_rb(fs_info, prealloc, BTRFS_FS_TREE_OBJECTID); @@ -1296,6 +1297,12 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, * sysfs entries. */ btrfs_free_qgroup_config(fs_info); + + if (quota_root) { + spin_lock(&fs_info->trans_lock); + list_del_init("a_root->dirty_list); + spin_unlock(&fs_info->trans_lock); + } btrfs_put_root(quota_root); } mutex_unlock(&fs_info->qgroup_ioctl_lock); From bca2cf55a7331e6b0ff95ad47fc2a7e8ecc7dc74 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:14 -0400 Subject: [PATCH 02/20] btrfs: drop the extent maps of the written part of a failed ordered extent If a truncated ordered extent fails to complete, the extent maps for the part that was written are kept while the extent itself is freed below. A later read can follow them to blocks that have been reallocated. Drop the maps for the whole range on error. Fixes: 77cef2ec5484 ("Btrfs: allow partial ordered extent completion") Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/inode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 343b6402bb35..271f6024b72b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3390,6 +3390,8 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) /* * Drop extent maps for the part of the extent we didn't write. + * On error the written part is freed below as well, so drop + * its maps too. * * We have an exception here for the free_space_inode, this is * because when we do btrfs_get_extent() on the free space inode @@ -3405,7 +3407,7 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) if (!btrfs_is_free_space_inode(inode)) { u64 unwritten_start = start; - if (truncated) + if (truncated && !ret) unwritten_start += logical_len; btrfs_drop_extent_map_range(inode, unwritten_start, From 4e89aff9ad6fcbec555c38169807c4023f93450f Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:15 -0400 Subject: [PATCH 03/20] btrfs: update the inode item when removing a free space inode btrfs_remove_free_space_inode() adds an orphan item for the free space inode and clears its link count in memory, but never writes the inode item. The item is only removed once the delayed iput evicts the inode, which happens in a later transaction, from the cleaner thread or a throttled transaction end. If we crash in between, or if evict can't reserve space for the truncation and leaves the orphan item for the next mount, the on-disk inode still has a link count of 1. btrfs_orphan_cleanup() then treats it as an inode with links and only deletes the orphan item. The free space header item is already gone at that point, so nothing ever finds the inode again, and its data extents are leaked. Write the zero link count to the inode item in the same transaction as the orphan item, so the orphan cleanup evicts the inode instead. Account for the extra tree root item update when removing a block group. Fixes: 455757c322cc ("Btrfs: delay iput when deleting a block group") Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 6 ++++-- fs/btrfs/free-space-cache.c | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 8ff21dde9a46..c6caf63b6807 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1397,12 +1397,14 @@ struct btrfs_trans_handle *btrfs_start_trans_remove_block_group( ASSERT(map->start == chunk_offset); /* - * We need to reserve 3 + N units from the metadata space info in order + * We need to reserve 4 + N units from the metadata space info in order * to remove a block group (done at btrfs_remove_chunk() and at * btrfs_remove_block_group()), which are used for: * * 1 unit for adding the free space inode's orphan (located in the tree * of tree roots). + * 1 unit for updating the free space inode's item (located in the tree + * of tree roots). * 1 unit for deleting the block group item (located in the extent * tree). * 1 unit for deleting the free space item (located in tree of tree @@ -1415,7 +1417,7 @@ struct btrfs_trans_handle *btrfs_start_trans_remove_block_group( * more device items and remove one chunk item), but this is done at * btrfs_remove_chunk() through a call to check_system_chunk(). */ - num_items = 3 + map->num_stripes; + num_items = 4 + map->num_stripes; btrfs_free_chunk_map(map); return btrfs_start_transaction_fallback_global_rsv(root, num_items); diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index e2af75a205ea..4572b3d64210 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -266,6 +266,16 @@ int btrfs_remove_free_space_inode(struct btrfs_trans_handle *trans, return ret; } clear_nlink(inode); + /* + * Persist the zero link count, so that if the delayed iput below + * doesn't get to evict the inode, the orphan cleanup on the next mount + * evicts it instead of only deleting the orphan item. + */ + ret = btrfs_update_inode(trans, BTRFS_I(inode)); + if (ret) { + btrfs_add_delayed_iput(BTRFS_I(inode)); + return ret; + } /* One for the block groups ref */ spin_lock(&block_group->lock); if (test_and_clear_bit(BLOCK_GROUP_FLAG_IREF, &block_group->runtime_flags)) { From fab81e9b9326582233f36d594c44bea9e66f8b52 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:16 -0400 Subject: [PATCH 04/20] btrfs: stop enabling the v1 space cache from the on-disk state Since commit 545e560a5b0f ("btrfs: disable v1 space cache") the mount options can no longer request the v1 space cache, but a filesystem with an active v1 cache and no free space tree still enables it from cache_generation, and remount does the same. Drop both, so SPACE_CACHE can never be set. btrfs_start_pre_rw_mount() then sees the on-disk cache as active but unwanted and cleans it up, as -o nospace_cache does today. That covers the read-only to read-write remount as well, so drop the toggle in btrfs_remount_cleanup(), which would otherwise start a transaction on remounts of a read-only filesystem with an old cache. When a remount that isn't read-only to read-write asks to change the free space tree, btrfs_reconfigure() warns and keeps the on-disk state, but the fixup that resets the options only ran for a filesystem with a free space tree or an active v1 cache. On one with neither, a requested FREE_SPACE_TREE stayed set and the next read-write remount created the tree. Clear it whenever the tree is not on disk, which also covers a v1 filesystem once its cache has been cleaned up. The cleanup is now unconditional, and the first read-write mount fails if it fails, as it did with -o nospace_cache. This also lets an old filesystem mount without options when the page size is larger than the sector size, which btrfs_check_features() rejected once SPACE_CACHE was set from the superblock. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/super.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 7fabb775d4e3..af7aa9adbfc3 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -777,12 +777,12 @@ void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info) /* * At this point we don't have explicit options set by the user, set - * them ourselves based on the state of the file system. + * them ourselves based on the state of the file system. An existing + * v1 space cache is no longer used and gets cleaned up once the + * filesystem is mounted read-write. */ if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); - else if (btrfs_free_space_cache_v1_active(fs_info)) - btrfs_set_opt(fs_info->mount_opt, SPACE_CACHE); } static void set_device_specific_options(struct btrfs_fs_info *fs_info) @@ -1285,8 +1285,6 @@ static inline void btrfs_remount_begin(struct btrfs_fs_info *fs_info, static inline void btrfs_remount_cleanup(struct btrfs_fs_info *fs_info, unsigned long long old_opts) { - const bool cache_opt = btrfs_test_opt(fs_info, SPACE_CACHE); - /* * We need to cleanup all defraggable inodes if the autodefragment is * close or the filesystem is read only. @@ -1303,10 +1301,6 @@ static inline void btrfs_remount_cleanup(struct btrfs_fs_info *fs_info, else if (btrfs_raw_test_opt(old_opts, DISCARD_ASYNC) && !btrfs_test_opt(fs_info, DISCARD_ASYNC)) btrfs_discard_cleanup(fs_info); - - /* If we toggled space cache */ - if (cache_opt != btrfs_free_space_cache_v1_active(fs_info)) - btrfs_set_free_space_cache_v1_active(fs_info, cache_opt); } static int btrfs_remount_rw(struct btrfs_fs_info *fs_info) @@ -1557,10 +1551,8 @@ static int btrfs_reconfigure(struct fs_context *fc) if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) { btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE); - } - if (btrfs_free_space_cache_v1_active(fs_info)) { + } else { btrfs_clear_opt(fs_info->mount_opt, FREE_SPACE_TREE); - btrfs_set_opt(fs_info->mount_opt, SPACE_CACHE); } } From 6cc6b6ebeaccb97760f9763f3c24f1b595d4c2b7 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:17 -0400 Subject: [PATCH 05/20] btrfs: remove the v1 space cache writeout from the transaction commit Nothing sets SPACE_CACHE anymore, so the dirty block group writers never have a cache to write out or wait for. Remove cache_save_setup(), btrfs_setup_space_cache(), the io_list handling, the io_bgs list and BTRFS_TRANS_CACHE_ENOSPC, and the abort-time cleanup of in-flight cache IO. The -ENOENT retry in btrfs_write_dirty_block_groups() handled a free space endio worker creating a block group during the commit critical section, so drop it too. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 397 +++-------------------------------------- fs/btrfs/block-group.h | 1 - fs/btrfs/disk-io.c | 44 ----- fs/btrfs/transaction.c | 9 +- fs/btrfs/transaction.h | 18 -- 5 files changed, 26 insertions(+), 443 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index c6caf63b6807..da96d4d82578 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1194,29 +1194,10 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, goto out; } - /* - * get the inode first so any iput calls done for the io_list - * aren't the final iput (no unlinks allowed now) - */ inode = lookup_free_space_inode(block_group, path); mutex_lock(&trans->transaction->cache_write_mutex); - /* - * Make sure our free space cache IO is done before removing the - * free space inode - */ spin_lock(&trans->transaction->dirty_bgs_lock); - if (!list_empty(&block_group->io_list)) { - list_del_init(&block_group->io_list); - - WARN_ON(!IS_ERR(inode) && inode != block_group->io_ctl.inode); - - spin_unlock(&trans->transaction->dirty_bgs_lock); - btrfs_wait_cache_io(trans, block_group, path); - btrfs_put_block_group(block_group); - spin_lock(&trans->transaction->dirty_bgs_lock); - } - if (!list_empty(&block_group->dirty_list)) { list_del_init(&block_group->dirty_list); remove_rsv = true; @@ -3415,197 +3396,6 @@ static int update_block_group_item(struct btrfs_trans_handle *trans, } -static void cache_save_setup(struct btrfs_block_group *block_group, - struct btrfs_trans_handle *trans, - struct btrfs_path *path) -{ - struct btrfs_fs_info *fs_info = block_group->fs_info; - struct inode *inode = NULL; - struct extent_changeset *data_reserved = NULL; - u64 alloc_hint = 0; - int dcs = BTRFS_DC_ERROR; - u64 cache_size = 0; - int retries = 0; - int ret = 0; - - if (!btrfs_test_opt(fs_info, SPACE_CACHE)) - return; - - /* - * If this block group is smaller than 100 megs don't bother caching the - * block group. - */ - if (block_group->length < (100 * SZ_1M)) { - spin_lock(&block_group->lock); - block_group->disk_cache_state = BTRFS_DC_WRITTEN; - spin_unlock(&block_group->lock); - return; - } - - if (TRANS_ABORTED(trans)) - return; -again: - inode = lookup_free_space_inode(block_group, path); - if (IS_ERR(inode) && PTR_ERR(inode) != -ENOENT) { - ret = PTR_ERR(inode); - btrfs_release_path(path); - goto out; - } - - if (IS_ERR(inode)) { - if (retries) { - ret = PTR_ERR(inode); - btrfs_err(fs_info, - "failed to lookup free space inode after creation for block group %llu: %d", - block_group->start, ret); - goto out_free; - } - retries++; - - if (block_group->ro) - goto out_free; - - ret = create_free_space_inode(trans, block_group, path); - if (ret) - goto out_free; - goto again; - } - - /* - * We want to set the generation to 0, that way if anything goes wrong - * from here on out we know not to trust this cache when we load up next - * time. - */ - BTRFS_I(inode)->generation = 0; - ret = btrfs_update_inode(trans, BTRFS_I(inode)); - if (unlikely(ret)) { - /* - * So theoretically we could recover from this, simply set the - * super cache generation to 0 so we know to invalidate the - * cache, but then we'd have to keep track of the block groups - * that fail this way so we know we _have_ to reset this cache - * before the next commit or risk reading stale cache. So to - * limit our exposure to horrible edge cases lets just abort the - * transaction, this only happens in really bad situations - * anyway. - */ - btrfs_abort_transaction(trans, ret); - goto out_put; - } - - /* We've already setup this transaction, go ahead and exit */ - if (block_group->cache_generation == trans->transid && - i_size_read(inode)) { - dcs = BTRFS_DC_SETUP; - goto out_put; - } - - if (i_size_read(inode) > 0) { - ret = btrfs_check_trunc_cache_free_space(fs_info, - &fs_info->global_block_rsv); - if (ret) - goto out_put; - - ret = btrfs_truncate_free_space_cache(trans, NULL, inode); - if (ret) - goto out_put; - } - - spin_lock(&block_group->lock); - if (block_group->cached != BTRFS_CACHE_FINISHED || - !btrfs_test_opt(fs_info, SPACE_CACHE)) { - /* - * don't bother trying to write stuff out _if_ - * a) we're not cached, - * b) we're with nospace_cache mount option, - * c) we're with v2 space_cache (FREE_SPACE_TREE). - */ - dcs = BTRFS_DC_WRITTEN; - spin_unlock(&block_group->lock); - goto out_put; - } - spin_unlock(&block_group->lock); - - /* - * We hit an ENOSPC when setting up the cache in this transaction, just - * skip doing the setup, we've already cleared the cache so we're safe. - */ - if (test_bit(BTRFS_TRANS_CACHE_ENOSPC, &trans->transaction->flags)) - goto out_put; - - /* - * Try to preallocate enough space based on how big the block group is. - * Keep in mind this has to include any pinned space which could end up - * taking up quite a bit since it's not folded into the other space - * cache. - */ - cache_size = div_u64(block_group->length, SZ_256M); - if (!cache_size) - cache_size = 1; - - cache_size *= 16; - cache_size *= fs_info->sectorsize; - - ret = btrfs_check_data_free_space(BTRFS_I(inode), &data_reserved, 0, - cache_size, false); - if (ret) - goto out_put; - - ret = btrfs_prealloc_file_range_trans(inode, trans, 0, 0, cache_size, - cache_size, cache_size, - &alloc_hint); - /* - * Our cache requires contiguous chunks so that we don't modify a bunch - * of metadata or split extents when writing the cache out, which means - * we can enospc if we are heavily fragmented in addition to just normal - * out of space conditions. So if we hit this just skip setting up any - * other block groups for this transaction, maybe we'll unpin enough - * space the next time around. - */ - if (!ret) - dcs = BTRFS_DC_SETUP; - else if (ret == -ENOSPC) - set_bit(BTRFS_TRANS_CACHE_ENOSPC, &trans->transaction->flags); - -out_put: - iput(inode); -out_free: - btrfs_release_path(path); -out: - spin_lock(&block_group->lock); - if (!ret && dcs == BTRFS_DC_SETUP) - block_group->cache_generation = trans->transid; - block_group->disk_cache_state = dcs; - spin_unlock(&block_group->lock); - - extent_changeset_free(data_reserved); -} - -int btrfs_setup_space_cache(struct btrfs_trans_handle *trans) -{ - struct btrfs_fs_info *fs_info = trans->fs_info; - struct btrfs_block_group *cache, *tmp; - struct btrfs_transaction *cur_trans = trans->transaction; - BTRFS_PATH_AUTO_FREE(path); - - if (list_empty(&cur_trans->dirty_bgs) || - !btrfs_test_opt(fs_info, SPACE_CACHE)) - return 0; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - /* Could add new block groups, use _safe just in case */ - list_for_each_entry_safe(cache, tmp, &cur_trans->dirty_bgs, - dirty_list) { - if (cache->disk_cache_state == BTRFS_DC_CLEAR) - cache_save_setup(cache, trans, path); - } - - return 0; -} - /* * Transaction commit does final block group cache writeback during a critical * section where nothing is allowed to change the FS. This is required in @@ -3624,10 +3414,8 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans) struct btrfs_block_group *cache; struct btrfs_transaction *cur_trans = trans->transaction; int ret = 0; - int should_put; BTRFS_PATH_AUTO_FREE(path); LIST_HEAD(dirty); - struct list_head *io = &cur_trans->io_bgs; int loops = 0; spin_lock(&cur_trans->dirty_bgs_lock); @@ -3653,7 +3441,7 @@ 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 - * writing out the cache + * updating its item */ mutex_lock(&trans->transaction->cache_write_mutex); while (!list_empty(&dirty)) { @@ -3661,23 +3449,8 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans) cache = list_first_entry(&dirty, struct btrfs_block_group, dirty_list); - /* - * This can happen if something re-dirties a block group that - * is already under IO. Just wait for it to finish and then do - * it all again - */ - if (!list_empty(&cache->io_list)) { - list_del_init(&cache->io_list); - btrfs_wait_cache_io(trans, cache, path); - btrfs_put_block_group(cache); - } - /* - * btrfs_wait_cache_io uses the cache->dirty_list to decide if - * it should update the cache_state. Don't delete until after - * we wait. - * * Since we're not running in the commit critical section * we need the dirty_bgs_lock to protect from update_block_group */ @@ -3685,66 +3458,33 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans) list_del_init(&cache->dirty_list); spin_unlock(&cur_trans->dirty_bgs_lock); - should_put = 1; - - cache_save_setup(cache, trans, path); - - if (cache->disk_cache_state == BTRFS_DC_SETUP) { - cache->io_ctl.inode = NULL; - ret = btrfs_write_out_cache(trans, cache, path); - if (ret == 0 && cache->io_ctl.inode) { - should_put = 0; - - /* - * The cache_write_mutex is protecting the - * io_list, also refer to the definition of - * btrfs_transaction::io_bgs for more details - */ - list_add_tail(&cache->io_list, io); - } else { - /* - * If we failed to write the cache, the - * generation will be bad and life goes on - */ - ret = 0; - } - } - if (!ret) { - ret = update_block_group_item(trans, path, cache); - /* - * Our block group might still be attached to the list - * of new block groups in the transaction handle of some - * other task (struct btrfs_trans_handle->new_bgs). This - * means its block group item isn't yet in the extent - * tree. If this happens ignore the error, as we will - * try again later in the critical section of the - * transaction commit. - */ - if (ret == -ENOENT) { - ret = 0; - spin_lock(&cur_trans->dirty_bgs_lock); - if (list_empty(&cache->dirty_list)) { - list_add_tail(&cache->dirty_list, - &cur_trans->dirty_bgs); - btrfs_get_block_group(cache); - drop_reserve = false; - } - spin_unlock(&cur_trans->dirty_bgs_lock); - } else if (ret) { - btrfs_abort_transaction(trans, ret); + ret = update_block_group_item(trans, path, cache); + /* + * Our block group might still be attached to the list of new + * block groups in the transaction handle of some other task + * (struct btrfs_trans_handle->new_bgs). This means its block + * group item isn't yet in the extent tree. If this happens + * ignore the error, as we will try again later in the critical + * section of the transaction commit. + */ + if (ret == -ENOENT) { + ret = 0; + spin_lock(&cur_trans->dirty_bgs_lock); + if (list_empty(&cache->dirty_list)) { + list_add_tail(&cache->dirty_list, + &cur_trans->dirty_bgs); + btrfs_get_block_group(cache); + drop_reserve = false; } + spin_unlock(&cur_trans->dirty_bgs_lock); + } else if (ret) { + btrfs_abort_transaction(trans, ret); } - /* If it's not on the io list, we need to put the block group */ - if (should_put) - btrfs_put_block_group(cache); + btrfs_put_block_group(cache); if (drop_reserve) btrfs_dec_delayed_refs_rsv_bg_updates(fs_info); - /* - * Avoid blocking other tasks for too long. It might even save - * us from writing caches for block groups that are going to be - * removed. - */ + /* Avoid blocking other tasks for too long. */ mutex_unlock(&trans->transaction->cache_write_mutex); if (ret) goto out; @@ -3789,121 +3529,34 @@ int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans) struct btrfs_block_group *cache; struct btrfs_transaction *cur_trans = trans->transaction; int ret = 0; - int should_put; BTRFS_PATH_AUTO_FREE(path); - struct list_head *io = &cur_trans->io_bgs; path = btrfs_alloc_path(); if (!path) return -ENOMEM; - /* - * Even though we are in the critical section of the transaction commit, - * we can still have concurrent tasks adding elements to this - * transaction's list of dirty block groups. These tasks correspond to - * endio free space workers started when writeback finishes for a - * space cache, which run inode.c:btrfs_finish_ordered_io(), and can - * allocate new block groups as a result of COWing nodes of the root - * tree when updating the free space inode. The writeback for the space - * caches is triggered by an earlier call to - * btrfs_start_dirty_block_groups() and iterations of the following - * loop. - * Also we want to do the cache_save_setup first and then run the - * delayed refs to make sure we have the best chance at doing this all - * in one shot. - */ spin_lock(&cur_trans->dirty_bgs_lock); while (!list_empty(&cur_trans->dirty_bgs)) { cache = list_first_entry(&cur_trans->dirty_bgs, struct btrfs_block_group, dirty_list); - - /* - * This can happen if cache_save_setup re-dirties a block group - * that is already under IO. Just wait for it to finish and - * then do it all again - */ - if (!list_empty(&cache->io_list)) { - spin_unlock(&cur_trans->dirty_bgs_lock); - list_del_init(&cache->io_list); - btrfs_wait_cache_io(trans, cache, path); - btrfs_put_block_group(cache); - spin_lock(&cur_trans->dirty_bgs_lock); - } - - /* - * Don't remove from the dirty list until after we've waited on - * any pending IO - */ list_del_init(&cache->dirty_list); spin_unlock(&cur_trans->dirty_bgs_lock); - should_put = 1; - - cache_save_setup(cache, trans, path); if (!ret) ret = btrfs_run_delayed_refs(trans, U64_MAX); - - if (!ret && cache->disk_cache_state == BTRFS_DC_SETUP) { - cache->io_ctl.inode = NULL; - ret = btrfs_write_out_cache(trans, cache, path); - if (ret == 0 && cache->io_ctl.inode) { - should_put = 0; - list_add_tail(&cache->io_list, io); - } else { - /* - * If we failed to write the cache, the - * generation will be bad and life goes on - */ - ret = 0; - } - } if (!ret) { ret = update_block_group_item(trans, path, cache); - /* - * One of the free space endio workers might have - * created a new block group while updating a free space - * cache's inode (at inode.c:btrfs_finish_ordered_io()) - * and hasn't released its transaction handle yet, in - * which case the new block group is still attached to - * its transaction handle and its creation has not - * finished yet (no block group item in the extent tree - * yet, etc). If this is the case, wait for all free - * space endio workers to finish and retry. This is a - * very rare case so no need for a more efficient and - * complex approach. - */ - if (ret == -ENOENT) { - wait_event(cur_trans->writer_wait, - atomic_read(&cur_trans->num_writers) == 1); - ret = update_block_group_item(trans, path, cache); - if (ret) - btrfs_abort_transaction(trans, ret); - } else if (ret) { + if (ret) btrfs_abort_transaction(trans, ret); - } } - /* If its not on the io list, we need to put the block group */ - if (should_put) - btrfs_put_block_group(cache); + btrfs_put_block_group(cache); btrfs_dec_delayed_refs_rsv_bg_updates(fs_info); spin_lock(&cur_trans->dirty_bgs_lock); } spin_unlock(&cur_trans->dirty_bgs_lock); - /* - * Refer to the definition of io_bgs member for details why it's safe - * to use it without any locking - */ - while (!list_empty(io)) { - cache = list_first_entry(io, struct btrfs_block_group, - io_list); - list_del_init(&cache->io_list); - btrfs_wait_cache_io(trans, cache, path); - btrfs_put_block_group(cache); - } - return ret; } diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h index b349f94cf929..d69432b236ec 100644 --- a/fs/btrfs/block-group.h +++ b/fs/btrfs/block-group.h @@ -368,7 +368,6 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache, void btrfs_dec_block_group_ro(struct btrfs_block_group *cache); int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans); int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans); -int btrfs_setup_space_cache(struct btrfs_trans_handle *trans); int btrfs_update_block_group(struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, bool alloc); int btrfs_add_reserved_bytes(struct btrfs_block_group *cache, diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 02adda065097..bca0b573fbf8 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -4859,26 +4859,6 @@ static void btrfs_destroy_pinned_extent(struct btrfs_fs_info *fs_info, } } -static void btrfs_cleanup_bg_io(struct btrfs_block_group *cache) -{ - struct inode *inode; - - inode = cache->io_ctl.inode; - if (inode) { - unsigned int nofs_flag; - - nofs_flag = memalloc_nofs_save(); - invalidate_inode_pages2(inode->i_mapping); - memalloc_nofs_restore(nofs_flag); - - BTRFS_I(inode)->generation = 0; - cache->io_ctl.inode = NULL; - iput(inode); - } - ASSERT(cache->io_ctl.pages == NULL); - btrfs_put_block_group(cache); -} - void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *cur_trans, struct btrfs_fs_info *fs_info) { @@ -4890,13 +4870,6 @@ void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *cur_trans, struct btrfs_block_group, dirty_list); - if (!list_empty(&cache->io_list)) { - spin_unlock(&cur_trans->dirty_bgs_lock); - list_del_init(&cache->io_list); - btrfs_cleanup_bg_io(cache); - spin_lock(&cur_trans->dirty_bgs_lock); - } - list_del_init(&cache->dirty_list); spin_lock(&cache->lock); cache->disk_cache_state = BTRFS_DC_ERROR; @@ -4908,22 +4881,6 @@ void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *cur_trans, spin_lock(&cur_trans->dirty_bgs_lock); } spin_unlock(&cur_trans->dirty_bgs_lock); - - /* - * Refer to the definition of io_bgs member for details why it's safe - * to use it without any locking - */ - while (!list_empty(&cur_trans->io_bgs)) { - cache = list_first_entry(&cur_trans->io_bgs, - struct btrfs_block_group, - io_list); - - list_del_init(&cache->io_list); - spin_lock(&cache->lock); - cache->disk_cache_state = BTRFS_DC_ERROR; - spin_unlock(&cache->lock); - btrfs_cleanup_bg_io(cache); - } } static void btrfs_free_all_qgroup_pertrans(struct btrfs_fs_info *fs_info) @@ -4959,7 +4916,6 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans) btrfs_cleanup_dirty_bgs(cur_trans, fs_info); ASSERT(list_empty(&cur_trans->dirty_bgs)); - ASSERT(list_empty(&cur_trans->io_bgs)); list_for_each_entry_safe(dev, tmp, &cur_trans->dev_update_list, post_commit_list) { diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 8df7b47b09e0..3d3639e4d352 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -411,7 +411,6 @@ static noinline int join_transaction(struct btrfs_fs_info *fs_info, INIT_LIST_HEAD(&cur_trans->dev_update_list); INIT_LIST_HEAD(&cur_trans->switch_commits); INIT_LIST_HEAD(&cur_trans->dirty_bgs); - INIT_LIST_HEAD(&cur_trans->io_bgs); INIT_LIST_HEAD(&cur_trans->dropped_roots); mutex_init(&cur_trans->cache_write_mutex); spin_lock_init(&cur_trans->dirty_bgs_lock); @@ -1404,7 +1403,6 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans) { struct btrfs_fs_info *fs_info = trans->fs_info; struct list_head *dirty_bgs = &trans->transaction->dirty_bgs; - struct list_head *io_bgs = &trans->transaction->io_bgs; struct extent_buffer *eb; int ret; @@ -1434,10 +1432,6 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans) if (ret) return ret; - ret = btrfs_setup_space_cache(trans); - if (ret) - return ret; - again: while (!list_empty(&fs_info->dirty_cowonly_roots)) { struct btrfs_root *root; @@ -1458,7 +1452,7 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans) if (ret) return ret; - while (!list_empty(dirty_bgs) || !list_empty(io_bgs)) { + while (!list_empty(dirty_bgs)) { ret = btrfs_write_dirty_block_groups(trans); if (ret) return ret; @@ -2589,7 +2583,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) switch_commit_roots(trans); ASSERT(list_empty(&cur_trans->dirty_bgs)); - ASSERT(list_empty(&cur_trans->io_bgs)); update_super_roots(fs_info); btrfs_set_super_log_root(fs_info->super_copy, 0); diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 0b2e69293b3b..d4bbe44f9556 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -47,7 +47,6 @@ enum btrfs_trans_state { #define BTRFS_TRANS_HAVE_FREE_BGS 0 #define BTRFS_TRANS_DIRTY_BG_RUN 1 -#define BTRFS_TRANS_CACHE_ENOSPC 2 struct btrfs_transaction { u64 transid; @@ -78,23 +77,6 @@ struct btrfs_transaction { struct list_head dev_update_list; struct list_head switch_commits; struct list_head dirty_bgs; - - /* - * There is no explicit lock which protects io_bgs, rather its - * consistency is implied by the fact that all the sites which modify - * it do so under some form of transaction critical section, namely: - * - * - btrfs_start_dirty_block_groups - This function can only ever be - * run by one of the transaction committers. Refer to - * BTRFS_TRANS_DIRTY_BG_RUN usage in btrfs_commit_transaction - * - * - btrfs_write_dirty_blockgroups - this is called by - * commit_cowonly_roots from transaction critical section - * (TRANS_STATE_COMMIT_DOING) - * - * - btrfs_cleanup_dirty_bgs - called on transaction abort - */ - struct list_head io_bgs; struct list_head dropped_roots; struct extent_io_tree pinned_extents; From 74c8f34811a3572a29eb52133e7ae89aa852df6e Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:18 -0400 Subject: [PATCH 06/20] btrfs: remove the free space cache endio workqueue Free space inodes are no longer written to, so nothing queues ordered extent completion on endio_freespace_worker. Remove it and always use endio_write_workers in btrfs_queue_ordered_fn(). Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 14 +++----------- fs/btrfs/fs.h | 1 - fs/btrfs/ordered-data.c | 7 ++----- fs/btrfs/super.c | 1 - 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index bca0b573fbf8..4056ad1ab728 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1780,7 +1780,6 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info) if (fs_info->rmw_workers) destroy_workqueue(fs_info->rmw_workers); btrfs_destroy_workqueue(fs_info->endio_write_workers); - btrfs_destroy_workqueue(fs_info->endio_freespace_worker); btrfs_destroy_workqueue(fs_info->delayed_workers); btrfs_destroy_workqueue(fs_info->caching_workers); btrfs_destroy_workqueue(fs_info->flush_workers); @@ -1991,9 +1990,6 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info) fs_info->endio_write_workers = btrfs_alloc_workqueue(fs_info, "endio-write", flags, max_active, 2); - fs_info->endio_freespace_worker = - btrfs_alloc_workqueue(fs_info, "freespace-write", flags, - max_active, 0); fs_info->delayed_workers = btrfs_alloc_workqueue(fs_info, "delayed-meta", flags, max_active, 0); @@ -2006,8 +2002,7 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info) if (!(fs_info->workers && fs_info->delalloc_workers && fs_info->flush_workers && fs_info->endio_workers && fs_info->endio_meta_workers && - fs_info->endio_write_workers && - fs_info->endio_freespace_worker && fs_info->rmw_workers && + fs_info->endio_write_workers && fs_info->rmw_workers && fs_info->caching_workers && fs_info->fixup_workers && fs_info->delayed_workers && fs_info->qgroup_rescan_workers && fs_info->discard_ctl.discard_workers)) { @@ -4441,9 +4436,8 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) * to finish an ordered extent - end_bbio_compressed_write() * calls btrfs_finish_ordered_extent() which in turns does a call to * btrfs_queue_ordered_fn(), and that queues the ordered extent - * completion either in the endio_write_workers work queue or in the - * fs_info->endio_freespace_worker work queue. We flush those queues - * below, so before we flush them we must flush this queue for the + * completion in the endio_write_workers work queue. We flush that + * queue below, so before we flush it we must flush this queue for the * workers of compressed writes. */ flush_workqueue(fs_info->endio_workers); @@ -4469,8 +4463,6 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) * btrfs_finish_ordered_io() when we are unmounting). */ btrfs_flush_workqueue(fs_info->endio_write_workers); - /* Ordered extents for free space inodes. */ - btrfs_flush_workqueue(fs_info->endio_freespace_worker); /* * Run delayed iputs in case an async reclaim worker is waiting for them * to be run as mentioned above. diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 649623c603e8..f1bb6c56c76c 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -712,7 +712,6 @@ struct btrfs_fs_info { struct workqueue_struct *endio_meta_workers; struct workqueue_struct *rmw_workers; struct btrfs_workqueue *endio_write_workers; - struct btrfs_workqueue *endio_freespace_worker; struct btrfs_workqueue *caching_workers; struct workqueue_struct *fixup_workers; diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index b32d4eabe0ab..e9f1cbeb555a 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -417,13 +417,10 @@ static bool can_finish_ordered_extent(struct btrfs_ordered_extent *ordered, static void btrfs_queue_ordered_fn(struct btrfs_ordered_extent *ordered) { - struct btrfs_inode *inode = ordered->inode; - struct btrfs_fs_info *fs_info = inode->root->fs_info; - struct btrfs_workqueue *wq = btrfs_is_free_space_inode(inode) ? - fs_info->endio_freespace_worker : fs_info->endio_write_workers; + struct btrfs_fs_info *fs_info = ordered->inode->root->fs_info; btrfs_init_work(&ordered->work, finish_ordered_fn, NULL); - btrfs_queue_work(wq, &ordered->work); + btrfs_queue_work(fs_info->endio_write_workers, &ordered->work); } void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered, diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index af7aa9adbfc3..2b99c6400f92 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1264,7 +1264,6 @@ static void btrfs_resize_thread_pool(struct btrfs_fs_info *fs_info, workqueue_set_max_active(fs_info->endio_workers, new_pool_size); workqueue_set_max_active(fs_info->endio_meta_workers, new_pool_size); btrfs_workqueue_set_max(fs_info->endio_write_workers, new_pool_size); - btrfs_workqueue_set_max(fs_info->endio_freespace_worker, new_pool_size); btrfs_workqueue_set_max(fs_info->delayed_workers, new_pool_size); } From af8841bbe1f8b38a1e4d97f3559bd67cd262844d Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:19 -0400 Subject: [PATCH 07/20] btrfs: remove the v1 space cache write path Nothing writes out a v1 space cache any more. Remove the writers and their io_ctl helpers, along with create_free_space_inode() and btrfs_prealloc_file_range_trans(), whose only user was the cache inode creation. The io_list and io_ctl block group fields were only used by the writers, so remove them too. btrfs_truncate_free_space_cache() only needed the block group to wait for and clear in-flight cache IO, so drop that parameter. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 4 - fs/btrfs/block-group.h | 3 - fs/btrfs/btrfs_inode.h | 4 - fs/btrfs/free-space-cache.c | 688 ------------------------------------ fs/btrfs/free-space-cache.h | 10 - fs/btrfs/inode.c | 9 - fs/btrfs/relocation.c | 2 +- 7 files changed, 1 insertion(+), 719 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index da96d4d82578..896b003e84ce 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1268,7 +1268,6 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, spin_lock(&trans->transaction->dirty_bgs_lock); WARN_ON(!list_empty(&block_group->dirty_list)); - WARN_ON(!list_empty(&block_group->io_list)); spin_unlock(&trans->transaction->dirty_bgs_lock); btrfs_remove_free_space_cache(block_group); @@ -2454,7 +2453,6 @@ static struct btrfs_block_group *btrfs_create_block_group( INIT_LIST_HEAD(&cache->ro_list); INIT_LIST_HEAD(&cache->discard_list); INIT_LIST_HEAD(&cache->dirty_list); - INIT_LIST_HEAD(&cache->io_list); INIT_LIST_HEAD(&cache->active_bg_list); btrfs_init_free_space_ctl(cache, cache->free_space_ctl); atomic_set(&cache->frozen, 0); @@ -4338,7 +4336,6 @@ void btrfs_put_block_group_cache(struct btrfs_fs_info *info) block_group->inode = NULL; spin_unlock(&block_group->lock); - ASSERT(block_group->io_ctl.inode == NULL); iput(&inode->vfs_inode); } else { spin_unlock(&block_group->lock); @@ -4475,7 +4472,6 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info) btrfs_remove_free_space_cache(block_group); ASSERT(block_group->cached != BTRFS_CACHE_STARTED); ASSERT(list_empty(&block_group->dirty_list)); - ASSERT(list_empty(&block_group->io_list)); ASSERT(list_empty(&block_group->bg_list)); ASSERT(refcount_read(&block_group->refs) == 1); ASSERT(block_group->swap_extents == 0); diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h index d69432b236ec..f7346a0170fc 100644 --- a/fs/btrfs/block-group.h +++ b/fs/btrfs/block-group.h @@ -228,9 +228,6 @@ struct btrfs_block_group { /* For dirty block groups */ struct list_head dirty_list; - struct list_head io_list; - - struct btrfs_io_ctl io_ctl; /* * Incremented when doing extent allocations and holding a read lock diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 03c00c99099d..d1c014da52a3 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -598,10 +598,6 @@ int btrfs_wait_on_delayed_iputs(struct btrfs_fs_info *fs_info); int btrfs_prealloc_file_range(struct inode *inode, int mode, u64 start, u64 num_bytes, u64 min_size, loff_t actual_len, u64 *alloc_hint); -int btrfs_prealloc_file_range_trans(struct inode *inode, - struct btrfs_trans_handle *trans, int mode, - u64 start, u64 num_bytes, u64 min_size, - loff_t actual_len, u64 *alloc_hint); int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_folio, u64 start, u64 end, struct writeback_control *wbc); void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio); diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 4572b3d64210..87db711278a7 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -164,78 +164,6 @@ struct inode *lookup_free_space_inode(struct btrfs_block_group *block_group, return inode; } -static int __create_free_space_inode(struct btrfs_root *root, - struct btrfs_trans_handle *trans, - struct btrfs_path *path, - u64 ino, u64 offset) -{ - struct btrfs_key key; - struct btrfs_disk_key disk_key; - struct btrfs_free_space_header *header; - struct btrfs_inode_item *inode_item; - struct extent_buffer *leaf; - /* We inline CRCs for the free disk space cache */ - const u64 flags = BTRFS_INODE_NOCOMPRESS | BTRFS_INODE_PREALLOC | - BTRFS_INODE_NODATASUM | BTRFS_INODE_NODATACOW; - int ret; - - ret = btrfs_insert_empty_inode(trans, root, path, ino); - if (ret) - return ret; - - leaf = path->nodes[0]; - inode_item = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_inode_item); - btrfs_item_key(leaf, &disk_key, path->slots[0]); - memzero_extent_buffer(leaf, (unsigned long)inode_item, - sizeof(*inode_item)); - btrfs_set_inode_generation(leaf, inode_item, trans->transid); - btrfs_set_inode_size(leaf, inode_item, 0); - btrfs_set_inode_nbytes(leaf, inode_item, 0); - btrfs_set_inode_uid(leaf, inode_item, 0); - btrfs_set_inode_gid(leaf, inode_item, 0); - btrfs_set_inode_mode(leaf, inode_item, S_IFREG | 0600); - btrfs_set_inode_flags(leaf, inode_item, flags); - btrfs_set_inode_nlink(leaf, inode_item, 1); - btrfs_set_inode_transid(leaf, inode_item, trans->transid); - btrfs_set_inode_block_group(leaf, inode_item, offset); - btrfs_release_path(path); - - key.objectid = BTRFS_FREE_SPACE_OBJECTID; - key.type = 0; - key.offset = offset; - ret = btrfs_insert_empty_item(trans, root, path, &key, - sizeof(struct btrfs_free_space_header)); - if (ret < 0) { - btrfs_release_path(path); - return ret; - } - - leaf = path->nodes[0]; - header = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_free_space_header); - memzero_extent_buffer(leaf, (unsigned long)header, sizeof(*header)); - btrfs_set_free_space_key(leaf, header, &disk_key); - btrfs_release_path(path); - - return 0; -} - -int create_free_space_inode(struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, - struct btrfs_path *path) -{ - int ret; - u64 ino; - - ret = btrfs_get_free_objectid(trans->fs_info->tree_root, &ino); - if (ret < 0) - return ret; - - return __create_free_space_inode(trans->fs_info->tree_root, trans, path, - ino, block_group->start); -} - /* * inode is an optional sink: if it is NULL, btrfs_remove_free_space_inode * handles lookup, otherwise it takes ownership and iputs the inode. @@ -302,7 +230,6 @@ int btrfs_remove_free_space_inode(struct btrfs_trans_handle *trans, } int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, struct inode *vfs_inode) { struct btrfs_truncate_control control = { @@ -316,33 +243,6 @@ int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans, struct btrfs_root *root = inode->root; struct extent_state *cached_state = NULL; int ret = 0; - bool locked = false; - - if (block_group) { - BTRFS_PATH_AUTO_FREE(path); - - path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto fail; - } - locked = true; - mutex_lock(&trans->transaction->cache_write_mutex); - if (!list_empty(&block_group->io_list)) { - list_del_init(&block_group->io_list); - - btrfs_wait_cache_io(trans, block_group, path); - btrfs_put_block_group(block_group); - } - - /* - * now that we've truncated the cache away, its no longer - * setup or written - */ - spin_lock(&block_group->lock); - block_group->disk_cache_state = BTRFS_DC_CLEAR; - spin_unlock(&block_group->lock); - } btrfs_i_size_write(inode, 0); truncate_pagecache(vfs_inode, 0); @@ -366,8 +266,6 @@ int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans, ret = btrfs_update_inode(trans, inode); fail: - if (locked) - mutex_unlock(&trans->transaction->cache_write_mutex); if (ret) btrfs_abort_transaction(trans, ret); @@ -500,21 +398,6 @@ static int io_ctl_prepare_pages(struct btrfs_io_ctl *io_ctl, bool uptodate) return 0; } -static void io_ctl_set_generation(struct btrfs_io_ctl *io_ctl, u64 generation) -{ - io_ctl_map_page(io_ctl, 1); - - /* - * Skip the csum areas. If we don't check crcs then we just have a - * 64bit chunk at the front of the first page. - */ - io_ctl->cur += (sizeof(u32) * io_ctl->num_pages); - io_ctl->size -= sizeof(u64) + (sizeof(u32) * io_ctl->num_pages); - - put_unaligned_le64(generation, io_ctl->cur); - io_ctl->cur += sizeof(u64); -} - static int io_ctl_check_generation(struct btrfs_io_ctl *io_ctl, u64 generation) { u64 cache_gen; @@ -538,23 +421,6 @@ static int io_ctl_check_generation(struct btrfs_io_ctl *io_ctl, u64 generation) return 0; } -static void io_ctl_set_crc(struct btrfs_io_ctl *io_ctl, int index) -{ - u32 *tmp; - u32 crc = ~(u32)0; - unsigned offset = 0; - - if (index == 0) - offset = sizeof(u32) * io_ctl->num_pages; - - crc = crc32c(crc, io_ctl->orig + offset, PAGE_SIZE - offset); - btrfs_crc32c_final(crc, (u8 *)&crc); - io_ctl_unmap_page(io_ctl); - tmp = page_address(io_ctl->pages[0]); - tmp += index; - *tmp = crc; -} - static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index) { u32 *tmp, val; @@ -584,76 +450,6 @@ static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index) return 0; } -static int io_ctl_add_entry(struct btrfs_io_ctl *io_ctl, u64 offset, u64 bytes, - void *bitmap) -{ - struct btrfs_free_space_entry *entry; - - if (!io_ctl->cur) - return -ENOSPC; - - entry = io_ctl->cur; - put_unaligned_le64(offset, &entry->offset); - put_unaligned_le64(bytes, &entry->bytes); - entry->type = (bitmap) ? BTRFS_FREE_SPACE_BITMAP : - BTRFS_FREE_SPACE_EXTENT; - io_ctl->cur += sizeof(struct btrfs_free_space_entry); - io_ctl->size -= sizeof(struct btrfs_free_space_entry); - - if (io_ctl->size >= sizeof(struct btrfs_free_space_entry)) - return 0; - - io_ctl_set_crc(io_ctl, io_ctl->index - 1); - - /* No more pages to map */ - if (io_ctl->index >= io_ctl->num_pages) - return 0; - - /* map the next page */ - io_ctl_map_page(io_ctl, 1); - return 0; -} - -static int io_ctl_add_bitmap(struct btrfs_io_ctl *io_ctl, void *bitmap) -{ - if (!io_ctl->cur) - return -ENOSPC; - - /* - * If we aren't at the start of the current page, unmap this one and - * map the next one if there is any left. - */ - if (io_ctl->cur != io_ctl->orig) { - io_ctl_set_crc(io_ctl, io_ctl->index - 1); - if (io_ctl->index >= io_ctl->num_pages) - return -ENOSPC; - io_ctl_map_page(io_ctl, 0); - } - - copy_page(io_ctl->cur, bitmap); - io_ctl_set_crc(io_ctl, io_ctl->index - 1); - if (io_ctl->index < io_ctl->num_pages) - io_ctl_map_page(io_ctl, 0); - return 0; -} - -static void io_ctl_zero_remaining_pages(struct btrfs_io_ctl *io_ctl) -{ - /* - * If we're not on the boundary we know we've modified the page and we - * need to crc the page. - */ - if (io_ctl->cur != io_ctl->orig) - io_ctl_set_crc(io_ctl, io_ctl->index - 1); - else - io_ctl_unmap_page(io_ctl); - - while (io_ctl->index < io_ctl->num_pages) { - io_ctl_map_page(io_ctl, 1); - io_ctl_set_crc(io_ctl, io_ctl->index - 1); - } -} - static int io_ctl_read_entry(struct btrfs_io_ctl *io_ctl, struct btrfs_free_space *entry, u8 *type) { @@ -1075,490 +871,6 @@ int load_free_space_cache(struct btrfs_block_group *block_group) return ret; } -static noinline_for_stack -int write_cache_extent_entries(struct btrfs_io_ctl *io_ctl, - struct btrfs_block_group *block_group, - int *entries, int *bitmaps, - struct list_head *bitmap_list) -{ - int ret; - struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; - struct btrfs_free_cluster *cluster = NULL; - struct btrfs_free_cluster *cluster_locked = NULL; - struct rb_node *node = rb_first(&ctl->free_space_offset); - struct btrfs_trim_range *trim_entry; - - /* Get the cluster for this block_group if it exists */ - if (!list_empty(&block_group->cluster_list)) { - cluster = list_first_entry(&block_group->cluster_list, - struct btrfs_free_cluster, block_group_list); - } - - if (!node && cluster) { - cluster_locked = cluster; - spin_lock(&cluster_locked->lock); - node = rb_first(&cluster->root); - cluster = NULL; - } - - /* Write out the extent entries */ - while (node) { - struct btrfs_free_space *e; - - e = rb_entry(node, struct btrfs_free_space, offset_index); - *entries += 1; - - ret = io_ctl_add_entry(io_ctl, e->offset, e->bytes, - e->bitmap); - if (ret) - goto fail; - - if (e->bitmap) { - list_add_tail(&e->list, bitmap_list); - *bitmaps += 1; - } - node = rb_next(node); - if (!node && cluster) { - node = rb_first(&cluster->root); - cluster_locked = cluster; - spin_lock(&cluster_locked->lock); - cluster = NULL; - } - } - if (cluster_locked) { - spin_unlock(&cluster_locked->lock); - cluster_locked = NULL; - } - - /* - * Make sure we don't miss any range that was removed from our rbtree - * because trimming is running. Otherwise after a umount+mount (or crash - * after committing the transaction) we would leak free space and get - * an inconsistent free space cache report from fsck. - */ - list_for_each_entry(trim_entry, &ctl->trimming_ranges, list) { - ret = io_ctl_add_entry(io_ctl, trim_entry->start, - trim_entry->bytes, NULL); - if (ret) - goto fail; - *entries += 1; - } - - return 0; -fail: - if (cluster_locked) - spin_unlock(&cluster_locked->lock); - return -ENOSPC; -} - -static noinline_for_stack int -update_cache_item(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct inode *inode, - struct btrfs_path *path, u64 offset, - int entries, int bitmaps) -{ - struct btrfs_key key; - struct btrfs_free_space_header *header; - struct extent_buffer *leaf; - int ret; - - key.objectid = BTRFS_FREE_SPACE_OBJECTID; - key.type = 0; - key.offset = offset; - - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); - if (ret < 0) { - btrfs_clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1, - EXTENT_DELALLOC, NULL); - return ret; - } - leaf = path->nodes[0]; - if (ret > 0) { - struct btrfs_key found_key; - ASSERT(path->slots[0]); - path->slots[0]--; - btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); - if (found_key.objectid != BTRFS_FREE_SPACE_OBJECTID || - found_key.offset != offset) { - btrfs_clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, - inode->i_size - 1, EXTENT_DELALLOC, - NULL); - btrfs_release_path(path); - return -ENOENT; - } - } - - BTRFS_I(inode)->generation = trans->transid; - header = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_free_space_header); - btrfs_set_free_space_entries(leaf, header, entries); - btrfs_set_free_space_bitmaps(leaf, header, bitmaps); - btrfs_set_free_space_generation(leaf, header, trans->transid); - btrfs_release_path(path); - - return 0; -} - -static noinline_for_stack int write_pinned_extent_entries( - struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, - struct btrfs_io_ctl *io_ctl, - int *entries) -{ - u64 start, extent_start, extent_end, len; - const u64 block_group_end = btrfs_block_group_end(block_group); - struct extent_io_tree *unpin = NULL; - int ret; - - /* - * We want to add any pinned extents to our free space cache - * so we don't leak the space - * - * We shouldn't have switched the pinned extents yet so this is the - * right one - */ - unpin = &trans->transaction->pinned_extents; - - start = block_group->start; - - while (start < block_group_end) { - if (!btrfs_find_first_extent_bit(unpin, start, - &extent_start, &extent_end, - EXTENT_DIRTY, NULL)) - return 0; - - /* This pinned extent is out of our range */ - if (extent_start >= block_group_end) - return 0; - - extent_start = max(extent_start, start); - extent_end = min(block_group_end, extent_end + 1); - len = extent_end - extent_start; - - *entries += 1; - ret = io_ctl_add_entry(io_ctl, extent_start, len, NULL); - if (ret) - return -ENOSPC; - - start = extent_end; - } - - return 0; -} - -static noinline_for_stack int -write_bitmap_entries(struct btrfs_io_ctl *io_ctl, struct list_head *bitmap_list) -{ - struct btrfs_free_space *entry, *next; - int ret; - - /* Write out the bitmaps */ - list_for_each_entry_safe(entry, next, bitmap_list, list) { - ret = io_ctl_add_bitmap(io_ctl, entry->bitmap); - if (ret) - return -ENOSPC; - list_del_init(&entry->list); - } - - return 0; -} - -static int flush_dirty_cache(struct inode *inode) -{ - int ret; - - ret = btrfs_wait_ordered_range(BTRFS_I(inode), 0, (u64)-1); - if (ret) - btrfs_clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1, - EXTENT_DELALLOC, NULL); - - return ret; -} - -static void noinline_for_stack -cleanup_bitmap_list(struct list_head *bitmap_list) -{ - struct btrfs_free_space *entry, *next; - - list_for_each_entry_safe(entry, next, bitmap_list, list) - list_del_init(&entry->list); -} - -static void noinline_for_stack -cleanup_write_cache_enospc(struct inode *inode, - struct btrfs_io_ctl *io_ctl, - struct extent_state **cached_state) -{ - io_ctl_drop_pages(io_ctl); - btrfs_unlock_extent(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1, - cached_state); -} - -static int __btrfs_wait_cache_io(struct btrfs_root *root, - struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, - struct btrfs_io_ctl *io_ctl, - struct btrfs_path *path, u64 offset) -{ - int ret; - struct inode *inode = io_ctl->inode; - - if (!inode) - return 0; - - /* Flush the dirty pages in the cache file. */ - ret = flush_dirty_cache(inode); - if (ret) - goto out; - - /* Update the cache item to tell everyone this cache file is valid. */ - ret = update_cache_item(trans, root, inode, path, offset, - io_ctl->entries, io_ctl->bitmaps); -out: - if (ret) { - invalidate_inode_pages2(inode->i_mapping); - BTRFS_I(inode)->generation = 0; - if (block_group) - btrfs_debug(root->fs_info, - "failed to write free space cache for block group %llu error %d", - block_group->start, ret); - } - btrfs_update_inode(trans, BTRFS_I(inode)); - - if (block_group) { - /* the dirty list is protected by the dirty_bgs_lock */ - spin_lock(&trans->transaction->dirty_bgs_lock); - - /* the disk_cache_state is protected by the block group lock */ - spin_lock(&block_group->lock); - - /* - * only mark this as written if we didn't get put back on - * the dirty list while waiting for IO. Otherwise our - * cache state won't be right, and we won't get written again - */ - if (!ret && list_empty(&block_group->dirty_list)) - block_group->disk_cache_state = BTRFS_DC_WRITTEN; - else if (ret) - block_group->disk_cache_state = BTRFS_DC_ERROR; - - spin_unlock(&block_group->lock); - spin_unlock(&trans->transaction->dirty_bgs_lock); - io_ctl->inode = NULL; - iput(inode); - } - - return ret; - -} - -int btrfs_wait_cache_io(struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, - struct btrfs_path *path) -{ - return __btrfs_wait_cache_io(block_group->fs_info->tree_root, trans, - block_group, &block_group->io_ctl, - path, block_group->start); -} - -/* - * Write out cached info to an inode. - * - * @inode: freespace inode we are writing out - * @ctl: free space cache we are going to write out - * @block_group: block_group for this cache if it belongs to a block_group - * @io_ctl: holds context for the io - * @trans: the trans handle - * - * This function writes out a free space cache struct to disk for quick recovery - * on mount. This will return 0 if it was successful in writing the cache out, - * or an errno if it was not. - */ -static int __btrfs_write_out_cache(struct inode *inode, - struct btrfs_block_group *block_group, - struct btrfs_trans_handle *trans) -{ - struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; - struct btrfs_io_ctl *io_ctl = &block_group->io_ctl; - struct extent_state *cached_state = NULL; - LIST_HEAD(bitmap_list); - int entries = 0; - int bitmaps = 0; - int ret; - bool must_iput = false; - int i_size; - - if (!i_size_read(inode)) - return -EIO; - - WARN_ON(io_ctl->pages); - ret = io_ctl_init(io_ctl, inode, 1); - if (ret) - return ret; - - if (block_group->flags & BTRFS_BLOCK_GROUP_DATA) { - down_write(&block_group->data_rwsem); - spin_lock(&block_group->lock); - if (block_group->delalloc_bytes) { - block_group->disk_cache_state = BTRFS_DC_WRITTEN; - spin_unlock(&block_group->lock); - up_write(&block_group->data_rwsem); - BTRFS_I(inode)->generation = 0; - ret = 0; - must_iput = true; - goto out; - } - spin_unlock(&block_group->lock); - } - - /* Lock all pages first so we can lock the extent safely. */ - ret = io_ctl_prepare_pages(io_ctl, false); - if (ret) - goto out_unlock; - - btrfs_lock_extent(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1, - &cached_state); - - io_ctl_set_generation(io_ctl, trans->transid); - - mutex_lock(&ctl->cache_writeout_mutex); - /* Write out the extent entries in the free space cache */ - spin_lock(&ctl->tree_lock); - ret = write_cache_extent_entries(io_ctl, block_group, &entries, &bitmaps, - &bitmap_list); - if (ret) - goto out_nospc_locked; - - /* - * Some spaces that are freed in the current transaction are pinned, - * they will be added into free space cache after the transaction is - * committed, we shouldn't lose them. - * - * If this changes while we are working we'll get added back to - * the dirty list and redo it. No locking needed - */ - ret = write_pinned_extent_entries(trans, block_group, io_ctl, &entries); - if (ret) - goto out_nospc_locked; - - /* - * At last, we write out all the bitmaps and keep cache_writeout_mutex - * locked while doing it because a concurrent trim can be manipulating - * or freeing the bitmap. - */ - ret = write_bitmap_entries(io_ctl, &bitmap_list); - spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); - if (ret) - goto out_nospc; - - /* Zero out the rest of the pages just to make sure */ - io_ctl_zero_remaining_pages(io_ctl); - - /* Everything is written out, now we dirty the pages in the file. */ - i_size = i_size_read(inode); - for (int i = 0; i < round_up(i_size, PAGE_SIZE) / PAGE_SIZE; i++) { - u64 dirty_start = i * PAGE_SIZE; - u64 dirty_len = min_t(u64, dirty_start + PAGE_SIZE, i_size) - dirty_start; - - ret = btrfs_dirty_folio(BTRFS_I(inode), page_folio(io_ctl->pages[i]), - dirty_start, dirty_len, &cached_state, false); - if (ret < 0) - goto out_nospc; - } - - if (block_group->flags & BTRFS_BLOCK_GROUP_DATA) - up_write(&block_group->data_rwsem); - /* - * Release the pages and unlock the extent, we will flush - * them out later - */ - io_ctl_drop_pages(io_ctl); - io_ctl_free(io_ctl); - - btrfs_unlock_extent(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1, - &cached_state); - - /* - * at this point the pages are under IO and we're happy, - * The caller is responsible for waiting on them and updating - * the cache and the inode - */ - io_ctl->entries = entries; - io_ctl->bitmaps = bitmaps; - - ret = btrfs_fdatawrite_range(BTRFS_I(inode), 0, (u64)-1); - if (ret) - goto out; - - return 0; - -out_nospc_locked: - cleanup_bitmap_list(&bitmap_list); - spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); - -out_nospc: - cleanup_write_cache_enospc(inode, io_ctl, &cached_state); - -out_unlock: - if (block_group->flags & BTRFS_BLOCK_GROUP_DATA) - up_write(&block_group->data_rwsem); - -out: - io_ctl->inode = NULL; - io_ctl_free(io_ctl); - if (ret) { - invalidate_inode_pages2(inode->i_mapping); - BTRFS_I(inode)->generation = 0; - } - btrfs_update_inode(trans, BTRFS_I(inode)); - if (must_iput) - iput(inode); - return ret; -} - -int btrfs_write_out_cache(struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, - struct btrfs_path *path) -{ - struct btrfs_fs_info *fs_info = trans->fs_info; - struct inode *inode; - int ret = 0; - - spin_lock(&block_group->lock); - if (block_group->disk_cache_state < BTRFS_DC_SETUP) { - spin_unlock(&block_group->lock); - return 0; - } - spin_unlock(&block_group->lock); - - inode = lookup_free_space_inode(block_group, path); - if (IS_ERR(inode)) - return 0; - - ret = __btrfs_write_out_cache(inode, block_group, trans); - if (ret) { - btrfs_debug(fs_info, - "failed to write free space cache for block group %llu error %d", - block_group->start, ret); - spin_lock(&block_group->lock); - block_group->disk_cache_state = BTRFS_DC_ERROR; - spin_unlock(&block_group->lock); - - block_group->io_ctl.inode = NULL; - iput(inode); - } - - /* - * if ret == 0 the caller is expected to call btrfs_wait_cache_io - * to wait for IO and put the inode - */ - - return ret; -} - static inline unsigned long offset_to_bit(u64 bitmap_start, u32 unit, u64 offset) { diff --git a/fs/btrfs/free-space-cache.h b/fs/btrfs/free-space-cache.h index 53fe8e293af1..2432f1783f47 100644 --- a/fs/btrfs/free-space-cache.h +++ b/fs/btrfs/free-space-cache.h @@ -105,23 +105,13 @@ int __init btrfs_free_space_init(void); void __cold btrfs_free_space_exit(void); struct inode *lookup_free_space_inode(struct btrfs_block_group *block_group, struct btrfs_path *path); -int create_free_space_inode(struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, - struct btrfs_path *path); int btrfs_remove_free_space_inode(struct btrfs_trans_handle *trans, struct inode *inode, struct btrfs_block_group *block_group); int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, struct inode *inode); int load_free_space_cache(struct btrfs_block_group *block_group); -int btrfs_wait_cache_io(struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, - struct btrfs_path *path); -int btrfs_write_out_cache(struct btrfs_trans_handle *trans, - struct btrfs_block_group *block_group, - struct btrfs_path *path); void btrfs_init_free_space_ctl(struct btrfs_block_group *block_group, struct btrfs_free_space_ctl *ctl); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 271f6024b72b..4ffbf2a9dfbc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9390,15 +9390,6 @@ int btrfs_prealloc_file_range(struct inode *inode, int mode, NULL); } -int btrfs_prealloc_file_range_trans(struct inode *inode, - struct btrfs_trans_handle *trans, int mode, - u64 start, u64 num_bytes, u64 min_size, - loff_t actual_len, u64 *alloc_hint) -{ - return __btrfs_prealloc_file_range(inode, mode, start, num_bytes, - min_size, actual_len, alloc_hint, trans); -} - /* * NOTE: in case you are adding MAY_EXEC check for directories: * we are marking them with IOP_FASTPERM_MAY_EXEC, allowing path lookup to diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 65cb9dcf0aac..b2ac5b836b21 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -3356,7 +3356,7 @@ static int delete_block_group_cache(struct btrfs_block_group *block_group, goto out; } - ret = btrfs_truncate_free_space_cache(trans, block_group, inode); + ret = btrfs_truncate_free_space_cache(trans, inode); btrfs_end_transaction(trans); btrfs_btree_balance_dirty(fs_info); From 6bf076866eb0b321edb2bd1c7a70827ff3b3bcd3 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:20 -0400 Subject: [PATCH 08/20] btrfs: rename cache_write_mutex to dirty_bgs_update_mutex 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: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- 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 896b003e84ce..d812fda5eee8 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)) { @@ -3395,15 +3399,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) @@ -3437,11 +3441,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; @@ -3483,12 +3487,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 @@ -3502,7 +3506,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); @@ -3598,10 +3602,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); @@ -3657,7 +3661,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 3d3639e4d352..3d7bc667eb03 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -412,7 +412,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); @@ -2314,18 +2314,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, @@ -2559,10 +2557,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 d4bbe44f9556..aa57c64a955d 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; From 8d598a2113c6824a9607c48d1f3939bd87f5ec56 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:21 -0400 Subject: [PATCH 09/20] btrfs: drop the transaction handle from the prealloc helpers The v1 space cache created its inode during the transaction commit, and btrfs_prealloc_file_range_trans() existed so that preallocation could reuse the open handle. It was the only caller passing a transaction, so __btrfs_prealloc_file_range() and insert_prealloc_file_extent() now always start their own. Fold the wrapper into btrfs_prealloc_file_range() and drop the parameter. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/inode.c | 47 ++++++++++------------------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 4ffbf2a9dfbc..95ec6026f08b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9158,14 +9158,13 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, } static struct btrfs_trans_handle *insert_prealloc_file_extent( - struct btrfs_trans_handle *trans_in, struct btrfs_inode *inode, struct btrfs_key *ins, u64 file_offset) { struct btrfs_file_extent_item stack_fi; struct btrfs_replace_extent_info extent_info; - struct btrfs_trans_handle *trans = trans_in; + struct btrfs_trans_handle *trans; struct btrfs_path *path; u64 start = ins->objectid; u64 len = ins->offset; @@ -9186,15 +9185,6 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( if (ret < 0) return ERR_PTR(ret); - if (trans) { - ret = insert_reserved_file_extent(trans, inode, - file_offset, &stack_fi, - true, qgroup_released); - if (ret) - goto free_qgroup; - return trans; - } - extent_info.disk_offset = start; extent_info.disk_len = len; extent_info.data_offset = 0; @@ -9234,12 +9224,12 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( return ERR_PTR(ret); } -static int __btrfs_prealloc_file_range(struct inode *inode, int mode, - u64 start, u64 num_bytes, u64 min_size, - loff_t actual_len, u64 *alloc_hint, - struct btrfs_trans_handle *trans) +int btrfs_prealloc_file_range(struct inode *inode, int mode, + u64 start, u64 num_bytes, u64 min_size, + loff_t actual_len, u64 *alloc_hint) { struct btrfs_fs_info *fs_info = inode_to_fs_info(inode); + struct btrfs_trans_handle *trans; struct extent_map *em; struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_key ins; @@ -9249,11 +9239,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, u64 cur_bytes; u64 last_alloc = (u64)-1; int ret = 0; - bool own_trans = true; u64 end = start + num_bytes - 1; - if (trans) - own_trans = false; while (num_bytes > 0) { cur_bytes = min_t(u64, num_bytes, SZ_256M); cur_bytes = max(cur_bytes, min_size); @@ -9279,8 +9266,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, clear_offset += ins.offset; last_alloc = ins.offset; - trans = insert_prealloc_file_extent(trans, BTRFS_I(inode), - &ins, cur_offset); + trans = insert_prealloc_file_extent(BTRFS_I(inode), &ins, + cur_offset); /* * Now that we inserted the prealloc extent we can finally * decrement the number of reservations in the block group. @@ -9352,8 +9339,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, range_start, range_end - range_start); if (ret) { btrfs_abort_transaction(trans, ret); - if (own_trans) - btrfs_end_transaction(trans); + btrfs_end_transaction(trans); break; } @@ -9365,15 +9351,11 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - if (own_trans) - btrfs_end_transaction(trans); + btrfs_end_transaction(trans); break; } - if (own_trans) { - btrfs_end_transaction(trans); - trans = NULL; - } + btrfs_end_transaction(trans); } if (clear_offset < end) btrfs_free_reserved_data_space(BTRFS_I(inode), NULL, clear_offset, @@ -9381,15 +9363,6 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, return ret; } -int btrfs_prealloc_file_range(struct inode *inode, int mode, - u64 start, u64 num_bytes, u64 min_size, - loff_t actual_len, u64 *alloc_hint) -{ - return __btrfs_prealloc_file_range(inode, mode, start, num_bytes, - min_size, actual_len, alloc_hint, - NULL); -} - /* * NOTE: in case you are adding MAY_EXEC check for directories: * we are marking them with IOP_FASTPERM_MAY_EXEC, allowing path lookup to From 324ad3a989753f9584f34553fac50d35d8df8bd1 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:22 -0400 Subject: [PATCH 10/20] btrfs: remove the v1 space cache load path Nothing writes a v1 space cache any more, and since commit 545e560a5b0f ("btrfs: disable v1 space cache") the mount option can't be enabled to read one either. Remove load_free_space_cache(), its io_ctl helpers and struct btrfs_io_ctl. Drop the gfp constraint on the inode mapping as well, it only covered the cache's page cache allocations. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 18 +- fs/btrfs/free-space-cache.c | 565 ------------------------------------ fs/btrfs/free-space-cache.h | 15 - 3 files changed, 1 insertion(+), 597 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index d812fda5eee8..a11685440e3c 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -904,22 +904,6 @@ static noinline void caching_thread(struct btrfs_work *work) down_read(&fs_info->commit_root_sem); load_block_group_size_class(caching_ctl); - if (btrfs_test_opt(fs_info, SPACE_CACHE)) { - ret = load_free_space_cache(block_group); - if (ret == 1) { - ret = 0; - goto done; - } - - /* - * We failed to load the space cache, set ourselves to - * CACHE_STARTED and carry on. - */ - spin_lock(&block_group->lock); - block_group->cached = BTRFS_CACHE_STARTED; - spin_unlock(&block_group->lock); - wake_up(&caching_ctl->wait); - } /* * If we are in the transaction that populated the free space tree we @@ -933,7 +917,7 @@ static noinline void caching_thread(struct btrfs_work *work) ret = btrfs_load_free_space_tree(caching_ctl); else ret = load_extent_tree_free(caching_ctl); -done: + spin_lock(&block_group->lock); block_group->caching_ctl = NULL; block_group->cached = ret ? BTRFS_CACHE_ERROR : BTRFS_CACHE_FINISHED; diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 87db711278a7..1e2fc2884d07 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include "extent-tree.h" @@ -23,7 +22,6 @@ #include "space-info.h" #include "block-group.h" #include "discard.h" -#include "subpage.h" #include "inode-item.h" #include "accessors.h" #include "file-item.h" @@ -57,11 +55,6 @@ static void bitmap_clear_bits(struct btrfs_free_space_ctl *ctl, struct btrfs_free_space *info, u64 offset, u64 bytes, bool update_stats); -static void btrfs_crc32c_final(u32 crc, u8 *result) -{ - put_unaligned_le32(~crc, result); -} - static void __btrfs_remove_free_space_cache(struct btrfs_free_space_ctl *ctl) { struct btrfs_free_space *info; @@ -123,10 +116,6 @@ static struct inode *__lookup_free_space_inode(struct btrfs_root *root, if (IS_ERR(inode)) return ERR_CAST(inode); - mapping_set_gfp_mask(inode->vfs_inode.i_mapping, - mapping_gfp_constraint(inode->vfs_inode.i_mapping, - ~(__GFP_FS | __GFP_HIGHMEM))); - return &inode->vfs_inode; } @@ -272,226 +261,6 @@ int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans, return ret; } -static void readahead_cache(struct inode *inode) -{ - struct file_ra_state ra; - pgoff_t last_index; - - file_ra_state_init(&ra, inode->i_mapping); - last_index = (i_size_read(inode) - 1) >> PAGE_SHIFT; - - page_cache_sync_readahead(inode->i_mapping, &ra, NULL, 0, last_index); -} - -static int io_ctl_init(struct btrfs_io_ctl *io_ctl, struct inode *inode, - int write) -{ - int num_pages; - - num_pages = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); - - /* Make sure we can fit our crcs and generation into the first page */ - if (write && (num_pages * sizeof(u32) + sizeof(u64)) > PAGE_SIZE) - return -ENOSPC; - - memset(io_ctl, 0, sizeof(struct btrfs_io_ctl)); - - io_ctl->pages = kzalloc_objs(struct page *, num_pages, GFP_NOFS); - if (!io_ctl->pages) - return -ENOMEM; - - io_ctl->num_pages = num_pages; - io_ctl->fs_info = inode_to_fs_info(inode); - io_ctl->inode = inode; - - return 0; -} -ALLOW_ERROR_INJECTION(io_ctl_init, ERRNO); - -static void io_ctl_free(struct btrfs_io_ctl *io_ctl) -{ - kfree(io_ctl->pages); - io_ctl->pages = NULL; -} - -static void io_ctl_unmap_page(struct btrfs_io_ctl *io_ctl) -{ - if (io_ctl->cur) { - io_ctl->cur = NULL; - io_ctl->orig = NULL; - } -} - -static void io_ctl_map_page(struct btrfs_io_ctl *io_ctl, int clear) -{ - ASSERT(io_ctl->index < io_ctl->num_pages); - io_ctl->page = io_ctl->pages[io_ctl->index++]; - io_ctl->cur = page_address(io_ctl->page); - io_ctl->orig = io_ctl->cur; - io_ctl->size = PAGE_SIZE; - if (clear) - clear_page(io_ctl->cur); -} - -static void io_ctl_drop_pages(struct btrfs_io_ctl *io_ctl) -{ - int i; - - io_ctl_unmap_page(io_ctl); - - for (i = 0; i < io_ctl->num_pages; i++) { - if (io_ctl->pages[i]) { - unlock_page(io_ctl->pages[i]); - put_page(io_ctl->pages[i]); - } - } -} - -static int io_ctl_prepare_pages(struct btrfs_io_ctl *io_ctl, bool uptodate) -{ - struct folio *folio; - struct inode *inode = io_ctl->inode; - gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping); - int i; - - for (i = 0; i < io_ctl->num_pages; i++) { - int ret; - - folio = __filemap_get_folio(inode->i_mapping, i, - FGP_LOCK | FGP_ACCESSED | FGP_CREAT, - mask); - if (IS_ERR(folio)) { - io_ctl_drop_pages(io_ctl); - return PTR_ERR(folio); - } - - ret = set_folio_extent_mapped(folio); - if (ret < 0) { - folio_unlock(folio); - folio_put(folio); - io_ctl_drop_pages(io_ctl); - return ret; - } - - io_ctl->pages[i] = &folio->page; - if (uptodate && !folio_test_uptodate(folio)) { - btrfs_read_folio(NULL, folio); - folio_lock(folio); - if (folio->mapping != inode->i_mapping) { - btrfs_err(BTRFS_I(inode)->root->fs_info, - "free space cache page truncated"); - io_ctl_drop_pages(io_ctl); - return -EIO; - } - if (!folio_test_uptodate(folio)) { - btrfs_err(BTRFS_I(inode)->root->fs_info, - "error reading free space cache"); - io_ctl_drop_pages(io_ctl); - return -EIO; - } - } - } - - for (i = 0; i < io_ctl->num_pages; i++) - clear_page_dirty_for_io(io_ctl->pages[i]); - - return 0; -} - -static int io_ctl_check_generation(struct btrfs_io_ctl *io_ctl, u64 generation) -{ - u64 cache_gen; - - /* - * Skip the crc area. If we don't check crcs then we just have a 64bit - * chunk at the front of the first page. - */ - io_ctl->cur += sizeof(u32) * io_ctl->num_pages; - io_ctl->size -= sizeof(u64) + (sizeof(u32) * io_ctl->num_pages); - - cache_gen = get_unaligned_le64(io_ctl->cur); - if (cache_gen != generation) { - btrfs_err_rl(io_ctl->fs_info, - "space cache generation (%llu) does not match inode (%llu)", - cache_gen, generation); - io_ctl_unmap_page(io_ctl); - return -EIO; - } - io_ctl->cur += sizeof(u64); - return 0; -} - -static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index) -{ - u32 *tmp, val; - u32 crc = ~(u32)0; - unsigned offset = 0; - - if (index >= io_ctl->num_pages) - return -EIO; - - if (index == 0) - offset = sizeof(u32) * io_ctl->num_pages; - - tmp = page_address(io_ctl->pages[0]); - tmp += index; - val = *tmp; - - io_ctl_map_page(io_ctl, 0); - crc = crc32c(crc, io_ctl->orig + offset, PAGE_SIZE - offset); - btrfs_crc32c_final(crc, (u8 *)&crc); - if (val != crc) { - btrfs_err_rl(io_ctl->fs_info, - "csum mismatch on free space cache"); - io_ctl_unmap_page(io_ctl); - return -EIO; - } - - return 0; -} - -static int io_ctl_read_entry(struct btrfs_io_ctl *io_ctl, - struct btrfs_free_space *entry, u8 *type) -{ - struct btrfs_free_space_entry *e; - int ret; - - if (!io_ctl->cur) { - ret = io_ctl_check_crc(io_ctl, io_ctl->index); - if (ret) - return ret; - } - - e = io_ctl->cur; - entry->offset = get_unaligned_le64(&e->offset); - entry->bytes = get_unaligned_le64(&e->bytes); - *type = e->type; - io_ctl->cur += sizeof(struct btrfs_free_space_entry); - io_ctl->size -= sizeof(struct btrfs_free_space_entry); - - if (io_ctl->size >= sizeof(struct btrfs_free_space_entry)) - return 0; - - io_ctl_unmap_page(io_ctl); - - return 0; -} - -static int io_ctl_read_bitmap(struct btrfs_io_ctl *io_ctl, - struct btrfs_free_space *entry) -{ - int ret; - - ret = io_ctl_check_crc(io_ctl, io_ctl->index); - if (ret) - return ret; - - copy_page(entry->bitmap, io_ctl->cur); - io_ctl_unmap_page(io_ctl); - - return 0; -} - static void recalculate_thresholds(struct btrfs_free_space_ctl *ctl) { struct btrfs_block_group *block_group = ctl->block_group; @@ -537,340 +306,6 @@ static void recalculate_thresholds(struct btrfs_free_space_ctl *ctl) div_u64(extent_bytes, sizeof(struct btrfs_free_space)); } -static int __load_free_space_cache(struct btrfs_root *root, struct inode *inode, - struct btrfs_free_space_ctl *ctl, - struct btrfs_path *path, u64 offset) -{ - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_free_space_header *header; - struct extent_buffer *leaf; - struct btrfs_io_ctl io_ctl; - struct btrfs_key key; - struct btrfs_free_space *e, *n; - LIST_HEAD(bitmaps); - u64 num_entries; - u64 num_bitmaps; - u64 generation; - u8 type; - int ret = 0; - - /* Nothing in the space cache, goodbye */ - if (!i_size_read(inode)) - return 0; - - key.objectid = BTRFS_FREE_SPACE_OBJECTID; - key.type = 0; - key.offset = offset; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) - return 0; - else if (ret > 0) { - btrfs_release_path(path); - return 0; - } - - ret = -1; - - leaf = path->nodes[0]; - header = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_free_space_header); - num_entries = btrfs_free_space_entries(leaf, header); - num_bitmaps = btrfs_free_space_bitmaps(leaf, header); - generation = btrfs_free_space_generation(leaf, header); - btrfs_release_path(path); - - if (!BTRFS_I(inode)->generation) { - btrfs_info(fs_info, - "the free space cache file (%llu) is invalid, skip it", - offset); - return 0; - } - - if (BTRFS_I(inode)->generation != generation) { - btrfs_err(fs_info, - "free space inode generation (%llu) did not match free space cache generation (%llu)", - BTRFS_I(inode)->generation, generation); - return 0; - } - - if (!num_entries) - return 0; - - ret = io_ctl_init(&io_ctl, inode, 0); - if (ret) - return ret; - - readahead_cache(inode); - - ret = io_ctl_prepare_pages(&io_ctl, true); - if (ret) - goto out; - - ret = io_ctl_check_crc(&io_ctl, 0); - if (ret) - goto free_cache; - - ret = io_ctl_check_generation(&io_ctl, generation); - if (ret) - goto free_cache; - - while (num_entries) { - e = kmem_cache_zalloc(btrfs_free_space_cachep, - GFP_NOFS); - if (!e) { - ret = -ENOMEM; - goto free_cache; - } - - ret = io_ctl_read_entry(&io_ctl, e, &type); - if (ret) { - kmem_cache_free(btrfs_free_space_cachep, e); - goto free_cache; - } - - if (!e->bytes) { - ret = -1; - kmem_cache_free(btrfs_free_space_cachep, e); - goto free_cache; - } - - if (type == BTRFS_FREE_SPACE_EXTENT) { - spin_lock(&ctl->tree_lock); - ret = link_free_space(ctl, e); - spin_unlock(&ctl->tree_lock); - if (ret) { - btrfs_err(fs_info, - "Duplicate entries in free space cache, dumping"); - kmem_cache_free(btrfs_free_space_cachep, e); - goto free_cache; - } - } else { - ASSERT(num_bitmaps); - num_bitmaps--; - e->bitmap = kmem_cache_zalloc( - btrfs_free_space_bitmap_cachep, GFP_NOFS); - if (!e->bitmap) { - ret = -ENOMEM; - kmem_cache_free( - btrfs_free_space_cachep, e); - goto free_cache; - } - spin_lock(&ctl->tree_lock); - ret = link_free_space(ctl, e); - if (ret) { - spin_unlock(&ctl->tree_lock); - btrfs_err(fs_info, - "Duplicate entries in free space cache, dumping"); - kmem_cache_free(btrfs_free_space_bitmap_cachep, e->bitmap); - kmem_cache_free(btrfs_free_space_cachep, e); - goto free_cache; - } - ctl->total_bitmaps++; - recalculate_thresholds(ctl); - spin_unlock(&ctl->tree_lock); - list_add_tail(&e->list, &bitmaps); - } - - num_entries--; - } - - io_ctl_unmap_page(&io_ctl); - - /* - * We add the bitmaps at the end of the entries in order that - * the bitmap entries are added to the cache. - */ - list_for_each_entry_safe(e, n, &bitmaps, list) { - list_del_init(&e->list); - ret = io_ctl_read_bitmap(&io_ctl, e); - if (ret) - goto free_cache; - } - - io_ctl_drop_pages(&io_ctl); - ret = 1; -out: - io_ctl_free(&io_ctl); - return ret; -free_cache: - io_ctl_drop_pages(&io_ctl); - - spin_lock(&ctl->tree_lock); - __btrfs_remove_free_space_cache(ctl); - spin_unlock(&ctl->tree_lock); - goto out; -} - -static int copy_free_space_cache(struct btrfs_free_space_ctl *ctl) -{ - struct btrfs_free_space *info; - struct rb_node *n; - int ret = 0; - - while (!ret && (n = rb_first(&ctl->free_space_offset)) != NULL) { - info = rb_entry(n, struct btrfs_free_space, offset_index); - if (!info->bitmap) { - const u64 offset = info->offset; - const u64 bytes = info->bytes; - - unlink_free_space(ctl, info, true); - spin_unlock(&ctl->tree_lock); - kmem_cache_free(btrfs_free_space_cachep, info); - ret = btrfs_add_free_space(ctl->block_group, offset, bytes); - spin_lock(&ctl->tree_lock); - } else { - u64 offset = info->offset; - u64 bytes = ctl->block_group->fs_info->sectorsize; - - ret = search_bitmap(ctl, info, &offset, &bytes, false); - if (ret == 0) { - bitmap_clear_bits(ctl, info, offset, bytes, true); - spin_unlock(&ctl->tree_lock); - ret = btrfs_add_free_space(ctl->block_group, offset, - bytes); - spin_lock(&ctl->tree_lock); - } else { - free_bitmap(ctl, info); - ret = 0; - } - } - cond_resched_lock(&ctl->tree_lock); - } - return ret; -} - -static struct lock_class_key btrfs_free_space_inode_key; - -int load_free_space_cache(struct btrfs_block_group *block_group) -{ - struct btrfs_fs_info *fs_info = block_group->fs_info; - struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; - struct btrfs_free_space_ctl tmp_ctl = {}; - struct inode *inode; - struct btrfs_path *path; - int ret = 0; - bool matched; - u64 used = block_group->used; - - /* - * Because we could potentially discard our loaded free space, we want - * to load everything into a temporary structure first, and then if it's - * valid copy it all into the actual free space ctl. - */ - btrfs_init_free_space_ctl(block_group, &tmp_ctl); - - /* - * If this block group has been marked to be cleared for one reason or - * another then we can't trust the on disk cache, so just return. - */ - spin_lock(&block_group->lock); - if (block_group->disk_cache_state != BTRFS_DC_WRITTEN) { - spin_unlock(&block_group->lock); - return 0; - } - spin_unlock(&block_group->lock); - - path = btrfs_alloc_path(); - if (!path) - return 0; - path->search_commit_root = true; - path->skip_locking = true; - - /* - * We must pass a path with search_commit_root set to btrfs_iget in - * order to avoid a deadlock when allocating extents for the tree root. - * - * When we are COWing an extent buffer from the tree root, when looking - * for a free extent, at extent-tree.c:find_free_extent(), we can find - * block group without its free space cache loaded. When we find one - * we must load its space cache which requires reading its free space - * cache's inode item from the root tree. If this inode item is located - * in the same leaf that we started COWing before, then we end up in - * deadlock on the extent buffer (trying to read lock it when we - * previously write locked it). - * - * It's safe to read the inode item using the commit root because - * block groups, once loaded, stay in memory forever (until they are - * removed) as well as their space caches once loaded. New block groups - * once created get their ->cached field set to BTRFS_CACHE_FINISHED so - * we will never try to read their inode item while the fs is mounted. - */ - inode = lookup_free_space_inode(block_group, path); - if (IS_ERR(inode)) { - btrfs_free_path(path); - return 0; - } - - /* We may have converted the inode and made the cache invalid. */ - spin_lock(&block_group->lock); - if (block_group->disk_cache_state != BTRFS_DC_WRITTEN) { - spin_unlock(&block_group->lock); - btrfs_free_path(path); - goto out; - } - spin_unlock(&block_group->lock); - - /* - * Reinitialize the class of struct inode's mapping->invalidate_lock for - * free space inodes to prevent false positives related to locks for normal - * inodes. - */ - lockdep_set_class(&(&inode->i_data)->invalidate_lock, - &btrfs_free_space_inode_key); - - ret = __load_free_space_cache(fs_info->tree_root, inode, &tmp_ctl, - path, block_group->start); - btrfs_free_path(path); - if (ret <= 0) - goto out; - - matched = (tmp_ctl.free_space == (block_group->length - used - - block_group->bytes_super)); - - if (matched) { - spin_lock(&tmp_ctl.tree_lock); - ret = copy_free_space_cache(&tmp_ctl); - spin_unlock(&tmp_ctl.tree_lock); - /* - * ret == 1 means we successfully loaded the free space cache, - * so we need to re-set it here. - */ - if (ret == 0) - ret = 1; - } else { - /* - * We need to call the _locked variant so we don't try to update - * the discard counters. - */ - spin_lock(&tmp_ctl.tree_lock); - __btrfs_remove_free_space_cache(&tmp_ctl); - spin_unlock(&tmp_ctl.tree_lock); - btrfs_warn(fs_info, - "block group %llu has wrong amount of free space", - block_group->start); - ret = -1; - } -out: - if (ret < 0) { - /* This cache is bogus, make sure it gets cleared */ - spin_lock(&block_group->lock); - block_group->disk_cache_state = BTRFS_DC_CLEAR; - spin_unlock(&block_group->lock); - ret = 0; - - btrfs_warn(fs_info, - "failed to load free space cache for block group %llu, rebuilding it now", - block_group->start); - } - - spin_lock(&ctl->tree_lock); - btrfs_discard_update_discardable(block_group); - spin_unlock(&ctl->tree_lock); - iput(inode); - return ret; -} - static inline unsigned long offset_to_bit(u64 bitmap_start, u32 unit, u64 offset) { diff --git a/fs/btrfs/free-space-cache.h b/fs/btrfs/free-space-cache.h index 2432f1783f47..29166cc09b90 100644 --- a/fs/btrfs/free-space-cache.h +++ b/fs/btrfs/free-space-cache.h @@ -14,7 +14,6 @@ #include "fs.h" struct inode; -struct page; struct btrfs_fs_info; struct btrfs_path; struct btrfs_trans_handle; @@ -88,19 +87,6 @@ struct btrfs_free_space_ctl { struct list_head trimming_ranges; }; -struct btrfs_io_ctl { - void *cur, *orig; - struct page *page; - struct page **pages; - struct btrfs_fs_info *fs_info; - struct inode *inode; - unsigned long size; - int index; - int num_pages; - int entries; - int bitmaps; -}; - int __init btrfs_free_space_init(void); void __cold btrfs_free_space_exit(void); struct inode *lookup_free_space_inode(struct btrfs_block_group *block_group, @@ -111,7 +97,6 @@ int btrfs_remove_free_space_inode(struct btrfs_trans_handle *trans, int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans, struct inode *inode); -int load_free_space_cache(struct btrfs_block_group *block_group); void btrfs_init_free_space_ctl(struct btrfs_block_group *block_group, struct btrfs_free_space_ctl *ctl); From d8a4bb425e6999de91a8d5d53dd3e696109f493e Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:23 -0400 Subject: [PATCH 11/20] btrfs: remove btrfs_disk_cache_state With neither the writer nor the loader left, nothing acts on disk_cache_state. Remove it, the need_clear handling when reading block groups, and the enum. While at it, drop the unused cache_generation field from struct btrfs_block_group. lookup_free_space_inode() converted old style space inodes by clearing disk_cache_state so the cache would be rewritten with the new inode flags. Without that it only sets flags on the in-memory inode, which every remaining caller truncates or deletes right after, so drop the conversion too. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 32 ++------------------------------ fs/btrfs/block-group.h | 10 ---------- fs/btrfs/disk-io.c | 4 ---- fs/btrfs/free-space-cache.c | 8 -------- 4 files changed, 2 insertions(+), 52 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index a11685440e3c..9a98e291769c 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -2496,8 +2496,7 @@ static int check_chunk_block_group_mappings(struct btrfs_fs_info *fs_info) static int read_one_block_group(struct btrfs_fs_info *info, struct btrfs_block_group_item_v2 *bgi, - const struct btrfs_key *key, - bool need_clear) + const struct btrfs_key *key) { struct btrfs_block_group *cache; const bool mixed = btrfs_fs_incompat(info, MIXED_GROUPS); @@ -2523,20 +2522,6 @@ static int read_one_block_group(struct btrfs_fs_info *info, btrfs_set_free_space_tree_thresholds(cache); - if (need_clear) { - /* - * When we mount with old space cache, we need to - * set BTRFS_DC_CLEAR and set dirty flag. - * - * a) Setting 'BTRFS_DC_CLEAR' makes sure that we - * truncate the old free space cache inode and - * setup a new one. - * b) Setting 'dirty flag' makes sure that we flush - * the new space cache info onto disk. - */ - if (btrfs_test_opt(info, SPACE_CACHE)) - cache->disk_cache_state = BTRFS_DC_CLEAR; - } if (!mixed && ((cache->flags & BTRFS_BLOCK_GROUP_METADATA) && (cache->flags & BTRFS_BLOCK_GROUP_DATA))) { btrfs_err(info, @@ -2677,8 +2662,6 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) struct btrfs_block_group *cache; struct btrfs_space_info *space_info; struct btrfs_key key; - bool need_clear = false; - u64 cache_gen; /* * Either no extent root (with ibadroots rescue option) or we have @@ -2699,13 +2682,6 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) if (!path) return -ENOMEM; - cache_gen = btrfs_super_cache_generation(info->super_copy); - if (btrfs_test_opt(info, SPACE_CACHE) && - btrfs_super_generation(info->super_copy) != cache_gen) - need_clear = true; - if (btrfs_test_opt(info, CLEAR_CACHE)) - need_clear = true; - while (1) { struct btrfs_block_group_item_v2 bgi; struct extent_buffer *leaf; @@ -2734,7 +2710,7 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) btrfs_item_key_to_cpu(leaf, &key, slot); btrfs_release_path(path); - ret = read_one_block_group(info, &bgi, &key, need_clear); + ret = read_one_block_group(info, &bgi, &key); if (ret < 0) goto error; key.objectid += key.offset; @@ -3597,10 +3573,6 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, spin_lock(&space_info->lock); spin_lock(&cache->lock); - if (btrfs_test_opt(info, SPACE_CACHE) && - cache->disk_cache_state < BTRFS_DC_CLEAR) - cache->disk_cache_state = BTRFS_DC_CLEAR; - old_val = cache->used; if (alloc) { old_val += num_bytes; diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h index f7346a0170fc..d567ed822e55 100644 --- a/fs/btrfs/block-group.h +++ b/fs/btrfs/block-group.h @@ -20,13 +20,6 @@ struct btrfs_fs_info; struct btrfs_inode; struct btrfs_trans_handle; -enum btrfs_disk_cache_state { - BTRFS_DC_WRITTEN, - BTRFS_DC_ERROR, - BTRFS_DC_CLEAR, - BTRFS_DC_SETUP, -}; - enum btrfs_block_group_size_class { /* Unset */ BTRFS_BG_SZ_NONE, @@ -131,7 +124,6 @@ struct btrfs_block_group { u64 delalloc_bytes; u64 bytes_super; u64 flags; - u64 cache_generation; u64 global_root_id; u64 remap_bytes; u32 identity_remap_count; @@ -171,8 +163,6 @@ struct btrfs_block_group { unsigned long full_stripe_len; unsigned long runtime_flags; - enum btrfs_disk_cache_state disk_cache_state; - /* Cache tracking stuff */ enum btrfs_caching_type cached; struct btrfs_caching_control *caching_ctl; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 4056ad1ab728..82b3b4579880 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -4863,10 +4863,6 @@ void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *cur_trans, dirty_list); list_del_init(&cache->dirty_list); - spin_lock(&cache->lock); - cache->disk_cache_state = BTRFS_DC_ERROR; - spin_unlock(&cache->lock); - spin_unlock(&cur_trans->dirty_bgs_lock); btrfs_put_block_group(cache); btrfs_dec_delayed_refs_rsv_bg_updates(fs_info); diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 1e2fc2884d07..e82216ebd5d0 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -124,7 +124,6 @@ struct inode *lookup_free_space_inode(struct btrfs_block_group *block_group, { struct btrfs_fs_info *fs_info = block_group->fs_info; struct inode *inode = NULL; - u32 flags = BTRFS_INODE_NODATASUM | BTRFS_INODE_NODATACOW; spin_lock(&block_group->lock); if (block_group->inode) @@ -139,13 +138,6 @@ struct inode *lookup_free_space_inode(struct btrfs_block_group *block_group, return inode; spin_lock(&block_group->lock); - if (!((BTRFS_I(inode)->flags & flags) == flags)) { - btrfs_info(fs_info, "Old style space inode found, converting."); - BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM | - BTRFS_INODE_NODATACOW; - block_group->disk_cache_state = BTRFS_DC_CLEAR; - } - if (!test_and_set_bit(BLOCK_GROUP_FLAG_IREF, &block_group->runtime_flags)) block_group->inode = BTRFS_I(igrab(inode)); spin_unlock(&block_group->lock); From 3d10f3cc05d7659fb5fae1c587bf8a9ec5e950df Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:24 -0400 Subject: [PATCH 12/20] btrfs: remove the SPACE_CACHE mount option flag Nothing sets BTRFS_MOUNT_SPACE_CACHE anymore, so every test of it is false. Remove the flag, the checks rejecting the v1 cache on zoned filesystems and for sector sizes other than the page size, and the deprecation warning. Show a read-only filesystem that still has an old cache as nospace_cache, since that's what's in effect. space_cache and space_cache=v1 keep falling back to no space cache with a warning. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 19 ++----------------- fs/btrfs/fs.h | 1 - fs/btrfs/super.c | 33 +++------------------------------ fs/btrfs/transaction.c | 4 +--- fs/btrfs/zoned.c | 9 --------- 5 files changed, 6 insertions(+), 60 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 82b3b4579880..23a17b829d98 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3057,7 +3057,6 @@ static int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info) int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) { int ret; - const bool cache_opt = btrfs_test_opt(fs_info, SPACE_CACHE); bool rebuild_free_space_tree = false; if (btrfs_test_opt(fs_info, CLEAR_CACHE) && @@ -3152,8 +3151,8 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) } } - if (cache_opt != btrfs_free_space_cache_v1_active(fs_info)) { - ret = btrfs_set_free_space_cache_v1_active(fs_info, cache_opt); + if (btrfs_free_space_cache_v1_active(fs_info)) { + ret = btrfs_set_free_space_cache_v1_active(fs_info, false); if (ret) return ret; } @@ -3269,20 +3268,6 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount) return -EINVAL; } - /* - * Subpage/bs > ps runtime limitation on v1 cache. - * - * V1 space cache still has some hard coded PAGE_SIZE usage, while - * we're already defaulting to v2 cache, no need to bother v1 as it's - * going to be deprecated anyway. - */ - if (fs_info->sectorsize != PAGE_SIZE && btrfs_test_opt(fs_info, SPACE_CACHE)) { - btrfs_warn(fs_info, - "v1 space cache is not supported for page size %lu with sectorsize %u", - PAGE_SIZE, fs_info->sectorsize); - return -EINVAL; - } - /* This can be called by remount, we need to protect the super block. */ spin_lock(&fs_info->super_lock); btrfs_set_super_incompat_flags(disk_super, incompat); diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index f1bb6c56c76c..2dd167c32d61 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -259,7 +259,6 @@ enum { BTRFS_MOUNT_NOSSD = (1ULL << 9), BTRFS_MOUNT_DISCARD_SYNC = (1ULL << 10), BTRFS_MOUNT_FORCE_COMPRESS = (1ULL << 11), - BTRFS_MOUNT_SPACE_CACHE = (1ULL << 12), BTRFS_MOUNT_CLEAR_CACHE = (1ULL << 13), BTRFS_MOUNT_USER_SUBVOL_RM_ALLOWED = (1ULL << 14), BTRFS_MOUNT_ENOSPC_DEBUG = (1ULL << 15), diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 2b99c6400f92..b424cc836bd4 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -524,7 +524,6 @@ static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param) btrfs_warn(NULL, "v1 space cache is deprecated, falling back to no space cache"); btrfs_set_opt(ctx->mount_opt, NOSPACECACHE); - btrfs_clear_opt(ctx->mount_opt, SPACE_CACHE); btrfs_clear_opt(ctx->mount_opt, FREE_SPACE_TREE); break; case Opt_space_cache_version: @@ -533,11 +532,9 @@ static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param) btrfs_warn(NULL, "v1 space cache is deprecated, falling back to no space cache"); btrfs_set_opt(ctx->mount_opt, NOSPACECACHE); - btrfs_clear_opt(ctx->mount_opt, SPACE_CACHE); btrfs_clear_opt(ctx->mount_opt, FREE_SPACE_TREE); break; case Opt_space_cache_v2: - btrfs_clear_opt(ctx->mount_opt, SPACE_CACHE); btrfs_set_opt(ctx->mount_opt, FREE_SPACE_TREE); break; default: @@ -723,13 +720,6 @@ bool btrfs_check_options(const struct btrfs_fs_info *info, if (btrfs_check_mountopts_zoned(info, mount_opt)) ret = false; - if (!test_bit(BTRFS_FS_STATE_REMOUNTING, &info->fs_state)) { - if (btrfs_raw_test_opt(*mount_opt, SPACE_CACHE)) { - btrfs_warn(info, -"space cache v1 is being deprecated and will be removed in a future release, please use -o space_cache=v2"); - } - } - return ret; } @@ -747,14 +737,6 @@ bool btrfs_check_options(const struct btrfs_fs_info *info, */ void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info) { - if (fs_info->sectorsize != PAGE_SIZE && btrfs_test_opt(fs_info, SPACE_CACHE)) { - btrfs_info(fs_info, - "forcing free space tree for sector size %u with page size %lu", - fs_info->sectorsize, PAGE_SIZE); - btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE); - btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); - } - /* * At this point our mount options are populated, so we only mess with * these settings if we don't have any settings already. @@ -769,9 +751,6 @@ void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info) return; } - if (btrfs_test_opt(fs_info, SPACE_CACHE)) - return; - if (btrfs_test_opt(fs_info, NOSPACECACHE)) return; @@ -1128,9 +1107,7 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) seq_puts(seq, ",discard=async"); if (!(info->sb->s_flags & SB_POSIXACL)) seq_puts(seq, ",noacl"); - if (btrfs_free_space_cache_v1_active(info)) - seq_puts(seq, ",space_cache"); - else if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) + if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) seq_puts(seq, ",space_cache=v2"); else seq_puts(seq, ",nospace_cache"); @@ -1464,7 +1441,6 @@ static void btrfs_emit_options(struct btrfs_fs_info *info, btrfs_info_if_set(info, old, DISCARD_SYNC, "turning on sync discard"); btrfs_info_if_set(info, old, DISCARD_ASYNC, "turning on async discard"); btrfs_info_if_set(info, old, FREE_SPACE_TREE, "enabling free space tree"); - btrfs_info_if_set(info, old, SPACE_CACHE, "enabling disk space caching"); btrfs_info_if_set(info, old, CLEAR_CACHE, "force clearing of disk cache"); btrfs_info_if_set(info, old, AUTO_DEFRAG, "enabling auto defrag"); btrfs_info_if_set(info, old, FRAGMENT_DATA, "fragmenting data"); @@ -1482,7 +1458,6 @@ static void btrfs_emit_options(struct btrfs_fs_info *info, btrfs_info_if_unset(info, old, SSD_SPREAD, "not using spread ssd allocation scheme"); btrfs_info_if_unset(info, old, NOBARRIER, "turning on barriers"); btrfs_info_if_unset(info, old, NOTREELOG, "enabling tree log"); - btrfs_info_if_unset(info, old, SPACE_CACHE, "disabling disk space caching"); btrfs_info_if_unset(info, old, FREE_SPACE_TREE, "disabling free space tree"); btrfs_info_if_unset(info, old, AUTO_DEFRAG, "disabling auto defrag"); btrfs_info_if_unset(info, old, COMPRESS, "use no compression"); @@ -1547,12 +1522,10 @@ static int btrfs_reconfigure(struct fs_context *fc) btrfs_warn(fs_info, "remount supports changing free space tree only from RO to RW"); /* Make sure free space cache options match the state on disk. */ - if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) { + if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); - btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE); - } else { + else btrfs_clear_opt(fs_info->mount_opt, FREE_SPACE_TREE); - } } ret = 0; diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 3d7bc667eb03..93bc829881d1 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -2030,9 +2030,7 @@ static void update_super_roots(struct btrfs_fs_info *fs_info) super->root = root_item->bytenr; super->generation = root_item->generation; super->root_level = root_item->level; - if (btrfs_test_opt(fs_info, SPACE_CACHE)) - super->cache_generation = root_item->generation; - else if (test_bit(BTRFS_FS_CLEANUP_SPACE_CACHE_V1, &fs_info->flags)) + if (test_bit(BTRFS_FS_CLEANUP_SPACE_CACHE_V1, &fs_info->flags)) super->cache_generation = 0; if (test_bit(BTRFS_FS_UPDATE_UUID_TREE_GEN, &fs_info->flags)) super->uuid_tree_generation = root_item->generation; diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 9f562cc34e38..1cda88332284 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -804,15 +804,6 @@ int btrfs_check_mountopts_zoned(const struct btrfs_fs_info *info, if (!btrfs_is_zoned(info)) return 0; - /* - * Space cache writing is not COWed. Disable that to avoid write errors - * in sequential zones. - */ - if (btrfs_raw_test_opt(*mount_opt, SPACE_CACHE)) { - btrfs_err(info, "zoned: space cache v1 is not supported"); - return -EINVAL; - } - if (btrfs_raw_test_opt(*mount_opt, NODATACOW)) { btrfs_err(info, "zoned: NODATACOW not supported"); return -EINVAL; From 9fb023c34f5e5bf1b3edf8670c0adda1790f82db Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:25 -0400 Subject: [PATCH 13/20] btrfs: replace btrfs_set_free_space_cache_v1_active() with a cleanup helper The only caller passes active = false. Turn it into btrfs_cleanup_free_space_cache_v1() and fold the block group loop into it. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 2 +- fs/btrfs/free-space-cache.c | 42 +++++++++++-------------------------- fs/btrfs/free-space-cache.h | 2 +- 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 23a17b829d98..c45cda4290d5 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3152,7 +3152,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) } if (btrfs_free_space_cache_v1_active(fs_info)) { - ret = btrfs_set_free_space_cache_v1_active(fs_info, false); + ret = btrfs_cleanup_free_space_cache_v1(fs_info); if (ret) return ret; } diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index e82216ebd5d0..4dcbefb0bfed 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2901,47 +2901,29 @@ bool btrfs_free_space_cache_v1_active(struct btrfs_fs_info *fs_info) return btrfs_super_cache_generation(fs_info->super_copy); } -static int cleanup_free_space_cache_v1(struct btrfs_fs_info *fs_info, - struct btrfs_trans_handle *trans) +int btrfs_cleanup_free_space_cache_v1(struct btrfs_fs_info *fs_info) { - struct btrfs_block_group *block_group; + struct btrfs_trans_handle *trans; struct rb_node *node; + int ret; btrfs_info(fs_info, "cleaning free space cache v1"); - node = rb_first_cached(&fs_info->block_group_cache_tree); - while (node) { - int ret; - - block_group = rb_entry(node, struct btrfs_block_group, cache_node); - ret = btrfs_remove_free_space_inode(trans, NULL, block_group); - if (ret) - return ret; - node = rb_next(node); - } - return 0; -} - -int btrfs_set_free_space_cache_v1_active(struct btrfs_fs_info *fs_info, bool active) -{ - struct btrfs_trans_handle *trans; - int ret; - /* - * update_super_roots will appropriately set or unset - * super_copy->cache_generation based on SPACE_CACHE and - * BTRFS_FS_CLEANUP_SPACE_CACHE_V1. For this reason, we need a - * transaction commit whether we are enabling space cache v1 and don't - * have any other work to do, or are disabling it and removing free - * space inodes. + * update_super_roots() zeroes super_copy->cache_generation while + * BTRFS_FS_CLEANUP_SPACE_CACHE_V1 is set, so this needs a commit. */ trans = btrfs_start_transaction(fs_info->tree_root, 0); if (IS_ERR(trans)) return PTR_ERR(trans); - if (!active) { - set_bit(BTRFS_FS_CLEANUP_SPACE_CACHE_V1, &fs_info->flags); - ret = cleanup_free_space_cache_v1(fs_info, trans); + set_bit(BTRFS_FS_CLEANUP_SPACE_CACHE_V1, &fs_info->flags); + for (node = rb_first_cached(&fs_info->block_group_cache_tree); node; + node = rb_next(node)) { + struct btrfs_block_group *block_group; + + block_group = rb_entry(node, struct btrfs_block_group, cache_node); + ret = btrfs_remove_free_space_inode(trans, NULL, block_group); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); btrfs_end_transaction(trans); diff --git a/fs/btrfs/free-space-cache.h b/fs/btrfs/free-space-cache.h index 29166cc09b90..f5f18e397b13 100644 --- a/fs/btrfs/free-space-cache.h +++ b/fs/btrfs/free-space-cache.h @@ -136,7 +136,7 @@ int btrfs_trim_block_group_bitmaps(struct btrfs_block_group *block_group, void btrfs_trim_fully_remapped_block_group(struct btrfs_block_group *bg); bool btrfs_free_space_cache_v1_active(struct btrfs_fs_info *fs_info); -int btrfs_set_free_space_cache_v1_active(struct btrfs_fs_info *fs_info, bool active); +int btrfs_cleanup_free_space_cache_v1(struct btrfs_fs_info *fs_info); /* Support functions for running our sanity tests */ #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS bool btrfs_use_bitmap(struct btrfs_free_space_ctl *ctl, From 28933a094ea7843fd5fd77c5d79190e6fd564f0b Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:26 -0400 Subject: [PATCH 14/20] btrfs: remove the free space cache trimming ranges cache_writeout_mutex and trimming_ranges let the v1 cache writer see ranges that were unlinked from the free space tree while being discarded. Nothing consumes the list anymore, and the tree itself is protected by tree_lock, so remove them. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/free-space-cache.c | 41 +++---------------------------------- fs/btrfs/free-space-cache.h | 2 -- 2 files changed, 3 insertions(+), 40 deletions(-) diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 4dcbefb0bfed..bff6e5b372d6 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -36,12 +36,6 @@ static struct kmem_cache *btrfs_free_space_cachep; static struct kmem_cache *btrfs_free_space_bitmap_cachep; -struct btrfs_trim_range { - u64 start; - u64 bytes; - struct list_head list; -}; - static int link_free_space(struct btrfs_free_space_ctl *ctl, struct btrfs_free_space *info); static void unlink_free_space(struct btrfs_free_space_ctl *ctl, @@ -1702,8 +1696,6 @@ void btrfs_init_free_space_ctl(struct btrfs_block_group *block_group, spin_lock_init(&ctl->tree_lock); ctl->block_group = block_group; ctl->free_space_bytes = RB_ROOT_CACHED; - INIT_LIST_HEAD(&ctl->trimming_ranges); - mutex_init(&ctl->cache_writeout_mutex); /* * we only want to have 32k of ram per block group for keeping @@ -2399,12 +2391,10 @@ void btrfs_init_free_cluster(struct btrfs_free_cluster *cluster) static int do_trimming(struct btrfs_block_group *block_group, u64 *total_trimmed, u64 start, u64 bytes, u64 reserved_start, u64 reserved_bytes, - enum btrfs_trim_state reserved_trim_state, - struct btrfs_trim_range *trim_entry) + enum btrfs_trim_state reserved_trim_state) { struct btrfs_space_info *space_info = block_group->space_info; struct btrfs_fs_info *fs_info = block_group->fs_info; - struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; int ret; bool bg_ro; const u64 end = start + bytes; @@ -2430,7 +2420,6 @@ static int do_trimming(struct btrfs_block_group *block_group, trim_state = BTRFS_TRIM_STATE_TRIMMED; } - mutex_lock(&ctl->cache_writeout_mutex); if (reserved_start < start) __btrfs_add_free_space(block_group, reserved_start, start - reserved_start, @@ -2439,8 +2428,6 @@ static int do_trimming(struct btrfs_block_group *block_group, __btrfs_add_free_space(block_group, end, reserved_end - end, reserved_trim_state); __btrfs_add_free_space(block_group, start, bytes, trim_state); - list_del(&trim_entry->list); - mutex_unlock(&ctl->cache_writeout_mutex); if (!bg_ro) { spin_lock(&space_info->lock); @@ -2478,9 +2465,6 @@ static int trim_no_bitmap(struct btrfs_block_group *block_group, const u64 max_discard_size = READ_ONCE(discard_ctl->max_discard_size); while (start < end) { - struct btrfs_trim_range trim_entry; - - mutex_lock(&ctl->cache_writeout_mutex); spin_lock(&ctl->tree_lock); if (ctl->free_space < minlen) @@ -2511,7 +2495,6 @@ static int trim_no_bitmap(struct btrfs_block_group *block_group, bytes = entry->bytes; if (bytes < minlen) { spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); goto next; } unlink_free_space(ctl, entry, true); @@ -2536,7 +2519,6 @@ static int trim_no_bitmap(struct btrfs_block_group *block_group, bytes = min(extent_start + extent_bytes, end) - start; if (bytes < minlen) { spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); goto next; } @@ -2545,14 +2527,9 @@ static int trim_no_bitmap(struct btrfs_block_group *block_group, } spin_unlock(&ctl->tree_lock); - trim_entry.start = extent_start; - trim_entry.bytes = extent_bytes; - list_add_tail(&trim_entry.list, &ctl->trimming_ranges); - mutex_unlock(&ctl->cache_writeout_mutex); ret = do_trimming(block_group, total_trimmed, start, bytes, - extent_start, extent_bytes, extent_trim_state, - &trim_entry); + extent_start, extent_bytes, extent_trim_state); if (ret) { block_group->discard_cursor = start + bytes; break; @@ -2576,7 +2553,6 @@ static int trim_no_bitmap(struct btrfs_block_group *block_group, out_unlock: block_group->discard_cursor = btrfs_block_group_end(block_group); spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); return ret; } @@ -2687,16 +2663,13 @@ static int trim_bitmaps(struct btrfs_block_group *block_group, while (offset < end) { bool next_bitmap = false; - struct btrfs_trim_range trim_entry; - mutex_lock(&ctl->cache_writeout_mutex); spin_lock(&ctl->tree_lock); if (ctl->free_space < minlen) { block_group->discard_cursor = btrfs_block_group_end(block_group); spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); break; } @@ -2712,7 +2685,6 @@ static int trim_bitmaps(struct btrfs_block_group *block_group, if (!entry || (async && minlen && start == offset && btrfs_free_space_trimmed(entry))) { spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); next_bitmap = true; goto next; } @@ -2738,7 +2710,6 @@ static int trim_bitmaps(struct btrfs_block_group *block_group, else entry->trim_state = BTRFS_TRIM_STATE_UNTRIMMED; spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); next_bitmap = true; goto next; } @@ -2749,14 +2720,12 @@ static int trim_bitmaps(struct btrfs_block_group *block_group, */ if (async && *total_trimmed) { spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); return ret; } bytes = min(bytes, end - start); if (bytes < minlen || (async && maxlen && bytes > maxlen)) { spin_unlock(&ctl->tree_lock); - mutex_unlock(&ctl->cache_writeout_mutex); goto next; } @@ -2776,13 +2745,9 @@ static int trim_bitmaps(struct btrfs_block_group *block_group, free_bitmap(ctl, entry); spin_unlock(&ctl->tree_lock); - trim_entry.start = start; - trim_entry.bytes = bytes; - list_add_tail(&trim_entry.list, &ctl->trimming_ranges); - mutex_unlock(&ctl->cache_writeout_mutex); ret = do_trimming(block_group, total_trimmed, start, bytes, - start, bytes, 0, &trim_entry); + start, bytes, 0); if (ret) { reset_trimming_bitmap(ctl, offset); block_group->discard_cursor = diff --git a/fs/btrfs/free-space-cache.h b/fs/btrfs/free-space-cache.h index f5f18e397b13..e22443598b8e 100644 --- a/fs/btrfs/free-space-cache.h +++ b/fs/btrfs/free-space-cache.h @@ -83,8 +83,6 @@ struct btrfs_free_space_ctl { s32 discardable_extents[BTRFS_STAT_NR_ENTRIES]; s64 discardable_bytes[BTRFS_STAT_NR_ENTRIES]; struct btrfs_block_group *block_group; - struct mutex cache_writeout_mutex; - struct list_head trimming_ranges; }; int __init btrfs_free_space_init(void); From 27b43d2e8f71dd30c784c543f7b17285847c279f Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:27 -0400 Subject: [PATCH 15/20] btrfs: remove BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE Free space inodes no longer reserve data or delalloc space, as nothing writes to them. Remove the flush mode and the special cases that selected it. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/delalloc-space.c | 13 ++----------- fs/btrfs/space-info.c | 2 -- fs/btrfs/space-info.h | 4 ---- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/fs/btrfs/delalloc-space.c b/fs/btrfs/delalloc-space.c index d357ed7efd99..77781852e417 100644 --- a/fs/btrfs/delalloc-space.c +++ b/fs/btrfs/delalloc-space.c @@ -132,9 +132,7 @@ int btrfs_alloc_data_chunk_ondemand(const struct btrfs_inode *inode, u64 bytes) /* Make sure bytes are sectorsize aligned */ bytes = ALIGN(bytes, fs_info->sectorsize); - if (btrfs_is_free_space_inode(inode)) - flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE; - else if (btrfs_is_zoned(fs_info) && btrfs_is_data_reloc_root(root)) + if (btrfs_is_zoned(fs_info) && btrfs_is_data_reloc_root(root)) flush = BTRFS_RESERVE_FLUSH_ZONED_RELOCATION; return btrfs_reserve_data_bytes(data_sinfo_for_inode(inode), bytes, flush); @@ -155,8 +153,6 @@ int btrfs_check_data_free_space(struct btrfs_inode *inode, if (noflush) flush = BTRFS_RESERVE_NO_FLUSH; - else if (btrfs_is_free_space_inode(inode)) - flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE; ret = btrfs_reserve_data_bytes(data_sinfo_for_inode(inode), len, flush); if (ret < 0) @@ -326,15 +322,10 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes, int ret = 0; /* - * If we are a free space inode we need to not flush since we will be in - * the middle of a transaction commit. We also don't need the delalloc - * mutex since we won't race with anybody. We need this mostly to make - * lockdep shut its filthy mouth. - * * If we have a transaction open (can happen if we call truncate_block * from truncate), then we need FLUSH_LIMIT so we don't deadlock. */ - if (noflush || btrfs_is_free_space_inode(inode)) { + if (noflush) { flush = BTRFS_RESERVE_NO_FLUSH; } else { if (current->journal_info) diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index 72fa01502461..82d4ac418a2d 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -1704,7 +1704,6 @@ static int handle_reserve_ticket(struct btrfs_space_info *space_info, evict_flush_states, ARRAY_SIZE(evict_flush_states)); break; - case BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE: case BTRFS_RESERVE_FLUSH_ZONED_RELOCATION: priority_reclaim_data_space(space_info, ticket); break; @@ -1968,7 +1967,6 @@ int btrfs_reserve_data_bytes(struct btrfs_space_info *space_info, u64 bytes, int ret; ASSERT(flush == BTRFS_RESERVE_FLUSH_DATA || - flush == BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE || flush == BTRFS_RESERVE_FLUSH_ZONED_RELOCATION || flush == BTRFS_RESERVE_NO_FLUSH, "flush=%d", flush); ASSERT(!current->journal_info || flush != BTRFS_RESERVE_FLUSH_DATA, diff --git a/fs/btrfs/space-info.h b/fs/btrfs/space-info.h index aa836e8a9d4a..d0130c8ba3dd 100644 --- a/fs/btrfs/space-info.h +++ b/fs/btrfs/space-info.h @@ -66,7 +66,6 @@ enum btrfs_reserve_flush_enum { * Can be interrupted by a fatal signal. */ BTRFS_RESERVE_FLUSH_DATA, - BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE, BTRFS_RESERVE_FLUSH_ALL, /* @@ -82,9 +81,6 @@ enum btrfs_reserve_flush_enum { * priority flushing for this, because otherwise we can deadlock on * waiting for a ticket, that cannot be granted, because we cannot do * any allocations. - * - * Apart from being specific to zoned relocation, it is equal to - * BTRFS_FLUSH_FREE_SPACE_INODE. */ BTRFS_RESERVE_FLUSH_ZONED_RELOCATION, From d6c5cf6c5834de8835500b0bbfb65b293427b127 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:28 -0400 Subject: [PATCH 16/20] btrfs: remove the free space inode ordered extent special cases Free space inodes never have ordered extents anymore. Drop the lockdep exceptions for them and btrfs_join_transaction_spacecache(), which was only used to finish their ordered extents during a commit. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/inode.c | 20 +++----------------- fs/btrfs/ordered-data.c | 20 ++------------------ fs/btrfs/transaction.c | 6 ------ fs/btrfs/transaction.h | 1 - 4 files changed, 5 insertions(+), 42 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 95ec6026f08b..0ca4952600c7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3221,7 +3221,6 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) int compress_type = 0; int ret = 0; u64 logical_len = ordered_extent->num_bytes; - bool freespace_inode; bool truncated = false; bool clear_reserved_extent = true; unsigned int clear_bits = 0; @@ -3238,9 +3237,7 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) if (!test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags)) clear_bits |= EXTENT_DEFRAG; - freespace_inode = btrfs_is_free_space_inode(inode); - if (!freespace_inode) - btrfs_lockdep_acquire(fs_info, btrfs_ordered_extent); + btrfs_lockdep_acquire(fs_info, btrfs_ordered_extent); if (unlikely(test_bit(BTRFS_ORDERED_IOERR, &ordered_extent->flags))) { ret = -EIO; @@ -3275,10 +3272,7 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) &cached_state); } - if (freespace_inode) - trans = btrfs_join_transaction_spacecache(root); - else - trans = btrfs_join_transaction(root); + trans = btrfs_join_transaction(root); if (IS_ERR(trans)) { ret = PTR_ERR(trans); trans = NULL; @@ -8137,7 +8131,6 @@ void btrfs_destroy_inode(struct inode *vfs_inode) struct btrfs_ordered_extent *ordered; struct btrfs_inode *inode = BTRFS_I(vfs_inode); struct btrfs_root *root = inode->root; - bool freespace_inode; WARN_ON(!hlist_empty(&vfs_inode->i_dentry)); WARN_ON(vfs_inode->i_data.nrpages); @@ -8160,12 +8153,6 @@ void btrfs_destroy_inode(struct inode *vfs_inode) if (!root) return; - /* - * If this is a free space inode do not take the ordered extents lockdep - * map. - */ - freespace_inode = btrfs_is_free_space_inode(inode); - while (1) { ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1); if (!ordered) @@ -8175,8 +8162,7 @@ void btrfs_destroy_inode(struct inode *vfs_inode) "found ordered extent %llu %llu on inode cleanup", ordered->file_offset, ordered->num_bytes); - if (!freespace_inode) - btrfs_lockdep_acquire(root->fs_info, btrfs_ordered_extent); + btrfs_lockdep_acquire(root->fs_info, btrfs_ordered_extent); btrfs_remove_ordered_extent(ordered); btrfs_put_ordered_extent(ordered); diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index e9f1cbeb555a..df74c75d6c29 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -654,13 +654,6 @@ void btrfs_remove_ordered_extent(struct btrfs_ordered_extent *entry) struct btrfs_fs_info *fs_info = root->fs_info; struct rb_node *node; bool pending; - bool freespace_inode; - - /* - * If this is a free space inode the thread has not acquired the ordered - * extents lockdep map. - */ - freespace_inode = btrfs_is_free_space_inode(btrfs_inode); btrfs_lockdep_acquire(fs_info, btrfs_trans_pending_ordered); /* This is paired with alloc_ordered_extent(). */ @@ -735,8 +728,7 @@ void btrfs_remove_ordered_extent(struct btrfs_ordered_extent *entry) } spin_unlock(&root->ordered_extent_lock); wake_up(&entry->wait); - if (!freespace_inode) - btrfs_lockdep_release(fs_info, btrfs_ordered_extent); + btrfs_lockdep_release(fs_info, btrfs_ordered_extent); } static void btrfs_run_ordered_extent_work(struct btrfs_work *work) @@ -867,16 +859,9 @@ void btrfs_start_ordered_extent_nowriteback(struct btrfs_ordered_extent *entry, u64 start = entry->file_offset; u64 end = start + entry->num_bytes - 1; struct btrfs_inode *inode = entry->inode; - bool freespace_inode; trace_btrfs_ordered_extent_start(inode, entry); - /* - * If this is a free space inode do not take the ordered extents lockdep - * map. - */ - freespace_inode = btrfs_is_free_space_inode(inode); - /* * pages in the range can be dirty, clean or writeback. We * start IO on any dirty ones so the wait doesn't stall waiting @@ -896,8 +881,7 @@ void btrfs_start_ordered_extent_nowriteback(struct btrfs_ordered_extent *entry, } } - if (!freespace_inode) - btrfs_might_wait_for_event(inode->root->fs_info, btrfs_ordered_extent); + btrfs_might_wait_for_event(inode->root->fs_info, btrfs_ordered_extent); wait_event(entry->wait, test_bit(BTRFS_ORDERED_COMPLETE, &entry->flags)); } diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 93bc829881d1..7431a06f168d 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -895,12 +895,6 @@ struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root) true); } -struct btrfs_trans_handle *btrfs_join_transaction_spacecache(struct btrfs_root *root) -{ - return start_transaction(root, 0, TRANS_JOIN_NOLOCK, - BTRFS_RESERVE_NO_FLUSH, true); -} - /* * Similar to regular join but it never starts a transaction when none is * running or when there's a running one at a state >= TRANS_STATE_UNBLOCKED. diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index aa57c64a955d..684b46e5f2e9 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -287,7 +287,6 @@ struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv( struct btrfs_root *root, unsigned int num_items); struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root); -struct btrfs_trans_handle *btrfs_join_transaction_spacecache(struct btrfs_root *root); struct btrfs_trans_handle *btrfs_join_transaction_nostart(struct btrfs_root *root); struct btrfs_trans_handle *btrfs_attach_transaction(struct btrfs_root *root); struct btrfs_trans_handle *btrfs_attach_transaction_barrier( From 557a72b63a018d3375b7d724897a65dd3a94367c Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:29 -0400 Subject: [PATCH 17/20] btrfs: remove the free space inode special cases from the COW paths Free space inodes are never written anymore, so drop the special cases for them in cow_file_range(), fallback_to_cow(), can_nocow_file_extent() and btrfs_finish_one_ordered(). Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/inode.c | 54 +++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 40 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 0ca4952600c7..3fa632a6c920 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1369,11 +1369,6 @@ static noinline int cow_file_range(struct btrfs_inode *inode, goto out_unlock; } - if (btrfs_is_free_space_inode(inode)) { - ret = -EINVAL; - goto out_unlock; - } - num_bytes = ALIGN(end - start + 1, blocksize); num_bytes = max(blocksize, num_bytes); ASSERT(num_bytes <= btrfs_super_total_bytes(fs_info->super_copy)); @@ -1683,7 +1678,6 @@ static int fallback_to_cow(struct btrfs_inode *inode, struct folio *locked_folio, const u64 start, const u64 end) { - const bool is_space_ino = btrfs_is_free_space_inode(inode); const bool is_reloc_ino = btrfs_is_data_reloc_root(inode->root); const u64 range_bytes = end + 1 - start; struct extent_io_tree *io_tree = &inode->io_tree; @@ -1716,23 +1710,22 @@ static int fallback_to_cow(struct btrfs_inode *inode, * extent_clear_unlock_delalloc()) the bytes_may_use counter of the * data space info, which we incremented in the step above. * - * If we need to fallback to cow and the inode corresponds to a free - * space cache inode or an inode of the data relocation tree, we must - * also increment bytes_may_use of the data space_info for the same - * reason. Space caches and relocated data extents always get a prealloc - * extent for them, however scrub or balance may have set the block - * group that contains that extent to RO mode and therefore force COW - * when starting writeback. + * If we need to fallback to cow and the inode is in the data relocation + * tree, we must also increment bytes_may_use of the data space_info for + * the same reason. Relocated data extents always get a prealloc extent, + * however scrub or balance may have set the block group that contains + * that extent to RO mode and therefore force COW when starting + * writeback. */ btrfs_lock_extent(io_tree, start, end, &cached_state); count = btrfs_count_range_bits(io_tree, &range_start, end, range_bytes, EXTENT_NORESERVE, false, NULL); - if (count > 0 || is_space_ino || is_reloc_ino) { + if (count > 0 || is_reloc_ino) { u64 bytes = count; struct btrfs_fs_info *fs_info = inode->root->fs_info; struct btrfs_space_info *sinfo = fs_info->data_sinfo; - if (is_space_ino || is_reloc_ino) + if (is_reloc_ino) bytes = range_bytes; spin_lock(&sinfo->lock); @@ -1797,7 +1790,6 @@ static int can_nocow_file_extent(struct btrfs_path *path, struct btrfs_inode *inode, struct can_nocow_file_extent_args *args) { - const bool is_freespace_inode = btrfs_is_free_space_inode(inode); struct extent_buffer *leaf = path->nodes[0]; struct btrfs_root *root = inode->root; struct btrfs_file_extent_item *fi; @@ -1810,8 +1802,7 @@ static int can_nocow_file_extent(struct btrfs_path *path, bool nowait = path->nowait; /* If there are pending snapshots for this root, we must do COW. */ - if (args->writeback_path && !is_freespace_inode && - atomic_read(&root->snapshot_force_cow)) + if (args->writeback_path && atomic_read(&root->snapshot_force_cow)) goto out; fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); @@ -1860,7 +1851,6 @@ static int can_nocow_file_extent(struct btrfs_path *path, ret = btrfs_cross_ref_exist(inode, key->offset - args->file_extent.offset, args->file_extent.disk_bytenr, path); - WARN_ON_ONCE(ret > 0 && is_freespace_inode); if (ret != 0) goto out; @@ -1895,7 +1885,6 @@ static int can_nocow_file_extent(struct btrfs_path *path, ret = btrfs_lookup_csums_list(csum_root, io_start, io_start + args->file_extent.num_bytes - 1, NULL, nowait); - WARN_ON_ONCE(ret > 0 && is_freespace_inode); if (ret != 0) goto out; @@ -3221,6 +3210,7 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) int compress_type = 0; int ret = 0; u64 logical_len = ordered_extent->num_bytes; + u64 unwritten_start; bool truncated = false; bool clear_reserved_extent = true; unsigned int clear_bits = 0; @@ -3386,27 +3376,11 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) * Drop extent maps for the part of the extent we didn't write. * On error the written part is freed below as well, so drop * its maps too. - * - * We have an exception here for the free_space_inode, this is - * because when we do btrfs_get_extent() on the free space inode - * we will search the commit root. If this is a new block group - * we won't find anything, and we will trip over the assert in - * writepage where we do ASSERT(em->block_start != - * EXTENT_MAP_HOLE). - * - * Theoretically we could also skip this for any NOCOW extent as - * we don't mess with the extent map tree in the NOCOW case, but - * for now simply skip this if we are the free space inode. */ - if (!btrfs_is_free_space_inode(inode)) { - u64 unwritten_start = start; - - if (truncated && !ret) - unwritten_start += logical_len; - - btrfs_drop_extent_map_range(inode, unwritten_start, - end, false); - } + unwritten_start = start; + if (truncated && !ret) + unwritten_start += logical_len; + btrfs_drop_extent_map_range(inode, unwritten_start, end, false); /* * If the ordered extent had an IOERR or something else went From 86ba02e70d841322c339adfdae328edbbba84545 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:30 -0400 Subject: [PATCH 18/20] btrfs: stop special-casing free space inodes in the delalloc accounting Free space inodes never have delalloc or outstanding extents any more, so they don't need to be kept off the root's delalloc inode list, out of the outstanding extents tracepoint, or exempt from the delalloc metadata release, which skipped the tree root since free space inodes are its only inodes with data. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 2 -- fs/btrfs/inode.c | 13 +++---------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index d1c014da52a3..f7aa0d145185 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -409,8 +409,6 @@ static inline void btrfs_mod_outstanding_extents(struct btrfs_inode *inode, { lockdep_assert_held(&inode->lock); inode->outstanding_extents += mod; - if (btrfs_is_free_space_inode(inode)) - return; trace_btrfs_inode_mod_outstanding_extents(inode->root, btrfs_ino(inode), mod, inode->outstanding_extents); } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 3fa632a6c920..fd8962000096 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2632,7 +2632,7 @@ void btrfs_set_delalloc_extent(struct btrfs_inode *inode, struct extent_state *s * and are therefore protected against concurrent calls of this * function and btrfs_clear_delalloc_extent(). */ - if (!btrfs_is_free_space_inode(inode) && prev_delalloc_bytes == 0) + if (prev_delalloc_bytes == 0) btrfs_add_delalloc_inode(inode); } @@ -2676,13 +2676,7 @@ void btrfs_clear_delalloc_extent(struct btrfs_inode *inode, btrfs_mod_outstanding_extents(inode, -num_extents); spin_unlock(&inode->lock); - /* - * We don't reserve metadata space for space cache inodes so we - * don't need to call delalloc_release_metadata if there is an - * error. - */ - if (bits & EXTENT_CLEAR_META_RESV && - root != fs_info->tree_root) + if (bits & EXTENT_CLEAR_META_RESV) btrfs_delalloc_release_metadata(inode, len, true); /* For sanity tests. */ @@ -2690,7 +2684,6 @@ void btrfs_clear_delalloc_extent(struct btrfs_inode *inode, return; if (!btrfs_is_data_reloc_root(root) && - !btrfs_is_free_space_inode(inode) && !(state->state & EXTENT_NORESERVE) && (bits & EXTENT_CLEAR_DATA_RESV)) btrfs_free_reserved_data_space_noquota(inode, len); @@ -2708,7 +2701,7 @@ void btrfs_clear_delalloc_extent(struct btrfs_inode *inode, * and are therefore protected against concurrent calls of this * function and btrfs_set_delalloc_extent(). */ - if (!btrfs_is_free_space_inode(inode) && new_delalloc_bytes == 0) { + if (new_delalloc_bytes == 0) { spin_lock(&root->delalloc_lock); btrfs_del_delalloc_inode(inode); spin_unlock(&root->delalloc_lock); From a09f52a06dc45eba3c739700a9540fadb4d1efff Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:31 -0400 Subject: [PATCH 19/20] btrfs: stop reading free space inodes from the commit root Free space inode data was only read when loading the v1 cache, which is gone, so btrfs_get_extent() and btrfs_lookup_bio_sums() no longer need to search the commit root for them. Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/file-item.c | 11 ----------- fs/btrfs/inode.c | 10 ---------- 2 files changed, 21 deletions(-) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index ae1fd4da38d3..ff8f8cad00fc 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -396,17 +396,6 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) if (nblocks > fs_info->csums_per_leaf) path->reada = READA_FORWARD; - /* - * the free space stuff is only read when it hasn't been - * updated in the current transaction. So, we can safely - * read from the commit root and sidestep a nasty deadlock - * between reading the free space cache and updating the csum tree. - */ - if (btrfs_is_free_space_inode(inode)) { - path->search_commit_root = true; - path->skip_locking = true; - } - /* * If we are searching for a csum of an extent from a past * transaction, we can search in the commit root and reduce diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index fd8962000096..b071ed59a583 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7232,16 +7232,6 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, /* Chances are we'll be called again, so go ahead and do readahead */ path->reada = READA_FORWARD; - /* - * The same explanation in load_free_space_cache applies here as well, - * we only read when we're loading the free space cache, and at that - * point the commit_root has everything we need. - */ - if (btrfs_is_free_space_inode(inode)) { - path->search_commit_root = true; - path->skip_locking = true; - } - ret = btrfs_lookup_file_extent(NULL, root, path, objectid, start, 0); if (ret < 0) { goto out; From 293aae0123e1c178c7f76fd7a93bcced41e6663c Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Thu, 15 Oct 2026 10:43:32 -0400 Subject: [PATCH 20/20] btrfs: remove TRANS_JOIN_NOLOCK btrfs_join_transaction_spacecache() was the only user of TRANS_JOIN_NOLOCK and is gone, so remove the join type, its entries in the blocked types table, and the special cases in join_transaction() and start_transaction(). Assisted-by: LLM Signed-off-by: Tal Zussman Signed-off-by: David Sterba --- fs/btrfs/transaction.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 7431a06f168d..d265fac023e5 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -42,7 +42,6 @@ enum { ENUM_BIT(TRANS_START), ENUM_BIT(TRANS_ATTACH), ENUM_BIT(TRANS_JOIN), - ENUM_BIT(TRANS_JOIN_NOLOCK), #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS ENUM_BIT(TRANS_DUMMY), #endif @@ -141,17 +140,14 @@ static const unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX] = { [TRANS_STATE_UNBLOCKED] = (TRANS_START | TRANS_ATTACH | TRANS_JOIN | - TRANS_JOIN_NOLOCK | TRANS_JOIN_NOSTART), [TRANS_STATE_SUPER_COMMITTED] = (TRANS_START | TRANS_ATTACH | TRANS_JOIN | - TRANS_JOIN_NOLOCK | TRANS_JOIN_NOSTART), [TRANS_STATE_COMPLETED] = (TRANS_START | TRANS_ATTACH | TRANS_JOIN | - TRANS_JOIN_NOLOCK | TRANS_JOIN_NOSTART), }; @@ -342,12 +338,6 @@ static noinline int join_transaction(struct btrfs_fs_info *fs_info, if (type == TRANS_ATTACH || type == TRANS_JOIN_NOSTART) return -ENOENT; - /* - * JOIN_NOLOCK only happens during the transaction commit, so - * it is impossible that ->running_transaction is NULL - */ - BUG_ON(type == TRANS_JOIN_NOLOCK); - cur_trans = kmalloc_obj(*cur_trans, GFP_NOFS); if (!cur_trans) return -ENOMEM; @@ -750,14 +740,8 @@ start_transaction(struct btrfs_root *root, unsigned int num_items, /* * Only TRANS_START and TRANS_JOIN require sb intwrite lock. * - * If we are JOIN_NOLOCK we're already committing a transaction and - * waiting on this guy, so we don't need to do the sb_start_intwrite - * because we're already holding a ref. We need this because we could - * have raced in and did an fsync() on a file which can kick a commit - * and then we deadlock with somebody doing a freeze. - * * If we are ATTACH, it means we just want to catch the current - * transaction and commit it, so we needn't do sb_start_intwrite(). + * transaction and commit it, so we needn't do sb_start_intwrite(). */ if (type & TRANS_SB_INTWRITER_MASK) sb_start_intwrite(fs_info->sb);