diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-10-01 19:05:46 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-10-01 19:05:46 -0400 |
commit | 323ac95bce442bbde514e3ce57e840402f80d909 (patch) | |
tree | 9b2241ed4de746087fdfb66f83fc142279648f9b /fs/btrfs/ctree.c | |
parent | cf749823857230017c86504bfdc70524f929ba96 (diff) |
Btrfs: don't read leaf blocks containing only checksums during truncate
Checksum items take up a significant portion of the metadata for large files.
It is possible to avoid reading them during truncates by checking the keys in
the higher level nodes.
If a given leaf is followed by another leaf where the lowest key is a checksum
item from the same file, we know we can safely delete the leaf without
reading it.
For a 32GB file on a 6 drive raid0 array, Btrfs needs 8s to delete
the file with a cold cache. It is read bound during the run.
With this change, Btrfs is able to delete the file in 0.5s
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r-- | fs/btrfs/ctree.c | 69 |
1 files changed, 38 insertions, 31 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index ff3261ff2e19..2eab4643dcbc 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -1388,7 +1388,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1388 | struct btrfs_key prealloc_block; | 1388 | struct btrfs_key prealloc_block; |
1389 | 1389 | ||
1390 | lowest_level = p->lowest_level; | 1390 | lowest_level = p->lowest_level; |
1391 | WARN_ON(lowest_level && ins_len); | 1391 | WARN_ON(lowest_level && ins_len > 0); |
1392 | WARN_ON(p->nodes[0] != NULL); | 1392 | WARN_ON(p->nodes[0] != NULL); |
1393 | WARN_ON(cow && root == root->fs_info->extent_root && | 1393 | WARN_ON(cow && root == root->fs_info->extent_root && |
1394 | !mutex_is_locked(&root->fs_info->alloc_mutex)); | 1394 | !mutex_is_locked(&root->fs_info->alloc_mutex)); |
@@ -3187,6 +3187,36 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
3187 | } | 3187 | } |
3188 | 3188 | ||
3189 | /* | 3189 | /* |
3190 | * a helper function to delete the leaf pointed to by path->slots[1] and | ||
3191 | * path->nodes[1]. bytenr is the node block pointer, but since the callers | ||
3192 | * already know it, it is faster to have them pass it down than to | ||
3193 | * read it out of the node again. | ||
3194 | * | ||
3195 | * This deletes the pointer in path->nodes[1] and frees the leaf | ||
3196 | * block extent. zero is returned if it all worked out, < 0 otherwise. | ||
3197 | * | ||
3198 | * The path must have already been setup for deleting the leaf, including | ||
3199 | * all the proper balancing. path->nodes[1] must be locked. | ||
3200 | */ | ||
3201 | noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans, | ||
3202 | struct btrfs_root *root, | ||
3203 | struct btrfs_path *path, u64 bytenr) | ||
3204 | { | ||
3205 | int ret; | ||
3206 | u64 root_gen = btrfs_header_generation(path->nodes[1]); | ||
3207 | |||
3208 | ret = del_ptr(trans, root, path, 1, path->slots[1]); | ||
3209 | if (ret) | ||
3210 | return ret; | ||
3211 | |||
3212 | ret = btrfs_free_extent(trans, root, bytenr, | ||
3213 | btrfs_level_size(root, 0), | ||
3214 | path->nodes[1]->start, | ||
3215 | btrfs_header_owner(path->nodes[1]), | ||
3216 | root_gen, 0, 0, 1); | ||
3217 | return ret; | ||
3218 | } | ||
3219 | /* | ||
3190 | * delete the item at the leaf level in path. If that empties | 3220 | * delete the item at the leaf level in path. If that empties |
3191 | * the leaf, remove it from the tree | 3221 | * the leaf, remove it from the tree |
3192 | */ | 3222 | */ |
@@ -3251,17 +3281,8 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
3251 | if (leaf == root->node) { | 3281 | if (leaf == root->node) { |
3252 | btrfs_set_header_level(leaf, 0); | 3282 | btrfs_set_header_level(leaf, 0); |
3253 | } else { | 3283 | } else { |
3254 | u64 root_gen = btrfs_header_generation(path->nodes[1]); | 3284 | ret = btrfs_del_leaf(trans, root, path, leaf->start); |
3255 | wret = del_ptr(trans, root, path, 1, path->slots[1]); | 3285 | BUG_ON(ret); |
3256 | if (wret) | ||
3257 | ret = wret; | ||
3258 | wret = btrfs_free_extent(trans, root, | ||
3259 | leaf->start, leaf->len, | ||
3260 | path->nodes[1]->start, | ||
3261 | btrfs_header_owner(path->nodes[1]), | ||
3262 | root_gen, 0, 0, 1); | ||
3263 | if (wret) | ||
3264 | ret = wret; | ||
3265 | } | 3286 | } |
3266 | } else { | 3287 | } else { |
3267 | int used = leaf_space_used(leaf, 0, nritems); | 3288 | int used = leaf_space_used(leaf, 0, nritems); |
@@ -3296,24 +3317,10 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
3296 | } | 3317 | } |
3297 | 3318 | ||
3298 | if (btrfs_header_nritems(leaf) == 0) { | 3319 | if (btrfs_header_nritems(leaf) == 0) { |
3299 | u64 root_gen; | 3320 | path->slots[1] = slot; |
3300 | u64 bytenr = leaf->start; | 3321 | ret = btrfs_del_leaf(trans, root, path, leaf->start); |
3301 | u32 blocksize = leaf->len; | 3322 | BUG_ON(ret); |
3302 | |||
3303 | root_gen = btrfs_header_generation( | ||
3304 | path->nodes[1]); | ||
3305 | |||
3306 | wret = del_ptr(trans, root, path, 1, slot); | ||
3307 | if (wret) | ||
3308 | ret = wret; | ||
3309 | |||
3310 | free_extent_buffer(leaf); | 3323 | free_extent_buffer(leaf); |
3311 | wret = btrfs_free_extent(trans, root, bytenr, | ||
3312 | blocksize, path->nodes[1]->start, | ||
3313 | btrfs_header_owner(path->nodes[1]), | ||
3314 | root_gen, 0, 0, 1); | ||
3315 | if (wret) | ||
3316 | ret = wret; | ||
3317 | } else { | 3324 | } else { |
3318 | /* if we're still in the path, make sure | 3325 | /* if we're still in the path, make sure |
3319 | * we're dirty. Otherwise, one of the | 3326 | * we're dirty. Otherwise, one of the |
@@ -3418,8 +3425,8 @@ again: | |||
3418 | level = btrfs_header_level(cur); | 3425 | level = btrfs_header_level(cur); |
3419 | sret = bin_search(cur, min_key, level, &slot); | 3426 | sret = bin_search(cur, min_key, level, &slot); |
3420 | 3427 | ||
3421 | /* at level = 0, we're done, setup the path and exit */ | 3428 | /* at the lowest level, we're done, setup the path and exit */ |
3422 | if (level == 0) { | 3429 | if (level == path->lowest_level) { |
3423 | if (slot >= nritems) | 3430 | if (slot >= nritems) |
3424 | goto find_next_key; | 3431 | goto find_next_key; |
3425 | ret = 0; | 3432 | ret = 0; |