Btrfs: Avoid recursive chunk allocations
[linux-2.6-block.git] / fs / btrfs / extent-tree.c
index 8f441783a6ed7f4d4ac7e4e4b55502deb9a715d8..a589912fdd51aaa2d6ea727352196d02fbf2b53f 100644 (file)
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 021110-1307, USA.
  */
-
 #include <linux/sched.h>
-#include <linux/crc32c.h>
 #include <linux/pagemap.h>
 #include "hash.h"
+#include "crc32c.h"
 #include "ctree.h"
 #include "disk-io.h"
 #include "print-tree.h"
 #include "transaction.h"
+#include "volumes.h"
 
-#define BLOCK_GROUP_DATA EXTENT_WRITEBACK
+#define BLOCK_GROUP_DATA     EXTENT_WRITEBACK
 #define BLOCK_GROUP_METADATA EXTENT_UPTODATE
+#define BLOCK_GROUP_SYSTEM   EXTENT_NEW
+
 #define BLOCK_GROUP_DIRTY EXTENT_DIRTY
 
 static int finish_current_insert(struct btrfs_trans_handle *trans, struct
                                 btrfs_root *extent_root);
 static int del_pending_extents(struct btrfs_trans_handle *trans, struct
                               btrfs_root *extent_root);
-static int find_previous_extent(struct btrfs_root *root,
-                               struct btrfs_path *path)
-{
-       struct btrfs_key found_key;
-       struct extent_buffer *leaf;
-       int ret;
 
-       while(1) {
-               if (path->slots[0] == 0) {
-                       ret = btrfs_prev_leaf(root, path);
-                       if (ret != 0)
-                               return ret;
-               } else {
-                       path->slots[0]--;
-               }
-               leaf = path->nodes[0];
-               btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-               if (found_key.type == BTRFS_EXTENT_ITEM_KEY)
-                       return 0;
-       }
-       return 1;
-}
 
 static int cache_block_group(struct btrfs_root *root,
                             struct btrfs_block_group_cache *block_group)
@@ -91,7 +72,7 @@ static int cache_block_group(struct btrfs_root *root,
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
        if (ret < 0)
                return ret;
-       ret = find_previous_extent(root, path);
+       ret = btrfs_previous_item(root, path, 0, BTRFS_EXTENT_ITEM_KEY);
        if (ret < 0)
                return ret;
        if (ret == 0) {
@@ -168,7 +149,8 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
        block_group_cache = &info->block_group_cache;
        ret = find_first_extent_bit(block_group_cache,
                                    bytenr, &start, &end,
-                                   BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA);
+                                   BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA |
+                                   BLOCK_GROUP_SYSTEM);
        if (ret) {
                return NULL;
        }
@@ -182,9 +164,15 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
                return block_group;
        return NULL;
 }
-static u64 noinline find_search_start(struct btrfs_root *root,
+
+static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
+{
+       return (cache->flags & bits) == bits;
+}
+
+static int noinline find_search_start(struct btrfs_root *root,
                              struct btrfs_block_group_cache **cache_ret,
-                             u64 search_start, int num, int data)
+                             u64 *start_ret, int num, int data)
 {
        int ret;
        struct btrfs_block_group_cache *cache = *cache_ret;
@@ -194,11 +182,12 @@ static u64 noinline find_search_start(struct btrfs_root *root,
        u64 start = 0;
        u64 cache_miss = 0;
        u64 total_fs_bytes;
+       u64 search_start = *start_ret;
        int wrapped = 0;
 
-       if (!cache) {
+       if (!cache)
                goto out;
-       }
+
        total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
        free_space_cache = &root->fs_info->free_space_cache;
 
@@ -208,6 +197,9 @@ again:
                goto out;
 
        last = max(search_start, cache->key.objectid);
+       if (!block_group_bits(cache, data) || cache->ro) {
+               goto new_group;
+       }
 
        spin_lock_irq(&free_space_cache->lock);
        state = find_first_extent_bit_state(free_space_cache, last, EXTENT_DIRTY);
@@ -230,22 +222,25 @@ again:
                        continue;
                }
                spin_unlock_irq(&free_space_cache->lock);
-               if (data != BTRFS_BLOCK_GROUP_MIXED &&
-                   start + num > cache->key.objectid + cache->key.offset)
+               if (cache->ro)
+                       goto new_group;
+               if (start + num > cache->key.objectid + cache->key.offset)
                        goto new_group;
                if (start + num  > total_fs_bytes)
                        goto new_group;
-               return start;
+               if (!block_group_bits(cache, data)) {
+                       printk("block group bits don't match %Lu %d\n", cache->flags, data);
+               }
+               *start_ret = start;
+               return 0;
        }
 out:
        cache = btrfs_lookup_block_group(root->fs_info, search_start);
        if (!cache) {
-               printk("Unable to find block group for %Lu\n",
-                      search_start);
+               printk("Unable to find block group for %Lu\n", search_start);
                WARN_ON(1);
-               return search_start;
        }
-       return search_start;
+       return -ENOSPC;
 
 new_group:
        last = cache->key.objectid + cache->key.offset;
@@ -256,7 +251,6 @@ no_cache:
                if (!wrapped) {
                        wrapped = 1;
                        last = search_start;
-                       data = BTRFS_BLOCK_GROUP_MIXED;
                        goto wrapped;
                }
                goto out;
@@ -283,6 +277,18 @@ static u64 div_factor(u64 num, int factor)
        return num;
 }
 
+static int block_group_state_bits(u64 flags)
+{
+       int bits = 0;
+       if (flags & BTRFS_BLOCK_GROUP_DATA)
+               bits |= BLOCK_GROUP_DATA;
+       if (flags & BTRFS_BLOCK_GROUP_METADATA)
+               bits |= BLOCK_GROUP_METADATA;
+       if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
+               bits |= BLOCK_GROUP_SYSTEM;
+       return bits;
+}
+
 struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
                                                 struct btrfs_block_group_cache
                                                 *hint, u64 search_start,
@@ -303,28 +309,20 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
        int bit;
        int ret;
        int full_search = 0;
-       int factor = 8;
-       int data_swap = 0;
+       int factor = 10;
 
        block_group_cache = &info->block_group_cache;
        total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
 
        if (!owner)
-               factor = 8;
-
-       if (data == BTRFS_BLOCK_GROUP_MIXED) {
-               bit = BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA;
                factor = 10;
-       } else if (data)
-               bit = BLOCK_GROUP_DATA;
-       else
-               bit = BLOCK_GROUP_METADATA;
+
+       bit = block_group_state_bits(data);
 
        if (search_start && search_start < total_fs_bytes) {
                struct btrfs_block_group_cache *shint;
                shint = btrfs_lookup_block_group(info, search_start);
-               if (shint && (shint->data == data ||
-                             shint->data == BTRFS_BLOCK_GROUP_MIXED)) {
+               if (shint && block_group_bits(shint, data) && !shint->ro) {
                        used = btrfs_block_group_used(&shint->item);
                        if (used + shint->pinned <
                            div_factor(shint->key.offset, factor)) {
@@ -332,8 +330,8 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
                        }
                }
        }
-       if (hint && hint->key.objectid < total_fs_bytes &&
-           (hint->data == data || hint->data == BTRFS_BLOCK_GROUP_MIXED)) {
+       if (hint && !hint->ro && block_group_bits(hint, data) &&
+           hint->key.objectid < total_fs_bytes) {
                used = btrfs_block_group_used(&hint->item);
                if (used + hint->pinned <
                    div_factor(hint->key.offset, factor)) {
@@ -369,13 +367,17 @@ again:
                if (cache->key.objectid > total_fs_bytes)
                        break;
 
-               if (full_search)
-                       free_check = cache->key.offset;
-               else
-                       free_check = div_factor(cache->key.offset, factor);
-               if (used + cache->pinned < free_check) {
-                       found_group = cache;
-                       goto found;
+               if (!cache->ro && block_group_bits(cache, data)) {
+                       if (full_search)
+                               free_check = cache->key.offset;
+                       else
+                               free_check = div_factor(cache->key.offset,
+                                                       factor);
+
+                       if (used + cache->pinned < free_check) {
+                               found_group = cache;
+                               goto found;
+                       }
                }
                cond_resched();
        }
@@ -384,12 +386,6 @@ again:
                full_search = 1;
                goto again;
        }
-       if (!data_swap) {
-               data_swap = 1;
-               bit = BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA;
-               last = search_start;
-               goto again;
-       }
 found:
        return found_group;
 }
@@ -400,16 +396,15 @@ static u64 hash_extent_ref(u64 root_objectid, u64 ref_generation,
        u32 high_crc = ~(u32)0;
        u32 low_crc = ~(u32)0;
        __le64 lenum;
-
        lenum = cpu_to_le64(root_objectid);
-       high_crc = crc32c(high_crc, &lenum, sizeof(lenum));
+       high_crc = btrfs_crc32c(high_crc, &lenum, sizeof(lenum));
        lenum = cpu_to_le64(ref_generation);
-       low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
+       low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
        if (owner >= BTRFS_FIRST_FREE_OBJECTID) {
                lenum = cpu_to_le64(owner);
-               low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
+               low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
                lenum = cpu_to_le64(owner_offset);
-               low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
+               low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
        }
        return ((u64)high_crc << 32) | (u64)low_crc;
 }
@@ -648,7 +643,7 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
        if (!path)
                return -ENOMEM;
 
-       path->reada = 0;
+       path->reada = 1;
        key.objectid = bytenr;
        btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
        key.offset = num_bytes;
@@ -668,7 +663,7 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
 
        btrfs_release_path(root->fs_info->extent_root, path);
 
-       path->reada = 0;
+       path->reada = 1;
        ret = btrfs_insert_extent_backref(trans, root->fs_info->extent_root,
                                          path, bytenr, root_objectid,
                                          ref_generation, owner, owner_offset);
@@ -700,7 +695,7 @@ static int lookup_extent_ref(struct btrfs_trans_handle *trans,
 
        WARN_ON(num_bytes < root->sectorsize);
        path = btrfs_alloc_path();
-       path->reada = 0;
+       path->reada = 1;
        key.objectid = bytenr;
        key.offset = num_bytes;
        btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
@@ -984,7 +979,6 @@ int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
                ret = get_state_private(block_group_cache, start, &ptr);
                if (ret)
                        break;
-
                cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
                err = write_one_cache_group(trans, root,
                                            path, cache);
@@ -1004,10 +998,112 @@ int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
        return werr;
 }
 
+static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info,
+                                                 u64 flags)
+{
+       struct list_head *head = &info->space_info;
+       struct list_head *cur;
+       struct btrfs_space_info *found;
+       list_for_each(cur, head) {
+               found = list_entry(cur, struct btrfs_space_info, list);
+               if (found->flags == flags)
+                       return found;
+       }
+       return NULL;
+
+}
+
+static int update_space_info(struct btrfs_fs_info *info, u64 flags,
+                            u64 total_bytes, u64 bytes_used,
+                            struct btrfs_space_info **space_info)
+{
+       struct btrfs_space_info *found;
+
+       found = __find_space_info(info, flags);
+       if (found) {
+               found->total_bytes += total_bytes;
+               found->bytes_used += bytes_used;
+               found->full = 0;
+               WARN_ON(found->total_bytes < found->bytes_used);
+               *space_info = found;
+               return 0;
+       }
+       found = kmalloc(sizeof(*found), GFP_NOFS);
+       if (!found)
+               return -ENOMEM;
+
+       list_add(&found->list, &info->space_info);
+       found->flags = flags;
+       found->total_bytes = total_bytes;
+       found->bytes_used = bytes_used;
+       found->bytes_pinned = 0;
+       found->full = 0;
+       *space_info = found;
+       return 0;
+}
+
+static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
+{
+       u64 extra_flags = flags & (BTRFS_BLOCK_GROUP_RAID0 |
+                                  BTRFS_BLOCK_GROUP_RAID1 |
+                                  BTRFS_BLOCK_GROUP_RAID10 |
+                                  BTRFS_BLOCK_GROUP_DUP);
+       if (extra_flags) {
+               if (flags & BTRFS_BLOCK_GROUP_DATA)
+                       fs_info->avail_data_alloc_bits |= extra_flags;
+               if (flags & BTRFS_BLOCK_GROUP_METADATA)
+                       fs_info->avail_metadata_alloc_bits |= extra_flags;
+               if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
+                       fs_info->avail_system_alloc_bits |= extra_flags;
+       }
+}
+
+static int do_chunk_alloc(struct btrfs_trans_handle *trans,
+                         struct btrfs_root *extent_root, u64 alloc_bytes,
+                         u64 flags)
+{
+       struct btrfs_space_info *space_info;
+       u64 thresh;
+       u64 start;
+       u64 num_bytes;
+       int ret;
+
+       space_info = __find_space_info(extent_root->fs_info, flags);
+       if (!space_info) {
+               ret = update_space_info(extent_root->fs_info, flags,
+                                       0, 0, &space_info);
+               BUG_ON(ret);
+       }
+       BUG_ON(!space_info);
+
+       if (space_info->full)
+               return 0;
+
+       thresh = div_factor(space_info->total_bytes, 6);
+       if ((space_info->bytes_used + space_info->bytes_pinned + alloc_bytes) <
+           thresh)
+               return 0;
+
+       ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes, flags);
+       if (ret == -ENOSPC) {
+printk("space info full %Lu\n", flags);
+               space_info->full = 1;
+               return 0;
+       }
+
+       BUG_ON(ret);
+
+       ret = btrfs_make_block_group(trans, extent_root, 0, flags,
+                    BTRFS_FIRST_CHUNK_TREE_OBJECTID, start, num_bytes);
+       BUG_ON(ret);
+
+       return 0;
+}
+
 static int update_block_group(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root,
                              u64 bytenr, u64 num_bytes, int alloc,
-                             int mark_free, int data)
+                             int mark_free)
 {
        struct btrfs_block_group_cache *cache;
        struct btrfs_fs_info *info = root->fs_info;
@@ -1032,44 +1128,11 @@ static int update_block_group(struct btrfs_trans_handle *trans,
                old_val = btrfs_block_group_used(&cache->item);
                num_bytes = min(total, cache->key.offset - byte_in_group);
                if (alloc) {
-                       if (cache->data != data &&
-                           old_val < (cache->key.offset >> 1)) {
-                               int bit_to_clear;
-                               int bit_to_set;
-                               cache->data = data;
-                               if (data) {
-                                       bit_to_clear = BLOCK_GROUP_METADATA;
-                                       bit_to_set = BLOCK_GROUP_DATA;
-                                       cache->item.flags &=
-                                               ~BTRFS_BLOCK_GROUP_MIXED;
-                                       cache->item.flags |=
-                                               BTRFS_BLOCK_GROUP_DATA;
-                               } else {
-                                       bit_to_clear = BLOCK_GROUP_DATA;
-                                       bit_to_set = BLOCK_GROUP_METADATA;
-                                       cache->item.flags &=
-                                               ~BTRFS_BLOCK_GROUP_MIXED;
-                                       cache->item.flags &=
-                                               ~BTRFS_BLOCK_GROUP_DATA;
-                               }
-                               clear_extent_bits(&info->block_group_cache,
-                                                 start, end, bit_to_clear,
-                                                 GFP_NOFS);
-                               set_extent_bits(&info->block_group_cache,
-                                               start, end, bit_to_set,
-                                               GFP_NOFS);
-                       } else if (cache->data != data &&
-                                  cache->data != BTRFS_BLOCK_GROUP_MIXED) {
-                               cache->data = BTRFS_BLOCK_GROUP_MIXED;
-                               set_extent_bits(&info->block_group_cache,
-                                               start, end,
-                                               BLOCK_GROUP_DATA |
-                                               BLOCK_GROUP_METADATA,
-                                               GFP_NOFS);
-                       }
                        old_val += num_bytes;
+                       cache->space_info->bytes_used += num_bytes;
                } else {
                        old_val -= num_bytes;
+                       cache->space_info->bytes_used -= num_bytes;
                        if (mark_free) {
                                set_extent_dirty(&info->free_space_cache,
                                                 bytenr, bytenr + num_bytes - 1,
@@ -1082,6 +1145,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
        }
        return 0;
 }
+
 static int update_pinned_extents(struct btrfs_root *root,
                                u64 bytenr, u64 num, int pin)
 {
@@ -1103,9 +1167,11 @@ static int update_pinned_extents(struct btrfs_root *root,
                          (bytenr - cache->key.objectid));
                if (pin) {
                        cache->pinned += len;
+                       cache->space_info->bytes_pinned += len;
                        fs_info->total_pinned += len;
                } else {
                        cache->pinned -= len;
+                       cache->space_info->bytes_pinned -= len;
                        fs_info->total_pinned -= len;
                }
                bytenr += len;
@@ -1218,7 +1284,9 @@ static int pin_down_bytes(struct btrfs_root *root, u64 bytenr, u32 num_bytes,
                                    root->fs_info->running_transaction->transid;
                                u64 header_transid =
                                        btrfs_header_generation(buf);
-                               if (header_transid == transid) {
+                               if (header_transid == transid &&
+                                   !btrfs_header_flag(buf,
+                                              BTRFS_HEADER_FLAG_WRITTEN)) {
                                        clean_tree_block(NULL, root, buf);
                                        free_extent_buffer(buf);
                                        return 1;
@@ -1264,7 +1332,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
        if (!path)
                return -ENOMEM;
 
-       path->reada = 0;
+       path->reada = 1;
        ret = lookup_extent_backref(trans, extent_root, path,
                                    bytenr, root_objectid,
                                    ref_generation,
@@ -1362,7 +1430,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
                        return ret;
                }
                ret = update_block_group(trans, root, bytenr, num_bytes, 0,
-                                        mark_free, 0);
+                                        mark_free);
                BUG_ON(ret);
        }
        btrfs_free_path(path);
@@ -1455,73 +1523,57 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
                                     u64 exclude_start, u64 exclude_nr,
                                     int data)
 {
-       struct btrfs_path *path;
-       struct btrfs_key key;
-       u64 hole_size = 0;
-       u64 aligned;
        int ret;
-       int slot = 0;
-       u64 last_byte = 0;
-       u64 *last_ptr = NULL;
        u64 orig_search_start = search_start;
-       int start_found;
-       struct extent_buffer *l;
        struct btrfs_root * root = orig_root->fs_info->extent_root;
        struct btrfs_fs_info *info = root->fs_info;
        u64 total_needed = num_bytes;
-       int level;
+       u64 *last_ptr = NULL;
        struct btrfs_block_group_cache *block_group;
        int full_scan = 0;
        int wrapped = 0;
-       int empty_cluster;
-       u64 cached_start;
+       int empty_cluster = 2 * 1024 * 1024;
 
        WARN_ON(num_bytes < root->sectorsize);
        btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
 
-       level = btrfs_header_level(root->node);
-
-       if (num_bytes >= 32 * 1024 * 1024 && hint_byte) {
-               data = BTRFS_BLOCK_GROUP_MIXED;
-       }
-
-       if (!data) {
+       if (data & BTRFS_BLOCK_GROUP_METADATA) {
                last_ptr = &root->fs_info->last_alloc;
-               empty_cluster = 128 * 1024;
+               empty_cluster = 256 * 1024;
        }
 
-       if (data && btrfs_test_opt(root, SSD)) {
+       if ((data & BTRFS_BLOCK_GROUP_DATA) && btrfs_test_opt(root, SSD)) {
                last_ptr = &root->fs_info->last_data_alloc;
-               empty_cluster = 2 * 1024 * 1024;
        }
 
        if (last_ptr) {
                if (*last_ptr)
                        hint_byte = *last_ptr;
                else {
-                       hint_byte = hint_byte &
-                               ~((u64)BTRFS_BLOCK_GROUP_SIZE - 1);
                        empty_size += empty_cluster;
                }
-               search_start = max(search_start, hint_byte);
        }
 
-       search_end = min(search_end,
-                        btrfs_super_total_bytes(&info->super_copy));
+       if (search_end == (u64)-1)
+               search_end = btrfs_super_total_bytes(&info->super_copy);
+
        if (hint_byte) {
                block_group = btrfs_lookup_block_group(info, hint_byte);
                if (!block_group)
                        hint_byte = search_start;
                block_group = btrfs_find_block_group(root, block_group,
                                                     hint_byte, data, 1);
+               if (last_ptr && *last_ptr == 0 && block_group)
+                       hint_byte = block_group->key.objectid;
        } else {
                block_group = btrfs_find_block_group(root,
                                                     trans->block_group,
                                                     search_start, data, 1);
        }
+       search_start = max(search_start, hint_byte);
 
        total_needed += empty_size;
-       path = btrfs_alloc_path();
+
 check_failed:
        if (!block_group) {
                block_group = btrfs_lookup_block_group(info, search_start);
@@ -1529,8 +1581,20 @@ check_failed:
                        block_group = btrfs_lookup_block_group(info,
                                                       orig_search_start);
        }
-       search_start = find_search_start(root, &block_group, search_start,
-                                        total_needed, data);
+       ret = find_search_start(root, &block_group, &search_start,
+                               total_needed, data);
+       if (ret == -ENOSPC && last_ptr && *last_ptr) {
+               *last_ptr = 0;
+               block_group = btrfs_lookup_block_group(info,
+                                                      orig_search_start);
+               search_start = orig_search_start;
+               ret = find_search_start(root, &block_group, &search_start,
+                                       total_needed, data);
+       }
+       if (ret == -ENOSPC)
+               goto enospc;
+       if (ret)
+               goto error;
 
        if (last_ptr && *last_ptr && search_start != *last_ptr) {
                *last_ptr = 0;
@@ -1538,138 +1602,55 @@ check_failed:
                        empty_size += empty_cluster;
                        total_needed += empty_size;
                }
-               search_start = find_search_start(root, &block_group,
-                                                search_start, total_needed,
-                                                data);
+               block_group = btrfs_lookup_block_group(info,
+                                                      orig_search_start);
+               search_start = orig_search_start;
+               ret = find_search_start(root, &block_group,
+                                       &search_start, total_needed, data);
+               if (ret == -ENOSPC)
+                       goto enospc;
+               if (ret)
+                       goto error;
        }
 
        search_start = stripe_align(root, search_start);
-       cached_start = search_start;
-       btrfs_init_path(path);
        ins->objectid = search_start;
-       ins->offset = 0;
-       start_found = 0;
-       path->reada = 2;
-
-       ret = btrfs_search_slot(trans, root, ins, path, 0, 0);
-       if (ret < 0)
-               goto error;
-       ret = find_previous_extent(root, path);
-       if (ret < 0)
-               goto error;
-       l = path->nodes[0];
-       btrfs_item_key_to_cpu(l, &key, path->slots[0]);
-       while (1) {
-               l = path->nodes[0];
-               slot = path->slots[0];
-               if (slot >= btrfs_header_nritems(l)) {
-                       ret = btrfs_next_leaf(root, path);
-                       if (ret == 0)
-                               continue;
-                       if (ret < 0)
-                               goto error;
-
-                       search_start = max(search_start,
-                                          block_group->key.objectid);
-                       if (!start_found) {
-                               aligned = stripe_align(root, search_start);
-                               ins->objectid = aligned;
-                               if (aligned >= search_end) {
-                                       ret = -ENOSPC;
-                                       goto error;
-                               }
-                               ins->offset = search_end - aligned;
-                               start_found = 1;
-                               goto check_pending;
-                       }
-                       ins->objectid = stripe_align(root,
-                                                    last_byte > search_start ?
-                                                    last_byte : search_start);
-                       if (search_end <= ins->objectid) {
-                               ret = -ENOSPC;
-                               goto error;
-                       }
-                       ins->offset = search_end - ins->objectid;
-                       BUG_ON(ins->objectid >= search_end);
-                       goto check_pending;
-               }
-               btrfs_item_key_to_cpu(l, &key, slot);
-
-               if (key.objectid >= search_start && key.objectid > last_byte &&
-                   start_found) {
-                       if (last_byte < search_start)
-                               last_byte = search_start;
-                       aligned = stripe_align(root, last_byte);
-                       hole_size = key.objectid - aligned;
-                       if (key.objectid > aligned && hole_size >= num_bytes) {
-                               ins->objectid = aligned;
-                               ins->offset = hole_size;
-                               goto check_pending;
-                       }
-               }
-               if (btrfs_key_type(&key) != BTRFS_EXTENT_ITEM_KEY) {
-                       if (!start_found && btrfs_key_type(&key) ==
-                           BTRFS_BLOCK_GROUP_ITEM_KEY) {
-                               last_byte = key.objectid;
-                               start_found = 1;
-                       }
-                       goto next;
-               }
-
-
-               start_found = 1;
-               last_byte = key.objectid + key.offset;
-
-               if (!full_scan && data != BTRFS_BLOCK_GROUP_MIXED &&
-                   last_byte >= block_group->key.objectid +
-                   block_group->key.offset) {
-                       btrfs_release_path(root, path);
-                       search_start = block_group->key.objectid +
-                               block_group->key.offset;
-                       goto new_group;
-               }
-next:
-               path->slots[0]++;
-               cond_resched();
-       }
-check_pending:
-       /* we have to make sure we didn't find an extent that has already
-        * been allocated by the map tree or the original allocation
-        */
-       btrfs_release_path(root, path);
-       BUG_ON(ins->objectid < search_start);
+       ins->offset = num_bytes;
 
        if (ins->objectid + num_bytes >= search_end)
                goto enospc;
-       if (!full_scan && data != BTRFS_BLOCK_GROUP_MIXED &&
-           ins->objectid + num_bytes > block_group->
-           key.objectid + block_group->key.offset) {
+
+       if (ins->objectid + num_bytes >
+           block_group->key.objectid + block_group->key.offset) {
                search_start = block_group->key.objectid +
                        block_group->key.offset;
                goto new_group;
        }
+
        if (test_range_bit(&info->extent_ins, ins->objectid,
                           ins->objectid + num_bytes -1, EXTENT_LOCKED, 0)) {
                search_start = ins->objectid + num_bytes;
                goto new_group;
        }
+
        if (test_range_bit(&info->pinned_extents, ins->objectid,
                           ins->objectid + num_bytes -1, EXTENT_DIRTY, 0)) {
                search_start = ins->objectid + num_bytes;
                goto new_group;
        }
+
        if (exclude_nr > 0 && (ins->objectid + num_bytes > exclude_start &&
            ins->objectid < exclude_start + exclude_nr)) {
                search_start = exclude_start + exclude_nr;
                goto new_group;
        }
-       if (!data) {
+
+       if (!(data & BTRFS_BLOCK_GROUP_DATA)) {
                block_group = btrfs_lookup_block_group(info, ins->objectid);
                if (block_group)
                        trans->block_group = block_group;
        }
        ins->offset = num_bytes;
-       btrfs_free_path(path);
        if (last_ptr) {
                *last_ptr = ins->objectid + ins->offset;
                if (*last_ptr ==
@@ -1691,7 +1672,6 @@ enospc:
                        if (!full_scan)
                                total_needed -= empty_size;
                        full_scan = 1;
-                       data = BTRFS_BLOCK_GROUP_MIXED;
                } else
                        wrapped = 1;
        }
@@ -1702,8 +1682,6 @@ enospc:
        goto check_failed;
 
 error:
-       btrfs_release_path(root, path);
-       btrfs_free_path(path);
        return ret;
 }
 /*
@@ -1715,7 +1693,8 @@ error:
  */
 int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
                       struct btrfs_root *root,
-                      u64 num_bytes, u64 root_objectid, u64 ref_generation,
+                      u64 num_bytes, u64 min_alloc_size,
+                      u64 root_objectid, u64 ref_generation,
                       u64 owner, u64 owner_offset,
                       u64 empty_size, u64 hint_byte,
                       u64 search_end, struct btrfs_key *ins, int data)
@@ -1725,7 +1704,7 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
        u64 super_used;
        u64 root_used;
        u64 search_start = 0;
-       u64 new_hint;
+       u64 alloc_profile;
        u32 sizes[2];
        struct btrfs_fs_info *info = root->fs_info;
        struct btrfs_root *extent_root = info->extent_root;
@@ -1734,15 +1713,45 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
        struct btrfs_path *path;
        struct btrfs_key keys[2];
 
-       new_hint = max(hint_byte, root->fs_info->alloc_start);
-       if (new_hint < btrfs_super_total_bytes(&info->super_copy))
-               hint_byte = new_hint;
+       if (data) {
+               alloc_profile = info->avail_data_alloc_bits &
+                               info->data_alloc_profile;
+               data = BTRFS_BLOCK_GROUP_DATA | alloc_profile;
+       } else if (root == root->fs_info->chunk_root) {
+               alloc_profile = info->avail_system_alloc_bits &
+                               info->system_alloc_profile;
+               data = BTRFS_BLOCK_GROUP_SYSTEM | alloc_profile;
+       } else {
+               alloc_profile = info->avail_metadata_alloc_bits &
+                               info->metadata_alloc_profile;
+               data = BTRFS_BLOCK_GROUP_METADATA | alloc_profile;
+       }
+again:
+       if (root->ref_cows) {
+               if (!(data & BTRFS_BLOCK_GROUP_METADATA)) {
+                       ret = do_chunk_alloc(trans, root->fs_info->extent_root,
+                                            2 * 1024 * 1024,
+                                            BTRFS_BLOCK_GROUP_METADATA |
+                                            (info->metadata_alloc_profile &
+                                             info->avail_metadata_alloc_bits));
+                       BUG_ON(ret);
+               }
+               ret = do_chunk_alloc(trans, root->fs_info->extent_root,
+                                    num_bytes + 2 * 1024 * 1024, data);
+               BUG_ON(ret);
+       }
 
        WARN_ON(num_bytes < root->sectorsize);
        ret = find_free_extent(trans, root, num_bytes, empty_size,
                               search_start, search_end, hint_byte, ins,
                               trans->alloc_exclude_start,
                               trans->alloc_exclude_nr, data);
+
+       if (ret == -ENOSPC && num_bytes > min_alloc_size) {
+               num_bytes = num_bytes >> 1;
+               num_bytes = max(num_bytes, min_alloc_size);
+               goto again;
+       }
        BUG_ON(ret);
        if (ret)
                return ret;
@@ -1763,7 +1772,6 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
                set_extent_bits(&root->fs_info->extent_ins, ins->objectid,
                                ins->objectid + ins->offset - 1,
                                EXTENT_LOCKED, GFP_NOFS);
-               WARN_ON(data == 1);
                goto update_block;
        }
 
@@ -1813,8 +1821,7 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
        }
 
 update_block:
-       ret = update_block_group(trans, root, ins->objectid, ins->offset, 1, 0,
-                                data);
+       ret = update_block_group(trans, root, ins->objectid, ins->offset, 1, 0);
        if (ret) {
                printk("update block group failed for %Lu %Lu\n",
                       ins->objectid, ins->offset);
@@ -1863,7 +1870,7 @@ struct extent_buffer *__btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
        int ret;
        struct extent_buffer *buf;
 
-       ret = btrfs_alloc_extent(trans, root, blocksize,
+       ret = btrfs_alloc_extent(trans, root, blocksize, blocksize,
                                 root_objectid, ref_generation,
                                 level, first_objectid, empty_size, hint,
                                 (u64)-1, &ins, 0);
@@ -1880,7 +1887,6 @@ struct extent_buffer *__btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
        }
        btrfs_set_header_generation(buf, trans->transid);
        clean_tree_block(trans, root, buf);
-       wait_on_tree_block_writeback(root, buf);
        btrfs_set_buffer_uptodate(buf);
 
        if (PageDirty(buf->first_page)) {
@@ -1890,10 +1896,6 @@ struct extent_buffer *__btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
 
        set_extent_dirty(&trans->transaction->dirty_pages, buf->start,
                         buf->start + buf->len - 1, GFP_NOFS);
-       set_extent_bits(&BTRFS_I(root->fs_info->btree_inode)->io_tree,
-                       buf->start, buf->start + buf->len - 1,
-                       EXTENT_CSUM, GFP_NOFS);
-       buf->flags |= EXTENT_CSUM;
        if (!btrfs_test_opt(root, SSD))
                btrfs_set_buffer_defrag(buf);
        trans->blocks_used++;
@@ -2054,11 +2056,12 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
                if (!next || !btrfs_buffer_uptodate(next)) {
                        free_extent_buffer(next);
                        reada_walk_down(root, cur, path->slots[*level]);
+
                        mutex_unlock(&root->fs_info->fs_mutex);
                        next = read_tree_block(root, bytenr, blocksize);
                        mutex_lock(&root->fs_info->fs_mutex);
 
-                       /* we dropped the lock, check one more time */
+                       /* we've dropped the lock, double check */
                        ret = lookup_extent_ref(trans, root, bytenr,
                                                blocksize, &refs);
                        BUG_ON(ret);
@@ -2076,6 +2079,8 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
                                BUG_ON(ret);
                                continue;
                        }
+               } else if (next) {
+                       btrfs_verify_block_csum(root, next);
                }
                WARN_ON(*level <= 0);
                if (path->nodes[*level-1])
@@ -2467,15 +2472,16 @@ out:
        return ret;
 }
 
-int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 new_size)
+int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
 {
        struct btrfs_trans_handle *trans;
        struct btrfs_root *tree_root = root->fs_info->tree_root;
        struct btrfs_path *path;
        u64 cur_byte;
        u64 total_found;
+       u64 shrink_last_byte;
+       struct btrfs_block_group_cache *shrink_block_group;
        struct btrfs_fs_info *info = root->fs_info;
-       struct extent_io_tree *block_group_cache;
        struct btrfs_key key;
        struct btrfs_key found_key;
        struct extent_buffer *leaf;
@@ -2483,17 +2489,29 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 new_size)
        int ret;
        int progress = 0;
 
-       btrfs_set_super_total_bytes(&info->super_copy, new_size);
-       clear_extent_dirty(&info->free_space_cache, new_size, (u64)-1,
-                          GFP_NOFS);
-       block_group_cache = &info->block_group_cache;
+       shrink_block_group = btrfs_lookup_block_group(root->fs_info,
+                                                     shrink_start);
+       BUG_ON(!shrink_block_group);
+
+       shrink_last_byte = shrink_start + shrink_block_group->key.offset;
+
+       shrink_block_group->space_info->total_bytes -=
+               shrink_block_group->key.offset;
+printk("shrink_extent_tree %Lu -> %Lu type %Lu\n", shrink_start, shrink_last_byte, shrink_block_group->flags);
        path = btrfs_alloc_path();
        root = root->fs_info->extent_root;
        path->reada = 2;
 
 again:
+       trans = btrfs_start_transaction(root, 1);
+       do_chunk_alloc(trans, root->fs_info->extent_root,
+                       btrfs_block_group_used(&shrink_block_group->item) +
+                       2 * 1024 * 1024, shrink_block_group->flags);
+       btrfs_end_transaction(trans, root);
+       shrink_block_group->ro = 1;
+
        total_found = 0;
-       key.objectid = new_size;
+       key.objectid = shrink_start;
        key.offset = 0;
        key.type = 0;
        cur_byte = key.objectid;
@@ -2502,13 +2520,15 @@ again:
        if (ret < 0)
                goto out;
 
-       ret = find_previous_extent(root, path);
+       ret = btrfs_previous_item(root, path, 0, BTRFS_EXTENT_ITEM_KEY);
        if (ret < 0)
                goto out;
+
        if (ret == 0) {
                leaf = path->nodes[0];
                btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-               if (found_key.objectid + found_key.offset > new_size) {
+               if (found_key.objectid + found_key.offset > shrink_start &&
+                   found_key.objectid < shrink_last_byte) {
                        cur_byte = found_key.objectid;
                        key.objectid = cur_byte;
                }
@@ -2537,6 +2557,9 @@ next:
 
                btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
 
+               if (found_key.objectid >= shrink_last_byte)
+                       break;
+
                if (progress && need_resched()) {
                        memcpy(&key, &found_key, sizeof(key));
                        mutex_unlock(&root->fs_info->fs_mutex);
@@ -2577,68 +2600,31 @@ next:
                goto again;
        }
 
+       /*
+        * we've freed all the extents, now remove the block
+        * group item from the tree
+        */
        trans = btrfs_start_transaction(root, 1);
-       key.objectid = new_size;
-       key.offset = 0;
-       key.type = 0;
-       while(1) {
-               u64 ptr;
-
-               ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
-               if (ret < 0)
-                       goto out;
-
-               leaf = path->nodes[0];
-               nritems = btrfs_header_nritems(leaf);
-bg_next:
-               if (path->slots[0] >= nritems) {
-                       ret = btrfs_next_leaf(root, path);
-                       if (ret < 0)
-                               break;
-                       if (ret == 1) {
-                               ret = 0;
-                               break;
-                       }
-                       leaf = path->nodes[0];
-                       btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+       memcpy(&key, &shrink_block_group->key, sizeof(key));
 
-                       /*
-                        * btrfs_next_leaf doesn't cow buffers, we have to
-                        * do the search again
-                        */
-                       memcpy(&key, &found_key, sizeof(key));
-                       btrfs_release_path(root, path);
-                       goto resched_check;
-               }
+       ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+       if (ret > 0)
+               ret = -EIO;
+       if (ret < 0)
+               goto out;
 
-               btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-               if (btrfs_key_type(&found_key) != BTRFS_BLOCK_GROUP_ITEM_KEY) {
-                       printk("shrinker found key %Lu %u %Lu\n",
-                               found_key.objectid, found_key.type,
-                               found_key.offset);
-                       path->slots[0]++;
-                       goto bg_next;
-               }
-               ret = get_state_private(&info->block_group_cache,
-                                       found_key.objectid, &ptr);
-               if (!ret)
-                       kfree((void *)(unsigned long)ptr);
+       leaf = path->nodes[0];
+       nritems = btrfs_header_nritems(leaf);
+       btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+       kfree(shrink_block_group);
 
-               clear_extent_bits(&info->block_group_cache, found_key.objectid,
-                                 found_key.objectid + found_key.offset - 1,
-                                 (unsigned int)-1, GFP_NOFS);
+       clear_extent_bits(&info->block_group_cache, found_key.objectid,
+                         found_key.objectid + found_key.offset - 1,
+                         (unsigned int)-1, GFP_NOFS);
 
-               key.objectid = found_key.objectid + 1;
-               btrfs_del_item(trans, root, path);
-               btrfs_release_path(root, path);
-resched_check:
-               if (need_resched()) {
-                       mutex_unlock(&root->fs_info->fs_mutex);
-                       cond_resched();
-                       mutex_lock(&root->fs_info->fs_mutex);
-               }
-       }
-       clear_extent_dirty(&info->free_space_cache, new_size, (u64)-1,
+       btrfs_del_item(trans, root, path);
+       clear_extent_dirty(&info->free_space_cache,
+                          shrink_start, shrink_last_byte - 1,
                           GFP_NOFS);
        btrfs_commit_transaction(trans, root);
 out:
@@ -2646,129 +2632,76 @@ out:
        return ret;
 }
 
-int btrfs_grow_extent_tree(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root, u64 new_size)
+int find_first_block_group(struct btrfs_root *root, struct btrfs_path *path,
+                          struct btrfs_key *key)
 {
-       struct btrfs_path *path;
-       u64 nr = 0;
-       u64 cur_byte;
-       u64 old_size;
-       unsigned long rem;
-       struct btrfs_block_group_cache *cache;
-       struct btrfs_block_group_item *item;
-       struct btrfs_fs_info *info = root->fs_info;
-       struct extent_io_tree *block_group_cache;
-       struct btrfs_key key;
-       struct extent_buffer *leaf;
        int ret;
-       int bit;
-
-       old_size = btrfs_super_total_bytes(&info->super_copy);
-       block_group_cache = &info->block_group_cache;
-
-       root = info->extent_root;
-
-       cache = btrfs_lookup_block_group(root->fs_info, old_size - 1);
-
-       cur_byte = cache->key.objectid + cache->key.offset;
-       if (cur_byte >= new_size)
-               goto set_size;
-
-       key.offset = BTRFS_BLOCK_GROUP_SIZE;
-       btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY);
-
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
+       struct btrfs_key found_key;
+       struct extent_buffer *leaf;
+       int slot;
 
-       while(cur_byte < new_size) {
-               key.objectid = cur_byte;
-               ret = btrfs_insert_empty_item(trans, root, path, &key,
-                                       sizeof(struct btrfs_block_group_item));
-               BUG_ON(ret);
+       ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+       if (ret < 0)
+               return ret;
+       while(1) {
+               slot = path->slots[0];
                leaf = path->nodes[0];
-               item = btrfs_item_ptr(leaf, path->slots[0],
-                                     struct btrfs_block_group_item);
-
-               btrfs_set_disk_block_group_used(leaf, item, 0);
-               div_long_long_rem(nr, 3, &rem);
-               if (rem) {
-                       btrfs_set_disk_block_group_flags(leaf, item,
-                                                BTRFS_BLOCK_GROUP_DATA);
-               } else {
-                       btrfs_set_disk_block_group_flags(leaf, item, 0);
-               }
-               nr++;
-
-               cache = kmalloc(sizeof(*cache), GFP_NOFS);
-               BUG_ON(!cache);
-
-               read_extent_buffer(leaf, &cache->item, (unsigned long)item,
-                                  sizeof(cache->item));
-
-               memcpy(&cache->key, &key, sizeof(key));
-               cache->cached = 0;
-               cache->pinned = 0;
-               cur_byte = key.objectid + key.offset;
-               btrfs_release_path(root, path);
-
-               if (cache->item.flags & BTRFS_BLOCK_GROUP_DATA) {
-                       bit = BLOCK_GROUP_DATA;
-                       cache->data = BTRFS_BLOCK_GROUP_DATA;
-               } else {
-                       bit = BLOCK_GROUP_METADATA;
-                       cache->data = 0;
+               if (slot >= btrfs_header_nritems(leaf)) {
+                       ret = btrfs_next_leaf(root, path);
+                       if (ret == 0)
+                               continue;
+                       if (ret < 0)
+                               goto error;
+                       break;
                }
+               btrfs_item_key_to_cpu(leaf, &found_key, slot);
 
-               /* use EXTENT_LOCKED to prevent merging */
-               set_extent_bits(block_group_cache, key.objectid,
-                               key.objectid + key.offset - 1,
-                               bit | EXTENT_LOCKED, GFP_NOFS);
-               set_state_private(block_group_cache, key.objectid,
-                                 (unsigned long)cache);
+               if (found_key.objectid >= key->objectid &&
+                   found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY)
+                       return 0;
+               path->slots[0]++;
        }
-       btrfs_free_path(path);
-set_size:
-       btrfs_set_super_total_bytes(&info->super_copy, new_size);
-       return 0;
+       ret = -ENOENT;
+error:
+       return ret;
 }
 
 int btrfs_read_block_groups(struct btrfs_root *root)
 {
        struct btrfs_path *path;
        int ret;
-       int err = 0;
        int bit;
        struct btrfs_block_group_cache *cache;
        struct btrfs_fs_info *info = root->fs_info;
+       struct btrfs_space_info *space_info;
        struct extent_io_tree *block_group_cache;
        struct btrfs_key key;
        struct btrfs_key found_key;
        struct extent_buffer *leaf;
 
        block_group_cache = &info->block_group_cache;
-
        root = info->extent_root;
        key.objectid = 0;
-       key.offset = BTRFS_BLOCK_GROUP_SIZE;
+       key.offset = 0;
        btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY);
-
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
 
        while(1) {
-               ret = btrfs_search_slot(NULL, info->extent_root,
-                                       &key, path, 0, 0);
-               if (ret != 0) {
-                       err = ret;
-                       break;
+               ret = find_first_block_group(root, path, &key);
+               if (ret > 0) {
+                       ret = 0;
+                       goto error;
                }
+               if (ret != 0)
+                       goto error;
+
                leaf = path->nodes[0];
                btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-               cache = kmalloc(sizeof(*cache), GFP_NOFS);
+               cache = kzalloc(sizeof(*cache), GFP_NOFS);
                if (!cache) {
-                       err = -1;
+                       ret = -ENOMEM;
                        break;
                }
 
@@ -2776,21 +2709,25 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                                   btrfs_item_ptr_offset(leaf, path->slots[0]),
                                   sizeof(cache->item));
                memcpy(&cache->key, &found_key, sizeof(found_key));
-               cache->cached = 0;
-               cache->pinned = 0;
+
                key.objectid = found_key.objectid + found_key.offset;
                btrfs_release_path(root, path);
-
-               if (cache->item.flags & BTRFS_BLOCK_GROUP_MIXED) {
-                       bit = BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA;
-                       cache->data = BTRFS_BLOCK_GROUP_MIXED;
-               } else if (cache->item.flags & BTRFS_BLOCK_GROUP_DATA) {
+               cache->flags = btrfs_block_group_flags(&cache->item);
+               bit = 0;
+               if (cache->flags & BTRFS_BLOCK_GROUP_DATA) {
                        bit = BLOCK_GROUP_DATA;
-                       cache->data = BTRFS_BLOCK_GROUP_DATA;
-               } else {
+               } else if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
+                       bit = BLOCK_GROUP_SYSTEM;
+               } else if (cache->flags & BTRFS_BLOCK_GROUP_METADATA) {
                        bit = BLOCK_GROUP_METADATA;
-                       cache->data = 0;
                }
+               set_avail_alloc_bits(info, cache->flags);
+
+               ret = update_space_info(info, cache->flags, found_key.offset,
+                                       btrfs_block_group_used(&cache->item),
+                                       &space_info);
+               BUG_ON(ret);
+               cache->space_info = space_info;
 
                /* use EXTENT_LOCKED to prevent merging */
                set_extent_bits(block_group_cache, found_key.objectid,
@@ -2803,7 +2740,56 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                    btrfs_super_total_bytes(&info->super_copy))
                        break;
        }
-
+       ret = 0;
+error:
        btrfs_free_path(path);
+       return ret;
+}
+
+int btrfs_make_block_group(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *root, u64 bytes_used,
+                          u64 type, u64 chunk_objectid, u64 chunk_offset,
+                          u64 size)
+{
+       int ret;
+       int bit = 0;
+       struct btrfs_root *extent_root;
+       struct btrfs_block_group_cache *cache;
+       struct extent_io_tree *block_group_cache;
+
+       extent_root = root->fs_info->extent_root;
+       block_group_cache = &root->fs_info->block_group_cache;
+
+       cache = kzalloc(sizeof(*cache), GFP_NOFS);
+       BUG_ON(!cache);
+       cache->key.objectid = chunk_offset;
+       cache->key.offset = size;
+
+       btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY);
+       memset(&cache->item, 0, sizeof(cache->item));
+       btrfs_set_block_group_used(&cache->item, bytes_used);
+       btrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid);
+       cache->flags = type;
+       btrfs_set_block_group_flags(&cache->item, type);
+
+       ret = update_space_info(root->fs_info, cache->flags, size, bytes_used,
+                               &cache->space_info);
+       BUG_ON(ret);
+
+       bit = block_group_state_bits(type);
+       set_extent_bits(block_group_cache, chunk_offset,
+                       chunk_offset + size - 1,
+                       bit | EXTENT_LOCKED, GFP_NOFS);
+
+       set_state_private(block_group_cache, chunk_offset,
+                         (unsigned long)cache);
+       ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item,
+                               sizeof(cache->item));
+       BUG_ON(ret);
+
+       finish_current_insert(trans, extent_root);
+       ret = del_pending_extents(trans, extent_root);
+       BUG_ON(ret);
+       set_avail_alloc_bits(extent_root->fs_info, type);
        return 0;
 }