summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobbie Ko <robbieko@synology.com>2018-05-08 06:11:38 -0400
committerDavid Sterba <dsterba@suse.com>2018-05-28 12:07:32 -0400
commit0f96f517dcaa58346c32be094aecd610b7d3c008 (patch)
treeb36856eb17e341312279dd093edc79eb3c5425ef
parent35c8eda12fc69e8a3f67c4615050ca4e76adec32 (diff)
btrfs: incremental send, improve rmdir performance for large directory
Currently when checking if a directory can be deleted, we always check if all its children have been processed. Example: A directory with 2,000,000 files was deleted original: 1994m57.071s patch: 1m38.554s [FIX] Instead of checking all children on all calls to can_rmdir(), we keep track of the directory index offset of the child last checked in the last call to can_rmdir(), and then use it as the starting point for future calls to can_rmdir(). Signed-off-by: Robbie Ko <robbieko@synology.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> [ update changelog ] Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/send.c31
1 files changed, 22 insertions, 9 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 29cfc0df1f27..c47f62b19226 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -235,6 +235,7 @@ struct orphan_dir_info {
235 struct rb_node node; 235 struct rb_node node;
236 u64 ino; 236 u64 ino;
237 u64 gen; 237 u64 gen;
238 u64 last_dir_index_offset;
238}; 239};
239 240
240struct name_cache_entry { 241struct name_cache_entry {
@@ -2861,6 +2862,7 @@ add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
2861 return ERR_PTR(-ENOMEM); 2862 return ERR_PTR(-ENOMEM);
2862 odi->ino = dir_ino; 2863 odi->ino = dir_ino;
2863 odi->gen = 0; 2864 odi->gen = 0;
2865 odi->last_dir_index_offset = 0;
2864 2866
2865 rb_link_node(&odi->node, parent, p); 2867 rb_link_node(&odi->node, parent, p);
2866 rb_insert_color(&odi->node, &sctx->orphan_dirs); 2868 rb_insert_color(&odi->node, &sctx->orphan_dirs);
@@ -2916,6 +2918,7 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
2916 struct btrfs_key found_key; 2918 struct btrfs_key found_key;
2917 struct btrfs_key loc; 2919 struct btrfs_key loc;
2918 struct btrfs_dir_item *di; 2920 struct btrfs_dir_item *di;
2921 struct orphan_dir_info *odi = NULL;
2919 2922
2920 /* 2923 /*
2921 * Don't try to rmdir the top/root subvolume dir. 2924 * Don't try to rmdir the top/root subvolume dir.
@@ -2930,6 +2933,11 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
2930 key.objectid = dir; 2933 key.objectid = dir;
2931 key.type = BTRFS_DIR_INDEX_KEY; 2934 key.type = BTRFS_DIR_INDEX_KEY;
2932 key.offset = 0; 2935 key.offset = 0;
2936
2937 odi = get_orphan_dir_info(sctx, dir);
2938 if (odi)
2939 key.offset = odi->last_dir_index_offset;
2940
2933 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 2941 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
2934 if (ret < 0) 2942 if (ret < 0)
2935 goto out; 2943 goto out;
@@ -2957,30 +2965,33 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
2957 2965
2958 dm = get_waiting_dir_move(sctx, loc.objectid); 2966 dm = get_waiting_dir_move(sctx, loc.objectid);
2959 if (dm) { 2967 if (dm) {
2960 struct orphan_dir_info *odi;
2961
2962 odi = add_orphan_dir_info(sctx, dir); 2968 odi = add_orphan_dir_info(sctx, dir);
2963 if (IS_ERR(odi)) { 2969 if (IS_ERR(odi)) {
2964 ret = PTR_ERR(odi); 2970 ret = PTR_ERR(odi);
2965 goto out; 2971 goto out;
2966 } 2972 }
2967 odi->gen = dir_gen; 2973 odi->gen = dir_gen;
2974 odi->last_dir_index_offset = found_key.offset;
2968 dm->rmdir_ino = dir; 2975 dm->rmdir_ino = dir;
2969 ret = 0; 2976 ret = 0;
2970 goto out; 2977 goto out;
2971 } 2978 }
2972 2979
2973 if (loc.objectid > send_progress) { 2980 if (loc.objectid > send_progress) {
2974 struct orphan_dir_info *odi; 2981 odi = add_orphan_dir_info(sctx, dir);
2975 2982 if (IS_ERR(odi)) {
2976 odi = get_orphan_dir_info(sctx, dir); 2983 ret = PTR_ERR(odi);
2977 free_orphan_dir_info(sctx, odi); 2984 goto out;
2985 }
2986 odi->gen = dir_gen;
2987 odi->last_dir_index_offset = found_key.offset;
2978 ret = 0; 2988 ret = 0;
2979 goto out; 2989 goto out;
2980 } 2990 }
2981 2991
2982 path->slots[0]++; 2992 path->slots[0]++;
2983 } 2993 }
2994 free_orphan_dir_info(sctx, odi);
2984 2995
2985 ret = 1; 2996 ret = 1;
2986 2997
@@ -3258,13 +3269,16 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
3258 3269
3259 if (rmdir_ino) { 3270 if (rmdir_ino) {
3260 struct orphan_dir_info *odi; 3271 struct orphan_dir_info *odi;
3272 u64 gen;
3261 3273
3262 odi = get_orphan_dir_info(sctx, rmdir_ino); 3274 odi = get_orphan_dir_info(sctx, rmdir_ino);
3263 if (!odi) { 3275 if (!odi) {
3264 /* already deleted */ 3276 /* already deleted */
3265 goto finish; 3277 goto finish;
3266 } 3278 }
3267 ret = can_rmdir(sctx, rmdir_ino, odi->gen, sctx->cur_ino); 3279 gen = odi->gen;
3280
3281 ret = can_rmdir(sctx, rmdir_ino, gen, sctx->cur_ino);
3268 if (ret < 0) 3282 if (ret < 0)
3269 goto out; 3283 goto out;
3270 if (!ret) 3284 if (!ret)
@@ -3275,13 +3289,12 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
3275 ret = -ENOMEM; 3289 ret = -ENOMEM;
3276 goto out; 3290 goto out;
3277 } 3291 }
3278 ret = get_cur_path(sctx, rmdir_ino, odi->gen, name); 3292 ret = get_cur_path(sctx, rmdir_ino, gen, name);
3279 if (ret < 0) 3293 if (ret < 0)
3280 goto out; 3294 goto out;
3281 ret = send_rmdir(sctx, name); 3295 ret = send_rmdir(sctx, name);
3282 if (ret < 0) 3296 if (ret < 0)
3283 goto out; 3297 goto out;
3284 free_orphan_dir_info(sctx, odi);
3285 } 3298 }
3286 3299
3287finish: 3300finish: