btrfs-progs: check: introduce traversal function for fsck
[platform/upstream/btrfs-progs.git] / extent-tree.c
index 231b4c4..6cc8d3f 100644 (file)
@@ -598,7 +598,7 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
 }
 #endif
 
-static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
+u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
 {
        u32 high_crc = ~(u32)0;
        u32 low_crc = ~(u32)0;
@@ -1686,7 +1686,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
                        }
                } else {
                        bytenr = btrfs_node_blockptr(buf, i);
-                       num_bytes = btrfs_level_size(root, level - 1);
+                       num_bytes = root->nodesize;
                        ret = process_func(trans, root, bytenr, num_bytes,
                                           parent, ref_root, level - 1, 0);
                        if (ret) {
@@ -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,36 +3977,102 @@ 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;
 
+       /*
+        * All supported file system should not use its 0 extent.
+        * As it's for hole
+        *
+        * And hole extent has no size limit, no need to loop.
+        */
        if (disk_bytenr == 0) {
                ret = btrfs_insert_file_extent(trans, root, objectid,
                                                file_pos, disk_bytenr,
                                                num_bytes, num_bytes);
                return ret;
        }
+       num_bytes = min_t(u64, num_bytes, BTRFS_MAX_EXTENT_SIZE);
 
-       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 +4080,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;
 }