diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
| -rw-r--r-- | fs/btrfs/tree-log.c | 80 |
1 files changed, 62 insertions, 18 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 9e1f2cd5e67a..1d1ba083ca6e 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
| @@ -94,8 +94,10 @@ | |||
| 94 | #define LOG_WALK_REPLAY_ALL 3 | 94 | #define LOG_WALK_REPLAY_ALL 3 |
| 95 | 95 | ||
| 96 | static int btrfs_log_inode(struct btrfs_trans_handle *trans, | 96 | static int btrfs_log_inode(struct btrfs_trans_handle *trans, |
| 97 | struct btrfs_root *root, struct inode *inode, | 97 | struct btrfs_root *root, struct inode *inode, |
| 98 | int inode_only); | 98 | int inode_only, |
| 99 | const loff_t start, | ||
| 100 | const loff_t end); | ||
| 99 | static int link_to_fixup_dir(struct btrfs_trans_handle *trans, | 101 | static int link_to_fixup_dir(struct btrfs_trans_handle *trans, |
| 100 | struct btrfs_root *root, | 102 | struct btrfs_root *root, |
| 101 | struct btrfs_path *path, u64 objectid); | 103 | struct btrfs_path *path, u64 objectid); |
| @@ -3298,7 +3300,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
| 3298 | struct list_head ordered_sums; | 3300 | struct list_head ordered_sums; |
| 3299 | int skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM; | 3301 | int skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM; |
| 3300 | bool has_extents = false; | 3302 | bool has_extents = false; |
| 3301 | bool need_find_last_extent = (*last_extent == 0); | 3303 | bool need_find_last_extent = true; |
| 3302 | bool done = false; | 3304 | bool done = false; |
| 3303 | 3305 | ||
| 3304 | INIT_LIST_HEAD(&ordered_sums); | 3306 | INIT_LIST_HEAD(&ordered_sums); |
| @@ -3352,8 +3354,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
| 3352 | */ | 3354 | */ |
| 3353 | if (ins_keys[i].type == BTRFS_EXTENT_DATA_KEY) { | 3355 | if (ins_keys[i].type == BTRFS_EXTENT_DATA_KEY) { |
| 3354 | has_extents = true; | 3356 | has_extents = true; |
| 3355 | if (need_find_last_extent && | 3357 | if (first_key.objectid == (u64)-1) |
| 3356 | first_key.objectid == (u64)-1) | ||
| 3357 | first_key = ins_keys[i]; | 3358 | first_key = ins_keys[i]; |
| 3358 | } else { | 3359 | } else { |
| 3359 | need_find_last_extent = false; | 3360 | need_find_last_extent = false; |
| @@ -3427,6 +3428,16 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, | |||
| 3427 | if (!has_extents) | 3428 | if (!has_extents) |
| 3428 | return ret; | 3429 | return ret; |
| 3429 | 3430 | ||
| 3431 | if (need_find_last_extent && *last_extent == first_key.offset) { | ||
| 3432 | /* | ||
| 3433 | * We don't have any leafs between our current one and the one | ||
| 3434 | * we processed before that can have file extent items for our | ||
| 3435 | * inode (and have a generation number smaller than our current | ||
| 3436 | * transaction id). | ||
| 3437 | */ | ||
| 3438 | need_find_last_extent = false; | ||
| 3439 | } | ||
| 3440 | |||
| 3430 | /* | 3441 | /* |
| 3431 | * Because we use btrfs_search_forward we could skip leaves that were | 3442 | * Because we use btrfs_search_forward we could skip leaves that were |
| 3432 | * not modified and then assume *last_extent is valid when it really | 3443 | * not modified and then assume *last_extent is valid when it really |
| @@ -3537,7 +3548,7 @@ fill_holes: | |||
| 3537 | 0, 0); | 3548 | 0, 0); |
| 3538 | if (ret) | 3549 | if (ret) |
| 3539 | break; | 3550 | break; |
| 3540 | *last_extent = offset + len; | 3551 | *last_extent = extent_end; |
| 3541 | } | 3552 | } |
| 3542 | /* | 3553 | /* |
| 3543 | * Need to let the callers know we dropped the path so they should | 3554 | * Need to let the callers know we dropped the path so they should |
| @@ -3849,8 +3860,10 @@ process: | |||
| 3849 | * This handles both files and directories. | 3860 | * This handles both files and directories. |
| 3850 | */ | 3861 | */ |
| 3851 | static int btrfs_log_inode(struct btrfs_trans_handle *trans, | 3862 | static int btrfs_log_inode(struct btrfs_trans_handle *trans, |
| 3852 | struct btrfs_root *root, struct inode *inode, | 3863 | struct btrfs_root *root, struct inode *inode, |
| 3853 | int inode_only) | 3864 | int inode_only, |
| 3865 | const loff_t start, | ||
| 3866 | const loff_t end) | ||
| 3854 | { | 3867 | { |
| 3855 | struct btrfs_path *path; | 3868 | struct btrfs_path *path; |
| 3856 | struct btrfs_path *dst_path; | 3869 | struct btrfs_path *dst_path; |
| @@ -3867,6 +3880,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, | |||
| 3867 | int ins_nr; | 3880 | int ins_nr; |
| 3868 | bool fast_search = false; | 3881 | bool fast_search = false; |
| 3869 | u64 ino = btrfs_ino(inode); | 3882 | u64 ino = btrfs_ino(inode); |
| 3883 | struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; | ||
| 3870 | 3884 | ||
| 3871 | path = btrfs_alloc_path(); | 3885 | path = btrfs_alloc_path(); |
| 3872 | if (!path) | 3886 | if (!path) |
| @@ -4040,13 +4054,35 @@ log_extents: | |||
| 4040 | goto out_unlock; | 4054 | goto out_unlock; |
| 4041 | } | 4055 | } |
| 4042 | } else if (inode_only == LOG_INODE_ALL) { | 4056 | } else if (inode_only == LOG_INODE_ALL) { |
| 4043 | struct extent_map_tree *tree = &BTRFS_I(inode)->extent_tree; | ||
| 4044 | struct extent_map *em, *n; | 4057 | struct extent_map *em, *n; |
| 4045 | 4058 | ||
| 4046 | write_lock(&tree->lock); | 4059 | write_lock(&em_tree->lock); |
| 4047 | list_for_each_entry_safe(em, n, &tree->modified_extents, list) | 4060 | /* |
| 4048 | list_del_init(&em->list); | 4061 | * We can't just remove every em if we're called for a ranged |
| 4049 | write_unlock(&tree->lock); | 4062 | * fsync - that is, one that doesn't cover the whole possible |
| 4063 | * file range (0 to LLONG_MAX). This is because we can have | ||
| 4064 | * em's that fall outside the range we're logging and therefore | ||
| 4065 | * their ordered operations haven't completed yet | ||
| 4066 | * (btrfs_finish_ordered_io() not invoked yet). This means we | ||
| 4067 | * didn't get their respective file extent item in the fs/subvol | ||
| 4068 | * tree yet, and need to let the next fast fsync (one which | ||
| 4069 | * consults the list of modified extent maps) find the em so | ||
| 4070 | * that it logs a matching file extent item and waits for the | ||
| 4071 | * respective ordered operation to complete (if it's still | ||
| 4072 | * running). | ||
| 4073 | * | ||
| 4074 | * Removing every em outside the range we're logging would make | ||
| 4075 | * the next fast fsync not log their matching file extent items, | ||
| 4076 | * therefore making us lose data after a log replay. | ||
| 4077 | */ | ||
| 4078 | list_for_each_entry_safe(em, n, &em_tree->modified_extents, | ||
| 4079 | list) { | ||
| 4080 | const u64 mod_end = em->mod_start + em->mod_len - 1; | ||
| 4081 | |||
| 4082 | if (em->mod_start >= start && mod_end <= end) | ||
| 4083 | list_del_init(&em->list); | ||
| 4084 | } | ||
| 4085 | write_unlock(&em_tree->lock); | ||
| 4050 | } | 4086 | } |
| 4051 | 4087 | ||
| 4052 | if (inode_only == LOG_INODE_ALL && S_ISDIR(inode->i_mode)) { | 4088 | if (inode_only == LOG_INODE_ALL && S_ISDIR(inode->i_mode)) { |
| @@ -4056,6 +4092,7 @@ log_extents: | |||
| 4056 | goto out_unlock; | 4092 | goto out_unlock; |
| 4057 | } | 4093 | } |
| 4058 | } | 4094 | } |
| 4095 | |||
| 4059 | BTRFS_I(inode)->logged_trans = trans->transid; | 4096 | BTRFS_I(inode)->logged_trans = trans->transid; |
| 4060 | BTRFS_I(inode)->last_log_commit = BTRFS_I(inode)->last_sub_trans; | 4097 | BTRFS_I(inode)->last_log_commit = BTRFS_I(inode)->last_sub_trans; |
| 4061 | out_unlock: | 4098 | out_unlock: |
| @@ -4152,7 +4189,10 @@ out: | |||
| 4152 | */ | 4189 | */ |
| 4153 | static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans, | 4190 | static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans, |
| 4154 | struct btrfs_root *root, struct inode *inode, | 4191 | struct btrfs_root *root, struct inode *inode, |
| 4155 | struct dentry *parent, int exists_only, | 4192 | struct dentry *parent, |
| 4193 | const loff_t start, | ||
| 4194 | const loff_t end, | ||
| 4195 | int exists_only, | ||
| 4156 | struct btrfs_log_ctx *ctx) | 4196 | struct btrfs_log_ctx *ctx) |
| 4157 | { | 4197 | { |
| 4158 | int inode_only = exists_only ? LOG_INODE_EXISTS : LOG_INODE_ALL; | 4198 | int inode_only = exists_only ? LOG_INODE_EXISTS : LOG_INODE_ALL; |
| @@ -4198,7 +4238,7 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans, | |||
| 4198 | if (ret) | 4238 | if (ret) |
| 4199 | goto end_no_trans; | 4239 | goto end_no_trans; |
| 4200 | 4240 | ||
| 4201 | ret = btrfs_log_inode(trans, root, inode, inode_only); | 4241 | ret = btrfs_log_inode(trans, root, inode, inode_only, start, end); |
| 4202 | if (ret) | 4242 | if (ret) |
| 4203 | goto end_trans; | 4243 | goto end_trans; |
| 4204 | 4244 | ||
| @@ -4226,7 +4266,8 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans, | |||
| 4226 | 4266 | ||
| 4227 | if (BTRFS_I(inode)->generation > | 4267 | if (BTRFS_I(inode)->generation > |
| 4228 | root->fs_info->last_trans_committed) { | 4268 | root->fs_info->last_trans_committed) { |
| 4229 | ret = btrfs_log_inode(trans, root, inode, inode_only); | 4269 | ret = btrfs_log_inode(trans, root, inode, inode_only, |
| 4270 | 0, LLONG_MAX); | ||
| 4230 | if (ret) | 4271 | if (ret) |
| 4231 | goto end_trans; | 4272 | goto end_trans; |
| 4232 | } | 4273 | } |
| @@ -4260,13 +4301,15 @@ end_no_trans: | |||
| 4260 | */ | 4301 | */ |
| 4261 | int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans, | 4302 | int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans, |
| 4262 | struct btrfs_root *root, struct dentry *dentry, | 4303 | struct btrfs_root *root, struct dentry *dentry, |
| 4304 | const loff_t start, | ||
| 4305 | const loff_t end, | ||
| 4263 | struct btrfs_log_ctx *ctx) | 4306 | struct btrfs_log_ctx *ctx) |
| 4264 | { | 4307 | { |
| 4265 | struct dentry *parent = dget_parent(dentry); | 4308 | struct dentry *parent = dget_parent(dentry); |
| 4266 | int ret; | 4309 | int ret; |
| 4267 | 4310 | ||
| 4268 | ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent, | 4311 | ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent, |
| 4269 | 0, ctx); | 4312 | start, end, 0, ctx); |
| 4270 | dput(parent); | 4313 | dput(parent); |
| 4271 | 4314 | ||
| 4272 | return ret; | 4315 | return ret; |
| @@ -4503,6 +4546,7 @@ int btrfs_log_new_name(struct btrfs_trans_handle *trans, | |||
| 4503 | root->fs_info->last_trans_committed)) | 4546 | root->fs_info->last_trans_committed)) |
| 4504 | return 0; | 4547 | return 0; |
| 4505 | 4548 | ||
| 4506 | return btrfs_log_inode_parent(trans, root, inode, parent, 1, NULL); | 4549 | return btrfs_log_inode_parent(trans, root, inode, parent, 0, |
| 4550 | LLONG_MAX, 1, NULL); | ||
| 4507 | } | 4551 | } |
| 4508 | 4552 | ||
