diff options
author | Robbie Ko <robbieko@synology.com> | 2018-05-08 06:11:38 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2018-05-28 12:07:32 -0400 |
commit | 0f96f517dcaa58346c32be094aecd610b7d3c008 (patch) | |
tree | b36856eb17e341312279dd093edc79eb3c5425ef | |
parent | 35c8eda12fc69e8a3f67c4615050ca4e76adec32 (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.c | 31 |
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 | ||
240 | struct name_cache_entry { | 241 | struct 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 | ||
3287 | finish: | 3300 | finish: |