diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 338 |
1 files changed, 194 insertions, 144 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index ab7168ee618f..72444811d275 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -3150,145 +3150,220 @@ static int extent_cmp(void *priv, struct list_head *a, struct list_head *b) | |||
3150 | return 0; | 3150 | return 0; |
3151 | } | 3151 | } |
3152 | 3152 | ||
3153 | struct log_args { | 3153 | static int drop_adjacent_extents(struct btrfs_trans_handle *trans, |
3154 | struct extent_buffer *src; | 3154 | struct btrfs_root *root, struct inode *inode, |
3155 | u64 next_offset; | 3155 | struct extent_map *em, |
3156 | int start_slot; | 3156 | struct btrfs_path *path) |
3157 | int nr; | 3157 | { |
3158 | }; | 3158 | struct btrfs_file_extent_item *fi; |
3159 | struct extent_buffer *leaf; | ||
3160 | struct btrfs_key key, new_key; | ||
3161 | struct btrfs_map_token token; | ||
3162 | u64 extent_end; | ||
3163 | u64 extent_offset = 0; | ||
3164 | int extent_type; | ||
3165 | int del_slot = 0; | ||
3166 | int del_nr = 0; | ||
3167 | int ret = 0; | ||
3168 | |||
3169 | while (1) { | ||
3170 | btrfs_init_map_token(&token); | ||
3171 | leaf = path->nodes[0]; | ||
3172 | path->slots[0]++; | ||
3173 | if (path->slots[0] >= btrfs_header_nritems(leaf)) { | ||
3174 | if (del_nr) { | ||
3175 | ret = btrfs_del_items(trans, root, path, | ||
3176 | del_slot, del_nr); | ||
3177 | if (ret) | ||
3178 | return ret; | ||
3179 | del_nr = 0; | ||
3180 | } | ||
3181 | |||
3182 | ret = btrfs_next_leaf_write(trans, root, path, 1); | ||
3183 | if (ret < 0) | ||
3184 | return ret; | ||
3185 | if (ret > 0) | ||
3186 | return 0; | ||
3187 | leaf = path->nodes[0]; | ||
3188 | } | ||
3189 | |||
3190 | btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); | ||
3191 | if (key.objectid != btrfs_ino(inode) || | ||
3192 | key.type != BTRFS_EXTENT_DATA_KEY || | ||
3193 | key.offset >= em->start + em->len) | ||
3194 | break; | ||
3195 | |||
3196 | fi = btrfs_item_ptr(leaf, path->slots[0], | ||
3197 | struct btrfs_file_extent_item); | ||
3198 | extent_type = btrfs_token_file_extent_type(leaf, fi, &token); | ||
3199 | if (extent_type == BTRFS_FILE_EXTENT_REG || | ||
3200 | extent_type == BTRFS_FILE_EXTENT_PREALLOC) { | ||
3201 | extent_offset = btrfs_token_file_extent_offset(leaf, | ||
3202 | fi, &token); | ||
3203 | extent_end = key.offset + | ||
3204 | btrfs_token_file_extent_num_bytes(leaf, fi, | ||
3205 | &token); | ||
3206 | } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) { | ||
3207 | extent_end = key.offset + | ||
3208 | btrfs_file_extent_inline_len(leaf, fi); | ||
3209 | } else { | ||
3210 | BUG(); | ||
3211 | } | ||
3212 | |||
3213 | if (extent_end <= em->len + em->start) { | ||
3214 | if (!del_nr) { | ||
3215 | del_slot = path->slots[0]; | ||
3216 | } | ||
3217 | del_nr++; | ||
3218 | continue; | ||
3219 | } | ||
3220 | |||
3221 | /* | ||
3222 | * Ok so we'll ignore previous items if we log a new extent, | ||
3223 | * which can lead to overlapping extents, so if we have an | ||
3224 | * existing extent we want to adjust we _have_ to check the next | ||
3225 | * guy to make sure we even need this extent anymore, this keeps | ||
3226 | * us from panicing in set_item_key_safe. | ||
3227 | */ | ||
3228 | if (path->slots[0] < btrfs_header_nritems(leaf) - 1) { | ||
3229 | struct btrfs_key tmp_key; | ||
3230 | |||
3231 | btrfs_item_key_to_cpu(leaf, &tmp_key, | ||
3232 | path->slots[0] + 1); | ||
3233 | if (tmp_key.objectid == btrfs_ino(inode) && | ||
3234 | tmp_key.type == BTRFS_EXTENT_DATA_KEY && | ||
3235 | tmp_key.offset <= em->start + em->len) { | ||
3236 | if (!del_nr) | ||
3237 | del_slot = path->slots[0]; | ||
3238 | del_nr++; | ||
3239 | continue; | ||
3240 | } | ||
3241 | } | ||
3242 | |||
3243 | BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE); | ||
3244 | memcpy(&new_key, &key, sizeof(new_key)); | ||
3245 | new_key.offset = em->start + em->len; | ||
3246 | btrfs_set_item_key_safe(trans, root, path, &new_key); | ||
3247 | extent_offset += em->start + em->len - key.offset; | ||
3248 | btrfs_set_token_file_extent_offset(leaf, fi, extent_offset, | ||
3249 | &token); | ||
3250 | btrfs_set_token_file_extent_num_bytes(leaf, fi, extent_end - | ||
3251 | (em->start + em->len), | ||
3252 | &token); | ||
3253 | btrfs_mark_buffer_dirty(leaf); | ||
3254 | } | ||
3255 | |||
3256 | if (del_nr) | ||
3257 | ret = btrfs_del_items(trans, root, path, del_slot, del_nr); | ||
3258 | |||
3259 | return ret; | ||
3260 | } | ||
3159 | 3261 | ||
3160 | static int log_one_extent(struct btrfs_trans_handle *trans, | 3262 | static int log_one_extent(struct btrfs_trans_handle *trans, |
3161 | struct inode *inode, struct btrfs_root *root, | 3263 | struct inode *inode, struct btrfs_root *root, |
3162 | struct extent_map *em, struct btrfs_path *path, | 3264 | struct extent_map *em, struct btrfs_path *path) |
3163 | struct btrfs_path *dst_path, struct log_args *args) | ||
3164 | { | 3265 | { |
3165 | struct btrfs_root *log = root->log_root; | 3266 | struct btrfs_root *log = root->log_root; |
3267 | struct btrfs_file_extent_item *fi; | ||
3268 | struct extent_buffer *leaf; | ||
3269 | struct list_head ordered_sums; | ||
3166 | struct btrfs_key key; | 3270 | struct btrfs_key key; |
3167 | u64 start = em->mod_start; | 3271 | u64 csum_offset = em->mod_start - em->start; |
3168 | u64 search_start = start; | 3272 | u64 csum_len = em->mod_len; |
3169 | u64 len = em->mod_len; | 3273 | u64 extent_offset = em->start - em->orig_start; |
3170 | u64 num_bytes; | 3274 | u64 block_len; |
3171 | int nritems; | ||
3172 | int ret; | 3275 | int ret; |
3276 | bool skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM; | ||
3173 | 3277 | ||
3174 | if (BTRFS_I(inode)->logged_trans == trans->transid) { | 3278 | INIT_LIST_HEAD(&ordered_sums); |
3175 | ret = __btrfs_drop_extents(trans, log, inode, dst_path, start, | 3279 | key.objectid = btrfs_ino(inode); |
3176 | start + len, NULL, 0); | 3280 | key.type = BTRFS_EXTENT_DATA_KEY; |
3177 | if (ret) | 3281 | key.offset = em->start; |
3178 | return ret; | 3282 | path->really_keep_locks = 1; |
3283 | |||
3284 | ret = btrfs_insert_empty_item(trans, log, path, &key, sizeof(*fi)); | ||
3285 | if (ret && ret != -EEXIST) { | ||
3286 | path->really_keep_locks = 0; | ||
3287 | return ret; | ||
3288 | } | ||
3289 | leaf = path->nodes[0]; | ||
3290 | fi = btrfs_item_ptr(leaf, path->slots[0], | ||
3291 | struct btrfs_file_extent_item); | ||
3292 | btrfs_set_file_extent_generation(leaf, fi, em->generation); | ||
3293 | if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) { | ||
3294 | skip_csum = true; | ||
3295 | btrfs_set_file_extent_type(leaf, fi, | ||
3296 | BTRFS_FILE_EXTENT_PREALLOC); | ||
3297 | } else { | ||
3298 | btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); | ||
3299 | if (em->block_start == 0) | ||
3300 | skip_csum = true; | ||
3301 | } | ||
3302 | |||
3303 | block_len = max(em->block_len, em->orig_block_len); | ||
3304 | if (em->compress_type != BTRFS_COMPRESS_NONE) { | ||
3305 | btrfs_set_file_extent_disk_bytenr(leaf, fi, em->block_start); | ||
3306 | btrfs_set_file_extent_disk_num_bytes(leaf, fi, block_len); | ||
3307 | } else if (em->block_start < EXTENT_MAP_LAST_BYTE) { | ||
3308 | btrfs_set_file_extent_disk_bytenr(leaf, fi, | ||
3309 | em->block_start - | ||
3310 | extent_offset); | ||
3311 | btrfs_set_file_extent_disk_num_bytes(leaf, fi, block_len); | ||
3312 | } else { | ||
3313 | btrfs_set_file_extent_disk_bytenr(leaf, fi, 0); | ||
3314 | btrfs_set_file_extent_disk_num_bytes(leaf, fi, 0); | ||
3179 | } | 3315 | } |
3180 | 3316 | ||
3181 | while (len) { | 3317 | btrfs_set_file_extent_offset(leaf, fi, em->start - em->orig_start); |
3182 | if (args->nr) | 3318 | btrfs_set_file_extent_num_bytes(leaf, fi, em->len); |
3183 | goto next_slot; | 3319 | btrfs_set_file_extent_ram_bytes(leaf, fi, em->len); |
3184 | again: | 3320 | btrfs_set_file_extent_compression(leaf, fi, em->compress_type); |
3185 | key.objectid = btrfs_ino(inode); | 3321 | btrfs_set_file_extent_encryption(leaf, fi, 0); |
3186 | key.type = BTRFS_EXTENT_DATA_KEY; | 3322 | btrfs_set_file_extent_other_encoding(leaf, fi, 0); |
3187 | key.offset = search_start; | 3323 | btrfs_mark_buffer_dirty(leaf); |
3188 | |||
3189 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); | ||
3190 | if (ret < 0) | ||
3191 | return ret; | ||
3192 | |||
3193 | if (ret) { | ||
3194 | /* | ||
3195 | * A rare case were we can have an em for a section of a | ||
3196 | * larger extent so we need to make sure that this em | ||
3197 | * falls within the extent we've found. If not we just | ||
3198 | * bail and go back to ye-olde way of doing things but | ||
3199 | * it happens often enough in testing that we need to do | ||
3200 | * this dance to make sure. | ||
3201 | */ | ||
3202 | do { | ||
3203 | if (path->slots[0] == 0) { | ||
3204 | btrfs_release_path(path); | ||
3205 | if (search_start == 0) | ||
3206 | return -ENOENT; | ||
3207 | search_start--; | ||
3208 | goto again; | ||
3209 | } | ||
3210 | 3324 | ||
3211 | path->slots[0]--; | 3325 | /* |
3212 | btrfs_item_key_to_cpu(path->nodes[0], &key, | 3326 | * Have to check the extent to the right of us to make sure it doesn't |
3213 | path->slots[0]); | 3327 | * fall in our current range. We're ok if the previous extent is in our |
3214 | if (key.objectid != btrfs_ino(inode) || | 3328 | * range since the recovery stuff will run us in key order and thus just |
3215 | key.type != BTRFS_EXTENT_DATA_KEY) { | 3329 | * drop the part we overwrote. |
3216 | btrfs_release_path(path); | 3330 | */ |
3217 | return -ENOENT; | 3331 | ret = drop_adjacent_extents(trans, log, inode, em, path); |
3218 | } | 3332 | btrfs_release_path(path); |
3219 | } while (key.offset > start); | 3333 | path->really_keep_locks = 0; |
3334 | if (ret) { | ||
3335 | return ret; | ||
3336 | } | ||
3220 | 3337 | ||
3221 | num_bytes = btrfs_file_extent_length(path); | 3338 | if (skip_csum) |
3222 | if (key.offset + num_bytes <= start) { | 3339 | return 0; |
3223 | btrfs_release_path(path); | ||
3224 | return -ENOENT; | ||
3225 | } | ||
3226 | } | ||
3227 | args->src = path->nodes[0]; | ||
3228 | next_slot: | ||
3229 | btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); | ||
3230 | num_bytes = btrfs_file_extent_length(path); | ||
3231 | if (args->nr && | ||
3232 | args->start_slot + args->nr == path->slots[0]) { | ||
3233 | args->nr++; | ||
3234 | } else if (args->nr) { | ||
3235 | ret = copy_items(trans, inode, dst_path, args->src, | ||
3236 | args->start_slot, args->nr, | ||
3237 | LOG_INODE_ALL); | ||
3238 | if (ret) | ||
3239 | return ret; | ||
3240 | args->nr = 1; | ||
3241 | args->start_slot = path->slots[0]; | ||
3242 | } else if (!args->nr) { | ||
3243 | args->nr = 1; | ||
3244 | args->start_slot = path->slots[0]; | ||
3245 | } | ||
3246 | nritems = btrfs_header_nritems(path->nodes[0]); | ||
3247 | path->slots[0]++; | ||
3248 | if (len < num_bytes) { | ||
3249 | /* I _think_ this is ok, envision we write to a | ||
3250 | * preallocated space that is adjacent to a previously | ||
3251 | * written preallocated space that gets merged when we | ||
3252 | * mark this preallocated space written. If we do not | ||
3253 | * have the adjacent extent in cache then when we copy | ||
3254 | * this extent it could end up being larger than our EM | ||
3255 | * thinks it is, which is a-ok, so just set len to 0. | ||
3256 | */ | ||
3257 | len = 0; | ||
3258 | } else { | ||
3259 | len -= num_bytes; | ||
3260 | } | ||
3261 | start = key.offset + num_bytes; | ||
3262 | args->next_offset = start; | ||
3263 | search_start = start; | ||
3264 | 3340 | ||
3265 | if (path->slots[0] < nritems) { | 3341 | /* block start is already adjusted for the file extent offset. */ |
3266 | if (len) | 3342 | ret = btrfs_lookup_csums_range(log->fs_info->csum_root, |
3267 | goto next_slot; | 3343 | em->block_start + csum_offset, |
3268 | break; | 3344 | em->block_start + csum_offset + |
3269 | } | 3345 | csum_len - 1, &ordered_sums, 0); |
3346 | if (ret) | ||
3347 | return ret; | ||
3270 | 3348 | ||
3271 | if (args->nr) { | 3349 | while (!list_empty(&ordered_sums)) { |
3272 | ret = copy_items(trans, inode, dst_path, args->src, | 3350 | struct btrfs_ordered_sum *sums = list_entry(ordered_sums.next, |
3273 | args->start_slot, args->nr, | 3351 | struct btrfs_ordered_sum, |
3274 | LOG_INODE_ALL); | 3352 | list); |
3275 | if (ret) | 3353 | if (!ret) |
3276 | return ret; | 3354 | ret = btrfs_csum_file_blocks(trans, log, sums); |
3277 | args->nr = 0; | 3355 | list_del(&sums->list); |
3278 | btrfs_release_path(path); | 3356 | kfree(sums); |
3279 | } | ||
3280 | } | 3357 | } |
3281 | 3358 | ||
3282 | return 0; | 3359 | return ret; |
3283 | } | 3360 | } |
3284 | 3361 | ||
3285 | static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, | 3362 | static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, |
3286 | struct btrfs_root *root, | 3363 | struct btrfs_root *root, |
3287 | struct inode *inode, | 3364 | struct inode *inode, |
3288 | struct btrfs_path *path, | 3365 | struct btrfs_path *path) |
3289 | struct btrfs_path *dst_path) | ||
3290 | { | 3366 | { |
3291 | struct log_args args; | ||
3292 | struct extent_map *em, *n; | 3367 | struct extent_map *em, *n; |
3293 | struct list_head extents; | 3368 | struct list_head extents; |
3294 | struct extent_map_tree *tree = &BTRFS_I(inode)->extent_tree; | 3369 | struct extent_map_tree *tree = &BTRFS_I(inode)->extent_tree; |
@@ -3297,8 +3372,6 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, | |||
3297 | 3372 | ||
3298 | INIT_LIST_HEAD(&extents); | 3373 | INIT_LIST_HEAD(&extents); |
3299 | 3374 | ||
3300 | memset(&args, 0, sizeof(args)); | ||
3301 | |||
3302 | write_lock(&tree->lock); | 3375 | write_lock(&tree->lock); |
3303 | test_gen = root->fs_info->last_trans_committed; | 3376 | test_gen = root->fs_info->last_trans_committed; |
3304 | 3377 | ||
@@ -3331,34 +3404,13 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, | |||
3331 | 3404 | ||
3332 | write_unlock(&tree->lock); | 3405 | write_unlock(&tree->lock); |
3333 | 3406 | ||
3334 | /* | 3407 | ret = log_one_extent(trans, inode, root, em, path); |
3335 | * If the previous EM and the last extent we left off on aren't | ||
3336 | * sequential then we need to copy the items we have and redo | ||
3337 | * our search | ||
3338 | */ | ||
3339 | if (args.nr && em->mod_start != args.next_offset) { | ||
3340 | ret = copy_items(trans, inode, dst_path, args.src, | ||
3341 | args.start_slot, args.nr, | ||
3342 | LOG_INODE_ALL); | ||
3343 | if (ret) { | ||
3344 | free_extent_map(em); | ||
3345 | write_lock(&tree->lock); | ||
3346 | continue; | ||
3347 | } | ||
3348 | btrfs_release_path(path); | ||
3349 | args.nr = 0; | ||
3350 | } | ||
3351 | |||
3352 | ret = log_one_extent(trans, inode, root, em, path, dst_path, &args); | ||
3353 | free_extent_map(em); | 3408 | free_extent_map(em); |
3354 | write_lock(&tree->lock); | 3409 | write_lock(&tree->lock); |
3355 | } | 3410 | } |
3356 | WARN_ON(!list_empty(&extents)); | 3411 | WARN_ON(!list_empty(&extents)); |
3357 | write_unlock(&tree->lock); | 3412 | write_unlock(&tree->lock); |
3358 | 3413 | ||
3359 | if (!ret && args.nr) | ||
3360 | ret = copy_items(trans, inode, dst_path, args.src, | ||
3361 | args.start_slot, args.nr, LOG_INODE_ALL); | ||
3362 | btrfs_release_path(path); | 3414 | btrfs_release_path(path); |
3363 | return ret; | 3415 | return ret; |
3364 | } | 3416 | } |
@@ -3551,10 +3603,8 @@ next_slot: | |||
3551 | 3603 | ||
3552 | log_extents: | 3604 | log_extents: |
3553 | if (fast_search) { | 3605 | if (fast_search) { |
3554 | btrfs_release_path(path); | ||
3555 | btrfs_release_path(dst_path); | 3606 | btrfs_release_path(dst_path); |
3556 | ret = btrfs_log_changed_extents(trans, root, inode, path, | 3607 | ret = btrfs_log_changed_extents(trans, root, inode, dst_path); |
3557 | dst_path); | ||
3558 | if (ret) { | 3608 | if (ret) { |
3559 | err = ret; | 3609 | err = ret; |
3560 | goto out_unlock; | 3610 | goto out_unlock; |