btrfs: speedup dead root detection during orphan cleanup
authorRobbie Ko <robbieko@synology.com>
Thu, 7 May 2020 02:54:40 +0000 (10:54 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 25 May 2020 09:25:29 +0000 (11:25 +0200)
When mounting, we handle deleted subvolume and orphan items.  First,
find add orphan roots, then add them to fs_root radix tree.  Second, in
tree-root, process each orphan item, skip if it is dead root.

The original algorithm is based on the list of dead_roots, one by one to
visit and check whether the objectid is consistent, the time complexity
is O (n ^ 2).  When processing 50000 deleted subvols, it takes about
120s.

Because btrfs_find_orphan_roots has already ran before us, and added
deleted subvol to fs_roots radix tree.

The fs root will only be removed from the fs_roots radix tree after the
cleaner process is started, and the cleaner will only start execution
after the mount is complete.

btrfs_orphan_cleanup can be called during the whole filesystem mount
lifetime, but only "tree root" will be used in this section of code, and
only mount time will be brought into tree root.

So we can quickly check whether the orphan item is dead root through the
fs_roots radix tree.

Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Robbie Ko <robbieko@synology.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/inode.c

index cc94291fdd18070cfa59653dd661a1ce198df8b8..7b854f711ce156006ff39cb7a05ec146462eb25e 100644 (file)
@@ -2997,18 +2997,16 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
                         * orphan must not get deleted.
                         * find_dead_roots already ran before us, so if this
                         * is a snapshot deletion, we should find the root
-                        * in the dead_roots list
+                        * in the fs_roots radix tree.
                         */
-                       spin_lock(&fs_info->trans_lock);
-                       list_for_each_entry(dead_root, &fs_info->dead_roots,
-                                           root_list) {
-                               if (dead_root->root_key.objectid ==
-                                   found_key.objectid) {
-                                       is_dead_root = 1;
-                                       break;
-                               }
-                       }
-                       spin_unlock(&fs_info->trans_lock);
+
+                       spin_lock(&fs_info->fs_roots_radix_lock);
+                       dead_root = radix_tree_lookup(&fs_info->fs_roots_radix,
+                                                        (unsigned long)found_key.objectid);
+                       if (dead_root && btrfs_root_refs(&dead_root->root_item) == 0)
+                               is_dead_root = 1;
+                       spin_unlock(&fs_info->fs_roots_radix_lock);
+
                        if (is_dead_root) {
                                /* prevent this orphan from being found again */
                                key.offset = found_key.objectid - 1;