diff options
Diffstat (limited to 'fs/btrfs/relocation.c')
-rw-r--r-- | fs/btrfs/relocation.c | 126 |
1 files changed, 116 insertions, 10 deletions
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index b26a5aea41b4..8a2c2a07987b 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "async-thread.h" | 31 | #include "async-thread.h" |
32 | #include "free-space-cache.h" | 32 | #include "free-space-cache.h" |
33 | #include "inode-map.h" | 33 | #include "inode-map.h" |
34 | #include "qgroup.h" | ||
34 | 35 | ||
35 | /* | 36 | /* |
36 | * backref_node, mapping_node and tree_block start with this | 37 | * backref_node, mapping_node and tree_block start with this |
@@ -3037,15 +3038,19 @@ int prealloc_file_extent_cluster(struct inode *inode, | |||
3037 | u64 num_bytes; | 3038 | u64 num_bytes; |
3038 | int nr = 0; | 3039 | int nr = 0; |
3039 | int ret = 0; | 3040 | int ret = 0; |
3041 | u64 prealloc_start = cluster->start - offset; | ||
3042 | u64 prealloc_end = cluster->end - offset; | ||
3043 | u64 cur_offset; | ||
3040 | 3044 | ||
3041 | BUG_ON(cluster->start != cluster->boundary[0]); | 3045 | BUG_ON(cluster->start != cluster->boundary[0]); |
3042 | inode_lock(inode); | 3046 | inode_lock(inode); |
3043 | 3047 | ||
3044 | ret = btrfs_check_data_free_space(inode, cluster->start, | 3048 | ret = btrfs_check_data_free_space(inode, prealloc_start, |
3045 | cluster->end + 1 - cluster->start); | 3049 | prealloc_end + 1 - prealloc_start); |
3046 | if (ret) | 3050 | if (ret) |
3047 | goto out; | 3051 | goto out; |
3048 | 3052 | ||
3053 | cur_offset = prealloc_start; | ||
3049 | while (nr < cluster->nr) { | 3054 | while (nr < cluster->nr) { |
3050 | start = cluster->boundary[nr] - offset; | 3055 | start = cluster->boundary[nr] - offset; |
3051 | if (nr + 1 < cluster->nr) | 3056 | if (nr + 1 < cluster->nr) |
@@ -3055,16 +3060,21 @@ int prealloc_file_extent_cluster(struct inode *inode, | |||
3055 | 3060 | ||
3056 | lock_extent(&BTRFS_I(inode)->io_tree, start, end); | 3061 | lock_extent(&BTRFS_I(inode)->io_tree, start, end); |
3057 | num_bytes = end + 1 - start; | 3062 | num_bytes = end + 1 - start; |
3063 | if (cur_offset < start) | ||
3064 | btrfs_free_reserved_data_space(inode, cur_offset, | ||
3065 | start - cur_offset); | ||
3058 | ret = btrfs_prealloc_file_range(inode, 0, start, | 3066 | ret = btrfs_prealloc_file_range(inode, 0, start, |
3059 | num_bytes, num_bytes, | 3067 | num_bytes, num_bytes, |
3060 | end + 1, &alloc_hint); | 3068 | end + 1, &alloc_hint); |
3069 | cur_offset = end + 1; | ||
3061 | unlock_extent(&BTRFS_I(inode)->io_tree, start, end); | 3070 | unlock_extent(&BTRFS_I(inode)->io_tree, start, end); |
3062 | if (ret) | 3071 | if (ret) |
3063 | break; | 3072 | break; |
3064 | nr++; | 3073 | nr++; |
3065 | } | 3074 | } |
3066 | btrfs_free_reserved_data_space(inode, cluster->start, | 3075 | if (cur_offset < prealloc_end) |
3067 | cluster->end + 1 - cluster->start); | 3076 | btrfs_free_reserved_data_space(inode, cur_offset, |
3077 | prealloc_end + 1 - cur_offset); | ||
3068 | out: | 3078 | out: |
3069 | inode_unlock(inode); | 3079 | inode_unlock(inode); |
3070 | return ret; | 3080 | return ret; |
@@ -3916,6 +3926,90 @@ int prepare_to_relocate(struct reloc_control *rc) | |||
3916 | return 0; | 3926 | return 0; |
3917 | } | 3927 | } |
3918 | 3928 | ||
3929 | /* | ||
3930 | * Qgroup fixer for data chunk relocation. | ||
3931 | * The data relocation is done in the following steps | ||
3932 | * 1) Copy data extents into data reloc tree | ||
3933 | * 2) Create tree reloc tree(special snapshot) for related subvolumes | ||
3934 | * 3) Modify file extents in tree reloc tree | ||
3935 | * 4) Merge tree reloc tree with original fs tree, by swapping tree blocks | ||
3936 | * | ||
3937 | * The problem is, data and tree reloc tree are not accounted to qgroup, | ||
3938 | * and 4) will only info qgroup to track tree blocks change, not file extents | ||
3939 | * in the tree blocks. | ||
3940 | * | ||
3941 | * The good news is, related data extents are all in data reloc tree, so we | ||
3942 | * only need to info qgroup to track all file extents in data reloc tree | ||
3943 | * before commit trans. | ||
3944 | */ | ||
3945 | static int qgroup_fix_relocated_data_extents(struct btrfs_trans_handle *trans, | ||
3946 | struct reloc_control *rc) | ||
3947 | { | ||
3948 | struct btrfs_fs_info *fs_info = rc->extent_root->fs_info; | ||
3949 | struct inode *inode = rc->data_inode; | ||
3950 | struct btrfs_root *data_reloc_root = BTRFS_I(inode)->root; | ||
3951 | struct btrfs_path *path; | ||
3952 | struct btrfs_key key; | ||
3953 | int ret = 0; | ||
3954 | |||
3955 | if (!fs_info->quota_enabled) | ||
3956 | return 0; | ||
3957 | |||
3958 | /* | ||
3959 | * Only for stage where we update data pointers the qgroup fix is | ||
3960 | * valid. | ||
3961 | * For MOVING_DATA stage, we will miss the timing of swapping tree | ||
3962 | * blocks, and won't fix it. | ||
3963 | */ | ||
3964 | if (!(rc->stage == UPDATE_DATA_PTRS && rc->extents_found)) | ||
3965 | return 0; | ||
3966 | |||
3967 | path = btrfs_alloc_path(); | ||
3968 | if (!path) | ||
3969 | return -ENOMEM; | ||
3970 | key.objectid = btrfs_ino(inode); | ||
3971 | key.type = BTRFS_EXTENT_DATA_KEY; | ||
3972 | key.offset = 0; | ||
3973 | |||
3974 | ret = btrfs_search_slot(NULL, data_reloc_root, &key, path, 0, 0); | ||
3975 | if (ret < 0) | ||
3976 | goto out; | ||
3977 | |||
3978 | lock_extent(&BTRFS_I(inode)->io_tree, 0, (u64)-1); | ||
3979 | while (1) { | ||
3980 | struct btrfs_file_extent_item *fi; | ||
3981 | |||
3982 | btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); | ||
3983 | if (key.objectid > btrfs_ino(inode)) | ||
3984 | break; | ||
3985 | if (key.type != BTRFS_EXTENT_DATA_KEY) | ||
3986 | goto next; | ||
3987 | fi = btrfs_item_ptr(path->nodes[0], path->slots[0], | ||
3988 | struct btrfs_file_extent_item); | ||
3989 | if (btrfs_file_extent_type(path->nodes[0], fi) != | ||
3990 | BTRFS_FILE_EXTENT_REG) | ||
3991 | goto next; | ||
3992 | ret = btrfs_qgroup_insert_dirty_extent(trans, fs_info, | ||
3993 | btrfs_file_extent_disk_bytenr(path->nodes[0], fi), | ||
3994 | btrfs_file_extent_disk_num_bytes(path->nodes[0], fi), | ||
3995 | GFP_NOFS); | ||
3996 | if (ret < 0) | ||
3997 | break; | ||
3998 | next: | ||
3999 | ret = btrfs_next_item(data_reloc_root, path); | ||
4000 | if (ret < 0) | ||
4001 | break; | ||
4002 | if (ret > 0) { | ||
4003 | ret = 0; | ||
4004 | break; | ||
4005 | } | ||
4006 | } | ||
4007 | unlock_extent(&BTRFS_I(inode)->io_tree, 0 , (u64)-1); | ||
4008 | out: | ||
4009 | btrfs_free_path(path); | ||
4010 | return ret; | ||
4011 | } | ||
4012 | |||
3919 | static noinline_for_stack int relocate_block_group(struct reloc_control *rc) | 4013 | static noinline_for_stack int relocate_block_group(struct reloc_control *rc) |
3920 | { | 4014 | { |
3921 | struct rb_root blocks = RB_ROOT; | 4015 | struct rb_root blocks = RB_ROOT; |
@@ -4102,10 +4196,16 @@ restart: | |||
4102 | 4196 | ||
4103 | /* get rid of pinned extents */ | 4197 | /* get rid of pinned extents */ |
4104 | trans = btrfs_join_transaction(rc->extent_root); | 4198 | trans = btrfs_join_transaction(rc->extent_root); |
4105 | if (IS_ERR(trans)) | 4199 | if (IS_ERR(trans)) { |
4106 | err = PTR_ERR(trans); | 4200 | err = PTR_ERR(trans); |
4107 | else | 4201 | goto out_free; |
4108 | btrfs_commit_transaction(trans, rc->extent_root); | 4202 | } |
4203 | err = qgroup_fix_relocated_data_extents(trans, rc); | ||
4204 | if (err < 0) { | ||
4205 | btrfs_abort_transaction(trans, err); | ||
4206 | goto out_free; | ||
4207 | } | ||
4208 | btrfs_commit_transaction(trans, rc->extent_root); | ||
4109 | out_free: | 4209 | out_free: |
4110 | btrfs_free_block_rsv(rc->extent_root, rc->block_rsv); | 4210 | btrfs_free_block_rsv(rc->extent_root, rc->block_rsv); |
4111 | btrfs_free_path(path); | 4211 | btrfs_free_path(path); |
@@ -4468,10 +4568,16 @@ int btrfs_recover_relocation(struct btrfs_root *root) | |||
4468 | unset_reloc_control(rc); | 4568 | unset_reloc_control(rc); |
4469 | 4569 | ||
4470 | trans = btrfs_join_transaction(rc->extent_root); | 4570 | trans = btrfs_join_transaction(rc->extent_root); |
4471 | if (IS_ERR(trans)) | 4571 | if (IS_ERR(trans)) { |
4472 | err = PTR_ERR(trans); | 4572 | err = PTR_ERR(trans); |
4473 | else | 4573 | goto out_free; |
4474 | err = btrfs_commit_transaction(trans, rc->extent_root); | 4574 | } |
4575 | err = qgroup_fix_relocated_data_extents(trans, rc); | ||
4576 | if (err < 0) { | ||
4577 | btrfs_abort_transaction(trans, err); | ||
4578 | goto out_free; | ||
4579 | } | ||
4580 | err = btrfs_commit_transaction(trans, rc->extent_root); | ||
4475 | out_free: | 4581 | out_free: |
4476 | kfree(rc); | 4582 | kfree(rc); |
4477 | out: | 4583 | out: |