From 33c949697e316f17bb5037ae83509f35ce28f6a9 Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Tue, 26 Jul 2016 13:30:05 +0100 Subject: New upstream release --- extent-tree.c | 251 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 185 insertions(+), 66 deletions(-) (limited to 'extent-tree.c') diff --git a/extent-tree.c b/extent-tree.c index 4a41717e..5ca53fa9 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -1904,6 +1904,16 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, thresh) return 0; + /* + * Avoid allocating given chunk type + */ + if (extent_root->fs_info->avoid_meta_chunk_alloc && + (flags & BTRFS_BLOCK_GROUP_METADATA)) + return 0; + if (extent_root->fs_info->avoid_sys_chunk_alloc && + (flags & BTRFS_BLOCK_GROUP_SYSTEM)) + return 0; + ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes, space_info->flags); if (ret == -ENOSPC) { @@ -2171,7 +2181,6 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_key key; struct btrfs_path *path; - struct btrfs_extent_ops *ops = root->fs_info->extent_ops; struct btrfs_root *extent_root = root->fs_info->extent_root; struct extent_buffer *leaf; struct btrfs_extent_item *ei; @@ -2372,14 +2381,6 @@ static int __free_extent(struct btrfs_trans_handle *trans, } } - if (ops && ops->free_extent) { - ret = ops->free_extent(root, bytenr, num_bytes); - if (ret > 0) { - pin = 0; - mark_free = 0; - } - } - if (pin) { ret = pin_down_bytes(trans, root, bytenr, num_bytes, is_data); @@ -2652,13 +2653,6 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, u64 alloc_profile; struct btrfs_fs_info *info = root->fs_info; - if (info->extent_ops) { - struct btrfs_extent_ops *ops = info->extent_ops; - ret = ops->alloc_extent(root, num_bytes, hint_byte, ins, !data); - BUG_ON(ret); - goto found; - } - if (data) { alloc_profile = info->avail_data_alloc_bits & info->data_alloc_profile; @@ -2692,7 +2686,6 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, trans->alloc_exclude_start, trans->alloc_exclude_nr, data); BUG_ON(ret); -found: clear_extent_dirty(&root->fs_info->free_space_cache, ins->objectid, ins->objectid + ins->offset - 1, GFP_NOFS); @@ -3909,16 +3902,74 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans, return 0; } +static void __get_extent_size(struct btrfs_root *root, struct btrfs_path *path, + u64 *start, u64 *len) +{ + struct btrfs_key key; + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + BUG_ON(!(key.type == BTRFS_EXTENT_ITEM_KEY || + key.type == BTRFS_METADATA_ITEM_KEY)); + *start = key.objectid; + if (key.type == BTRFS_EXTENT_ITEM_KEY) + *len = key.offset; + else + *len = root->nodesize; +} + /* - * Record a file extent. Do all the required works, such as inserting - * file extent item, inserting extent item and backref item into extent - * tree and updating block accounting. + * Find first overlap extent for range [bytenr, bytenr + len) + * Return 0 for found and point path to it. + * Return >0 for not found. + * Return <0 for err */ -int btrfs_record_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 file_pos, u64 disk_bytenr, - u64 num_bytes) +int btrfs_search_overlap_extent(struct btrfs_root *root, + struct btrfs_path *path, u64 bytenr, u64 len) +{ + struct btrfs_key key; + u64 cur_start; + u64 cur_len; + int ret; + + key.objectid = bytenr; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + return ret; + BUG_ON(ret == 0); + + ret = btrfs_previous_extent_item(root, path, 0); + if (ret < 0) + return ret; + /* no previous, check next extent */ + if (ret > 0) + goto next; + __get_extent_size(root, path, &cur_start, &cur_len); + /* Tail overlap */ + if (cur_start + cur_len > bytenr) + return 1; + +next: + ret = btrfs_next_extent_item(root, path, bytenr + len); + if (ret < 0) + return ret; + /* No next, prev already checked, no overlap */ + if (ret > 0) + return 0; + __get_extent_size(root, path, &cur_start, &cur_len); + /* head overlap*/ + if (cur_start < bytenr + len) + return 1; + return 0; +} + +static int __btrfs_record_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 file_pos, u64 disk_bytenr, + u64 *ret_num_bytes) { int ret; struct btrfs_fs_info *info = root->fs_info; @@ -3926,10 +3977,19 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans, struct extent_buffer *leaf; struct btrfs_file_extent_item *fi; struct btrfs_key ins_key; - struct btrfs_path path; + struct btrfs_path *path; struct btrfs_extent_item *ei; u64 nbytes; + u64 extent_num_bytes; + u64 extent_bytenr; + u64 extent_offset; + u64 num_bytes = *ret_num_bytes; + num_bytes = min_t(u64, num_bytes, BTRFS_MAX_EXTENT_SIZE); + /* + * All supported file system should not use its 0 extent. + * As it's for hole + */ if (disk_bytenr == 0) { ret = btrfs_insert_file_extent(trans, root, objectid, file_pos, disk_bytenr, @@ -3937,25 +3997,80 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans, return ret; } - btrfs_init_path(&path); + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + /* First to check extent overlap */ + ret = btrfs_search_overlap_extent(extent_root, path, disk_bytenr, + num_bytes); + if (ret < 0) + goto fail; + if (ret > 0) { + /* Found overlap */ + u64 cur_start; + u64 cur_len; + + __get_extent_size(extent_root, path, &cur_start, &cur_len); + /* + * For convert case, this extent should be a subset of + * existing one. + */ + BUG_ON(disk_bytenr < cur_start); + + extent_bytenr = cur_start; + extent_num_bytes = cur_len; + extent_offset = disk_bytenr - extent_bytenr; + } else { + /* No overlap, create new extent */ + btrfs_release_path(path); + ins_key.objectid = disk_bytenr; + ins_key.offset = num_bytes; + ins_key.type = BTRFS_EXTENT_ITEM_KEY; + + ret = btrfs_insert_empty_item(trans, extent_root, path, + &ins_key, sizeof(*ei)); + if (ret == 0) { + leaf = path->nodes[0]; + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_item); + + btrfs_set_extent_refs(leaf, ei, 0); + btrfs_set_extent_generation(leaf, ei, 0); + btrfs_set_extent_flags(leaf, ei, + BTRFS_EXTENT_FLAG_DATA); + btrfs_mark_buffer_dirty(leaf); + + ret = btrfs_update_block_group(trans, root, disk_bytenr, + num_bytes, 1, 0); + if (ret) + goto fail; + } else if (ret != -EEXIST) { + goto fail; + } + btrfs_extent_post_op(trans, extent_root); + extent_bytenr = disk_bytenr; + extent_num_bytes = num_bytes; + extent_offset = 0; + } + btrfs_release_path(path); ins_key.objectid = objectid; ins_key.offset = file_pos; btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY); - ret = btrfs_insert_empty_item(trans, root, &path, &ins_key, + ret = btrfs_insert_empty_item(trans, root, path, &ins_key, sizeof(*fi)); if (ret) goto fail; - leaf = path.nodes[0]; - fi = btrfs_item_ptr(leaf, path.slots[0], + leaf = path->nodes[0]; + fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); btrfs_set_file_extent_generation(leaf, fi, trans->transid); btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); - btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr); - btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes); - btrfs_set_file_extent_offset(leaf, fi, 0); + btrfs_set_file_extent_disk_bytenr(leaf, fi, extent_bytenr); + btrfs_set_file_extent_disk_num_bytes(leaf, fi, extent_num_bytes); + btrfs_set_file_extent_offset(leaf, fi, extent_offset); btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes); - btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes); + btrfs_set_file_extent_ram_bytes(leaf, fi, extent_num_bytes); btrfs_set_file_extent_compression(leaf, fi, 0); btrfs_set_file_extent_encryption(leaf, fi, 0); btrfs_set_file_extent_other_encoding(leaf, fi, 0); @@ -3963,43 +4078,47 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans, nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes; btrfs_set_stack_inode_nbytes(inode, nbytes); + btrfs_release_path(path); - btrfs_release_path(&path); - - ins_key.objectid = disk_bytenr; - ins_key.offset = num_bytes; - ins_key.type = BTRFS_EXTENT_ITEM_KEY; - - ret = btrfs_insert_empty_item(trans, extent_root, &path, - &ins_key, sizeof(*ei)); - if (ret == 0) { - leaf = path.nodes[0]; - ei = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_extent_item); - - btrfs_set_extent_refs(leaf, ei, 0); - btrfs_set_extent_generation(leaf, ei, 0); - btrfs_set_extent_flags(leaf, ei, BTRFS_EXTENT_FLAG_DATA); - - btrfs_mark_buffer_dirty(leaf); - - ret = btrfs_update_block_group(trans, root, disk_bytenr, - num_bytes, 1, 0); - if (ret) - goto fail; - } else if (ret != -EEXIST) { - goto fail; - } - btrfs_extent_post_op(trans, extent_root); - - ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0, - root->root_key.objectid, - objectid, file_pos); + ret = btrfs_inc_extent_ref(trans, root, extent_bytenr, extent_num_bytes, + 0, root->root_key.objectid, objectid, + file_pos - extent_offset); if (ret) goto fail; ret = 0; + *ret_num_bytes = min(extent_num_bytes - extent_offset, num_bytes); fail: - btrfs_release_path(&path); + btrfs_free_path(path); + return ret; +} + +/* + * Record a file extent. Do all the required works, such as inserting + * file extent item, inserting extent item and backref item into extent + * tree and updating block accounting. + */ +int btrfs_record_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 file_pos, u64 disk_bytenr, + u64 num_bytes) +{ + u64 cur_disk_bytenr = disk_bytenr; + u64 cur_file_pos = file_pos; + u64 cur_num_bytes = num_bytes; + int ret = 0; + + while (num_bytes > 0) { + ret = __btrfs_record_file_extent(trans, root, objectid, + inode, cur_file_pos, + cur_disk_bytenr, + &cur_num_bytes); + if (ret < 0) + break; + cur_disk_bytenr += cur_num_bytes; + cur_file_pos += cur_num_bytes; + num_bytes -= cur_num_bytes; + } return ret; } -- cgit v1.2.3