diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 172 |
1 files changed, 162 insertions, 10 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 9f7fc51ca334..e7d7a837512a 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -3194,7 +3194,7 @@ static int log_inode_item(struct btrfs_trans_handle *trans, | |||
3194 | static noinline int copy_items(struct btrfs_trans_handle *trans, | 3194 | static noinline int copy_items(struct btrfs_trans_handle *trans, |
3195 | struct inode *inode, | 3195 | struct inode *inode, |
3196 | struct btrfs_path *dst_path, | 3196 | struct btrfs_path *dst_path, |
3197 | struct extent_buffer *src, | 3197 | struct btrfs_path *src_path, u64 *last_extent, |
3198 | int start_slot, int nr, int inode_only) | 3198 | int start_slot, int nr, int inode_only) |
3199 | { | 3199 | { |
3200 | unsigned long src_offset; | 3200 | unsigned long src_offset; |
@@ -3202,6 +3202,8 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
3202 | struct btrfs_root *log = BTRFS_I(inode)->root->log_root; | 3202 | struct btrfs_root *log = BTRFS_I(inode)->root->log_root; |
3203 | struct btrfs_file_extent_item *extent; | 3203 | struct btrfs_file_extent_item *extent; |
3204 | struct btrfs_inode_item *inode_item; | 3204 | struct btrfs_inode_item *inode_item; |
3205 | struct extent_buffer *src = src_path->nodes[0]; | ||
3206 | struct btrfs_key first_key, last_key, key; | ||
3205 | int ret; | 3207 | int ret; |
3206 | struct btrfs_key *ins_keys; | 3208 | struct btrfs_key *ins_keys; |
3207 | u32 *ins_sizes; | 3209 | u32 *ins_sizes; |
@@ -3209,6 +3211,9 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
3209 | int i; | 3211 | int i; |
3210 | struct list_head ordered_sums; | 3212 | struct list_head ordered_sums; |
3211 | int skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM; | 3213 | int skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM; |
3214 | bool has_extents = false; | ||
3215 | bool need_find_last_extent = (*last_extent == 0); | ||
3216 | bool done = false; | ||
3212 | 3217 | ||
3213 | INIT_LIST_HEAD(&ordered_sums); | 3218 | INIT_LIST_HEAD(&ordered_sums); |
3214 | 3219 | ||
@@ -3217,6 +3222,8 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
3217 | if (!ins_data) | 3222 | if (!ins_data) |
3218 | return -ENOMEM; | 3223 | return -ENOMEM; |
3219 | 3224 | ||
3225 | first_key.objectid = (u64)-1; | ||
3226 | |||
3220 | ins_sizes = (u32 *)ins_data; | 3227 | ins_sizes = (u32 *)ins_data; |
3221 | ins_keys = (struct btrfs_key *)(ins_data + nr * sizeof(u32)); | 3228 | ins_keys = (struct btrfs_key *)(ins_data + nr * sizeof(u32)); |
3222 | 3229 | ||
@@ -3237,6 +3244,9 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
3237 | 3244 | ||
3238 | src_offset = btrfs_item_ptr_offset(src, start_slot + i); | 3245 | src_offset = btrfs_item_ptr_offset(src, start_slot + i); |
3239 | 3246 | ||
3247 | if ((i == (nr - 1))) | ||
3248 | last_key = ins_keys[i]; | ||
3249 | |||
3240 | if (ins_keys[i].type == BTRFS_INODE_ITEM_KEY) { | 3250 | if (ins_keys[i].type == BTRFS_INODE_ITEM_KEY) { |
3241 | inode_item = btrfs_item_ptr(dst_path->nodes[0], | 3251 | inode_item = btrfs_item_ptr(dst_path->nodes[0], |
3242 | dst_path->slots[0], | 3252 | dst_path->slots[0], |
@@ -3248,6 +3258,21 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
3248 | src_offset, ins_sizes[i]); | 3258 | src_offset, ins_sizes[i]); |
3249 | } | 3259 | } |
3250 | 3260 | ||
3261 | /* | ||
3262 | * We set need_find_last_extent here in case we know we were | ||
3263 | * processing other items and then walk into the first extent in | ||
3264 | * the inode. If we don't hit an extent then nothing changes, | ||
3265 | * we'll do the last search the next time around. | ||
3266 | */ | ||
3267 | if (ins_keys[i].type == BTRFS_EXTENT_DATA_KEY) { | ||
3268 | has_extents = true; | ||
3269 | if (need_find_last_extent && | ||
3270 | first_key.objectid == (u64)-1) | ||
3271 | first_key = ins_keys[i]; | ||
3272 | } else { | ||
3273 | need_find_last_extent = false; | ||
3274 | } | ||
3275 | |||
3251 | /* take a reference on file data extents so that truncates | 3276 | /* take a reference on file data extents so that truncates |
3252 | * or deletes of this inode don't have to relog the inode | 3277 | * or deletes of this inode don't have to relog the inode |
3253 | * again | 3278 | * again |
@@ -3312,6 +3337,126 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
3312 | list_del(&sums->list); | 3337 | list_del(&sums->list); |
3313 | kfree(sums); | 3338 | kfree(sums); |
3314 | } | 3339 | } |
3340 | |||
3341 | if (!has_extents) | ||
3342 | return ret; | ||
3343 | |||
3344 | /* | ||
3345 | * Because we use btrfs_search_forward we could skip leaves that were | ||
3346 | * not modified and then assume *last_extent is valid when it really | ||
3347 | * isn't. So back up to the previous leaf and read the end of the last | ||
3348 | * extent before we go and fill in holes. | ||
3349 | */ | ||
3350 | if (need_find_last_extent) { | ||
3351 | u64 len; | ||
3352 | |||
3353 | ret = btrfs_prev_leaf(BTRFS_I(inode)->root, src_path); | ||
3354 | if (ret < 0) | ||
3355 | return ret; | ||
3356 | if (ret) | ||
3357 | goto fill_holes; | ||
3358 | if (src_path->slots[0]) | ||
3359 | src_path->slots[0]--; | ||
3360 | src = src_path->nodes[0]; | ||
3361 | btrfs_item_key_to_cpu(src, &key, src_path->slots[0]); | ||
3362 | if (key.objectid != btrfs_ino(inode) || | ||
3363 | key.type != BTRFS_EXTENT_DATA_KEY) | ||
3364 | goto fill_holes; | ||
3365 | extent = btrfs_item_ptr(src, src_path->slots[0], | ||
3366 | struct btrfs_file_extent_item); | ||
3367 | if (btrfs_file_extent_type(src, extent) == | ||
3368 | BTRFS_FILE_EXTENT_INLINE) { | ||
3369 | len = btrfs_file_extent_inline_len(src, extent); | ||
3370 | *last_extent = ALIGN(key.offset + len, | ||
3371 | log->sectorsize); | ||
3372 | } else { | ||
3373 | len = btrfs_file_extent_num_bytes(src, extent); | ||
3374 | *last_extent = key.offset + len; | ||
3375 | } | ||
3376 | } | ||
3377 | fill_holes: | ||
3378 | /* So we did prev_leaf, now we need to move to the next leaf, but a few | ||
3379 | * things could have happened | ||
3380 | * | ||
3381 | * 1) A merge could have happened, so we could currently be on a leaf | ||
3382 | * that holds what we were copying in the first place. | ||
3383 | * 2) A split could have happened, and now not all of the items we want | ||
3384 | * are on the same leaf. | ||
3385 | * | ||
3386 | * So we need to adjust how we search for holes, we need to drop the | ||
3387 | * path and re-search for the first extent key we found, and then walk | ||
3388 | * forward until we hit the last one we copied. | ||
3389 | */ | ||
3390 | if (need_find_last_extent) { | ||
3391 | /* btrfs_prev_leaf could return 1 without releasing the path */ | ||
3392 | btrfs_release_path(src_path); | ||
3393 | ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &first_key, | ||
3394 | src_path, 0, 0); | ||
3395 | if (ret < 0) | ||
3396 | return ret; | ||
3397 | ASSERT(ret == 0); | ||
3398 | src = src_path->nodes[0]; | ||
3399 | i = src_path->slots[0]; | ||
3400 | } else { | ||
3401 | i = start_slot; | ||
3402 | } | ||
3403 | |||
3404 | /* | ||
3405 | * Ok so here we need to go through and fill in any holes we may have | ||
3406 | * to make sure that holes are punched for those areas in case they had | ||
3407 | * extents previously. | ||
3408 | */ | ||
3409 | while (!done) { | ||
3410 | u64 offset, len; | ||
3411 | u64 extent_end; | ||
3412 | |||
3413 | if (i >= btrfs_header_nritems(src_path->nodes[0])) { | ||
3414 | ret = btrfs_next_leaf(BTRFS_I(inode)->root, src_path); | ||
3415 | if (ret < 0) | ||
3416 | return ret; | ||
3417 | ASSERT(ret == 0); | ||
3418 | src = src_path->nodes[0]; | ||
3419 | i = 0; | ||
3420 | } | ||
3421 | |||
3422 | btrfs_item_key_to_cpu(src, &key, i); | ||
3423 | if (!btrfs_comp_cpu_keys(&key, &last_key)) | ||
3424 | done = true; | ||
3425 | if (key.objectid != btrfs_ino(inode) || | ||
3426 | key.type != BTRFS_EXTENT_DATA_KEY) { | ||
3427 | i++; | ||
3428 | continue; | ||
3429 | } | ||
3430 | extent = btrfs_item_ptr(src, i, struct btrfs_file_extent_item); | ||
3431 | if (btrfs_file_extent_type(src, extent) == | ||
3432 | BTRFS_FILE_EXTENT_INLINE) { | ||
3433 | len = btrfs_file_extent_inline_len(src, extent); | ||
3434 | extent_end = ALIGN(key.offset + len, log->sectorsize); | ||
3435 | } else { | ||
3436 | len = btrfs_file_extent_num_bytes(src, extent); | ||
3437 | extent_end = key.offset + len; | ||
3438 | } | ||
3439 | i++; | ||
3440 | |||
3441 | if (*last_extent == key.offset) { | ||
3442 | *last_extent = extent_end; | ||
3443 | continue; | ||
3444 | } | ||
3445 | offset = *last_extent; | ||
3446 | len = key.offset - *last_extent; | ||
3447 | ret = btrfs_insert_file_extent(trans, log, btrfs_ino(inode), | ||
3448 | offset, 0, 0, len, 0, len, 0, | ||
3449 | 0, 0); | ||
3450 | if (ret) | ||
3451 | break; | ||
3452 | *last_extent = offset + len; | ||
3453 | } | ||
3454 | /* | ||
3455 | * Need to let the callers know we dropped the path so they should | ||
3456 | * re-search. | ||
3457 | */ | ||
3458 | if (!ret && need_find_last_extent) | ||
3459 | ret = 1; | ||
3315 | return ret; | 3460 | return ret; |
3316 | } | 3461 | } |
3317 | 3462 | ||
@@ -3630,6 +3775,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, | |||
3630 | struct btrfs_key max_key; | 3775 | struct btrfs_key max_key; |
3631 | struct btrfs_root *log = root->log_root; | 3776 | struct btrfs_root *log = root->log_root; |
3632 | struct extent_buffer *src = NULL; | 3777 | struct extent_buffer *src = NULL; |
3778 | u64 last_extent = 0; | ||
3633 | int err = 0; | 3779 | int err = 0; |
3634 | int ret; | 3780 | int ret; |
3635 | int nritems; | 3781 | int nritems; |
@@ -3745,11 +3891,15 @@ again: | |||
3745 | goto next_slot; | 3891 | goto next_slot; |
3746 | } | 3892 | } |
3747 | 3893 | ||
3748 | ret = copy_items(trans, inode, dst_path, src, ins_start_slot, | 3894 | ret = copy_items(trans, inode, dst_path, path, &last_extent, |
3749 | ins_nr, inode_only); | 3895 | ins_start_slot, ins_nr, inode_only); |
3750 | if (ret) { | 3896 | if (ret < 0) { |
3751 | err = ret; | 3897 | err = ret; |
3752 | goto out_unlock; | 3898 | goto out_unlock; |
3899 | } if (ret) { | ||
3900 | ins_nr = 0; | ||
3901 | btrfs_release_path(path); | ||
3902 | continue; | ||
3753 | } | 3903 | } |
3754 | ins_nr = 1; | 3904 | ins_nr = 1; |
3755 | ins_start_slot = path->slots[0]; | 3905 | ins_start_slot = path->slots[0]; |
@@ -3763,13 +3913,14 @@ next_slot: | |||
3763 | goto again; | 3913 | goto again; |
3764 | } | 3914 | } |
3765 | if (ins_nr) { | 3915 | if (ins_nr) { |
3766 | ret = copy_items(trans, inode, dst_path, src, | 3916 | ret = copy_items(trans, inode, dst_path, path, |
3767 | ins_start_slot, | 3917 | &last_extent, ins_start_slot, |
3768 | ins_nr, inode_only); | 3918 | ins_nr, inode_only); |
3769 | if (ret) { | 3919 | if (ret < 0) { |
3770 | err = ret; | 3920 | err = ret; |
3771 | goto out_unlock; | 3921 | goto out_unlock; |
3772 | } | 3922 | } |
3923 | ret = 0; | ||
3773 | ins_nr = 0; | 3924 | ins_nr = 0; |
3774 | } | 3925 | } |
3775 | btrfs_release_path(path); | 3926 | btrfs_release_path(path); |
@@ -3784,12 +3935,13 @@ next_slot: | |||
3784 | } | 3935 | } |
3785 | } | 3936 | } |
3786 | if (ins_nr) { | 3937 | if (ins_nr) { |
3787 | ret = copy_items(trans, inode, dst_path, src, ins_start_slot, | 3938 | ret = copy_items(trans, inode, dst_path, path, &last_extent, |
3788 | ins_nr, inode_only); | 3939 | ins_start_slot, ins_nr, inode_only); |
3789 | if (ret) { | 3940 | if (ret < 0) { |
3790 | err = ret; | 3941 | err = ret; |
3791 | goto out_unlock; | 3942 | goto out_unlock; |
3792 | } | 3943 | } |
3944 | ret = 0; | ||
3793 | ins_nr = 0; | 3945 | ins_nr = 0; |
3794 | } | 3946 | } |
3795 | 3947 | ||