Btrfs: Fix cache_block_group to catch holes at the start of the group
[deliverable/linux.git] / fs / btrfs / extent-tree.c
index c31e84d426533209872ac54a1cbfb8e5dc4db48b..9151850266b0bb7ae85cec3703d1bc4780f1bdb8 100644 (file)
@@ -39,6 +39,7 @@ static int cache_block_group(struct btrfs_root *root,
        u64 i;
        u64 last = 0;
        u64 hole_size;
+       u64 first_free;
        int found = 0;
 
        root = root->fs_info->extent_root;
@@ -51,16 +52,22 @@ static int cache_block_group(struct btrfs_root *root,
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
+
        path->reada = 2;
+       first_free = block_group->key.objectid;
        key.objectid = block_group->key.objectid;
        key.flags = 0;
        key.offset = 0;
+
        btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+
        if (ret < 0)
                return ret;
+
        if (ret && path->slots[0] > 0)
                path->slots[0]--;
+
        while(1) {
                leaf = btrfs_buffer_leaf(path->nodes[0]);
                slot = path->slots[0];
@@ -71,50 +78,48 @@ static int cache_block_group(struct btrfs_root *root,
                        if (ret == 0) {
                                continue;
                        } else {
-                               if (found) {
-                                       hole_size = block_group->key.objectid +
-                                               block_group->key.offset - last;
-                               } else {
-                                       last = block_group->key.objectid;
-                                       hole_size = block_group->key.offset;
-                               }
-                               for (i = 0; i < hole_size; i++) {
-                                       set_radix_bit(extent_radix,
-                                                     last + i);
-                               }
                                break;
                        }
                }
+
                btrfs_disk_key_to_cpu(&key, &leaf->items[slot].key);
+               if (key.objectid < block_group->key.objectid) {
+                       if (key.objectid + key.offset > first_free)
+                               first_free = key.objectid + key.offset;
+                       goto next;
+               }
+
                if (key.objectid >= block_group->key.objectid +
                    block_group->key.offset) {
-                       if (found) {
-                               hole_size = block_group->key.objectid +
-                                       block_group->key.offset - last;
-                       } else {
-                               last = block_group->key.objectid;
-                               hole_size = block_group->key.offset;
-                       }
-                       for (i = 0; i < hole_size; i++) {
-                               set_radix_bit(extent_radix, last + i);
-                       }
                        break;
                }
+
                if (btrfs_key_type(&key) == BTRFS_EXTENT_ITEM_KEY) {
                        if (!found) {
-                               last = key.objectid + key.offset;
+                               last = first_free;
                                found = 1;
-                       } else {
-                               hole_size = key.objectid - last;
-                               for (i = 0; i < hole_size; i++) {
-                                       set_radix_bit(extent_radix, last + i);
-                               }
-                               last = key.objectid + key.offset;
                        }
+                       hole_size = key.objectid - last;
+                       for (i = 0; i < hole_size; i++) {
+                               set_radix_bit(extent_radix, last + i);
+                       }
+                       last = key.objectid + key.offset;
                }
+next:
                path->slots[0]++;
        }
 
+       if (!found)
+               last = first_free;
+       if (block_group->key.objectid +
+           block_group->key.offset > last) {
+               hole_size = block_group->key.objectid +
+                       block_group->key.offset - last;
+               for (i = 0; i < hole_size; i++) {
+                       set_radix_bit(extent_radix,
+                                       last + i);
+               }
+       }
        block_group->cached = 1;
 err:
        btrfs_free_path(path);
@@ -244,7 +249,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
        if (search_start) {
                struct btrfs_block_group_cache *shint;
                shint = btrfs_lookup_block_group(info, search_start);
-               if (shint->data == data) {
+               if (shint && shint->data == data) {
                        used = btrfs_block_group_used(&shint->item);
                        if (used + shint->pinned <
                            div_factor(shint->key.offset, factor)) {
@@ -858,16 +863,23 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
        btrfs_set_extent_refs(ei, refs);
        btrfs_mark_buffer_dirty(path->nodes[0]);
        if (refs == 0) {
-               u64 super_blocks_used;
+               u64 super_blocks_used, root_blocks_used;
 
                if (pin) {
                        ret = pin_down_block(root, blocknr, 0);
                        BUG_ON(ret);
                }
 
+               /* block accounting for super block */
                super_blocks_used = btrfs_super_blocks_used(&info->super_copy);
                btrfs_set_super_blocks_used(&info->super_copy,
                                            super_blocks_used - num_blocks);
+
+               /* block accounting for root item */
+               root_blocks_used = btrfs_root_blocks_used(&root->root_item);
+               btrfs_set_root_blocks_used(&root->root_item,
+                                          root_blocks_used - num_blocks);
+
                ret = btrfs_del_item(trans, extent_root, path);
                if (ret) {
                        return ret;
@@ -1175,7 +1187,7 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
 {
        int ret;
        int pending_ret;
-       u64 super_blocks_used;
+       u64 super_blocks_used, root_blocks_used;
        u64 search_start = 0;
        struct btrfs_fs_info *info = root->fs_info;
        struct btrfs_root *extent_root = info->extent_root;
@@ -1193,10 +1205,16 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
        if (ret)
                return ret;
 
+       /* block accounting for super block */
        super_blocks_used = btrfs_super_blocks_used(&info->super_copy);
        btrfs_set_super_blocks_used(&info->super_copy, super_blocks_used +
                                    num_blocks);
 
+       /* block accounting for root item */
+       root_blocks_used = btrfs_root_blocks_used(&root->root_item);
+       btrfs_set_root_blocks_used(&root->root_item, root_blocks_used +
+                                  num_blocks);
+
        if (root == extent_root) {
                BUG_ON(num_blocks != 1);
                set_radix_bit(&root->fs_info->extent_ins_radix, ins->objectid);
@@ -1242,8 +1260,7 @@ struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
        struct buffer_head *buf;
 
        ret = btrfs_alloc_extent(trans, root, root->root_key.objectid,
-                                1, empty_size, hint,
-                                (unsigned long)-1, &ins, 0);
+                                1, empty_size, hint, (u64)-1, &ins, 0);
        if (ret) {
                BUG_ON(ret > 0);
                return ERR_PTR(ret);
This page took 0.027926 seconds and 5 git commands to generate.