btrfs: move the root freeing stuff into btrfs_put_root
authorJosef Bacik <josef@toxicpanda.com>
Fri, 14 Feb 2020 21:11:42 +0000 (16:11 -0500)
committerDavid Sterba <dsterba@suse.com>
Mon, 23 Mar 2020 16:01:59 +0000 (17:01 +0100)
There are a few different ways to free roots, either you allocated them
yourself and you just do

free_extent_buffer(root->node);
free_extent_buffer(root->commit_node);
btrfs_put_root(root);

Which is the pattern for log roots.  Or for snapshots/subvolumes that
are being dropped you simply call btrfs_free_fs_root() which does all
the cleanup for you.

Unify this all into btrfs_put_root(), so that we don't free up things
associated with the root until the last reference is dropped.  This
makes the root freeing code much more significant.

The only caveat is at close_ctree() time we have to free the extent
buffers for all of our main roots (extent_root, chunk_root, etc) because
we have to drop the btree_inode and we'll run into issues if we hold
onto those nodes until ->kill_sb() time.  This will be addressed in the
future when we kill the btree_inode.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/free-space-tree.c
fs/btrfs/qgroup.c
fs/btrfs/relocation.c
fs/btrfs/tests/btrfs-tests.c
fs/btrfs/tree-log.c

index 37418d1d082067a4f4ec7c980fe2daf1e0765ec8..242c072d69a74e5934e5086519da002c15864001 100644 (file)
@@ -1255,11 +1255,8 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
        return root;
 
 fail:
-       if (leaf) {
+       if (leaf)
                btrfs_tree_unlock(leaf);
-               free_extent_buffer(root->commit_root);
-               free_extent_buffer(leaf);
-       }
        btrfs_put_root(root);
 
        return ERR_PTR(ret);
@@ -1382,10 +1379,10 @@ struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
                                     generation, level, NULL);
        if (IS_ERR(root->node)) {
                ret = PTR_ERR(root->node);
+               root->node = NULL;
                goto find_fail;
        } else if (!btrfs_buffer_uptodate(root->node, generation, 0)) {
                ret = -EIO;
-               free_extent_buffer(root->node);
                goto find_fail;
        }
        root->commit_root = btrfs_root_node(root);
@@ -1617,14 +1614,14 @@ again:
        if (ret) {
                btrfs_put_root(root);
                if (ret == -EEXIST) {
-                       btrfs_free_fs_root(root);
+                       btrfs_put_root(root);
                        goto again;
                }
                goto fail;
        }
        return root;
 fail:
-       btrfs_free_fs_root(root);
+       btrfs_put_root(root);
        return ERR_PTR(ret);
 }
 
@@ -1996,11 +1993,35 @@ static void free_root_pointers(struct btrfs_fs_info *info, bool free_chunk_root)
        free_root_extent_buffers(info->csum_root);
        free_root_extent_buffers(info->quota_root);
        free_root_extent_buffers(info->uuid_root);
+       free_root_extent_buffers(info->fs_root);
        if (free_chunk_root)
                free_root_extent_buffers(info->chunk_root);
        free_root_extent_buffers(info->free_space_root);
 }
 
+void btrfs_put_root(struct btrfs_root *root)
+{
+       if (!root)
+               return;
+
+       if (refcount_dec_and_test(&root->refs)) {
+               WARN_ON(!RB_EMPTY_ROOT(&root->inode_tree));
+               if (root->anon_dev)
+                       free_anon_bdev(root->anon_dev);
+               btrfs_drew_lock_destroy(&root->snapshot_lock);
+               free_extent_buffer(root->node);
+               free_extent_buffer(root->commit_root);
+               kfree(root->free_ino_ctl);
+               kfree(root->free_ino_pinned);
+#ifdef CONFIG_BTRFS_DEBUG
+               spin_lock(&root->fs_info->fs_roots_radix_lock);
+               list_del_init(&root->leak_list);
+               spin_unlock(&root->fs_info->fs_roots_radix_lock);
+#endif
+               kfree(root);
+       }
+}
+
 void btrfs_free_fs_roots(struct btrfs_fs_info *fs_info)
 {
        int ret;
@@ -2012,13 +2033,10 @@ void btrfs_free_fs_roots(struct btrfs_fs_info *fs_info)
                                     struct btrfs_root, root_list);
                list_del(&gang[0]->root_list);
 
-               if (test_bit(BTRFS_ROOT_IN_RADIX, &gang[0]->state)) {
+               if (test_bit(BTRFS_ROOT_IN_RADIX, &gang[0]->state))
                        btrfs_drop_and_free_fs_root(fs_info, gang[0]);
-               } else {
-                       free_extent_buffer(gang[0]->node);
-                       free_extent_buffer(gang[0]->commit_root);
+               else
                        btrfs_put_root(gang[0]);
-               }
        }
 
        while (1) {
@@ -2223,11 +2241,11 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info,
        if (IS_ERR(log_tree_root->node)) {
                btrfs_warn(fs_info, "failed to read log tree");
                ret = PTR_ERR(log_tree_root->node);
+               log_tree_root->node = NULL;
                btrfs_put_root(log_tree_root);
                return ret;
        } else if (!extent_buffer_uptodate(log_tree_root->node)) {
                btrfs_err(fs_info, "failed to read log tree");
-               free_extent_buffer(log_tree_root->node);
                btrfs_put_root(log_tree_root);
                return -EIO;
        }
@@ -2236,7 +2254,6 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info,
        if (ret) {
                btrfs_handle_fs_error(fs_info, ret,
                                      "Failed to recover log tree");
-               free_extent_buffer(log_tree_root->node);
                btrfs_put_root(log_tree_root);
                return ret;
        }
@@ -3901,8 +3918,6 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
        if (test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state)) {
                btrfs_free_log(NULL, root);
                if (root->reloc_root) {
-                       free_extent_buffer(root->reloc_root->node);
-                       free_extent_buffer(root->reloc_root->commit_root);
                        btrfs_put_root(root->reloc_root);
                        root->reloc_root = NULL;
                }
@@ -3916,19 +3931,6 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
                iput(root->ino_cache_inode);
                root->ino_cache_inode = NULL;
        }
-       btrfs_free_fs_root(root);
-}
-
-void btrfs_free_fs_root(struct btrfs_root *root)
-{
-       WARN_ON(!RB_EMPTY_ROOT(&root->inode_tree));
-       if (root->anon_dev)
-               free_anon_bdev(root->anon_dev);
-       btrfs_drew_lock_destroy(&root->snapshot_lock);
-       free_extent_buffer(root->node);
-       free_extent_buffer(root->commit_root);
-       kfree(root->free_ino_ctl);
-       kfree(root->free_ino_pinned);
        btrfs_put_root(root);
 }
 
@@ -4093,8 +4095,6 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
        btrfs_sysfs_remove_mounted(fs_info);
        btrfs_sysfs_remove_fsid(fs_info->fs_devices);
 
-       btrfs_free_fs_roots(fs_info);
-
        btrfs_put_block_group_cache(fs_info);
 
        /*
@@ -4106,6 +4106,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
 
        clear_bit(BTRFS_FS_OPEN, &fs_info->flags);
        free_root_pointers(fs_info, true);
+       btrfs_free_fs_roots(fs_info);
 
        /*
         * We must free the block groups after dropping the fs_roots as we could
index 59c885860bf862c3e4830341b97e1985139d41b3..cd629113f61c66dfef993cfe6af9f267c64af33d 100644 (file)
@@ -76,7 +76,6 @@ void btrfs_btree_balance_dirty(struct btrfs_fs_info *fs_info);
 void btrfs_btree_balance_dirty_nodelay(struct btrfs_fs_info *fs_info);
 void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
                                 struct btrfs_root *root);
-void btrfs_free_fs_root(struct btrfs_root *root);
 
 #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
 struct btrfs_root *btrfs_alloc_dummy_root(struct btrfs_fs_info *fs_info);
@@ -98,20 +97,7 @@ static inline struct btrfs_root *btrfs_grab_root(struct btrfs_root *root)
        return NULL;
 }
 
-static inline void btrfs_put_root(struct btrfs_root *root)
-{
-       if (!root)
-               return;
-       if (refcount_dec_and_test(&root->refs)) {
-#ifdef CONFIG_BTRFS_DEBUG
-               spin_lock(&root->fs_info->fs_roots_radix_lock);
-               list_del_init(&root->leak_list);
-               spin_unlock(&root->fs_info->fs_roots_radix_lock);
-#endif
-               kfree(root);
-       }
-}
-
+void btrfs_put_root(struct btrfs_root *root);
 void btrfs_mark_buffer_dirty(struct extent_buffer *buf);
 int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid,
                          int atomic);
index 001605b672b86a73070940cc17e9d081a9555540..3e7bd2388ed2b0a7879400ed195258c04a76c49f 100644 (file)
@@ -5485,13 +5485,10 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref, int for_reloc)
                }
        }
 
-       if (test_bit(BTRFS_ROOT_IN_RADIX, &root->state)) {
+       if (test_bit(BTRFS_ROOT_IN_RADIX, &root->state))
                btrfs_add_dropped_root(trans, root);
-       } else {
-               free_extent_buffer(root->node);
-               free_extent_buffer(root->commit_root);
+       else
                btrfs_put_root(root);
-       }
        root_dropped = true;
 out_end_trans:
        btrfs_end_transaction_throttle(trans);
index 36af71040974568c342e273ac4925949ed52ea8a..39e45b8a503163ed07ad0fb31852ca37499d5b21 100644 (file)
@@ -64,12 +64,21 @@ void btrfs_extent_buffer_leak_debug_check(struct btrfs_fs_info *fs_info)
        struct extent_buffer *eb;
        unsigned long flags;
 
+       /*
+        * If we didn't get into open_ctree our allocated_ebs will not be
+        * initialized, so just skip this.
+        */
+       if (!fs_info->allocated_ebs.next)
+               return;
+
        spin_lock_irqsave(&fs_info->eb_leak_lock, flags);
        while (!list_empty(&fs_info->allocated_ebs)) {
                eb = list_first_entry(&fs_info->allocated_ebs,
                                      struct extent_buffer, leak_list);
-               pr_err("BTRFS: buffer leak start %llu len %lu refs %d bflags %lu\n",
-                      eb->start, eb->len, atomic_read(&eb->refs), eb->bflags);
+               pr_err(
+       "BTRFS: buffer leak start %llu len %lu refs %d bflags %lu owner %llu\n",
+                      eb->start, eb->len, atomic_read(&eb->refs), eb->bflags,
+                      btrfs_header_owner(eb));
                list_del(&eb->leak_list);
                kmem_cache_free(extent_buffer_cache, eb);
        }
@@ -4875,7 +4884,6 @@ out_free_ulist:
 
 static void __free_extent_buffer(struct extent_buffer *eb)
 {
-       btrfs_leak_debug_del(&eb->fs_info->eb_leak_lock, &eb->leak_list);
        kmem_cache_free(extent_buffer_cache, eb);
 }
 
@@ -4941,6 +4949,7 @@ static void btrfs_release_extent_buffer_pages(struct extent_buffer *eb)
 static inline void btrfs_release_extent_buffer(struct extent_buffer *eb)
 {
        btrfs_release_extent_buffer_pages(eb);
+       btrfs_leak_debug_del(&eb->fs_info->eb_leak_lock, &eb->leak_list);
        __free_extent_buffer(eb);
 }
 
@@ -5329,6 +5338,7 @@ static int release_extent_buffer(struct extent_buffer *eb)
                        spin_unlock(&eb->refs_lock);
                }
 
+               btrfs_leak_debug_del(&eb->fs_info->eb_leak_lock, &eb->leak_list);
                /* Should be safe to release our pages at this point */
                btrfs_release_extent_buffer_pages(eb);
 #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
index bc43950eb32f08cfe1afaec94012097804dbef4f..8b1f5c8897b750a6c740922e16833117d05ae3e2 100644 (file)
@@ -1251,8 +1251,6 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
        btrfs_free_tree_block(trans, free_space_root, free_space_root->node,
                              0, 1);
 
-       free_extent_buffer(free_space_root->node);
-       free_extent_buffer(free_space_root->commit_root);
        btrfs_put_root(free_space_root);
 
        return btrfs_commit_transaction(trans);
index 75bc3b6864984aeb7bfea34e0bf330d14950013a..c3888fb367e767ad192dc6a4d30c24b64b078fe2 100644 (file)
@@ -1038,11 +1038,8 @@ out_add_root:
 out_free_path:
        btrfs_free_path(path);
 out_free_root:
-       if (ret) {
-               free_extent_buffer(quota_root->node);
-               free_extent_buffer(quota_root->commit_root);
+       if (ret)
                btrfs_put_root(quota_root);
-       }
 out:
        if (ret) {
                ulist_free(fs_info->qgroup_ulist);
@@ -1105,8 +1102,6 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
        btrfs_tree_unlock(quota_root->node);
        btrfs_free_tree_block(trans, quota_root, quota_root->node, 0, 1);
 
-       free_extent_buffer(quota_root->node);
-       free_extent_buffer(quota_root->commit_root);
        btrfs_put_root(quota_root);
 
 end_trans:
index 2641b6c5c362332f42e9151d1c1ffab30b42d1f4..7f31dd57de541adaecf5961cbc9fea0960714fcc 100644 (file)
@@ -2579,10 +2579,6 @@ void free_reloc_roots(struct list_head *list)
                reloc_root = list_entry(list->next, struct btrfs_root,
                                        root_list);
                __del_reloc_root(reloc_root);
-               free_extent_buffer(reloc_root->node);
-               free_extent_buffer(reloc_root->commit_root);
-               reloc_root->node = NULL;
-               reloc_root->commit_root = NULL;
        }
 }
 
index 69c9afef06e3229f5aaa8f17e4875de3a66f9e0a..42e62fd2809c14e9fdf156cb29f9823fb0ef82bd 100644 (file)
@@ -194,6 +194,7 @@ void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info)
        cleanup_srcu_struct(&fs_info->subvol_srcu);
        kfree(fs_info->super_copy);
        btrfs_check_leaked_roots(fs_info);
+       btrfs_extent_buffer_leak_debug_check(fs_info);
        kfree(fs_info->fs_devices);
        kfree(fs_info);
 }
@@ -205,10 +206,6 @@ void btrfs_free_dummy_root(struct btrfs_root *root)
        /* Will be freed by btrfs_free_fs_roots */
        if (WARN_ON(test_bit(BTRFS_ROOT_IN_RADIX, &root->state)))
                return;
-       if (root->node) {
-               /* One for allocate_extent_buffer */
-               free_extent_buffer(root->node);
-       }
        btrfs_put_root(root);
 }
 
index 0eebd6a02d419cc9008c344fc924311f23c4138d..58c111474ba5bb5d0630bea17f85d1ad6927ddb6 100644 (file)
@@ -3299,7 +3299,6 @@ static void free_log_tree(struct btrfs_trans_handle *trans,
 
        clear_extent_bits(&log->dirty_log_pages, 0, (u64)-1,
                          EXTENT_DIRTY | EXTENT_NEW | EXTENT_NEED_WAIT);
-       free_extent_buffer(log->node);
        btrfs_put_root(log);
 }
 
@@ -6246,8 +6245,6 @@ again:
                                ret = btrfs_pin_extent_for_log_replay(trans,
                                                        log->node->start,
                                                        log->node->len);
-                       free_extent_buffer(log->node);
-                       free_extent_buffer(log->commit_root);
                        btrfs_put_root(log);
 
                        if (!ret)
@@ -6285,8 +6282,6 @@ again:
 
                wc.replay_dest->log_root = NULL;
                btrfs_put_root(wc.replay_dest);
-               free_extent_buffer(log->node);
-               free_extent_buffer(log->commit_root);
                btrfs_put_root(log);
 
                if (ret)
@@ -6318,7 +6313,6 @@ next:
        if (ret)
                return ret;
 
-       free_extent_buffer(log_root_tree->node);
        log_root_tree->log_root = NULL;
        clear_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags);
        btrfs_put_root(log_root_tree);