Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btrfs: more efficient chunk map iteration when device replace finishes

When iterating the chunk maps when a device replace finishes we are doing
a full rbtree search for each chunk map, which is not the most efficient
thing to do, wasting CPU time. As we are holding a write lock on the tree
during the whole iteration, we can simply start from the first node in the
tree and then move to the next chunk map by doing a rb_next() call - the
only exception is when we need to reschedule, in which case we have to do
a full rbtree search since we dropped the write lock and the tree may have
changed (chunk maps may have been removed and the tree got rebalanced).
So just do that.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Filipe Manana and committed by
David Sterba
68a505bb b79f1c2c

+25 -11
+25 -11
fs/btrfs/dev-replace.c
··· 824 824 struct btrfs_device *srcdev, 825 825 struct btrfs_device *tgtdev) 826 826 { 827 - u64 start = 0; 828 - int i; 827 + struct rb_node *node; 829 828 830 829 /* 831 830 * The chunk mutex must be held so that no new chunks can be created ··· 835 836 lockdep_assert_held(&fs_info->chunk_mutex); 836 837 837 838 write_lock(&fs_info->mapping_tree_lock); 838 - do { 839 + node = rb_first_cached(&fs_info->mapping_tree); 840 + while (node) { 841 + struct rb_node *next = rb_next(node); 839 842 struct btrfs_chunk_map *map; 843 + u64 next_start; 840 844 841 - map = btrfs_find_chunk_map_nolock(fs_info, start, U64_MAX); 842 - if (!map) 843 - break; 844 - for (i = 0; i < map->num_stripes; i++) 845 + map = rb_entry(node, struct btrfs_chunk_map, rb_node); 846 + next_start = map->start + map->chunk_len; 847 + 848 + for (int i = 0; i < map->num_stripes; i++) 845 849 if (srcdev == map->stripes[i].dev) 846 850 map->stripes[i].dev = tgtdev; 847 - start = map->start + map->chunk_len; 848 - btrfs_free_chunk_map(map); 849 - cond_resched_rwlock_write(&fs_info->mapping_tree_lock); 850 - } while (start); 851 + 852 + if (cond_resched_rwlock_write(&fs_info->mapping_tree_lock)) { 853 + map = btrfs_find_chunk_map_nolock(fs_info, next_start, U64_MAX); 854 + if (!map) 855 + break; 856 + node = &map->rb_node; 857 + /* 858 + * Drop the lookup reference since we are holding the 859 + * lock in write mode and no one can remove the chunk 860 + * map from the tree and drop its tree reference. 861 + */ 862 + btrfs_free_chunk_map(map); 863 + } else { 864 + node = next; 865 + } 866 + } 851 867 write_unlock(&fs_info->mapping_tree_lock); 852 868 } 853 869