]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - fs/btrfs/extent-tree.c
Btrfs: Add a clear_cache mount option
[linux-2.6.git] / fs / btrfs / extent-tree.c
index 2c214781fee61c9d953abbf8f214e1e0ef21beaf..1a94ee4c4fbbc5c78a78bd8ce91b181bdeeba6b6 100644 (file)
 #include <linux/blkdev.h>
 #include <linux/sort.h>
 #include <linux/rcupdate.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
 #include "compat.h"
 #include "hash.h"
-#include "crc32c.h"
 #include "ctree.h"
 #include "disk-io.h"
 #include "print-tree.h"
 #include "transaction.h"
 #include "volumes.h"
 #include "locking.h"
-#include "ref-cache.h"
+#include "free-space-cache.h"
 
-#define PENDING_EXTENT_INSERT 0
-#define PENDING_EXTENT_DELETE 1
-#define PENDING_BACKREF_UPDATE 2
-
-struct pending_extent_op {
-       int type;
-       u64 bytenr;
-       u64 num_bytes;
-       u64 parent;
-       u64 orig_parent;
-       u64 generation;
-       u64 orig_generation;
-       int level;
-       struct list_head list;
-       int del;
-};
-
-static int __btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
-                                        struct btrfs_root *root, u64 parent,
-                                        u64 root_objectid, u64 ref_generation,
-                                        u64 owner, struct btrfs_key *ins,
-                                        int ref_mod);
-static int update_reserved_extents(struct btrfs_root *root,
-                                  u64 bytenr, u64 num, int reserve);
 static int update_block_group(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root,
-                             u64 bytenr, u64 num_bytes, int alloc,
-                             int mark_free);
-static noinline int __btrfs_free_extent(struct btrfs_trans_handle *trans,
-                                       struct btrfs_root *root,
-                                       u64 bytenr, u64 num_bytes, u64 parent,
-                                       u64 root_objectid, u64 ref_generation,
-                                       u64 owner_objectid, int pin,
-                                       int ref_to_drop);
-
+                             u64 bytenr, u64 num_bytes, int alloc);
+static int update_reserved_bytes(struct btrfs_block_group_cache *cache,
+                                u64 num_bytes, int reserve, int sinfo);
+static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               u64 bytenr, u64 num_bytes, u64 parent,
+                               u64 root_objectid, u64 owner_objectid,
+                               u64 owner_offset, int refs_to_drop,
+                               struct btrfs_delayed_extent_op *extra_op);
+static void __run_delayed_extent_op(struct btrfs_delayed_extent_op *extent_op,
+                                   struct extent_buffer *leaf,
+                                   struct btrfs_extent_item *ei);
+static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
+                                     struct btrfs_root *root,
+                                     u64 parent, u64 root_objectid,
+                                     u64 flags, u64 owner, u64 offset,
+                                     struct btrfs_key *ins, int ref_mod);
+static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
+                                    struct btrfs_root *root,
+                                    u64 parent, u64 root_objectid,
+                                    u64 flags, struct btrfs_disk_key *key,
+                                    int level, struct btrfs_key *ins);
 static int do_chunk_alloc(struct btrfs_trans_handle *trans,
                          struct btrfs_root *extent_root, u64 alloc_bytes,
                          u64 flags, int force);
+static int find_next_key(struct btrfs_path *path, int level,
+                        struct btrfs_key *key);
+static void dump_space_info(struct btrfs_space_info *info, u64 bytes,
+                           int dump_block_groups);
+
+static noinline int
+block_group_cache_done(struct btrfs_block_group_cache *cache)
+{
+       smp_mb();
+       return cache->cached == BTRFS_CACHE_FINISHED;
+}
 
 static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
 {
        return (cache->flags & bits) == bits;
 }
 
+void btrfs_get_block_group(struct btrfs_block_group_cache *cache)
+{
+       atomic_inc(&cache->count);
+}
+
+void btrfs_put_block_group(struct btrfs_block_group_cache *cache)
+{
+       if (atomic_dec_and_test(&cache->count)) {
+               WARN_ON(cache->pinned > 0);
+               WARN_ON(cache->reserved > 0);
+               WARN_ON(cache->reserved_pinned > 0);
+               kfree(cache);
+       }
+}
+
 /*
  * this adds the block group to the fs_info rb tree for the block group
  * cache
@@ -149,35 +165,124 @@ block_group_cache_tree_search(struct btrfs_fs_info *info, u64 bytenr,
                }
        }
        if (ret)
-               atomic_inc(&ret->count);
+               btrfs_get_block_group(ret);
        spin_unlock(&info->block_group_cache_lock);
 
        return ret;
 }
 
+static int add_excluded_extent(struct btrfs_root *root,
+                              u64 start, u64 num_bytes)
+{
+       u64 end = start + num_bytes - 1;
+       set_extent_bits(&root->fs_info->freed_extents[0],
+                       start, end, EXTENT_UPTODATE, GFP_NOFS);
+       set_extent_bits(&root->fs_info->freed_extents[1],
+                       start, end, EXTENT_UPTODATE, GFP_NOFS);
+       return 0;
+}
+
+static void free_excluded_extents(struct btrfs_root *root,
+                                 struct btrfs_block_group_cache *cache)
+{
+       u64 start, end;
+
+       start = cache->key.objectid;
+       end = start + cache->key.offset - 1;
+
+       clear_extent_bits(&root->fs_info->freed_extents[0],
+                         start, end, EXTENT_UPTODATE, GFP_NOFS);
+       clear_extent_bits(&root->fs_info->freed_extents[1],
+                         start, end, EXTENT_UPTODATE, GFP_NOFS);
+}
+
+static int exclude_super_stripes(struct btrfs_root *root,
+                                struct btrfs_block_group_cache *cache)
+{
+       u64 bytenr;
+       u64 *logical;
+       int stripe_len;
+       int i, nr, ret;
+
+       if (cache->key.objectid < BTRFS_SUPER_INFO_OFFSET) {
+               stripe_len = BTRFS_SUPER_INFO_OFFSET - cache->key.objectid;
+               cache->bytes_super += stripe_len;
+               ret = add_excluded_extent(root, cache->key.objectid,
+                                         stripe_len);
+               BUG_ON(ret);
+       }
+
+       for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+               bytenr = btrfs_sb_offset(i);
+               ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
+                                      cache->key.objectid, bytenr,
+                                      0, &logical, &nr, &stripe_len);
+               BUG_ON(ret);
+
+               while (nr--) {
+                       cache->bytes_super += stripe_len;
+                       ret = add_excluded_extent(root, logical[nr],
+                                                 stripe_len);
+                       BUG_ON(ret);
+               }
+
+               kfree(logical);
+       }
+       return 0;
+}
+
+static struct btrfs_caching_control *
+get_caching_control(struct btrfs_block_group_cache *cache)
+{
+       struct btrfs_caching_control *ctl;
+
+       spin_lock(&cache->lock);
+       if (cache->cached != BTRFS_CACHE_STARTED) {
+               spin_unlock(&cache->lock);
+               return NULL;
+       }
+
+       /* We're loading it the fast way, so we don't have a caching_ctl. */
+       if (!cache->caching_ctl) {
+               spin_unlock(&cache->lock);
+               return NULL;
+       }
+
+       ctl = cache->caching_ctl;
+       atomic_inc(&ctl->count);
+       spin_unlock(&cache->lock);
+       return ctl;
+}
+
+static void put_caching_control(struct btrfs_caching_control *ctl)
+{
+       if (atomic_dec_and_test(&ctl->count))
+               kfree(ctl);
+}
+
 /*
  * this is only called by cache_block_group, since we could have freed extents
  * we need to check the pinned_extents for any extents that can't be used yet
  * since their free space will be released as soon as the transaction commits.
  */
-static int add_new_free_space(struct btrfs_block_group_cache *block_group,
+static u64 add_new_free_space(struct btrfs_block_group_cache *block_group,
                              struct btrfs_fs_info *info, u64 start, u64 end)
 {
-       u64 extent_start, extent_end, size;
+       u64 extent_start, extent_end, size, total_added = 0;
        int ret;
 
-       mutex_lock(&info->pinned_mutex);
        while (start < end) {
-               ret = find_first_extent_bit(&info->pinned_extents, start,
+               ret = find_first_extent_bit(info->pinned_extents, start,
                                            &extent_start, &extent_end,
-                                           EXTENT_DIRTY);
+                                           EXTENT_DIRTY | EXTENT_UPTODATE);
                if (ret)
                        break;
 
-               if (extent_start == start) {
+               if (extent_start <= start) {
                        start = extent_end + 1;
                } else if (extent_start > start && extent_start < end) {
                        size = extent_start - start;
+                       total_added += size;
                        ret = btrfs_add_free_space(block_group, start,
                                                   size);
                        BUG_ON(ret);
@@ -189,113 +294,218 @@ static int add_new_free_space(struct btrfs_block_group_cache *block_group,
 
        if (start < end) {
                size = end - start;
+               total_added += size;
                ret = btrfs_add_free_space(block_group, start, size);
                BUG_ON(ret);
        }
-       mutex_unlock(&info->pinned_mutex);
-
-       return 0;
-}
-
-static int remove_sb_from_cache(struct btrfs_root *root,
-                               struct btrfs_block_group_cache *cache)
-{
-       u64 bytenr;
-       u64 *logical;
-       int stripe_len;
-       int i, nr, ret;
 
-       for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
-               bytenr = btrfs_sb_offset(i);
-               ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
-                                      cache->key.objectid, bytenr, 0,
-                                      &logical, &nr, &stripe_len);
-               BUG_ON(ret);
-               while (nr--) {
-                       btrfs_remove_free_space(cache, logical[nr],
-                                               stripe_len);
-               }
-               kfree(logical);
-       }
-       return 0;
+       return total_added;
 }
 
-static int cache_block_group(struct btrfs_root *root,
-                            struct btrfs_block_group_cache *block_group)
+static int caching_kthread(void *data)
 {
+       struct btrfs_block_group_cache *block_group = data;
+       struct btrfs_fs_info *fs_info = block_group->fs_info;
+       struct btrfs_caching_control *caching_ctl = block_group->caching_ctl;
+       struct btrfs_root *extent_root = fs_info->extent_root;
        struct btrfs_path *path;
-       int ret = 0;
-       struct btrfs_key key;
        struct extent_buffer *leaf;
-       int slot;
-       u64 last;
-
-       if (!block_group)
-               return 0;
-
-       root = root->fs_info->extent_root;
-
-       if (block_group->cached)
-               return 0;
+       struct btrfs_key key;
+       u64 total_found = 0;
+       u64 last = 0;
+       u32 nritems;
+       int ret = 0;
 
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
 
-       path->reada = 2;
+       exclude_super_stripes(extent_root, block_group);
+       spin_lock(&block_group->space_info->lock);
+       block_group->space_info->bytes_readonly += block_group->bytes_super;
+       spin_unlock(&block_group->space_info->lock);
+
+       last = max_t(u64, block_group->key.objectid, BTRFS_SUPER_INFO_OFFSET);
+
        /*
-        * we get into deadlocks with paths held by callers of this function.
-        * since the alloc_mutex is protecting things right now, just
-        * skip the locking here
+        * We don't want to deadlock with somebody trying to allocate a new
+        * extent for the extent root while also trying to search the extent
+        * root to add free space.  So we skip locking and search the commit
+        * root, since its read-only
         */
        path->skip_locking = 1;
-       last = max_t(u64, block_group->key.objectid, BTRFS_SUPER_INFO_OFFSET);
+       path->search_commit_root = 1;
+       path->reada = 2;
+
        key.objectid = last;
        key.offset = 0;
-       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
-       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+       key.type = BTRFS_EXTENT_ITEM_KEY;
+again:
+       mutex_lock(&caching_ctl->mutex);
+       /* need to make sure the commit_root doesn't disappear */
+       down_read(&fs_info->extent_commit_sem);
+
+       ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
        if (ret < 0)
                goto err;
 
+       leaf = path->nodes[0];
+       nritems = btrfs_header_nritems(leaf);
+
        while (1) {
-               leaf = path->nodes[0];
-               slot = path->slots[0];
-               if (slot >= btrfs_header_nritems(leaf)) {
-                       ret = btrfs_next_leaf(root, path);
-                       if (ret < 0)
-                               goto err;
-                       if (ret == 0)
-                               continue;
-                       else
+               smp_mb();
+               if (fs_info->closing > 1) {
+                       last = (u64)-1;
+                       break;
+               }
+
+               if (path->slots[0] < nritems) {
+                       btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+               } else {
+                       ret = find_next_key(path, 0, &key);
+                       if (ret)
                                break;
+
+                       caching_ctl->progress = last;
+                       btrfs_release_path(extent_root, path);
+                       up_read(&fs_info->extent_commit_sem);
+                       mutex_unlock(&caching_ctl->mutex);
+                       if (btrfs_transaction_in_commit(fs_info))
+                               schedule_timeout(1);
+                       else
+                               cond_resched();
+                       goto again;
+               }
+
+               if (key.objectid < block_group->key.objectid) {
+                       path->slots[0]++;
+                       continue;
                }
-               btrfs_item_key_to_cpu(leaf, &key, slot);
-               if (key.objectid < block_group->key.objectid)
-                       goto next;
 
                if (key.objectid >= block_group->key.objectid +
                    block_group->key.offset)
                        break;
 
-               if (btrfs_key_type(&key) == BTRFS_EXTENT_ITEM_KEY) {
-                       add_new_free_space(block_group, root->fs_info, last,
-                                          key.objectid);
-
+               if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+                       total_found += add_new_free_space(block_group,
+                                                         fs_info, last,
+                                                         key.objectid);
                        last = key.objectid + key.offset;
+
+                       if (total_found > (1024 * 1024 * 2)) {
+                               total_found = 0;
+                               wake_up(&caching_ctl->wait);
+                       }
                }
-next:
                path->slots[0]++;
        }
+       ret = 0;
 
-       add_new_free_space(block_group, root->fs_info, last,
-                          block_group->key.objectid +
-                          block_group->key.offset);
+       total_found += add_new_free_space(block_group, fs_info, last,
+                                         block_group->key.objectid +
+                                         block_group->key.offset);
+       caching_ctl->progress = (u64)-1;
+
+       spin_lock(&block_group->lock);
+       block_group->caching_ctl = NULL;
+       block_group->cached = BTRFS_CACHE_FINISHED;
+       spin_unlock(&block_group->lock);
 
-       block_group->cached = 1;
-       remove_sb_from_cache(root, block_group);
-       ret = 0;
 err:
        btrfs_free_path(path);
+       up_read(&fs_info->extent_commit_sem);
+
+       free_excluded_extents(extent_root, block_group);
+
+       mutex_unlock(&caching_ctl->mutex);
+       wake_up(&caching_ctl->wait);
+
+       put_caching_control(caching_ctl);
+       atomic_dec(&block_group->space_info->caching_threads);
+       btrfs_put_block_group(block_group);
+
+       return 0;
+}
+
+static int cache_block_group(struct btrfs_block_group_cache *cache,
+                            struct btrfs_trans_handle *trans,
+                            int load_cache_only)
+{
+       struct btrfs_fs_info *fs_info = cache->fs_info;
+       struct btrfs_caching_control *caching_ctl;
+       struct task_struct *tsk;
+       int ret = 0;
+
+       smp_mb();
+       if (cache->cached != BTRFS_CACHE_NO)
+               return 0;
+
+       /*
+        * We can't do the read from on-disk cache during a commit since we need
+        * to have the normal tree locking.
+        */
+       if (!trans->transaction->in_commit) {
+               spin_lock(&cache->lock);
+               if (cache->cached != BTRFS_CACHE_NO) {
+                       spin_unlock(&cache->lock);
+                       return 0;
+               }
+               cache->cached = BTRFS_CACHE_STARTED;
+               spin_unlock(&cache->lock);
+
+               ret = load_free_space_cache(fs_info, cache);
+
+               spin_lock(&cache->lock);
+               if (ret == 1) {
+                       cache->cached = BTRFS_CACHE_FINISHED;
+                       cache->last_byte_to_unpin = (u64)-1;
+               } else {
+                       cache->cached = BTRFS_CACHE_NO;
+               }
+               spin_unlock(&cache->lock);
+               if (ret == 1)
+                       return 0;
+       }
+
+       if (load_cache_only)
+               return 0;
+
+       caching_ctl = kzalloc(sizeof(*caching_ctl), GFP_KERNEL);
+       BUG_ON(!caching_ctl);
+
+       INIT_LIST_HEAD(&caching_ctl->list);
+       mutex_init(&caching_ctl->mutex);
+       init_waitqueue_head(&caching_ctl->wait);
+       caching_ctl->block_group = cache;
+       caching_ctl->progress = cache->key.objectid;
+       /* one for caching kthread, one for caching block group list */
+       atomic_set(&caching_ctl->count, 2);
+
+       spin_lock(&cache->lock);
+       if (cache->cached != BTRFS_CACHE_NO) {
+               spin_unlock(&cache->lock);
+               kfree(caching_ctl);
+               return 0;
+       }
+       cache->caching_ctl = caching_ctl;
+       cache->cached = BTRFS_CACHE_STARTED;
+       spin_unlock(&cache->lock);
+
+       down_write(&fs_info->extent_commit_sem);
+       list_add_tail(&caching_ctl->list, &fs_info->caching_block_groups);
+       up_write(&fs_info->extent_commit_sem);
+
+       atomic_inc(&cache->space_info->caching_threads);
+       btrfs_get_block_group(cache);
+
+       tsk = kthread_run(caching_kthread, cache, "btrfs-cache-%llu\n",
+                         cache->key.objectid);
+       if (IS_ERR(tsk)) {
+               ret = PTR_ERR(tsk);
+               printk(KERN_ERR "error running thread %d\n", ret);
+               BUG();
+       }
+
        return ret;
 }
 
@@ -313,7 +523,7 @@ btrfs_lookup_first_block_group(struct btrfs_fs_info *info, u64 bytenr)
 }
 
 /*
- * return the block group that contains teh given bytenr
+ * return the block group that contains the given bytenr
  */
 struct btrfs_block_group_cache *btrfs_lookup_block_group(
                                                 struct btrfs_fs_info *info,
@@ -326,21 +536,18 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(
        return cache;
 }
 
-static inline void put_block_group(struct btrfs_block_group_cache *cache)
-{
-       if (atomic_dec_and_test(&cache->count))
-               kfree(cache);
-}
-
 static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info,
                                                  u64 flags)
 {
        struct list_head *head = &info->space_info;
        struct btrfs_space_info *found;
 
+       flags &= BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_SYSTEM |
+                BTRFS_BLOCK_GROUP_METADATA;
+
        rcu_read_lock();
        list_for_each_entry_rcu(found, head, list) {
-               if (found->flags == flags) {
+               if (found->flags & flags) {
                        rcu_read_unlock();
                        return found;
                }
@@ -399,12 +606,12 @@ again:
                            div_factor(cache->key.offset, factor)) {
                                group_start = cache->key.objectid;
                                spin_unlock(&cache->lock);
-                               put_block_group(cache);
+                               btrfs_put_block_group(cache);
                                goto found;
                        }
                }
                spin_unlock(&cache->lock);
-               put_block_group(cache);
+               btrfs_put_block_group(cache);
                cond_resched();
        }
        if (!wrapped) {
@@ -440,6 +647,113 @@ int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len)
        return ret;
 }
 
+/*
+ * helper function to lookup reference count and flags of extent.
+ *
+ * the head node for delayed ref is used to store the sum of all the
+ * reference count modifications queued up in the rbtree. the head
+ * node may also store the extent flags to set. This way you can check
+ * to see what the reference count and extent flags would be if all of
+ * the delayed refs are not processed.
+ */
+int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
+                            struct btrfs_root *root, u64 bytenr,
+                            u64 num_bytes, u64 *refs, u64 *flags)
+{
+       struct btrfs_delayed_ref_head *head;
+       struct btrfs_delayed_ref_root *delayed_refs;
+       struct btrfs_path *path;
+       struct btrfs_extent_item *ei;
+       struct extent_buffer *leaf;
+       struct btrfs_key key;
+       u32 item_size;
+       u64 num_refs;
+       u64 extent_flags;
+       int ret;
+
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+
+       key.objectid = bytenr;
+       key.type = BTRFS_EXTENT_ITEM_KEY;
+       key.offset = num_bytes;
+       if (!trans) {
+               path->skip_locking = 1;
+               path->search_commit_root = 1;
+       }
+again:
+       ret = btrfs_search_slot(trans, root->fs_info->extent_root,
+                               &key, path, 0, 0);
+       if (ret < 0)
+               goto out_free;
+
+       if (ret == 0) {
+               leaf = path->nodes[0];
+               item_size = btrfs_item_size_nr(leaf, path->slots[0]);
+               if (item_size >= sizeof(*ei)) {
+                       ei = btrfs_item_ptr(leaf, path->slots[0],
+                                           struct btrfs_extent_item);
+                       num_refs = btrfs_extent_refs(leaf, ei);
+                       extent_flags = btrfs_extent_flags(leaf, ei);
+               } else {
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+                       struct btrfs_extent_item_v0 *ei0;
+                       BUG_ON(item_size != sizeof(*ei0));
+                       ei0 = btrfs_item_ptr(leaf, path->slots[0],
+                                            struct btrfs_extent_item_v0);
+                       num_refs = btrfs_extent_refs_v0(leaf, ei0);
+                       /* FIXME: this isn't correct for data */
+                       extent_flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
+#else
+                       BUG();
+#endif
+               }
+               BUG_ON(num_refs == 0);
+       } else {
+               num_refs = 0;
+               extent_flags = 0;
+               ret = 0;
+       }
+
+       if (!trans)
+               goto out;
+
+       delayed_refs = &trans->transaction->delayed_refs;
+       spin_lock(&delayed_refs->lock);
+       head = btrfs_find_delayed_ref_head(trans, bytenr);
+       if (head) {
+               if (!mutex_trylock(&head->mutex)) {
+                       atomic_inc(&head->node.refs);
+                       spin_unlock(&delayed_refs->lock);
+
+                       btrfs_release_path(root->fs_info->extent_root, path);
+
+                       mutex_lock(&head->mutex);
+                       mutex_unlock(&head->mutex);
+                       btrfs_put_delayed_ref(&head->node);
+                       goto again;
+               }
+               if (head->extent_op && head->extent_op->update_flags)
+                       extent_flags |= head->extent_op->flags_to_set;
+               else
+                       BUG_ON(num_refs == 0);
+
+               num_refs += head->node.ref_mod;
+               mutex_unlock(&head->mutex);
+       }
+       spin_unlock(&delayed_refs->lock);
+out:
+       WARN_ON(num_refs == 0);
+       if (refs)
+               *refs = num_refs;
+       if (flags)
+               *flags = extent_flags;
+out_free:
+       btrfs_free_path(path);
+       return ret;
+}
+
 /*
  * Back reference rules.  Back refs have three main goals:
  *
@@ -454,1571 +768,3313 @@ int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len)
  *    maintenance.  This is actually the same as #2, but with a slightly
  *    different use case.
  *
- * File extents can be referenced by:
+ * There are two kinds of back refs. The implicit back refs is optimized
+ * for pointers in non-shared tree blocks. For a given pointer in a block,
+ * back refs of this kind provide information about the block's owner tree
+ * and the pointer's key. These information allow us to find the block by
+ * b-tree searching. The full back refs is for pointers in tree blocks not
+ * referenced by their owner trees. The location of tree block is recorded
+ * in the back refs. Actually the full back refs is generic, and can be
+ * used in all cases the implicit back refs is used. The major shortcoming
+ * of the full back refs is its overhead. Every time a tree block gets
+ * COWed, we have to update back refs entry for all pointers in it.
+ *
+ * For a newly allocated tree block, we use implicit back refs for
+ * pointers in it. This means most tree related operations only involve
+ * implicit back refs. For a tree block created in old transaction, the
+ * only way to drop a reference to it is COW it. So we can detect the
+ * event that tree block loses its owner tree's reference and do the
+ * back refs conversion.
+ *
+ * When a tree block is COW'd through a tree, there are four cases:
+ *
+ * The reference count of the block is one and the tree is the block's
+ * owner tree. Nothing to do in this case.
+ *
+ * The reference count of the block is one and the tree is not the
+ * block's owner tree. In this case, full back refs is used for pointers
+ * in the block. Remove these full back refs, add implicit back refs for
+ * every pointers in the new block.
+ *
+ * The reference count of the block is greater than one and the tree is
+ * the block's owner tree. In this case, implicit back refs is used for
+ * pointers in the block. Add full back refs for every pointers in the
+ * block, increase lower level extents' reference counts. The original
+ * implicit back refs are entailed to the new block.
+ *
+ * The reference count of the block is greater than one and the tree is
+ * not the block's owner tree. Add implicit back refs for every pointer in
+ * the new block, increase lower level extents' reference count.
+ *
+ * Back Reference Key composing:
+ *
+ * The key objectid corresponds to the first byte in the extent,
+ * The key type is used to differentiate between types of back refs.
+ * There are different meanings of the key offset for different types
+ * of back refs.
+ *
+ * File extents can be referenced by:
  *
  * - multiple snapshots, subvolumes, or different generations in one subvol
  * - different files inside a single subvolume
  * - different offsets inside a file (bookend extents in file.c)
  *
- * The extent ref structure has fields for:
+ * The extent ref structure for the implicit back refs has fields for:
  *
  * - Objectid of the subvolume root
- * - Generation number of the tree holding the reference
  * - objectid of the file holding the reference
- * - number of references holding by parent node (alway 1 for tree blocks)
- *
- * Btree leaf may hold multiple references to a file extent. In most cases,
- * these references are from same file and the corresponding offsets inside
- * the file are close together.
+ * - original offset in the file
+ * - how many bookend extents
  *
- * When a file extent is allocated the fields are filled in:
- *     (root_key.objectid, trans->transid, inode objectid, 1)
+ * The key offset for the implicit back refs is hash of the first
+ * three fields.
  *
- * When a leaf is cow'd new references are added for every file extent found
- * in the leaf.  It looks similar to the create case, but trans->transid will
- * be different when the block is cow'd.
+ * The extent ref structure for the full back refs has field for:
  *
- *     (root_key.objectid, trans->transid, inode objectid,
- *      number of references in the leaf)
- *
- * When a file extent is removed either during snapshot deletion or
- * file truncation, we find the corresponding back reference and check
- * the following fields:
- *
- *     (btrfs_header_owner(leaf), btrfs_header_generation(leaf),
- *      inode objectid)
- *
- * Btree extents can be referenced by:
+ * - number of pointers in the tree leaf
  *
- * - Different subvolumes
- * - Different generations of the same subvolume
+ * The key offset for the implicit back refs is the first byte of
+ * the tree leaf
  *
- * When a tree block is created, back references are inserted:
+ * When a file extent is allocated, The implicit back refs is used.
+ * the fields are filled in:
  *
- * (root->root_key.objectid, trans->transid, level, 1)
+ *     (root_key.objectid, inode objectid, offset in file, 1)
  *
- * When a tree block is cow'd, new back references are added for all the
- * blocks it points to. If the tree block isn't in reference counted root,
- * the old back references are removed. These new back references are of
- * the form (trans->transid will have increased since creation):
+ * When a file extent is removed file truncation, we find the
+ * corresponding implicit back refs and check the following fields:
  *
- * (root->root_key.objectid, trans->transid, level, 1)
+ *     (btrfs_header_owner(leaf), inode objectid, offset in file)
  *
- * When a backref is in deleting, the following fields are checked:
+ * Btree extents can be referenced by:
  *
- * if backref was for a tree root:
- *     (btrfs_header_owner(itself), btrfs_header_generation(itself), level)
- * else
- *     (btrfs_header_owner(parent), btrfs_header_generation(parent), level)
+ * - Different subvolumes
  *
- * Back Reference Key composing:
+ * Both the implicit back refs and the full back refs for tree blocks
+ * only consist of key. The key offset for the implicit back refs is
+ * objectid of block's owner tree. The key offset for the full back refs
+ * is the first byte of parent block.
  *
- * The key objectid corresponds to the first byte in the extent, the key
- * type is set to BTRFS_EXTENT_REF_KEY, and the key offset is the first
- * byte of parent extent. If a extent is tree root, the key offset is set
- * to the key objectid.
+ * When implicit back refs is used, information about the lowest key and
+ * level of the tree block are required. These information are stored in
+ * tree block info structure.
  */
 
-static noinline int lookup_extent_backref(struct btrfs_trans_handle *trans,
-                                         struct btrfs_root *root,
-                                         struct btrfs_path *path,
-                                         u64 bytenr, u64 parent,
-                                         u64 ref_root, u64 ref_generation,
-                                         u64 owner_objectid, int del)
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
+                                 struct btrfs_root *root,
+                                 struct btrfs_path *path,
+                                 u64 owner, u32 extra_size)
 {
+       struct btrfs_extent_item *item;
+       struct btrfs_extent_item_v0 *ei0;
+       struct btrfs_extent_ref_v0 *ref0;
+       struct btrfs_tree_block_info *bi;
+       struct extent_buffer *leaf;
        struct btrfs_key key;
-       struct btrfs_extent_ref *ref;
+       struct btrfs_key found_key;
+       u32 new_size = sizeof(*item);
+       u64 refs;
+       int ret;
+
+       leaf = path->nodes[0];
+       BUG_ON(btrfs_item_size_nr(leaf, path->slots[0]) != sizeof(*ei0));
+
+       btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+       ei0 = btrfs_item_ptr(leaf, path->slots[0],
+                            struct btrfs_extent_item_v0);
+       refs = btrfs_extent_refs_v0(leaf, ei0);
+
+       if (owner == (u64)-1) {
+               while (1) {
+                       if (path->slots[0] >= btrfs_header_nritems(leaf)) {
+                               ret = btrfs_next_leaf(root, path);
+                               if (ret < 0)
+                                       return ret;
+                               BUG_ON(ret > 0);
+                               leaf = path->nodes[0];
+                       }
+                       btrfs_item_key_to_cpu(leaf, &found_key,
+                                             path->slots[0]);
+                       BUG_ON(key.objectid != found_key.objectid);
+                       if (found_key.type != BTRFS_EXTENT_REF_V0_KEY) {
+                               path->slots[0]++;
+                               continue;
+                       }
+                       ref0 = btrfs_item_ptr(leaf, path->slots[0],
+                                             struct btrfs_extent_ref_v0);
+                       owner = btrfs_ref_objectid_v0(leaf, ref0);
+                       break;
+               }
+       }
+       btrfs_release_path(root, path);
+
+       if (owner < BTRFS_FIRST_FREE_OBJECTID)
+               new_size += sizeof(*bi);
+
+       new_size -= sizeof(*ei0);
+       ret = btrfs_search_slot(trans, root, &key, path,
+                               new_size + extra_size, 1);
+       if (ret < 0)
+               return ret;
+       BUG_ON(ret);
+
+       ret = btrfs_extend_item(trans, root, path, new_size);
+       BUG_ON(ret);
+
+       leaf = path->nodes[0];
+       item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
+       btrfs_set_extent_refs(leaf, item, refs);
+       /* FIXME: get real generation */
+       btrfs_set_extent_generation(leaf, item, 0);
+       if (owner < BTRFS_FIRST_FREE_OBJECTID) {
+               btrfs_set_extent_flags(leaf, item,
+                                      BTRFS_EXTENT_FLAG_TREE_BLOCK |
+                                      BTRFS_BLOCK_FLAG_FULL_BACKREF);
+               bi = (struct btrfs_tree_block_info *)(item + 1);
+               /* FIXME: get first key of the block */
+               memset_extent_buffer(leaf, 0, (unsigned long)bi, sizeof(*bi));
+               btrfs_set_tree_block_level(leaf, bi, (int)owner);
+       } else {
+               btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_DATA);
+       }
+       btrfs_mark_buffer_dirty(leaf);
+       return 0;
+}
+#endif
+
+static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
+{
+       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));
+       lenum = cpu_to_le64(owner);
+       low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
+       lenum = cpu_to_le64(offset);
+       low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
+
+       return ((u64)high_crc << 31) ^ (u64)low_crc;
+}
+
+static u64 hash_extent_data_ref_item(struct extent_buffer *leaf,
+                                    struct btrfs_extent_data_ref *ref)
+{
+       return hash_extent_data_ref(btrfs_extent_data_ref_root(leaf, ref),
+                                   btrfs_extent_data_ref_objectid(leaf, ref),
+                                   btrfs_extent_data_ref_offset(leaf, ref));
+}
+
+static int match_extent_data_ref(struct extent_buffer *leaf,
+                                struct btrfs_extent_data_ref *ref,
+                                u64 root_objectid, u64 owner, u64 offset)
+{
+       if (btrfs_extent_data_ref_root(leaf, ref) != root_objectid ||
+           btrfs_extent_data_ref_objectid(leaf, ref) != owner ||
+           btrfs_extent_data_ref_offset(leaf, ref) != offset)
+               return 0;
+       return 1;
+}
+
+static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans,
+                                          struct btrfs_root *root,
+                                          struct btrfs_path *path,
+                                          u64 bytenr, u64 parent,
+                                          u64 root_objectid,
+                                          u64 owner, u64 offset)
+{
+       struct btrfs_key key;
+       struct btrfs_extent_data_ref *ref;
        struct extent_buffer *leaf;
-       u64 ref_objectid;
+       u32 nritems;
        int ret;
+       int recow;
+       int err = -ENOENT;
 
        key.objectid = bytenr;
-       key.type = BTRFS_EXTENT_REF_KEY;
-       key.offset = parent;
+       if (parent) {
+               key.type = BTRFS_SHARED_DATA_REF_KEY;
+               key.offset = parent;
+       } else {
+               key.type = BTRFS_EXTENT_DATA_REF_KEY;
+               key.offset = hash_extent_data_ref(root_objectid,
+                                                 owner, offset);
+       }
+again:
+       recow = 0;
+       ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+       if (ret < 0) {
+               err = ret;
+               goto fail;
+       }
 
-       ret = btrfs_search_slot(trans, root, &key, path, del ? -1 : 0, 1);
-       if (ret < 0)
-               goto out;
-       if (ret > 0) {
-               ret = -ENOENT;
-               goto out;
+       if (parent) {
+               if (!ret)
+                       return 0;
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+               key.type = BTRFS_EXTENT_REF_V0_KEY;
+               btrfs_release_path(root, path);
+               ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+               if (ret < 0) {
+                       err = ret;
+                       goto fail;
+               }
+               if (!ret)
+                       return 0;
+#endif
+               goto fail;
        }
 
        leaf = path->nodes[0];
-       ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_ref);
-       ref_objectid = btrfs_ref_objectid(leaf, ref);
-       if (btrfs_ref_root(leaf, ref) != ref_root ||
-           btrfs_ref_generation(leaf, ref) != ref_generation ||
-           (ref_objectid != owner_objectid &&
-            ref_objectid != BTRFS_MULTIPLE_OBJECTIDS)) {
-               ret = -EIO;
-               WARN_ON(1);
-               goto out;
+       nritems = btrfs_header_nritems(leaf);
+       while (1) {
+               if (path->slots[0] >= nritems) {
+                       ret = btrfs_next_leaf(root, path);
+                       if (ret < 0)
+                               err = ret;
+                       if (ret)
+                               goto fail;
+
+                       leaf = path->nodes[0];
+                       nritems = btrfs_header_nritems(leaf);
+                       recow = 1;
+               }
+
+               btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+               if (key.objectid != bytenr ||
+                   key.type != BTRFS_EXTENT_DATA_REF_KEY)
+                       goto fail;
+
+               ref = btrfs_item_ptr(leaf, path->slots[0],
+                                    struct btrfs_extent_data_ref);
+
+               if (match_extent_data_ref(leaf, ref, root_objectid,
+                                         owner, offset)) {
+                       if (recow) {
+                               btrfs_release_path(root, path);
+                               goto again;
+                       }
+                       err = 0;
+                       break;
+               }
+               path->slots[0]++;
        }
-       ret = 0;
-out:
-       return ret;
+fail:
+       return err;
 }
 
-static noinline int insert_extent_backref(struct btrfs_trans_handle *trans,
-                                         struct btrfs_root *root,
-                                         struct btrfs_path *path,
-                                         u64 bytenr, u64 parent,
-                                         u64 ref_root, u64 ref_generation,
-                                         u64 owner_objectid,
-                                         int refs_to_add)
+static noinline int insert_extent_data_ref(struct btrfs_trans_handle *trans,
+                                          struct btrfs_root *root,
+                                          struct btrfs_path *path,
+                                          u64 bytenr, u64 parent,
+                                          u64 root_objectid, u64 owner,
+                                          u64 offset, int refs_to_add)
 {
        struct btrfs_key key;
        struct extent_buffer *leaf;
-       struct btrfs_extent_ref *ref;
+       u32 size;
        u32 num_refs;
        int ret;
 
        key.objectid = bytenr;
-       key.type = BTRFS_EXTENT_REF_KEY;
-       key.offset = parent;
+       if (parent) {
+               key.type = BTRFS_SHARED_DATA_REF_KEY;
+               key.offset = parent;
+               size = sizeof(struct btrfs_shared_data_ref);
+       } else {
+               key.type = BTRFS_EXTENT_DATA_REF_KEY;
+               key.offset = hash_extent_data_ref(root_objectid,
+                                                 owner, offset);
+               size = sizeof(struct btrfs_extent_data_ref);
+       }
 
-       ret = btrfs_insert_empty_item(trans, root, path, &key, sizeof(*ref));
-       if (ret == 0) {
-               leaf = path->nodes[0];
-               ref = btrfs_item_ptr(leaf, path->slots[0],
-                                    struct btrfs_extent_ref);
-               btrfs_set_ref_root(leaf, ref, ref_root);
-               btrfs_set_ref_generation(leaf, ref, ref_generation);
-               btrfs_set_ref_objectid(leaf, ref, owner_objectid);
-               btrfs_set_ref_num_refs(leaf, ref, refs_to_add);
-       } else if (ret == -EEXIST) {
-               u64 existing_owner;
-
-               BUG_ON(owner_objectid < BTRFS_FIRST_FREE_OBJECTID);
-               leaf = path->nodes[0];
+       ret = btrfs_insert_empty_item(trans, root, path, &key, size);
+       if (ret && ret != -EEXIST)
+               goto fail;
+
+       leaf = path->nodes[0];
+       if (parent) {
+               struct btrfs_shared_data_ref *ref;
                ref = btrfs_item_ptr(leaf, path->slots[0],
-                                    struct btrfs_extent_ref);
-               if (btrfs_ref_root(leaf, ref) != ref_root ||
-                   btrfs_ref_generation(leaf, ref) != ref_generation) {
-                       ret = -EIO;
-                       WARN_ON(1);
-                       goto out;
+                                    struct btrfs_shared_data_ref);
+               if (ret == 0) {
+                       btrfs_set_shared_data_ref_count(leaf, ref, refs_to_add);
+               } else {
+                       num_refs = btrfs_shared_data_ref_count(leaf, ref);
+                       num_refs += refs_to_add;
+                       btrfs_set_shared_data_ref_count(leaf, ref, num_refs);
                }
+       } else {
+               struct btrfs_extent_data_ref *ref;
+               while (ret == -EEXIST) {
+                       ref = btrfs_item_ptr(leaf, path->slots[0],
+                                            struct btrfs_extent_data_ref);
+                       if (match_extent_data_ref(leaf, ref, root_objectid,
+                                                 owner, offset))
+                               break;
+                       btrfs_release_path(root, path);
+                       key.offset++;
+                       ret = btrfs_insert_empty_item(trans, root, path, &key,
+                                                     size);
+                       if (ret && ret != -EEXIST)
+                               goto fail;
 
-               num_refs = btrfs_ref_num_refs(leaf, ref);
-               BUG_ON(num_refs == 0);
-               btrfs_set_ref_num_refs(leaf, ref, num_refs + refs_to_add);
-
-               existing_owner = btrfs_ref_objectid(leaf, ref);
-               if (existing_owner != owner_objectid &&
-                   existing_owner != BTRFS_MULTIPLE_OBJECTIDS) {
-                       btrfs_set_ref_objectid(leaf, ref,
-                                       BTRFS_MULTIPLE_OBJECTIDS);
+                       leaf = path->nodes[0];
+               }
+               ref = btrfs_item_ptr(leaf, path->slots[0],
+                                    struct btrfs_extent_data_ref);
+               if (ret == 0) {
+                       btrfs_set_extent_data_ref_root(leaf, ref,
+                                                      root_objectid);
+                       btrfs_set_extent_data_ref_objectid(leaf, ref, owner);
+                       btrfs_set_extent_data_ref_offset(leaf, ref, offset);
+                       btrfs_set_extent_data_ref_count(leaf, ref, refs_to_add);
+               } else {
+                       num_refs = btrfs_extent_data_ref_count(leaf, ref);
+                       num_refs += refs_to_add;
+                       btrfs_set_extent_data_ref_count(leaf, ref, num_refs);
                }
-               ret = 0;
-       } else {
-               goto out;
        }
-       btrfs_unlock_up_safe(path, 1);
-       btrfs_mark_buffer_dirty(path->nodes[0]);
-out:
+       btrfs_mark_buffer_dirty(leaf);
+       ret = 0;
+fail:
        btrfs_release_path(root, path);
        return ret;
 }
 
-static noinline int remove_extent_backref(struct btrfs_trans_handle *trans,
-                                         struct btrfs_root *root,
-                                         struct btrfs_path *path,
-                                         int refs_to_drop)
+static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans,
+                                          struct btrfs_root *root,
+                                          struct btrfs_path *path,
+                                          int refs_to_drop)
 {
+       struct btrfs_key key;
+       struct btrfs_extent_data_ref *ref1 = NULL;
+       struct btrfs_shared_data_ref *ref2 = NULL;
        struct extent_buffer *leaf;
-       struct btrfs_extent_ref *ref;
-       u32 num_refs;
+       u32 num_refs = 0;
        int ret = 0;
 
        leaf = path->nodes[0];
-       ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_ref);
-       num_refs = btrfs_ref_num_refs(leaf, ref);
+       btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+
+       if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
+               ref1 = btrfs_item_ptr(leaf, path->slots[0],
+                                     struct btrfs_extent_data_ref);
+               num_refs = btrfs_extent_data_ref_count(leaf, ref1);
+       } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
+               ref2 = btrfs_item_ptr(leaf, path->slots[0],
+                                     struct btrfs_shared_data_ref);
+               num_refs = btrfs_shared_data_ref_count(leaf, ref2);
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+       } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
+               struct btrfs_extent_ref_v0 *ref0;
+               ref0 = btrfs_item_ptr(leaf, path->slots[0],
+                                     struct btrfs_extent_ref_v0);
+               num_refs = btrfs_ref_count_v0(leaf, ref0);
+#endif
+       } else {
+               BUG();
+       }
+
        BUG_ON(num_refs < refs_to_drop);
        num_refs -= refs_to_drop;
+
        if (num_refs == 0) {
                ret = btrfs_del_item(trans, root, path);
        } else {
-               btrfs_set_ref_num_refs(leaf, ref, num_refs);
+               if (key.type == BTRFS_EXTENT_DATA_REF_KEY)
+                       btrfs_set_extent_data_ref_count(leaf, ref1, num_refs);
+               else if (key.type == BTRFS_SHARED_DATA_REF_KEY)
+                       btrfs_set_shared_data_ref_count(leaf, ref2, num_refs);
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+               else {
+                       struct btrfs_extent_ref_v0 *ref0;
+                       ref0 = btrfs_item_ptr(leaf, path->slots[0],
+                                       struct btrfs_extent_ref_v0);
+                       btrfs_set_ref_count_v0(leaf, ref0, num_refs);
+               }
+#endif
                btrfs_mark_buffer_dirty(leaf);
        }
-       btrfs_release_path(root, path);
        return ret;
 }
 
-#ifdef BIO_RW_DISCARD
-static void btrfs_issue_discard(struct block_device *bdev,
-                               u64 start, u64 len)
+static noinline u32 extent_data_ref_count(struct btrfs_root *root,
+                                         struct btrfs_path *path,
+                                         struct btrfs_extent_inline_ref *iref)
 {
-       blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL);
-}
+       struct btrfs_key key;
+       struct extent_buffer *leaf;
+       struct btrfs_extent_data_ref *ref1;
+       struct btrfs_shared_data_ref *ref2;
+       u32 num_refs = 0;
+
+       leaf = path->nodes[0];
+       btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+       if (iref) {
+               if (btrfs_extent_inline_ref_type(leaf, iref) ==
+                   BTRFS_EXTENT_DATA_REF_KEY) {
+                       ref1 = (struct btrfs_extent_data_ref *)(&iref->offset);
+                       num_refs = btrfs_extent_data_ref_count(leaf, ref1);
+               } else {
+                       ref2 = (struct btrfs_shared_data_ref *)(iref + 1);
+                       num_refs = btrfs_shared_data_ref_count(leaf, ref2);
+               }
+       } else if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
+               ref1 = btrfs_item_ptr(leaf, path->slots[0],
+                                     struct btrfs_extent_data_ref);
+               num_refs = btrfs_extent_data_ref_count(leaf, ref1);
+       } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
+               ref2 = btrfs_item_ptr(leaf, path->slots[0],
+                                     struct btrfs_shared_data_ref);
+               num_refs = btrfs_shared_data_ref_count(leaf, ref2);
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+       } else if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
+               struct btrfs_extent_ref_v0 *ref0;
+               ref0 = btrfs_item_ptr(leaf, path->slots[0],
+                                     struct btrfs_extent_ref_v0);
+               num_refs = btrfs_ref_count_v0(leaf, ref0);
 #endif
+       } else {
+               WARN_ON(1);
+       }
+       return num_refs;
+}
 
-static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
-                               u64 num_bytes)
+static noinline int lookup_tree_block_ref(struct btrfs_trans_handle *trans,
+                                         struct btrfs_root *root,
+                                         struct btrfs_path *path,
+                                         u64 bytenr, u64 parent,
+                                         u64 root_objectid)
 {
-#ifdef BIO_RW_DISCARD
+       struct btrfs_key key;
        int ret;
-       u64 map_length = num_bytes;
-       struct btrfs_multi_bio *multi = NULL;
-
-       /* Tell the block device(s) that the sectors can be discarded */
-       ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
-                             bytenr, &map_length, &multi, 0);
-       if (!ret) {
-               struct btrfs_bio_stripe *stripe = multi->stripes;
-               int i;
-
-               if (map_length > num_bytes)
-                       map_length = num_bytes;
 
-               for (i = 0; i < multi->num_stripes; i++, stripe++) {
-                       btrfs_issue_discard(stripe->dev->bdev,
-                                           stripe->physical,
-                                           map_length);
-               }
-               kfree(multi);
+       key.objectid = bytenr;
+       if (parent) {
+               key.type = BTRFS_SHARED_BLOCK_REF_KEY;
+               key.offset = parent;
+       } else {
+               key.type = BTRFS_TREE_BLOCK_REF_KEY;
+               key.offset = root_objectid;
        }
 
-       return ret;
-#else
-       return 0;
+       ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+       if (ret > 0)
+               ret = -ENOENT;
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+       if (ret == -ENOENT && parent) {
+               btrfs_release_path(root, path);
+               key.type = BTRFS_EXTENT_REF_V0_KEY;
+               ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+               if (ret > 0)
+                       ret = -ENOENT;
+       }
 #endif
+       return ret;
 }
 
-static int __btrfs_update_extent_ref(struct btrfs_trans_handle *trans,
-                                    struct btrfs_root *root, u64 bytenr,
-                                    u64 num_bytes,
-                                    u64 orig_parent, u64 parent,
-                                    u64 orig_root, u64 ref_root,
-                                    u64 orig_generation, u64 ref_generation,
-                                    u64 owner_objectid)
+static noinline int insert_tree_block_ref(struct btrfs_trans_handle *trans,
+                                         struct btrfs_root *root,
+                                         struct btrfs_path *path,
+                                         u64 bytenr, u64 parent,
+                                         u64 root_objectid)
 {
+       struct btrfs_key key;
        int ret;
-       int pin = owner_objectid < BTRFS_FIRST_FREE_OBJECTID;
 
-       ret = btrfs_update_delayed_ref(trans, bytenr, num_bytes,
-                                      orig_parent, parent, orig_root,
-                                      ref_root, orig_generation,
-                                      ref_generation, owner_objectid, pin);
-       BUG_ON(ret);
+       key.objectid = bytenr;
+       if (parent) {
+               key.type = BTRFS_SHARED_BLOCK_REF_KEY;
+               key.offset = parent;
+       } else {
+               key.type = BTRFS_TREE_BLOCK_REF_KEY;
+               key.offset = root_objectid;
+       }
+
+       ret = btrfs_insert_empty_item(trans, root, path, &key, 0);
+       btrfs_release_path(root, path);
        return ret;
 }
 
-int btrfs_update_extent_ref(struct btrfs_trans_handle *trans,
-                           struct btrfs_root *root, u64 bytenr,
-                           u64 num_bytes, u64 orig_parent, u64 parent,
-                           u64 ref_root, u64 ref_generation,
-                           u64 owner_objectid)
+static inline int extent_ref_type(u64 parent, u64 owner)
 {
-       int ret;
-       if (ref_root == BTRFS_TREE_LOG_OBJECTID &&
-           owner_objectid < BTRFS_FIRST_FREE_OBJECTID)
-               return 0;
-
-       ret = __btrfs_update_extent_ref(trans, root, bytenr, num_bytes,
-                                       orig_parent, parent, ref_root,
-                                       ref_root, ref_generation,
-                                       ref_generation, owner_objectid);
-       return ret;
+       int type;
+       if (owner < BTRFS_FIRST_FREE_OBJECTID) {
+               if (parent > 0)
+                       type = BTRFS_SHARED_BLOCK_REF_KEY;
+               else
+                       type = BTRFS_TREE_BLOCK_REF_KEY;
+       } else {
+               if (parent > 0)
+                       type = BTRFS_SHARED_DATA_REF_KEY;
+               else
+                       type = BTRFS_EXTENT_DATA_REF_KEY;
+       }
+       return type;
 }
-static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
-                                 struct btrfs_root *root, u64 bytenr,
-                                 u64 num_bytes,
-                                 u64 orig_parent, u64 parent,
-                                 u64 orig_root, u64 ref_root,
-                                 u64 orig_generation, u64 ref_generation,
-                                 u64 owner_objectid)
-{
-       int ret;
 
-       ret = btrfs_add_delayed_ref(trans, bytenr, num_bytes, parent, ref_root,
-                                   ref_generation, owner_objectid,
-                                   BTRFS_ADD_DELAYED_REF, 0);
-       BUG_ON(ret);
-       return ret;
-}
+static int find_next_key(struct btrfs_path *path, int level,
+                        struct btrfs_key *key)
 
-static noinline_for_stack int add_extent_ref(struct btrfs_trans_handle *trans,
-                         struct btrfs_root *root, u64 bytenr,
-                         u64 num_bytes, u64 parent, u64 ref_root,
-                         u64 ref_generation, u64 owner_objectid,
-                         int refs_to_add)
 {
-       struct btrfs_path *path;
-       int ret;
-       struct btrfs_key key;
-       struct extent_buffer *l;
-       struct btrfs_extent_item *item;
-       u32 refs;
+       for (; level < BTRFS_MAX_LEVEL; level++) {
+               if (!path->nodes[level])
+                       break;
+               if (path->slots[level] + 1 >=
+                   btrfs_header_nritems(path->nodes[level]))
+                       continue;
+               if (level == 0)
+                       btrfs_item_key_to_cpu(path->nodes[level], key,
+                                             path->slots[level] + 1);
+               else
+                       btrfs_node_key_to_cpu(path->nodes[level], key,
+                                             path->slots[level] + 1);
+               return 0;
+       }
+       return 1;
+}
 
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
+/*
+ * look for inline back ref. if back ref is found, *ref_ret is set
+ * to the address of inline back ref, and 0 is returned.
+ *
+ * if back ref isn't found, *ref_ret is set to the address where it
+ * should be inserted, and -ENOENT is returned.
+ *
+ * if insert is true and there are too many inline back refs, the path
+ * points to the extent item, and -EAGAIN is returned.
+ *
+ * NOTE: inline back refs are ordered in the same way that back ref
+ *      items in the tree are ordered.
+ */
+static noinline_for_stack
+int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                struct btrfs_extent_inline_ref **ref_ret,
+                                u64 bytenr, u64 num_bytes,
+                                u64 parent, u64 root_objectid,
+                                u64 owner, u64 offset, int insert)
+{
+       struct btrfs_key key;
+       struct extent_buffer *leaf;
+       struct btrfs_extent_item *ei;
+       struct btrfs_extent_inline_ref *iref;
+       u64 flags;
+       u64 item_size;
+       unsigned long ptr;
+       unsigned long end;
+       int extra_size;
+       int type;
+       int want;
+       int ret;
+       int err = 0;
 
-       path->reada = 1;
-       path->leave_spinning = 1;
        key.objectid = bytenr;
        key.type = BTRFS_EXTENT_ITEM_KEY;
        key.offset = num_bytes;
 
-       /* first find the extent item and update its reference count */
-       ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key,
-                               path, 0, 1);
+       want = extent_ref_type(parent, owner);
+       if (insert) {
+               extra_size = btrfs_extent_inline_ref_size(want);
+               path->keep_locks = 1;
+       } else
+               extra_size = -1;
+       ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1);
        if (ret < 0) {
-               btrfs_set_path_blocking(path);
-               return ret;
+               err = ret;
+               goto out;
        }
+       BUG_ON(ret);
 
-       if (ret > 0) {
-               WARN_ON(1);
-               btrfs_free_path(path);
-               return -EIO;
+       leaf = path->nodes[0];
+       item_size = btrfs_item_size_nr(leaf, path->slots[0]);
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+       if (item_size < sizeof(*ei)) {
+               if (!insert) {
+                       err = -ENOENT;
+                       goto out;
+               }
+               ret = convert_extent_item_v0(trans, root, path, owner,
+                                            extra_size);
+               if (ret < 0) {
+                       err = ret;
+                       goto out;
+               }
+               leaf = path->nodes[0];
+               item_size = btrfs_item_size_nr(leaf, path->slots[0]);
        }
-       l = path->nodes[0];
+#endif
+       BUG_ON(item_size < sizeof(*ei));
 
-       btrfs_item_key_to_cpu(l, &key, path->slots[0]);
-       if (key.objectid != bytenr) {
-               btrfs_print_leaf(root->fs_info->extent_root, path->nodes[0]);
-               printk(KERN_ERR "btrfs wanted %llu found %llu\n",
-                      (unsigned long long)bytenr,
-                      (unsigned long long)key.objectid);
-               BUG();
+       ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
+       flags = btrfs_extent_flags(leaf, ei);
+
+       ptr = (unsigned long)(ei + 1);
+       end = (unsigned long)ei + item_size;
+
+       if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+               ptr += sizeof(struct btrfs_tree_block_info);
+               BUG_ON(ptr > end);
+       } else {
+               BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA));
        }
-       BUG_ON(key.type != BTRFS_EXTENT_ITEM_KEY);
 
-       item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
+       err = -ENOENT;
+       while (1) {
+               if (ptr >= end) {
+                       WARN_ON(ptr > end);
+                       break;
+               }
+               iref = (struct btrfs_extent_inline_ref *)ptr;
+               type = btrfs_extent_inline_ref_type(leaf, iref);
+               if (want < type)
+                       break;
+               if (want > type) {
+                       ptr += btrfs_extent_inline_ref_size(type);
+                       continue;
+               }
 
-       refs = btrfs_extent_refs(l, item);
-       btrfs_set_extent_refs(l, item, refs + refs_to_add);
-       btrfs_unlock_up_safe(path, 1);
+               if (type == BTRFS_EXTENT_DATA_REF_KEY) {
+                       struct btrfs_extent_data_ref *dref;
+                       dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+                       if (match_extent_data_ref(leaf, dref, root_objectid,
+                                                 owner, offset)) {
+                               err = 0;
+                               break;
+                       }
+                       if (hash_extent_data_ref_item(leaf, dref) <
+                           hash_extent_data_ref(root_objectid, owner, offset))
+                               break;
+               } else {
+                       u64 ref_offset;
+                       ref_offset = btrfs_extent_inline_ref_offset(leaf, iref);
+                       if (parent > 0) {
+                               if (parent == ref_offset) {
+                                       err = 0;
+                                       break;
+                               }
+                               if (ref_offset < parent)
+                                       break;
+                       } else {
+                               if (root_objectid == ref_offset) {
+                                       err = 0;
+                                       break;
+                               }
+                               if (ref_offset < root_objectid)
+                                       break;
+                       }
+               }
+               ptr += btrfs_extent_inline_ref_size(type);
+       }
+       if (err == -ENOENT && insert) {
+               if (item_size + extra_size >=
+                   BTRFS_MAX_EXTENT_ITEM_SIZE(root)) {
+                       err = -EAGAIN;
+                       goto out;
+               }
+               /*
+                * To add new inline back ref, we have to make sure
+                * there is no corresponding back ref item.
+                * For simplicity, we just do not add new inline back
+                * ref if there is any kind of item for this block
+                */
+               if (find_next_key(path, 0, &key) == 0 &&
+                   key.objectid == bytenr &&
+                   key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) {
+                       err = -EAGAIN;
+                       goto out;
+               }
+       }
+       *ref_ret = (struct btrfs_extent_inline_ref *)ptr;
+out:
+       if (insert) {
+               path->keep_locks = 0;
+               btrfs_unlock_up_safe(path, 1);
+       }
+       return err;
+}
 
-       btrfs_mark_buffer_dirty(path->nodes[0]);
+/*
+ * helper to add new inline back ref
+ */
+static noinline_for_stack
+int setup_inline_extent_backref(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               struct btrfs_path *path,
+                               struct btrfs_extent_inline_ref *iref,
+                               u64 parent, u64 root_objectid,
+                               u64 owner, u64 offset, int refs_to_add,
+                               struct btrfs_delayed_extent_op *extent_op)
+{
+       struct extent_buffer *leaf;
+       struct btrfs_extent_item *ei;
+       unsigned long ptr;
+       unsigned long end;
+       unsigned long item_offset;
+       u64 refs;
+       int size;
+       int type;
+       int ret;
 
-       btrfs_release_path(root->fs_info->extent_root, path);
+       leaf = path->nodes[0];
+       ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
+       item_offset = (unsigned long)iref - (unsigned long)ei;
 
-       path->reada = 1;
-       path->leave_spinning = 1;
+       type = extent_ref_type(parent, owner);
+       size = btrfs_extent_inline_ref_size(type);
 
-       /* now insert the actual backref */
-       ret = insert_extent_backref(trans, root->fs_info->extent_root,
-                                   path, bytenr, parent,
-                                   ref_root, ref_generation,
-                                   owner_objectid, refs_to_add);
+       ret = btrfs_extend_item(trans, root, path, size);
        BUG_ON(ret);
-       btrfs_free_path(path);
+
+       ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
+       refs = btrfs_extent_refs(leaf, ei);
+       refs += refs_to_add;
+       btrfs_set_extent_refs(leaf, ei, refs);
+       if (extent_op)
+               __run_delayed_extent_op(extent_op, leaf, ei);
+
+       ptr = (unsigned long)ei + item_offset;
+       end = (unsigned long)ei + btrfs_item_size_nr(leaf, path->slots[0]);
+       if (ptr < end - size)
+               memmove_extent_buffer(leaf, ptr + size, ptr,
+                                     end - size - ptr);
+
+       iref = (struct btrfs_extent_inline_ref *)ptr;
+       btrfs_set_extent_inline_ref_type(leaf, iref, type);
+       if (type == BTRFS_EXTENT_DATA_REF_KEY) {
+               struct btrfs_extent_data_ref *dref;
+               dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+               btrfs_set_extent_data_ref_root(leaf, dref, root_objectid);
+               btrfs_set_extent_data_ref_objectid(leaf, dref, owner);
+               btrfs_set_extent_data_ref_offset(leaf, dref, offset);
+               btrfs_set_extent_data_ref_count(leaf, dref, refs_to_add);
+       } else if (type == BTRFS_SHARED_DATA_REF_KEY) {
+               struct btrfs_shared_data_ref *sref;
+               sref = (struct btrfs_shared_data_ref *)(iref + 1);
+               btrfs_set_shared_data_ref_count(leaf, sref, refs_to_add);
+               btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
+       } else if (type == BTRFS_SHARED_BLOCK_REF_KEY) {
+               btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
+       } else {
+               btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
+       }
+       btrfs_mark_buffer_dirty(leaf);
        return 0;
 }
 
-int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
-                        struct btrfs_root *root,
-                        u64 bytenr, u64 num_bytes, u64 parent,
-                        u64 ref_root, u64 ref_generation,
-                        u64 owner_objectid)
+static int lookup_extent_backref(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                struct btrfs_extent_inline_ref **ref_ret,
+                                u64 bytenr, u64 num_bytes, u64 parent,
+                                u64 root_objectid, u64 owner, u64 offset)
 {
        int ret;
-       if (ref_root == BTRFS_TREE_LOG_OBJECTID &&
-           owner_objectid < BTRFS_FIRST_FREE_OBJECTID)
-               return 0;
-
-       ret = __btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, 0, parent,
-                                    0, ref_root, 0, ref_generation,
-                                    owner_objectid);
-       return ret;
-}
 
-static int drop_delayed_ref(struct btrfs_trans_handle *trans,
-                                       struct btrfs_root *root,
-                                       struct btrfs_delayed_ref_node *node)
-{
-       int ret = 0;
-       struct btrfs_delayed_ref *ref = btrfs_delayed_node_to_ref(node);
+       ret = lookup_inline_extent_backref(trans, root, path, ref_ret,
+                                          bytenr, num_bytes, parent,
+                                          root_objectid, owner, offset, 0);
+       if (ret != -ENOENT)
+               return ret;
 
-       BUG_ON(node->ref_mod == 0);
-       ret = __btrfs_free_extent(trans, root, node->bytenr, node->num_bytes,
-                                 node->parent, ref->root, ref->generation,
-                                 ref->owner_objectid, ref->pin, node->ref_mod);
+       btrfs_release_path(root, path);
+       *ref_ret = NULL;
 
+       if (owner < BTRFS_FIRST_FREE_OBJECTID) {
+               ret = lookup_tree_block_ref(trans, root, path, bytenr, parent,
+                                           root_objectid);
+       } else {
+               ret = lookup_extent_data_ref(trans, root, path, bytenr, parent,
+                                            root_objectid, owner, offset);
+       }
        return ret;
 }
 
-/* helper function to actually process a single delayed ref entry */
-static noinline int run_one_delayed_ref(struct btrfs_trans_handle *trans,
-                                       struct btrfs_root *root,
-                                       struct btrfs_delayed_ref_node *node,
-                                       int insert_reserved)
+/*
+ * helper to update/remove inline back ref
+ */
+static noinline_for_stack
+int update_inline_extent_backref(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                struct btrfs_extent_inline_ref *iref,
+                                int refs_to_mod,
+                                struct btrfs_delayed_extent_op *extent_op)
 {
+       struct extent_buffer *leaf;
+       struct btrfs_extent_item *ei;
+       struct btrfs_extent_data_ref *dref = NULL;
+       struct btrfs_shared_data_ref *sref = NULL;
+       unsigned long ptr;
+       unsigned long end;
+       u32 item_size;
+       int size;
+       int type;
        int ret;
-       struct btrfs_delayed_ref *ref;
+       u64 refs;
 
-       if (node->parent == (u64)-1) {
-               struct btrfs_delayed_ref_head *head;
-               /*
-                * we've hit the end of the chain and we were supposed
-                * to insert this extent into the tree.  But, it got
-                * deleted before we ever needed to insert it, so all
-                * we have to do is clean up the accounting
-                */
-               if (insert_reserved) {
-                       update_reserved_extents(root, node->bytenr,
-                                               node->num_bytes, 0);
-               }
-               head = btrfs_delayed_node_to_head(node);
-               mutex_unlock(&head->mutex);
-               return 0;
+       leaf = path->nodes[0];
+       ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
+       refs = btrfs_extent_refs(leaf, ei);
+       WARN_ON(refs_to_mod < 0 && refs + refs_to_mod <= 0);
+       refs += refs_to_mod;
+       btrfs_set_extent_refs(leaf, ei, refs);
+       if (extent_op)
+               __run_delayed_extent_op(extent_op, leaf, ei);
+
+       type = btrfs_extent_inline_ref_type(leaf, iref);
+
+       if (type == BTRFS_EXTENT_DATA_REF_KEY) {
+               dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+               refs = btrfs_extent_data_ref_count(leaf, dref);
+       } else if (type == BTRFS_SHARED_DATA_REF_KEY) {
+               sref = (struct btrfs_shared_data_ref *)(iref + 1);
+               refs = btrfs_shared_data_ref_count(leaf, sref);
+       } else {
+               refs = 1;
+               BUG_ON(refs_to_mod != -1);
        }
 
-       ref = btrfs_delayed_node_to_ref(node);
-       if (ref->action == BTRFS_ADD_DELAYED_REF) {
-               if (insert_reserved) {
-                       struct btrfs_key ins;
-
-                       ins.objectid = node->bytenr;
-                       ins.offset = node->num_bytes;
-                       ins.type = BTRFS_EXTENT_ITEM_KEY;
-
-                       /* record the full extent allocation */
-                       ret = __btrfs_alloc_reserved_extent(trans, root,
-                                       node->parent, ref->root,
-                                       ref->generation, ref->owner_objectid,
-                                       &ins, node->ref_mod);
-                       update_reserved_extents(root, node->bytenr,
-                                               node->num_bytes, 0);
-               } else {
-                       /* just add one backref */
-                       ret = add_extent_ref(trans, root, node->bytenr,
-                                    node->num_bytes,
-                                    node->parent, ref->root, ref->generation,
-                                    ref->owner_objectid, node->ref_mod);
-               }
+       BUG_ON(refs_to_mod < 0 && refs < -refs_to_mod);
+       refs += refs_to_mod;
+
+       if (refs > 0) {
+               if (type == BTRFS_EXTENT_DATA_REF_KEY)
+                       btrfs_set_extent_data_ref_count(leaf, dref, refs);
+               else
+                       btrfs_set_shared_data_ref_count(leaf, sref, refs);
+       } else {
+               size =  btrfs_extent_inline_ref_size(type);
+               item_size = btrfs_item_size_nr(leaf, path->slots[0]);
+               ptr = (unsigned long)iref;
+               end = (unsigned long)ei + item_size;
+               if (ptr + size < end)
+                       memmove_extent_buffer(leaf, ptr, ptr + size,
+                                             end - ptr - size);
+               item_size -= size;
+               ret = btrfs_truncate_item(trans, root, path, item_size, 1);
                BUG_ON(ret);
-       } else if (ref->action == BTRFS_DROP_DELAYED_REF) {
-               WARN_ON(insert_reserved);
-               ret = drop_delayed_ref(trans, root, node);
        }
+       btrfs_mark_buffer_dirty(leaf);
        return 0;
 }
 
-static noinline struct btrfs_delayed_ref_node *
-select_delayed_ref(struct btrfs_delayed_ref_head *head)
+static noinline_for_stack
+int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                u64 bytenr, u64 num_bytes, u64 parent,
+                                u64 root_objectid, u64 owner,
+                                u64 offset, int refs_to_add,
+                                struct btrfs_delayed_extent_op *extent_op)
 {
-       struct rb_node *node;
-       struct btrfs_delayed_ref_node *ref;
-       int action = BTRFS_ADD_DELAYED_REF;
-again:
-       /*
-        * select delayed ref of type BTRFS_ADD_DELAYED_REF first.
-        * this prevents ref count from going down to zero when
-        * there still are pending delayed ref.
-        */
-       node = rb_prev(&head->node.rb_node);
-       while (1) {
-               if (!node)
-                       break;
-               ref = rb_entry(node, struct btrfs_delayed_ref_node,
-                               rb_node);
-               if (ref->bytenr != head->node.bytenr)
-                       break;
-               if (btrfs_delayed_node_to_ref(ref)->action == action)
-                       return ref;
-               node = rb_prev(node);
+       struct btrfs_extent_inline_ref *iref;
+       int ret;
+
+       ret = lookup_inline_extent_backref(trans, root, path, &iref,
+                                          bytenr, num_bytes, parent,
+                                          root_objectid, owner, offset, 1);
+       if (ret == 0) {
+               BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID);
+               ret = update_inline_extent_backref(trans, root, path, iref,
+                                                  refs_to_add, extent_op);
+       } else if (ret == -ENOENT) {
+               ret = setup_inline_extent_backref(trans, root, path, iref,
+                                                 parent, root_objectid,
+                                                 owner, offset, refs_to_add,
+                                                 extent_op);
        }
-       if (action == BTRFS_ADD_DELAYED_REF) {
-               action = BTRFS_DROP_DELAYED_REF;
-               goto again;
+       return ret;
+}
+
+static int insert_extent_backref(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                u64 bytenr, u64 parent, u64 root_objectid,
+                                u64 owner, u64 offset, int refs_to_add)
+{
+       int ret;
+       if (owner < BTRFS_FIRST_FREE_OBJECTID) {
+               BUG_ON(refs_to_add != 1);
+               ret = insert_tree_block_ref(trans, root, path, bytenr,
+                                           parent, root_objectid);
+       } else {
+               ret = insert_extent_data_ref(trans, root, path, bytenr,
+                                            parent, root_objectid,
+                                            owner, offset, refs_to_add);
        }
-       return NULL;
+       return ret;
 }
 
-static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
-                                      struct btrfs_root *root,
-                                      struct list_head *cluster)
+static int remove_extent_backref(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                struct btrfs_extent_inline_ref *iref,
+                                int refs_to_drop, int is_data)
 {
-       struct btrfs_delayed_ref_root *delayed_refs;
-       struct btrfs_delayed_ref_node *ref;
-       struct btrfs_delayed_ref_head *locked_ref = NULL;
        int ret;
-       int count = 0;
-       int must_insert_reserved = 0;
 
-       delayed_refs = &trans->transaction->delayed_refs;
-       while (1) {
-               if (!locked_ref) {
-                       /* pick a new head ref from the cluster list */
-                       if (list_empty(cluster))
-                               break;
+       BUG_ON(!is_data && refs_to_drop != 1);
+       if (iref) {
+               ret = update_inline_extent_backref(trans, root, path, iref,
+                                                  -refs_to_drop, NULL);
+       } else if (is_data) {
+               ret = remove_extent_data_ref(trans, root, path, refs_to_drop);
+       } else {
+               ret = btrfs_del_item(trans, root, path);
+       }
+       return ret;
+}
 
-                       locked_ref = list_entry(cluster->next,
-                                    struct btrfs_delayed_ref_head, cluster);
+static void btrfs_issue_discard(struct block_device *bdev,
+                               u64 start, u64 len)
+{
+       blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL,
+                       BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER);
+}
 
-                       /* grab the lock that says we are going to process
-                        * all the refs for this head */
-                       ret = btrfs_delayed_ref_lock(trans, locked_ref);
+static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
+                               u64 num_bytes)
+{
+       int ret;
+       u64 map_length = num_bytes;
+       struct btrfs_multi_bio *multi = NULL;
 
-                       /*
-                        * we may have dropped the spin lock to get the head
-                        * mutex lock, and that might have given someone else
-                        * time to free the head.  If that's true, it has been
-                        * removed from our list and we can move on.
-                        */
-                       if (ret == -EAGAIN) {
-                               locked_ref = NULL;
-                               count++;
-                               continue;
-                       }
-               }
+       if (!btrfs_test_opt(root, DISCARD))
+               return 0;
 
-               /*
-                * record the must insert reserved flag before we
-                * drop the spin lock.
-                */
-               must_insert_reserved = locked_ref->must_insert_reserved;
-               locked_ref->must_insert_reserved = 0;
+       /* Tell the block device(s) that the sectors can be discarded */
+       ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
+                             bytenr, &map_length, &multi, 0);
+       if (!ret) {
+               struct btrfs_bio_stripe *stripe = multi->stripes;
+               int i;
 
-               /*
-                * locked_ref is the head node, so we have to go one
-                * node back for any delayed ref updates
-                */
-               ref = select_delayed_ref(locked_ref);
-               if (!ref) {
-                       /* All delayed refs have been processed, Go ahead
-                        * and send the head node to run_one_delayed_ref,
-                        * so that any accounting fixes can happen
-                        */
-                       ref = &locked_ref->node;
-                       list_del_init(&locked_ref->cluster);
-                       locked_ref = NULL;
-               }
+               if (map_length > num_bytes)
+                       map_length = num_bytes;
 
-               ref->in_tree = 0;
-               rb_erase(&ref->rb_node, &delayed_refs->root);
-               delayed_refs->num_entries--;
-               spin_unlock(&delayed_refs->lock);
+               for (i = 0; i < multi->num_stripes; i++, stripe++) {
+                       btrfs_issue_discard(stripe->dev->bdev,
+                                           stripe->physical,
+                                           map_length);
+               }
+               kfree(multi);
+       }
 
-               ret = run_one_delayed_ref(trans, root, ref,
-                                         must_insert_reserved);
-               BUG_ON(ret);
-               btrfs_put_delayed_ref(ref);
+       return ret;
+}
 
-               count++;
-               cond_resched();
-               spin_lock(&delayed_refs->lock);
+int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root,
+                        u64 bytenr, u64 num_bytes, u64 parent,
+                        u64 root_objectid, u64 owner, u64 offset)
+{
+       int ret;
+       BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID &&
+              root_objectid == BTRFS_TREE_LOG_OBJECTID);
+
+       if (owner < BTRFS_FIRST_FREE_OBJECTID) {
+               ret = btrfs_add_delayed_tree_ref(trans, bytenr, num_bytes,
+                                       parent, root_objectid, (int)owner,
+                                       BTRFS_ADD_DELAYED_REF, NULL);
+       } else {
+               ret = btrfs_add_delayed_data_ref(trans, bytenr, num_bytes,
+                                       parent, root_objectid, owner, offset,
+                                       BTRFS_ADD_DELAYED_REF, NULL);
        }
-       return count;
+       return ret;
 }
 
-/*
- * this starts processing the delayed reference count updates and
- * extent insertions we have queued up so far.  count can be
- * 0, which means to process everything in the tree at the start
- * of the run (but not newly added entries), or it can be some target
- * number you'd like to process.
- */
-int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root, unsigned long count)
+static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
+                                 struct btrfs_root *root,
+                                 u64 bytenr, u64 num_bytes,
+                                 u64 parent, u64 root_objectid,
+                                 u64 owner, u64 offset, int refs_to_add,
+                                 struct btrfs_delayed_extent_op *extent_op)
 {
-       struct rb_node *node;
-       struct btrfs_delayed_ref_root *delayed_refs;
-       struct btrfs_delayed_ref_node *ref;
-       struct list_head cluster;
+       struct btrfs_path *path;
+       struct extent_buffer *leaf;
+       struct btrfs_extent_item *item;
+       u64 refs;
        int ret;
-       int run_all = count == (unsigned long)-1;
-       int run_most = 0;
+       int err = 0;
 
-       if (root == root->fs_info->extent_root)
-               root = root->fs_info->tree_root;
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
 
-       delayed_refs = &trans->transaction->delayed_refs;
-       INIT_LIST_HEAD(&cluster);
-again:
-       spin_lock(&delayed_refs->lock);
-       if (count == 0) {
-               count = delayed_refs->num_entries * 2;
-               run_most = 1;
-       }
-       while (1) {
-               if (!(run_all || run_most) &&
-                   delayed_refs->num_heads_ready < 64)
-                       break;
+       path->reada = 1;
+       path->leave_spinning = 1;
+       /* this will setup the path even if it fails to insert the back ref */
+       ret = insert_inline_extent_backref(trans, root->fs_info->extent_root,
+                                          path, bytenr, num_bytes, parent,
+                                          root_objectid, owner, offset,
+                                          refs_to_add, extent_op);
+       if (ret == 0)
+               goto out;
 
-               /*
-                * go find something we can process in the rbtree.  We start at
-                * the beginning of the tree, and then build a cluster
-                * of refs to process starting at the first one we are able to
-                * lock
-                */
-               ret = btrfs_find_ref_cluster(trans, &cluster,
-                                            delayed_refs->run_delayed_start);
-               if (ret)
-                       break;
+       if (ret != -EAGAIN) {
+               err = ret;
+               goto out;
+       }
 
-               ret = run_clustered_refs(trans, root, &cluster);
-               BUG_ON(ret < 0);
+       leaf = path->nodes[0];
+       item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
+       refs = btrfs_extent_refs(leaf, item);
+       btrfs_set_extent_refs(leaf, item, refs + refs_to_add);
+       if (extent_op)
+               __run_delayed_extent_op(extent_op, leaf, item);
 
-               count -= min_t(unsigned long, ret, count);
+       btrfs_mark_buffer_dirty(leaf);
+       btrfs_release_path(root->fs_info->extent_root, path);
 
-               if (count == 0)
-                       break;
-       }
+       path->reada = 1;
+       path->leave_spinning = 1;
 
-       if (run_all) {
-               node = rb_first(&delayed_refs->root);
-               if (!node)
-                       goto out;
-               count = (unsigned long)-1;
+       /* now insert the actual backref */
+       ret = insert_extent_backref(trans, root->fs_info->extent_root,
+                                   path, bytenr, parent, root_objectid,
+                                   owner, offset, refs_to_add);
+       BUG_ON(ret);
+out:
+       btrfs_free_path(path);
+       return err;
+}
 
-               while (node) {
-                       ref = rb_entry(node, struct btrfs_delayed_ref_node,
-                                      rb_node);
-                       if (btrfs_delayed_ref_is_head(ref)) {
-                               struct btrfs_delayed_ref_head *head;
+static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               struct btrfs_delayed_ref_node *node,
+                               struct btrfs_delayed_extent_op *extent_op,
+                               int insert_reserved)
+{
+       int ret = 0;
+       struct btrfs_delayed_data_ref *ref;
+       struct btrfs_key ins;
+       u64 parent = 0;
+       u64 ref_root = 0;
+       u64 flags = 0;
 
-                               head = btrfs_delayed_node_to_head(ref);
-                               atomic_inc(&ref->refs);
+       ins.objectid = node->bytenr;
+       ins.offset = node->num_bytes;
+       ins.type = BTRFS_EXTENT_ITEM_KEY;
 
-                               spin_unlock(&delayed_refs->lock);
-                               mutex_lock(&head->mutex);
-                               mutex_unlock(&head->mutex);
+       ref = btrfs_delayed_node_to_data_ref(node);
+       if (node->type == BTRFS_SHARED_DATA_REF_KEY)
+               parent = ref->parent;
+       else
+               ref_root = ref->root;
 
-                               btrfs_put_delayed_ref(ref);
-                               cond_resched();
-                               goto again;
-                       }
-                       node = rb_next(node);
+       if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
+               if (extent_op) {
+                       BUG_ON(extent_op->update_key);
+                       flags |= extent_op->flags_to_set;
                }
-               spin_unlock(&delayed_refs->lock);
-               schedule_timeout(1);
-               goto again;
+               ret = alloc_reserved_file_extent(trans, root,
+                                                parent, ref_root, flags,
+                                                ref->objectid, ref->offset,
+                                                &ins, node->ref_mod);
+       } else if (node->action == BTRFS_ADD_DELAYED_REF) {
+               ret = __btrfs_inc_extent_ref(trans, root, node->bytenr,
+                                            node->num_bytes, parent,
+                                            ref_root, ref->objectid,
+                                            ref->offset, node->ref_mod,
+                                            extent_op);
+       } else if (node->action == BTRFS_DROP_DELAYED_REF) {
+               ret = __btrfs_free_extent(trans, root, node->bytenr,
+                                         node->num_bytes, parent,
+                                         ref_root, ref->objectid,
+                                         ref->offset, node->ref_mod,
+                                         extent_op);
+       } else {
+               BUG();
        }
-out:
-       spin_unlock(&delayed_refs->lock);
-       return 0;
+       return ret;
 }
 
-int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
-                         struct btrfs_root *root, u64 objectid, u64 bytenr)
+static void __run_delayed_extent_op(struct btrfs_delayed_extent_op *extent_op,
+                                   struct extent_buffer *leaf,
+                                   struct btrfs_extent_item *ei)
 {
-       struct btrfs_root *extent_root = root->fs_info->extent_root;
+       u64 flags = btrfs_extent_flags(leaf, ei);
+       if (extent_op->update_flags) {
+               flags |= extent_op->flags_to_set;
+               btrfs_set_extent_flags(leaf, ei, flags);
+       }
+
+       if (extent_op->update_key) {
+               struct btrfs_tree_block_info *bi;
+               BUG_ON(!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK));
+               bi = (struct btrfs_tree_block_info *)(ei + 1);
+               btrfs_set_tree_block_key(leaf, bi, &extent_op->key);
+       }
+}
+
+static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_delayed_ref_node *node,
+                                struct btrfs_delayed_extent_op *extent_op)
+{
+       struct btrfs_key key;
        struct btrfs_path *path;
+       struct btrfs_extent_item *ei;
        struct extent_buffer *leaf;
-       struct btrfs_extent_ref *ref_item;
-       struct btrfs_key key;
-       struct btrfs_key found_key;
-       u64 ref_root;
-       u64 last_snapshot;
-       u32 nritems;
+       u32 item_size;
        int ret;
+       int err = 0;
 
-       key.objectid = bytenr;
-       key.offset = (u64)-1;
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+
+       key.objectid = node->bytenr;
        key.type = BTRFS_EXTENT_ITEM_KEY;
+       key.offset = node->num_bytes;
 
-       path = btrfs_alloc_path();
-       ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
-       if (ret < 0)
+       path->reada = 1;
+       path->leave_spinning = 1;
+       ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key,
+                               path, 0, 1);
+       if (ret < 0) {
+               err = ret;
                goto out;
-       BUG_ON(ret == 0);
-
-       ret = -ENOENT;
-       if (path->slots[0] == 0)
+       }
+       if (ret > 0) {
+               err = -EIO;
                goto out;
+       }
 
-       path->slots[0]--;
        leaf = path->nodes[0];
-       btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+       item_size = btrfs_item_size_nr(leaf, path->slots[0]);
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+       if (item_size < sizeof(*ei)) {
+               ret = convert_extent_item_v0(trans, root->fs_info->extent_root,
+                                            path, (u64)-1, 0);
+               if (ret < 0) {
+                       err = ret;
+                       goto out;
+               }
+               leaf = path->nodes[0];
+               item_size = btrfs_item_size_nr(leaf, path->slots[0]);
+       }
+#endif
+       BUG_ON(item_size < sizeof(*ei));
+       ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
+       __run_delayed_extent_op(extent_op, leaf, ei);
 
-       if (found_key.objectid != bytenr ||
-           found_key.type != BTRFS_EXTENT_ITEM_KEY)
-               goto out;
+       btrfs_mark_buffer_dirty(leaf);
+out:
+       btrfs_free_path(path);
+       return err;
+}
+
+static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               struct btrfs_delayed_ref_node *node,
+                               struct btrfs_delayed_extent_op *extent_op,
+                               int insert_reserved)
+{
+       int ret = 0;
+       struct btrfs_delayed_tree_ref *ref;
+       struct btrfs_key ins;
+       u64 parent = 0;
+       u64 ref_root = 0;
+
+       ins.objectid = node->bytenr;
+       ins.offset = node->num_bytes;
+       ins.type = BTRFS_EXTENT_ITEM_KEY;
+
+       ref = btrfs_delayed_node_to_tree_ref(node);
+       if (node->type == BTRFS_SHARED_BLOCK_REF_KEY)
+               parent = ref->parent;
+       else
+               ref_root = ref->root;
+
+       BUG_ON(node->ref_mod != 1);
+       if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
+               BUG_ON(!extent_op || !extent_op->update_flags ||
+                      !extent_op->update_key);
+               ret = alloc_reserved_tree_block(trans, root,
+                                               parent, ref_root,
+                                               extent_op->flags_to_set,
+                                               &extent_op->key,
+                                               ref->level, &ins);
+       } else if (node->action == BTRFS_ADD_DELAYED_REF) {
+               ret = __btrfs_inc_extent_ref(trans, root, node->bytenr,
+                                            node->num_bytes, parent, ref_root,
+                                            ref->level, 0, 1, extent_op);
+       } else if (node->action == BTRFS_DROP_DELAYED_REF) {
+               ret = __btrfs_free_extent(trans, root, node->bytenr,
+                                         node->num_bytes, parent, ref_root,
+                                         ref->level, 0, 1, extent_op);
+       } else {
+               BUG();
+       }
+       return ret;
+}
+
+/* helper function to actually process a single delayed ref entry */
+static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *root,
+                              struct btrfs_delayed_ref_node *node,
+                              struct btrfs_delayed_extent_op *extent_op,
+                              int insert_reserved)
+{
+       int ret;
+       if (btrfs_delayed_ref_is_head(node)) {
+               struct btrfs_delayed_ref_head *head;
+               /*
+                * we've hit the end of the chain and we were supposed
+                * to insert this extent into the tree.  But, it got
+                * deleted before we ever needed to insert it, so all
+                * we have to do is clean up the accounting
+                */
+               BUG_ON(extent_op);
+               head = btrfs_delayed_node_to_head(node);
+               if (insert_reserved) {
+                       btrfs_pin_extent(root, node->bytenr,
+                                        node->num_bytes, 1);
+                       if (head->is_data) {
+                               ret = btrfs_del_csums(trans, root,
+                                                     node->bytenr,
+                                                     node->num_bytes);
+                               BUG_ON(ret);
+                       }
+               }
+               mutex_unlock(&head->mutex);
+               return 0;
+       }
+
+       if (node->type == BTRFS_TREE_BLOCK_REF_KEY ||
+           node->type == BTRFS_SHARED_BLOCK_REF_KEY)
+               ret = run_delayed_tree_ref(trans, root, node, extent_op,
+                                          insert_reserved);
+       else if (node->type == BTRFS_EXTENT_DATA_REF_KEY ||
+                node->type == BTRFS_SHARED_DATA_REF_KEY)
+               ret = run_delayed_data_ref(trans, root, node, extent_op,
+                                          insert_reserved);
+       else
+               BUG();
+       return ret;
+}
 
-       last_snapshot = btrfs_root_last_snapshot(&root->root_item);
+static noinline struct btrfs_delayed_ref_node *
+select_delayed_ref(struct btrfs_delayed_ref_head *head)
+{
+       struct rb_node *node;
+       struct btrfs_delayed_ref_node *ref;
+       int action = BTRFS_ADD_DELAYED_REF;
+again:
+       /*
+        * select delayed ref of type BTRFS_ADD_DELAYED_REF first.
+        * this prevents ref count from going down to zero when
+        * there still are pending delayed ref.
+        */
+       node = rb_prev(&head->node.rb_node);
        while (1) {
-               leaf = path->nodes[0];
-               nritems = btrfs_header_nritems(leaf);
-               if (path->slots[0] >= nritems) {
-                       ret = btrfs_next_leaf(extent_root, path);
-                       if (ret < 0)
-                               goto out;
-                       if (ret == 0)
-                               continue;
+               if (!node)
                        break;
-               }
-               btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-               if (found_key.objectid != bytenr)
+               ref = rb_entry(node, struct btrfs_delayed_ref_node,
+                               rb_node);
+               if (ref->bytenr != head->node.bytenr)
                        break;
+               if (ref->action == action)
+                       return ref;
+               node = rb_prev(node);
+       }
+       if (action == BTRFS_ADD_DELAYED_REF) {
+               action = BTRFS_DROP_DELAYED_REF;
+               goto again;
+       }
+       return NULL;
+}
+
+static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
+                                      struct btrfs_root *root,
+                                      struct list_head *cluster)
+{
+       struct btrfs_delayed_ref_root *delayed_refs;
+       struct btrfs_delayed_ref_node *ref;
+       struct btrfs_delayed_ref_head *locked_ref = NULL;
+       struct btrfs_delayed_extent_op *extent_op;
+       int ret;
+       int count = 0;
+       int must_insert_reserved = 0;
+
+       delayed_refs = &trans->transaction->delayed_refs;
+       while (1) {
+               if (!locked_ref) {
+                       /* pick a new head ref from the cluster list */
+                       if (list_empty(cluster))
+                               break;
+
+                       locked_ref = list_entry(cluster->next,
+                                    struct btrfs_delayed_ref_head, cluster);
+
+                       /* grab the lock that says we are going to process
+                        * all the refs for this head */
+                       ret = btrfs_delayed_ref_lock(trans, locked_ref);
+
+                       /*
+                        * we may have dropped the spin lock to get the head
+                        * mutex lock, and that might have given someone else
+                        * time to free the head.  If that's true, it has been
+                        * removed from our list and we can move on.
+                        */
+                       if (ret == -EAGAIN) {
+                               locked_ref = NULL;
+                               count++;
+                               continue;
+                       }
+               }
+
+               /*
+                * record the must insert reserved flag before we
+                * drop the spin lock.
+                */
+               must_insert_reserved = locked_ref->must_insert_reserved;
+               locked_ref->must_insert_reserved = 0;
+
+               extent_op = locked_ref->extent_op;
+               locked_ref->extent_op = NULL;
+
+               /*
+                * locked_ref is the head node, so we have to go one
+                * node back for any delayed ref updates
+                */
+               ref = select_delayed_ref(locked_ref);
+               if (!ref) {
+                       /* All delayed refs have been processed, Go ahead
+                        * and send the head node to run_one_delayed_ref,
+                        * so that any accounting fixes can happen
+                        */
+                       ref = &locked_ref->node;
+
+                       if (extent_op && must_insert_reserved) {
+                               kfree(extent_op);
+                               extent_op = NULL;
+                       }
+
+                       if (extent_op) {
+                               spin_unlock(&delayed_refs->lock);
+
+                               ret = run_delayed_extent_op(trans, root,
+                                                           ref, extent_op);
+                               BUG_ON(ret);
+                               kfree(extent_op);
+
+                               cond_resched();
+                               spin_lock(&delayed_refs->lock);
+                               continue;
+                       }
+
+                       list_del_init(&locked_ref->cluster);
+                       locked_ref = NULL;
+               }
+
+               ref->in_tree = 0;
+               rb_erase(&ref->rb_node, &delayed_refs->root);
+               delayed_refs->num_entries--;
+
+               spin_unlock(&delayed_refs->lock);
+
+               ret = run_one_delayed_ref(trans, root, ref, extent_op,
+                                         must_insert_reserved);
+               BUG_ON(ret);
+
+               btrfs_put_delayed_ref(ref);
+               kfree(extent_op);
+               count++;
+
+               cond_resched();
+               spin_lock(&delayed_refs->lock);
+       }
+       return count;
+}
+
+/*
+ * this starts processing the delayed reference count updates and
+ * extent insertions we have queued up so far.  count can be
+ * 0, which means to process everything in the tree at the start
+ * of the run (but not newly added entries), or it can be some target
+ * number you'd like to process.
+ */
+int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *root, unsigned long count)
+{
+       struct rb_node *node;
+       struct btrfs_delayed_ref_root *delayed_refs;
+       struct btrfs_delayed_ref_node *ref;
+       struct list_head cluster;
+       int ret;
+       int run_all = count == (unsigned long)-1;
+       int run_most = 0;
+
+       if (root == root->fs_info->extent_root)
+               root = root->fs_info->tree_root;
+
+       delayed_refs = &trans->transaction->delayed_refs;
+       INIT_LIST_HEAD(&cluster);
+again:
+       spin_lock(&delayed_refs->lock);
+       if (count == 0) {
+               count = delayed_refs->num_entries * 2;
+               run_most = 1;
+       }
+       while (1) {
+               if (!(run_all || run_most) &&
+                   delayed_refs->num_heads_ready < 64)
+                       break;
+
+               /*
+                * go find something we can process in the rbtree.  We start at
+                * the beginning of the tree, and then build a cluster
+                * of refs to process starting at the first one we are able to
+                * lock
+                */
+               ret = btrfs_find_ref_cluster(trans, &cluster,
+                                            delayed_refs->run_delayed_start);
+               if (ret)
+                       break;
+
+               ret = run_clustered_refs(trans, root, &cluster);
+               BUG_ON(ret < 0);
+
+               count -= min_t(unsigned long, ret, count);
+
+               if (count == 0)
+                       break;
+       }
+
+       if (run_all) {
+               node = rb_first(&delayed_refs->root);
+               if (!node)
+                       goto out;
+               count = (unsigned long)-1;
+
+               while (node) {
+                       ref = rb_entry(node, struct btrfs_delayed_ref_node,
+                                      rb_node);
+                       if (btrfs_delayed_ref_is_head(ref)) {
+                               struct btrfs_delayed_ref_head *head;
+
+                               head = btrfs_delayed_node_to_head(ref);
+                               atomic_inc(&ref->refs);
+
+                               spin_unlock(&delayed_refs->lock);
+                               mutex_lock(&head->mutex);
+                               mutex_unlock(&head->mutex);
+
+                               btrfs_put_delayed_ref(ref);
+                               cond_resched();
+                               goto again;
+                       }
+                       node = rb_next(node);
+               }
+               spin_unlock(&delayed_refs->lock);
+               schedule_timeout(1);
+               goto again;
+       }
+out:
+       spin_unlock(&delayed_refs->lock);
+       return 0;
+}
+
+int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               u64 bytenr, u64 num_bytes, u64 flags,
+                               int is_data)
+{
+       struct btrfs_delayed_extent_op *extent_op;
+       int ret;
+
+       extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS);
+       if (!extent_op)
+               return -ENOMEM;
+
+       extent_op->flags_to_set = flags;
+       extent_op->update_flags = 1;
+       extent_op->update_key = 0;
+       extent_op->is_data = is_data ? 1 : 0;
+
+       ret = btrfs_add_delayed_extent_op(trans, bytenr, num_bytes, extent_op);
+       if (ret)
+               kfree(extent_op);
+       return ret;
+}
+
+static noinline int check_delayed_ref(struct btrfs_trans_handle *trans,
+                                     struct btrfs_root *root,
+                                     struct btrfs_path *path,
+                                     u64 objectid, u64 offset, u64 bytenr)
+{
+       struct btrfs_delayed_ref_head *head;
+       struct btrfs_delayed_ref_node *ref;
+       struct btrfs_delayed_data_ref *data_ref;
+       struct btrfs_delayed_ref_root *delayed_refs;
+       struct rb_node *node;
+       int ret = 0;
+
+       ret = -ENOENT;
+       delayed_refs = &trans->transaction->delayed_refs;
+       spin_lock(&delayed_refs->lock);
+       head = btrfs_find_delayed_ref_head(trans, bytenr);
+       if (!head)
+               goto out;
+
+       if (!mutex_trylock(&head->mutex)) {
+               atomic_inc(&head->node.refs);
+               spin_unlock(&delayed_refs->lock);
+
+               btrfs_release_path(root->fs_info->extent_root, path);
+
+               mutex_lock(&head->mutex);
+               mutex_unlock(&head->mutex);
+               btrfs_put_delayed_ref(&head->node);
+               return -EAGAIN;
+       }
+
+       node = rb_prev(&head->node.rb_node);
+       if (!node)
+               goto out_unlock;
+
+       ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
+
+       if (ref->bytenr != bytenr)
+               goto out_unlock;
+
+       ret = 1;
+       if (ref->type != BTRFS_EXTENT_DATA_REF_KEY)
+               goto out_unlock;
+
+       data_ref = btrfs_delayed_node_to_data_ref(ref);
+
+       node = rb_prev(node);
+       if (node) {
+               ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
+               if (ref->bytenr == bytenr)
+                       goto out_unlock;
+       }
+
+       if (data_ref->root != root->root_key.objectid ||
+           data_ref->objectid != objectid || data_ref->offset != offset)
+               goto out_unlock;
+
+       ret = 0;
+out_unlock:
+       mutex_unlock(&head->mutex);
+out:
+       spin_unlock(&delayed_refs->lock);
+       return ret;
+}
+
+static noinline int check_committed_ref(struct btrfs_trans_handle *trans,
+                                       struct btrfs_root *root,
+                                       struct btrfs_path *path,
+                                       u64 objectid, u64 offset, u64 bytenr)
+{
+       struct btrfs_root *extent_root = root->fs_info->extent_root;
+       struct extent_buffer *leaf;
+       struct btrfs_extent_data_ref *ref;
+       struct btrfs_extent_inline_ref *iref;
+       struct btrfs_extent_item *ei;
+       struct btrfs_key key;
+       u32 item_size;
+       int ret;
+
+       key.objectid = bytenr;
+       key.offset = (u64)-1;
+       key.type = BTRFS_EXTENT_ITEM_KEY;
+
+       ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
+       if (ret < 0)
+               goto out;
+       BUG_ON(ret == 0);
+
+       ret = -ENOENT;
+       if (path->slots[0] == 0)
+               goto out;
+
+       path->slots[0]--;
+       leaf = path->nodes[0];
+       btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+
+       if (key.objectid != bytenr || key.type != BTRFS_EXTENT_ITEM_KEY)
+               goto out;
+
+       ret = 1;
+       item_size = btrfs_item_size_nr(leaf, path->slots[0]);
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+       if (item_size < sizeof(*ei)) {
+               WARN_ON(item_size != sizeof(struct btrfs_extent_item_v0));
+               goto out;
+       }
+#endif
+       ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
+
+       if (item_size != sizeof(*ei) +
+           btrfs_extent_inline_ref_size(BTRFS_EXTENT_DATA_REF_KEY))
+               goto out;
+
+       if (btrfs_extent_generation(leaf, ei) <=
+           btrfs_root_last_snapshot(&root->root_item))
+               goto out;
+
+       iref = (struct btrfs_extent_inline_ref *)(ei + 1);
+       if (btrfs_extent_inline_ref_type(leaf, iref) !=
+           BTRFS_EXTENT_DATA_REF_KEY)
+               goto out;
+
+       ref = (struct btrfs_extent_data_ref *)(&iref->offset);
+       if (btrfs_extent_refs(leaf, ei) !=
+           btrfs_extent_data_ref_count(leaf, ref) ||
+           btrfs_extent_data_ref_root(leaf, ref) !=
+           root->root_key.objectid ||
+           btrfs_extent_data_ref_objectid(leaf, ref) != objectid ||
+           btrfs_extent_data_ref_offset(leaf, ref) != offset)
+               goto out;
+
+       ret = 0;
+out:
+       return ret;
+}
+
+int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
+                         struct btrfs_root *root,
+                         u64 objectid, u64 offset, u64 bytenr)
+{
+       struct btrfs_path *path;
+       int ret;
+       int ret2;
+
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOENT;
+
+       do {
+               ret = check_committed_ref(trans, root, path, objectid,
+                                         offset, bytenr);
+               if (ret && ret != -ENOENT)
+                       goto out;
+
+               ret2 = check_delayed_ref(trans, root, path, objectid,
+                                        offset, bytenr);
+       } while (ret2 == -EAGAIN);
+
+       if (ret2 && ret2 != -ENOENT) {
+               ret = ret2;
+               goto out;
+       }
+
+       if (ret != -ENOENT || ret2 != -ENOENT)
+               ret = 0;
+out:
+       btrfs_free_path(path);
+       if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID)
+               WARN_ON(ret > 0);
+       return ret;
+}
+
+#if 0
+int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+                   struct extent_buffer *buf, u32 nr_extents)
+{
+       struct btrfs_key key;
+       struct btrfs_file_extent_item *fi;
+       u64 root_gen;
+       u32 nritems;
+       int i;
+       int level;
+       int ret = 0;
+       int shared = 0;
+
+       if (!root->ref_cows)
+               return 0;
+
+       if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
+               shared = 0;
+               root_gen = root->root_key.offset;
+       } else {
+               shared = 1;
+               root_gen = trans->transid - 1;
+       }
+
+       level = btrfs_header_level(buf);
+       nritems = btrfs_header_nritems(buf);
+
+       if (level == 0) {
+               struct btrfs_leaf_ref *ref;
+               struct btrfs_extent_info *info;
+
+               ref = btrfs_alloc_leaf_ref(root, nr_extents);
+               if (!ref) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               ref->root_gen = root_gen;
+               ref->bytenr = buf->start;
+               ref->owner = btrfs_header_owner(buf);
+               ref->generation = btrfs_header_generation(buf);
+               ref->nritems = nr_extents;
+               info = ref->extents;
+
+               for (i = 0; nr_extents > 0 && i < nritems; i++) {
+                       u64 disk_bytenr;
+                       btrfs_item_key_to_cpu(buf, &key, i);
+                       if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
+                               continue;
+                       fi = btrfs_item_ptr(buf, i,
+                                           struct btrfs_file_extent_item);
+                       if (btrfs_file_extent_type(buf, fi) ==
+                           BTRFS_FILE_EXTENT_INLINE)
+                               continue;
+                       disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
+                       if (disk_bytenr == 0)
+                               continue;
+
+                       info->bytenr = disk_bytenr;
+                       info->num_bytes =
+                               btrfs_file_extent_disk_num_bytes(buf, fi);
+                       info->objectid = key.objectid;
+                       info->offset = key.offset;
+                       info++;
+               }
+
+               ret = btrfs_add_leaf_ref(root, ref, shared);
+               if (ret == -EEXIST && shared) {
+                       struct btrfs_leaf_ref *old;
+                       old = btrfs_lookup_leaf_ref(root, ref->bytenr);
+                       BUG_ON(!old);
+                       btrfs_remove_leaf_ref(root, old);
+                       btrfs_free_leaf_ref(root, old);
+                       ret = btrfs_add_leaf_ref(root, ref, shared);
+               }
+               WARN_ON(ret);
+               btrfs_free_leaf_ref(root, ref);
+       }
+out:
+       return ret;
+}
+
+/* when a block goes through cow, we update the reference counts of
+ * everything that block points to.  The internal pointers of the block
+ * can be in just about any order, and it is likely to have clusters of
+ * things that are close together and clusters of things that are not.
+ *
+ * To help reduce the seeks that come with updating all of these reference
+ * counts, sort them by byte number before actual updates are done.
+ *
+ * struct refsort is used to match byte number to slot in the btree block.
+ * we sort based on the byte number and then use the slot to actually
+ * find the item.
+ *
+ * struct refsort is smaller than strcut btrfs_item and smaller than
+ * struct btrfs_key_ptr.  Since we're currently limited to the page size
+ * for a btree block, there's no way for a kmalloc of refsorts for a
+ * single node to be bigger than a page.
+ */
+struct refsort {
+       u64 bytenr;
+       u32 slot;
+};
+
+/*
+ * for passing into sort()
+ */
+static int refsort_cmp(const void *a_void, const void *b_void)
+{
+       const struct refsort *a = a_void;
+       const struct refsort *b = b_void;
+
+       if (a->bytenr < b->bytenr)
+               return -1;
+       if (a->bytenr > b->bytenr)
+               return 1;
+       return 0;
+}
+#endif
+
+static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *root,
+                          struct extent_buffer *buf,
+                          int full_backref, int inc)
+{
+       u64 bytenr;
+       u64 num_bytes;
+       u64 parent;
+       u64 ref_root;
+       u32 nritems;
+       struct btrfs_key key;
+       struct btrfs_file_extent_item *fi;
+       int i;
+       int level;
+       int ret = 0;
+       int (*process_func)(struct btrfs_trans_handle *, struct btrfs_root *,
+                           u64, u64, u64, u64, u64, u64);
+
+       ref_root = btrfs_header_owner(buf);
+       nritems = btrfs_header_nritems(buf);
+       level = btrfs_header_level(buf);
+
+       if (!root->ref_cows && level == 0)
+               return 0;
+
+       if (inc)
+               process_func = btrfs_inc_extent_ref;
+       else
+               process_func = btrfs_free_extent;
+
+       if (full_backref)
+               parent = buf->start;
+       else
+               parent = 0;
+
+       for (i = 0; i < nritems; i++) {
+               if (level == 0) {
+                       btrfs_item_key_to_cpu(buf, &key, i);
+                       if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
+                               continue;
+                       fi = btrfs_item_ptr(buf, i,
+                                           struct btrfs_file_extent_item);
+                       if (btrfs_file_extent_type(buf, fi) ==
+                           BTRFS_FILE_EXTENT_INLINE)
+                               continue;
+                       bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
+                       if (bytenr == 0)
+                               continue;
+
+                       num_bytes = btrfs_file_extent_disk_num_bytes(buf, fi);
+                       key.offset -= btrfs_file_extent_offset(buf, fi);
+                       ret = process_func(trans, root, bytenr, num_bytes,
+                                          parent, ref_root, key.objectid,
+                                          key.offset);
+                       if (ret)
+                               goto fail;
+               } else {
+                       bytenr = btrfs_node_blockptr(buf, i);
+                       num_bytes = btrfs_level_size(root, level - 1);
+                       ret = process_func(trans, root, bytenr, num_bytes,
+                                          parent, ref_root, level - 1, 0);
+                       if (ret)
+                               goto fail;
+               }
+       }
+       return 0;
+fail:
+       BUG();
+       return ret;
+}
+
+int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+                 struct extent_buffer *buf, int full_backref)
+{
+       return __btrfs_mod_ref(trans, root, buf, full_backref, 1);
+}
+
+int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+                 struct extent_buffer *buf, int full_backref)
+{
+       return __btrfs_mod_ref(trans, root, buf, full_backref, 0);
+}
+
+static int write_one_cache_group(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                struct btrfs_block_group_cache *cache)
+{
+       int ret;
+       struct btrfs_root *extent_root = root->fs_info->extent_root;
+       unsigned long bi;
+       struct extent_buffer *leaf;
+
+       ret = btrfs_search_slot(trans, extent_root, &cache->key, path, 0, 1);
+       if (ret < 0)
+               goto fail;
+       BUG_ON(ret);
+
+       leaf = path->nodes[0];
+       bi = btrfs_item_ptr_offset(leaf, path->slots[0]);
+       write_extent_buffer(leaf, &cache->item, bi, sizeof(cache->item));
+       btrfs_mark_buffer_dirty(leaf);
+       btrfs_release_path(extent_root, path);
+fail:
+       if (ret)
+               return ret;
+       return 0;
+
+}
+
+static struct btrfs_block_group_cache *
+next_block_group(struct btrfs_root *root,
+                struct btrfs_block_group_cache *cache)
+{
+       struct rb_node *node;
+       spin_lock(&root->fs_info->block_group_cache_lock);
+       node = rb_next(&cache->cache_node);
+       btrfs_put_block_group(cache);
+       if (node) {
+               cache = rb_entry(node, struct btrfs_block_group_cache,
+                                cache_node);
+               btrfs_get_block_group(cache);
+       } else
+               cache = NULL;
+       spin_unlock(&root->fs_info->block_group_cache_lock);
+       return cache;
+}
+
+static int cache_save_setup(struct btrfs_block_group_cache *block_group,
+                           struct btrfs_trans_handle *trans,
+                           struct btrfs_path *path)
+{
+       struct btrfs_root *root = block_group->fs_info->tree_root;
+       struct inode *inode = NULL;
+       u64 alloc_hint = 0;
+       int num_pages = 0;
+       int retries = 0;
+       int ret = 0;
+
+       /*
+        * If this block group is smaller than 100 megs don't bother caching the
+        * block group.
+        */
+       if (block_group->key.offset < (100 * 1024 * 1024)) {
+               spin_lock(&block_group->lock);
+               block_group->disk_cache_state = BTRFS_DC_WRITTEN;
+               spin_unlock(&block_group->lock);
+               return 0;
+       }
+
+again:
+       inode = lookup_free_space_inode(root, block_group, path);
+       if (IS_ERR(inode) && PTR_ERR(inode) != -ENOENT) {
+               ret = PTR_ERR(inode);
+               btrfs_release_path(root, path);
+               goto out;
+       }
+
+       if (IS_ERR(inode)) {
+               BUG_ON(retries);
+               retries++;
+
+               if (block_group->ro)
+                       goto out_free;
+
+               ret = create_free_space_inode(root, trans, block_group, path);
+               if (ret)
+                       goto out_free;
+               goto again;
+       }
+
+       /*
+        * We want to set the generation to 0, that way if anything goes wrong
+        * from here on out we know not to trust this cache when we load up next
+        * time.
+        */
+       BTRFS_I(inode)->generation = 0;
+       ret = btrfs_update_inode(trans, root, inode);
+       WARN_ON(ret);
+
+       if (i_size_read(inode) > 0) {
+               ret = btrfs_truncate_free_space_cache(root, trans, path,
+                                                     inode);
+               if (ret)
+                       goto out_put;
+       }
+
+       spin_lock(&block_group->lock);
+       if (block_group->cached != BTRFS_CACHE_FINISHED) {
+               spin_unlock(&block_group->lock);
+               goto out_put;
+       }
+       spin_unlock(&block_group->lock);
+
+       num_pages = (int)div64_u64(block_group->key.offset, 1024 * 1024 * 1024);
+       if (!num_pages)
+               num_pages = 1;
+
+       /*
+        * Just to make absolutely sure we have enough space, we're going to
+        * preallocate 12 pages worth of space for each block group.  In
+        * practice we ought to use at most 8, but we need extra space so we can
+        * add our header and have a terminator between the extents and the
+        * bitmaps.
+        */
+       num_pages *= 16;
+       num_pages *= PAGE_CACHE_SIZE;
+
+       ret = btrfs_check_data_free_space(inode, num_pages);
+       if (ret)
+               goto out_put;
+
+       ret = btrfs_prealloc_file_range_trans(inode, trans, 0, 0, num_pages,
+                                             num_pages, num_pages,
+                                             &alloc_hint);
+       btrfs_free_reserved_data_space(inode, num_pages);
+out_put:
+       iput(inode);
+out_free:
+       btrfs_release_path(root, path);
+out:
+       spin_lock(&block_group->lock);
+       if (ret)
+               block_group->disk_cache_state = BTRFS_DC_ERROR;
+       else
+               block_group->disk_cache_state = BTRFS_DC_SETUP;
+       spin_unlock(&block_group->lock);
+
+       return ret;
+}
+
+int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
+                                  struct btrfs_root *root)
+{
+       struct btrfs_block_group_cache *cache;
+       int err = 0;
+       struct btrfs_path *path;
+       u64 last = 0;
+
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+
+again:
+       while (1) {
+               cache = btrfs_lookup_first_block_group(root->fs_info, last);
+               while (cache) {
+                       if (cache->disk_cache_state == BTRFS_DC_CLEAR)
+                               break;
+                       cache = next_block_group(root, cache);
+               }
+               if (!cache) {
+                       if (last == 0)
+                               break;
+                       last = 0;
+                       continue;
+               }
+               err = cache_save_setup(cache, trans, path);
+               last = cache->key.objectid + cache->key.offset;
+               btrfs_put_block_group(cache);
+       }
+
+       while (1) {
+               if (last == 0) {
+                       err = btrfs_run_delayed_refs(trans, root,
+                                                    (unsigned long)-1);
+                       BUG_ON(err);
+               }
+
+               cache = btrfs_lookup_first_block_group(root->fs_info, last);
+               while (cache) {
+                       if (cache->disk_cache_state == BTRFS_DC_CLEAR) {
+                               btrfs_put_block_group(cache);
+                               goto again;
+                       }
+
+                       if (cache->dirty)
+                               break;
+                       cache = next_block_group(root, cache);
+               }
+               if (!cache) {
+                       if (last == 0)
+                               break;
+                       last = 0;
+                       continue;
+               }
+
+               if (cache->disk_cache_state == BTRFS_DC_SETUP)
+                       cache->disk_cache_state = BTRFS_DC_NEED_WRITE;
+               cache->dirty = 0;
+               last = cache->key.objectid + cache->key.offset;
+
+               err = write_one_cache_group(trans, root, path, cache);
+               BUG_ON(err);
+               btrfs_put_block_group(cache);
+       }
+
+       while (1) {
+               /*
+                * I don't think this is needed since we're just marking our
+                * preallocated extent as written, but just in case it can't
+                * hurt.
+                */
+               if (last == 0) {
+                       err = btrfs_run_delayed_refs(trans, root,
+                                                    (unsigned long)-1);
+                       BUG_ON(err);
+               }
+
+               cache = btrfs_lookup_first_block_group(root->fs_info, last);
+               while (cache) {
+                       /*
+                        * Really this shouldn't happen, but it could if we
+                        * couldn't write the entire preallocated extent and
+                        * splitting the extent resulted in a new block.
+                        */
+                       if (cache->dirty) {
+                               btrfs_put_block_group(cache);
+                               goto again;
+                       }
+                       if (cache->disk_cache_state == BTRFS_DC_NEED_WRITE)
+                               break;
+                       cache = next_block_group(root, cache);
+               }
+               if (!cache) {
+                       if (last == 0)
+                               break;
+                       last = 0;
+                       continue;
+               }
+
+               btrfs_write_out_cache(root, trans, cache, path);
+
+               /*
+                * If we didn't have an error then the cache state is still
+                * NEED_WRITE, so we can set it to WRITTEN.
+                */
+               if (cache->disk_cache_state == BTRFS_DC_NEED_WRITE)
+                       cache->disk_cache_state = BTRFS_DC_WRITTEN;
+               last = cache->key.objectid + cache->key.offset;
+               btrfs_put_block_group(cache);
+       }
+
+       btrfs_free_path(path);
+       return 0;
+}
+
+int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr)
+{
+       struct btrfs_block_group_cache *block_group;
+       int readonly = 0;
+
+       block_group = btrfs_lookup_block_group(root->fs_info, bytenr);
+       if (!block_group || block_group->ro)
+               readonly = 1;
+       if (block_group)
+               btrfs_put_block_group(block_group);
+       return readonly;
+}
+
+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;
+       int i;
+       int factor;
+
+       if (flags & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1 |
+                    BTRFS_BLOCK_GROUP_RAID10))
+               factor = 2;
+       else
+               factor = 1;
+
+       found = __find_space_info(info, flags);
+       if (found) {
+               spin_lock(&found->lock);
+               found->total_bytes += total_bytes;
+               found->bytes_used += bytes_used;
+               found->disk_used += bytes_used * factor;
+               found->full = 0;
+               spin_unlock(&found->lock);
+               *space_info = found;
+               return 0;
+       }
+       found = kzalloc(sizeof(*found), GFP_NOFS);
+       if (!found)
+               return -ENOMEM;
+
+       for (i = 0; i < BTRFS_NR_RAID_TYPES; i++)
+               INIT_LIST_HEAD(&found->block_groups[i]);
+       init_rwsem(&found->groups_sem);
+       spin_lock_init(&found->lock);
+       found->flags = flags & (BTRFS_BLOCK_GROUP_DATA |
+                               BTRFS_BLOCK_GROUP_SYSTEM |
+                               BTRFS_BLOCK_GROUP_METADATA);
+       found->total_bytes = total_bytes;
+       found->bytes_used = bytes_used;
+       found->disk_used = bytes_used * factor;
+       found->bytes_pinned = 0;
+       found->bytes_reserved = 0;
+       found->bytes_readonly = 0;
+       found->bytes_may_use = 0;
+       found->full = 0;
+       found->force_alloc = 0;
+       *space_info = found;
+       list_add_rcu(&found->list, &info->space_info);
+       atomic_set(&found->caching_threads, 0);
+       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;
+       }
+}
+
+u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags)
+{
+       u64 num_devices = root->fs_info->fs_devices->rw_devices;
+
+       if (num_devices == 1)
+               flags &= ~(BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID0);
+       if (num_devices < 4)
+               flags &= ~BTRFS_BLOCK_GROUP_RAID10;
+
+       if ((flags & BTRFS_BLOCK_GROUP_DUP) &&
+           (flags & (BTRFS_BLOCK_GROUP_RAID1 |
+                     BTRFS_BLOCK_GROUP_RAID10))) {
+               flags &= ~BTRFS_BLOCK_GROUP_DUP;
+       }
+
+       if ((flags & BTRFS_BLOCK_GROUP_RAID1) &&
+           (flags & BTRFS_BLOCK_GROUP_RAID10)) {
+               flags &= ~BTRFS_BLOCK_GROUP_RAID1;
+       }
+
+       if ((flags & BTRFS_BLOCK_GROUP_RAID0) &&
+           ((flags & BTRFS_BLOCK_GROUP_RAID1) |
+            (flags & BTRFS_BLOCK_GROUP_RAID10) |
+            (flags & BTRFS_BLOCK_GROUP_DUP)))
+               flags &= ~BTRFS_BLOCK_GROUP_RAID0;
+       return flags;
+}
+
+static u64 get_alloc_profile(struct btrfs_root *root, u64 flags)
+{
+       if (flags & BTRFS_BLOCK_GROUP_DATA)
+               flags |= root->fs_info->avail_data_alloc_bits &
+                        root->fs_info->data_alloc_profile;
+       else if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
+               flags |= root->fs_info->avail_system_alloc_bits &
+                        root->fs_info->system_alloc_profile;
+       else if (flags & BTRFS_BLOCK_GROUP_METADATA)
+               flags |= root->fs_info->avail_metadata_alloc_bits &
+                        root->fs_info->metadata_alloc_profile;
+       return btrfs_reduce_alloc_profile(root, flags);
+}
+
+static u64 btrfs_get_alloc_profile(struct btrfs_root *root, int data)
+{
+       u64 flags;
+
+       if (data)
+               flags = BTRFS_BLOCK_GROUP_DATA;
+       else if (root == root->fs_info->chunk_root)
+               flags = BTRFS_BLOCK_GROUP_SYSTEM;
+       else
+               flags = BTRFS_BLOCK_GROUP_METADATA;
+
+       return get_alloc_profile(root, flags);
+}
+
+void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *inode)
+{
+       BTRFS_I(inode)->space_info = __find_space_info(root->fs_info,
+                                                      BTRFS_BLOCK_GROUP_DATA);
+}
+
+/*
+ * This will check the space that the inode allocates from to make sure we have
+ * enough space for bytes.
+ */
+int btrfs_check_data_free_space(struct inode *inode, u64 bytes)
+{
+       struct btrfs_space_info *data_sinfo;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       u64 used;
+       int ret = 0, committed = 0, alloc_chunk = 1;
+
+       /* make sure bytes are sectorsize aligned */
+       bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1);
+
+       if (root == root->fs_info->tree_root) {
+               alloc_chunk = 0;
+               committed = 1;
+       }
+
+       data_sinfo = BTRFS_I(inode)->space_info;
+       if (!data_sinfo)
+               goto alloc;
+
+again:
+       /* make sure we have enough space to handle the data first */
+       spin_lock(&data_sinfo->lock);
+       used = data_sinfo->bytes_used + data_sinfo->bytes_reserved +
+               data_sinfo->bytes_pinned + data_sinfo->bytes_readonly +
+               data_sinfo->bytes_may_use;
+
+       if (used + bytes > data_sinfo->total_bytes) {
+               struct btrfs_trans_handle *trans;
+
+               /*
+                * if we don't have enough free bytes in this space then we need
+                * to alloc a new chunk.
+                */
+               if (!data_sinfo->full && alloc_chunk) {
+                       u64 alloc_target;
+
+                       data_sinfo->force_alloc = 1;
+                       spin_unlock(&data_sinfo->lock);
+alloc:
+                       alloc_target = btrfs_get_alloc_profile(root, 1);
+                       trans = btrfs_join_transaction(root, 1);
+                       if (IS_ERR(trans))
+                               return PTR_ERR(trans);
+
+                       ret = do_chunk_alloc(trans, root->fs_info->extent_root,
+                                            bytes + 2 * 1024 * 1024,
+                                            alloc_target, 0);
+                       btrfs_end_transaction(trans, root);
+                       if (ret < 0)
+                               return ret;
+
+                       if (!data_sinfo) {
+                               btrfs_set_inode_space_info(root, inode);
+                               data_sinfo = BTRFS_I(inode)->space_info;
+                       }
+                       goto again;
+               }
+               spin_unlock(&data_sinfo->lock);
+
+               /* commit the current transaction and try again */
+               if (!committed && !root->fs_info->open_ioctl_trans) {
+                       committed = 1;
+                       trans = btrfs_join_transaction(root, 1);
+                       if (IS_ERR(trans))
+                               return PTR_ERR(trans);
+                       ret = btrfs_commit_transaction(trans, root);
+                       if (ret)
+                               return ret;
+                       goto again;
+               }
+
+#if 0 /* I hope we never need this code again, just in case */
+               printk(KERN_ERR "no space left, need %llu, %llu bytes_used, "
+                      "%llu bytes_reserved, " "%llu bytes_pinned, "
+                      "%llu bytes_readonly, %llu may use %llu total\n",
+                      (unsigned long long)bytes,
+                      (unsigned long long)data_sinfo->bytes_used,
+                      (unsigned long long)data_sinfo->bytes_reserved,
+                      (unsigned long long)data_sinfo->bytes_pinned,
+                      (unsigned long long)data_sinfo->bytes_readonly,
+                      (unsigned long long)data_sinfo->bytes_may_use,
+                      (unsigned long long)data_sinfo->total_bytes);
+#endif
+               return -ENOSPC;
+       }
+       data_sinfo->bytes_may_use += bytes;
+       BTRFS_I(inode)->reserved_bytes += bytes;
+       spin_unlock(&data_sinfo->lock);
+
+       return 0;
+}
+
+/*
+ * called when we are clearing an delalloc extent from the
+ * inode's io_tree or there was an error for whatever reason
+ * after calling btrfs_check_data_free_space
+ */
+void btrfs_free_reserved_data_space(struct inode *inode, u64 bytes)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_space_info *data_sinfo;
+
+       /* make sure bytes are sectorsize aligned */
+       bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1);
+
+       data_sinfo = BTRFS_I(inode)->space_info;
+       spin_lock(&data_sinfo->lock);
+       data_sinfo->bytes_may_use -= bytes;
+       BTRFS_I(inode)->reserved_bytes -= bytes;
+       spin_unlock(&data_sinfo->lock);
+}
+
+static void force_metadata_allocation(struct btrfs_fs_info *info)
+{
+       struct list_head *head = &info->space_info;
+       struct btrfs_space_info *found;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(found, head, list) {
+               if (found->flags & BTRFS_BLOCK_GROUP_METADATA)
+                       found->force_alloc = 1;
+       }
+       rcu_read_unlock();
+}
+
+static int should_alloc_chunk(struct btrfs_space_info *sinfo,
+                             u64 alloc_bytes)
+{
+       u64 num_bytes = sinfo->total_bytes - sinfo->bytes_readonly;
+
+       if (sinfo->bytes_used + sinfo->bytes_reserved +
+           alloc_bytes + 256 * 1024 * 1024 < num_bytes)
+               return 0;
+
+       if (sinfo->bytes_used + sinfo->bytes_reserved +
+           alloc_bytes < div_factor(num_bytes, 8))
+               return 0;
+
+       return 1;
+}
+
+static int do_chunk_alloc(struct btrfs_trans_handle *trans,
+                         struct btrfs_root *extent_root, u64 alloc_bytes,
+                         u64 flags, int force)
+{
+       struct btrfs_space_info *space_info;
+       struct btrfs_fs_info *fs_info = extent_root->fs_info;
+       int ret = 0;
+
+       mutex_lock(&fs_info->chunk_mutex);
+
+       flags = btrfs_reduce_alloc_profile(extent_root, flags);
+
+       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 (found_key.type != BTRFS_EXTENT_REF_KEY) {
-                       path->slots[0]++;
-                       continue;
-               }
+       spin_lock(&space_info->lock);
+       if (space_info->force_alloc)
+               force = 1;
+       if (space_info->full) {
+               spin_unlock(&space_info->lock);
+               goto out;
+       }
 
-               ref_item = btrfs_item_ptr(leaf, path->slots[0],
-                                         struct btrfs_extent_ref);
-               ref_root = btrfs_ref_root(leaf, ref_item);
-               if ((ref_root != root->root_key.objectid &&
-                    ref_root != BTRFS_TREE_LOG_OBJECTID) ||
-                    objectid != btrfs_ref_objectid(leaf, ref_item)) {
-                       ret = 1;
-                       goto out;
-               }
-               if (btrfs_ref_generation(leaf, ref_item) <= last_snapshot) {
-                       ret = 1;
-                       goto out;
-               }
+       if (!force && !should_alloc_chunk(space_info, alloc_bytes)) {
+               spin_unlock(&space_info->lock);
+               goto out;
+       }
+       spin_unlock(&space_info->lock);
 
-               path->slots[0]++;
+       /*
+        * If we have mixed data/metadata chunks we want to make sure we keep
+        * allocating mixed chunks instead of individual chunks.
+        */
+       if (btrfs_mixed_space_info(space_info))
+               flags |= (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA);
+
+       /*
+        * if we're doing a data chunk, go ahead and make sure that
+        * we keep a reasonable number of metadata chunks allocated in the
+        * FS as well.
+        */
+       if (flags & BTRFS_BLOCK_GROUP_DATA && fs_info->metadata_ratio) {
+               fs_info->data_chunk_allocations++;
+               if (!(fs_info->data_chunk_allocations %
+                     fs_info->metadata_ratio))
+                       force_metadata_allocation(fs_info);
        }
-       ret = 0;
+
+       ret = btrfs_alloc_chunk(trans, extent_root, flags);
+       spin_lock(&space_info->lock);
+       if (ret)
+               space_info->full = 1;
+       else
+               ret = 1;
+       space_info->force_alloc = 0;
+       spin_unlock(&space_info->lock);
 out:
-       btrfs_free_path(path);
+       mutex_unlock(&extent_root->fs_info->chunk_mutex);
        return ret;
 }
 
-int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-                   struct extent_buffer *buf, u32 nr_extents)
+static int maybe_allocate_chunk(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               struct btrfs_space_info *sinfo, u64 num_bytes)
 {
-       struct btrfs_key key;
-       struct btrfs_file_extent_item *fi;
-       u64 root_gen;
-       u32 nritems;
-       int i;
-       int level;
-       int ret = 0;
-       int shared = 0;
+       int ret;
+       int end_trans = 0;
 
-       if (!root->ref_cows)
+       if (sinfo->full)
                return 0;
 
-       if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
-               shared = 0;
-               root_gen = root->root_key.offset;
-       } else {
-               shared = 1;
-               root_gen = trans->transid - 1;
+       spin_lock(&sinfo->lock);
+       ret = should_alloc_chunk(sinfo, num_bytes + 2 * 1024 * 1024);
+       spin_unlock(&sinfo->lock);
+       if (!ret)
+               return 0;
+
+       if (!trans) {
+               trans = btrfs_join_transaction(root, 1);
+               BUG_ON(IS_ERR(trans));
+               end_trans = 1;
        }
 
-       level = btrfs_header_level(buf);
-       nritems = btrfs_header_nritems(buf);
+       ret = do_chunk_alloc(trans, root->fs_info->extent_root,
+                            num_bytes + 2 * 1024 * 1024,
+                            get_alloc_profile(root, sinfo->flags), 0);
 
-       if (level == 0) {
-               struct btrfs_leaf_ref *ref;
-               struct btrfs_extent_info *info;
+       if (end_trans)
+               btrfs_end_transaction(trans, root);
 
-               ref = btrfs_alloc_leaf_ref(root, nr_extents);
-               if (!ref) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
+       return ret == 1 ? 1 : 0;
+}
 
-               ref->root_gen = root_gen;
-               ref->bytenr = buf->start;
-               ref->owner = btrfs_header_owner(buf);
-               ref->generation = btrfs_header_generation(buf);
-               ref->nritems = nr_extents;
-               info = ref->extents;
+/*
+ * shrink metadata reservation for delalloc
+ */
+static int shrink_delalloc(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *root, u64 to_reclaim)
+{
+       struct btrfs_block_rsv *block_rsv;
+       u64 reserved;
+       u64 max_reclaim;
+       u64 reclaimed = 0;
+       int pause = 1;
+       int ret;
 
-               for (i = 0; nr_extents > 0 && i < nritems; i++) {
-                       u64 disk_bytenr;
-                       btrfs_item_key_to_cpu(buf, &key, i);
-                       if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
-                               continue;
-                       fi = btrfs_item_ptr(buf, i,
-                                           struct btrfs_file_extent_item);
-                       if (btrfs_file_extent_type(buf, fi) ==
-                           BTRFS_FILE_EXTENT_INLINE)
-                               continue;
-                       disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
-                       if (disk_bytenr == 0)
-                               continue;
+       block_rsv = &root->fs_info->delalloc_block_rsv;
+       spin_lock(&block_rsv->lock);
+       reserved = block_rsv->reserved;
+       spin_unlock(&block_rsv->lock);
 
-                       info->bytenr = disk_bytenr;
-                       info->num_bytes =
-                               btrfs_file_extent_disk_num_bytes(buf, fi);
-                       info->objectid = key.objectid;
-                       info->offset = key.offset;
-                       info++;
-               }
+       if (reserved == 0)
+               return 0;
 
-               ret = btrfs_add_leaf_ref(root, ref, shared);
-               if (ret == -EEXIST && shared) {
-                       struct btrfs_leaf_ref *old;
-                       old = btrfs_lookup_leaf_ref(root, ref->bytenr);
-                       BUG_ON(!old);
-                       btrfs_remove_leaf_ref(root, old);
-                       btrfs_free_leaf_ref(root, old);
-                       ret = btrfs_add_leaf_ref(root, ref, shared);
+       max_reclaim = min(reserved, to_reclaim);
+
+       while (1) {
+               ret = btrfs_start_one_delalloc_inode(root, trans ? 1 : 0);
+               if (!ret) {
+                       __set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(pause);
+                       pause <<= 1;
+                       if (pause > HZ / 10)
+                               pause = HZ / 10;
+               } else {
+                       pause = 1;
                }
-               WARN_ON(ret);
-               btrfs_free_leaf_ref(root, ref);
-       }
-out:
-       return ret;
-}
 
-/* when a block goes through cow, we update the reference counts of
- * everything that block points to.  The internal pointers of the block
- * can be in just about any order, and it is likely to have clusters of
- * things that are close together and clusters of things that are not.
- *
- * To help reduce the seeks that come with updating all of these reference
- * counts, sort them by byte number before actual updates are done.
- *
- * struct refsort is used to match byte number to slot in the btree block.
- * we sort based on the byte number and then use the slot to actually
- * find the item.
- *
- * struct refsort is smaller than strcut btrfs_item and smaller than
- * struct btrfs_key_ptr.  Since we're currently limited to the page size
- * for a btree block, there's no way for a kmalloc of refsorts for a
- * single node to be bigger than a page.
- */
-struct refsort {
-       u64 bytenr;
-       u32 slot;
-};
+               spin_lock(&block_rsv->lock);
+               if (reserved > block_rsv->reserved)
+                       reclaimed = reserved - block_rsv->reserved;
+               reserved = block_rsv->reserved;
+               spin_unlock(&block_rsv->lock);
 
-/*
- * for passing into sort()
- */
-static int refsort_cmp(const void *a_void, const void *b_void)
-{
-       const struct refsort *a = a_void;
-       const struct refsort *b = b_void;
+               if (reserved == 0 || reclaimed >= max_reclaim)
+                       break;
 
-       if (a->bytenr < b->bytenr)
-               return -1;
-       if (a->bytenr > b->bytenr)
-               return 1;
-       return 0;
+               if (trans && trans->transaction->blocked)
+                       return -EAGAIN;
+       }
+       return reclaimed >= to_reclaim;
 }
 
-
-noinline int btrfs_inc_ref(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root,
-                          struct extent_buffer *orig_buf,
-                          struct extent_buffer *buf, u32 *nr_extents)
+static int should_retry_reserve(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               struct btrfs_block_rsv *block_rsv,
+                               u64 num_bytes, int *retries)
 {
-       u64 bytenr;
-       u64 ref_root;
-       u64 orig_root;
-       u64 ref_generation;
-       u64 orig_generation;
-       struct refsort *sorted;
-       u32 nritems;
-       u32 nr_file_extents = 0;
-       struct btrfs_key key;
-       struct btrfs_file_extent_item *fi;
-       int i;
-       int level;
-       int ret = 0;
-       int faili = 0;
-       int refi = 0;
-       int slot;
-       int (*process_func)(struct btrfs_trans_handle *, struct btrfs_root *,
-                           u64, u64, u64, u64, u64, u64, u64, u64, u64);
+       struct btrfs_space_info *space_info = block_rsv->space_info;
+       int ret;
 
-       ref_root = btrfs_header_owner(buf);
-       ref_generation = btrfs_header_generation(buf);
-       orig_root = btrfs_header_owner(orig_buf);
-       orig_generation = btrfs_header_generation(orig_buf);
+       if ((*retries) > 2)
+               return -ENOSPC;
 
-       nritems = btrfs_header_nritems(buf);
-       level = btrfs_header_level(buf);
+       ret = maybe_allocate_chunk(trans, root, space_info, num_bytes);
+       if (ret)
+               return 1;
 
-       sorted = kmalloc(sizeof(struct refsort) * nritems, GFP_NOFS);
-       BUG_ON(!sorted);
+       if (trans && trans->transaction->in_commit)
+               return -ENOSPC;
 
-       if (root->ref_cows) {
-               process_func = __btrfs_inc_extent_ref;
-       } else {
-               if (level == 0 &&
-                   root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID)
-                       goto out;
-               if (level != 0 &&
-                   root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID)
-                       goto out;
-               process_func = __btrfs_update_extent_ref;
-       }
+       ret = shrink_delalloc(trans, root, num_bytes);
+       if (ret)
+               return ret;
 
-       /*
-        * we make two passes through the items.  In the first pass we
-        * only record the byte number and slot.  Then we sort based on
-        * byte number and do the actual work based on the sorted results
-        */
-       for (i = 0; i < nritems; i++) {
-               cond_resched();
-               if (level == 0) {
-                       btrfs_item_key_to_cpu(buf, &key, i);
-                       if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
-                               continue;
-                       fi = btrfs_item_ptr(buf, i,
-                                           struct btrfs_file_extent_item);
-                       if (btrfs_file_extent_type(buf, fi) ==
-                           BTRFS_FILE_EXTENT_INLINE)
-                               continue;
-                       bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
-                       if (bytenr == 0)
-                               continue;
+       spin_lock(&space_info->lock);
+       if (space_info->bytes_pinned < num_bytes)
+               ret = 1;
+       spin_unlock(&space_info->lock);
+       if (ret)
+               return -ENOSPC;
 
-                       nr_file_extents++;
-                       sorted[refi].bytenr = bytenr;
-                       sorted[refi].slot = i;
-                       refi++;
-               } else {
-                       bytenr = btrfs_node_blockptr(buf, i);
-                       sorted[refi].bytenr = bytenr;
-                       sorted[refi].slot = i;
-                       refi++;
-               }
-       }
-       /*
-        * if refi == 0, we didn't actually put anything into the sorted
-        * array and we're done
-        */
-       if (refi == 0)
-               goto out;
+       (*retries)++;
 
-       sort(sorted, refi, sizeof(struct refsort), refsort_cmp, NULL);
+       if (trans)
+               return -EAGAIN;
 
-       for (i = 0; i < refi; i++) {
-               cond_resched();
-               slot = sorted[i].slot;
-               bytenr = sorted[i].bytenr;
+       trans = btrfs_join_transaction(root, 1);
+       BUG_ON(IS_ERR(trans));
+       ret = btrfs_commit_transaction(trans, root);
+       BUG_ON(ret);
 
-               if (level == 0) {
-                       btrfs_item_key_to_cpu(buf, &key, slot);
-                       fi = btrfs_item_ptr(buf, slot,
-                                           struct btrfs_file_extent_item);
+       return 1;
+}
 
-                       bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
-                       if (bytenr == 0)
-                               continue;
+static int reserve_metadata_bytes(struct btrfs_block_rsv *block_rsv,
+                                 u64 num_bytes)
+{
+       struct btrfs_space_info *space_info = block_rsv->space_info;
+       u64 unused;
+       int ret = -ENOSPC;
 
-                       ret = process_func(trans, root, bytenr,
-                                  btrfs_file_extent_disk_num_bytes(buf, fi),
-                                  orig_buf->start, buf->start,
-                                  orig_root, ref_root,
-                                  orig_generation, ref_generation,
-                                  key.objectid);
+       spin_lock(&space_info->lock);
+       unused = space_info->bytes_used + space_info->bytes_reserved +
+                space_info->bytes_pinned + space_info->bytes_readonly;
 
-                       if (ret) {
-                               faili = slot;
-                               WARN_ON(1);
-                               goto fail;
-                       }
+       if (unused < space_info->total_bytes)
+               unused = space_info->total_bytes - unused;
+       else
+               unused = 0;
+
+       if (unused >= num_bytes) {
+               if (block_rsv->priority >= 10) {
+                       space_info->bytes_reserved += num_bytes;
+                       ret = 0;
                } else {
-                       ret = process_func(trans, root, bytenr, buf->len,
-                                          orig_buf->start, buf->start,
-                                          orig_root, ref_root,
-                                          orig_generation, ref_generation,
-                                          level - 1);
-                       if (ret) {
-                               faili = slot;
-                               WARN_ON(1);
-                               goto fail;
+                       if ((unused + block_rsv->reserved) *
+                           block_rsv->priority >=
+                           (num_bytes + block_rsv->reserved) * 10) {
+                               space_info->bytes_reserved += num_bytes;
+                               ret = 0;
                        }
                }
        }
-out:
-       kfree(sorted);
-       if (nr_extents) {
-               if (level == 0)
-                       *nr_extents = nr_file_extents;
-               else
-                       *nr_extents = nritems;
-       }
-       return 0;
-fail:
-       kfree(sorted);
-       WARN_ON(1);
+       spin_unlock(&space_info->lock);
+
        return ret;
 }
 
-int btrfs_update_ref(struct btrfs_trans_handle *trans,
-                    struct btrfs_root *root, struct extent_buffer *orig_buf,
-                    struct extent_buffer *buf, int start_slot, int nr)
-
+static struct btrfs_block_rsv *get_block_rsv(struct btrfs_trans_handle *trans,
+                                            struct btrfs_root *root)
 {
-       u64 bytenr;
-       u64 ref_root;
-       u64 orig_root;
-       u64 ref_generation;
-       u64 orig_generation;
-       struct btrfs_key key;
-       struct btrfs_file_extent_item *fi;
-       int i;
-       int ret;
-       int slot;
-       int level;
+       struct btrfs_block_rsv *block_rsv;
+       if (root->ref_cows)
+               block_rsv = trans->block_rsv;
+       else
+               block_rsv = root->block_rsv;
 
-       BUG_ON(start_slot < 0);
-       BUG_ON(start_slot + nr > btrfs_header_nritems(buf));
+       if (!block_rsv)
+               block_rsv = &root->fs_info->empty_block_rsv;
 
-       ref_root = btrfs_header_owner(buf);
-       ref_generation = btrfs_header_generation(buf);
-       orig_root = btrfs_header_owner(orig_buf);
-       orig_generation = btrfs_header_generation(orig_buf);
-       level = btrfs_header_level(buf);
+       return block_rsv;
+}
 
-       if (!root->ref_cows) {
-               if (level == 0 &&
-                   root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID)
-                       return 0;
-               if (level != 0 &&
-                   root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID)
-                       return 0;
+static int block_rsv_use_bytes(struct btrfs_block_rsv *block_rsv,
+                              u64 num_bytes)
+{
+       int ret = -ENOSPC;
+       spin_lock(&block_rsv->lock);
+       if (block_rsv->reserved >= num_bytes) {
+               block_rsv->reserved -= num_bytes;
+               if (block_rsv->reserved < block_rsv->size)
+                       block_rsv->full = 0;
+               ret = 0;
+       }
+       spin_unlock(&block_rsv->lock);
+       return ret;
+}
+
+static void block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv,
+                               u64 num_bytes, int update_size)
+{
+       spin_lock(&block_rsv->lock);
+       block_rsv->reserved += num_bytes;
+       if (update_size)
+               block_rsv->size += num_bytes;
+       else if (block_rsv->reserved >= block_rsv->size)
+               block_rsv->full = 1;
+       spin_unlock(&block_rsv->lock);
+}
+
+void block_rsv_release_bytes(struct btrfs_block_rsv *block_rsv,
+                            struct btrfs_block_rsv *dest, u64 num_bytes)
+{
+       struct btrfs_space_info *space_info = block_rsv->space_info;
+
+       spin_lock(&block_rsv->lock);
+       if (num_bytes == (u64)-1)
+               num_bytes = block_rsv->size;
+       block_rsv->size -= num_bytes;
+       if (block_rsv->reserved >= block_rsv->size) {
+               num_bytes = block_rsv->reserved - block_rsv->size;
+               block_rsv->reserved = block_rsv->size;
+               block_rsv->full = 1;
+       } else {
+               num_bytes = 0;
        }
+       spin_unlock(&block_rsv->lock);
 
-       for (i = 0, slot = start_slot; i < nr; i++, slot++) {
-               cond_resched();
-               if (level == 0) {
-                       btrfs_item_key_to_cpu(buf, &key, slot);
-                       if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
-                               continue;
-                       fi = btrfs_item_ptr(buf, slot,
-                                           struct btrfs_file_extent_item);
-                       if (btrfs_file_extent_type(buf, fi) ==
-                           BTRFS_FILE_EXTENT_INLINE)
-                               continue;
-                       bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
-                       if (bytenr == 0)
-                               continue;
-                       ret = __btrfs_update_extent_ref(trans, root, bytenr,
-                                   btrfs_file_extent_disk_num_bytes(buf, fi),
-                                   orig_buf->start, buf->start,
-                                   orig_root, ref_root, orig_generation,
-                                   ref_generation, key.objectid);
-                       if (ret)
-                               goto fail;
+       if (num_bytes > 0) {
+               if (dest) {
+                       block_rsv_add_bytes(dest, num_bytes, 0);
                } else {
-                       bytenr = btrfs_node_blockptr(buf, slot);
-                       ret = __btrfs_update_extent_ref(trans, root, bytenr,
-                                           buf->len, orig_buf->start,
-                                           buf->start, orig_root, ref_root,
-                                           orig_generation, ref_generation,
-                                           level - 1);
-                       if (ret)
-                               goto fail;
+                       spin_lock(&space_info->lock);
+                       space_info->bytes_reserved -= num_bytes;
+                       spin_unlock(&space_info->lock);
                }
        }
-       return 0;
-fail:
-       WARN_ON(1);
-       return -1;
 }
 
-static int write_one_cache_group(struct btrfs_trans_handle *trans,
-                                struct btrfs_root *root,
-                                struct btrfs_path *path,
-                                struct btrfs_block_group_cache *cache)
+static int block_rsv_migrate_bytes(struct btrfs_block_rsv *src,
+                                  struct btrfs_block_rsv *dst, u64 num_bytes)
 {
        int ret;
-       struct btrfs_root *extent_root = root->fs_info->extent_root;
-       unsigned long bi;
-       struct extent_buffer *leaf;
-
-       ret = btrfs_search_slot(trans, extent_root, &cache->key, path, 0, 1);
-       if (ret < 0)
-               goto fail;
-       BUG_ON(ret);
 
-       leaf = path->nodes[0];
-       bi = btrfs_item_ptr_offset(leaf, path->slots[0]);
-       write_extent_buffer(leaf, &cache->item, bi, sizeof(cache->item));
-       btrfs_mark_buffer_dirty(leaf);
-       btrfs_release_path(extent_root, path);
-fail:
+       ret = block_rsv_use_bytes(src, num_bytes);
        if (ret)
                return ret;
+
+       block_rsv_add_bytes(dst, num_bytes, 1);
        return 0;
+}
 
+void btrfs_init_block_rsv(struct btrfs_block_rsv *rsv)
+{
+       memset(rsv, 0, sizeof(*rsv));
+       spin_lock_init(&rsv->lock);
+       atomic_set(&rsv->usage, 1);
+       rsv->priority = 6;
+       INIT_LIST_HEAD(&rsv->list);
 }
 
-int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
-                                  struct btrfs_root *root)
+struct btrfs_block_rsv *btrfs_alloc_block_rsv(struct btrfs_root *root)
 {
-       struct btrfs_block_group_cache *cache, *entry;
-       struct rb_node *n;
-       int err = 0;
-       int werr = 0;
-       struct btrfs_path *path;
-       u64 last = 0;
+       struct btrfs_block_rsv *block_rsv;
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       u64 alloc_target;
 
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
+       block_rsv = kmalloc(sizeof(*block_rsv), GFP_NOFS);
+       if (!block_rsv)
+               return NULL;
 
-       while (1) {
-               cache = NULL;
-               spin_lock(&root->fs_info->block_group_cache_lock);
-               for (n = rb_first(&root->fs_info->block_group_cache_tree);
-                    n; n = rb_next(n)) {
-                       entry = rb_entry(n, struct btrfs_block_group_cache,
-                                        cache_node);
-                       if (entry->dirty) {
-                               cache = entry;
-                               break;
-                       }
-               }
-               spin_unlock(&root->fs_info->block_group_cache_lock);
+       btrfs_init_block_rsv(block_rsv);
 
-               if (!cache)
-                       break;
+       alloc_target = btrfs_get_alloc_profile(root, 0);
+       block_rsv->space_info = __find_space_info(fs_info,
+                                                 BTRFS_BLOCK_GROUP_METADATA);
 
-               cache->dirty = 0;
-               last += cache->key.offset;
+       return block_rsv;
+}
 
-               err = write_one_cache_group(trans, root,
-                                           path, cache);
-               /*
-                * if we fail to write the cache group, we want
-                * to keep it marked dirty in hopes that a later
-                * write will work
-                */
-               if (err) {
-                       werr = err;
-                       continue;
-               }
+void btrfs_free_block_rsv(struct btrfs_root *root,
+                         struct btrfs_block_rsv *rsv)
+{
+       if (rsv && atomic_dec_and_test(&rsv->usage)) {
+               btrfs_block_rsv_release(root, rsv, (u64)-1);
+               if (!rsv->durable)
+                       kfree(rsv);
        }
-       btrfs_free_path(path);
-       return werr;
 }
 
-int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr)
+/*
+ * make the block_rsv struct be able to capture freed space.
+ * the captured space will re-add to the the block_rsv struct
+ * after transaction commit
+ */
+void btrfs_add_durable_block_rsv(struct btrfs_fs_info *fs_info,
+                                struct btrfs_block_rsv *block_rsv)
 {
-       struct btrfs_block_group_cache *block_group;
-       int readonly = 0;
-
-       block_group = btrfs_lookup_block_group(root->fs_info, bytenr);
-       if (!block_group || block_group->ro)
-               readonly = 1;
-       if (block_group)
-               put_block_group(block_group);
-       return readonly;
+       block_rsv->durable = 1;
+       mutex_lock(&fs_info->durable_block_rsv_mutex);
+       list_add_tail(&block_rsv->list, &fs_info->durable_block_rsv_list);
+       mutex_unlock(&fs_info->durable_block_rsv_mutex);
 }
 
-static int update_space_info(struct btrfs_fs_info *info, u64 flags,
-                            u64 total_bytes, u64 bytes_used,
-                            struct btrfs_space_info **space_info)
+int btrfs_block_rsv_add(struct btrfs_trans_handle *trans,
+                       struct btrfs_root *root,
+                       struct btrfs_block_rsv *block_rsv,
+                       u64 num_bytes, int *retries)
 {
-       struct btrfs_space_info *found;
+       int ret;
 
-       found = __find_space_info(info, flags);
-       if (found) {
-               spin_lock(&found->lock);
-               found->total_bytes += total_bytes;
-               found->bytes_used += bytes_used;
-               found->full = 0;
-               spin_unlock(&found->lock);
-               *space_info = found;
+       if (num_bytes == 0)
+               return 0;
+again:
+       ret = reserve_metadata_bytes(block_rsv, num_bytes);
+       if (!ret) {
+               block_rsv_add_bytes(block_rsv, num_bytes, 1);
                return 0;
        }
-       found = kzalloc(sizeof(*found), GFP_NOFS);
-       if (!found)
-               return -ENOMEM;
 
-       INIT_LIST_HEAD(&found->block_groups);
-       init_rwsem(&found->groups_sem);
-       spin_lock_init(&found->lock);
-       found->flags = flags;
-       found->total_bytes = total_bytes;
-       found->bytes_used = bytes_used;
-       found->bytes_pinned = 0;
-       found->bytes_reserved = 0;
-       found->bytes_readonly = 0;
-       found->bytes_delalloc = 0;
-       found->full = 0;
-       found->force_alloc = 0;
-       *space_info = found;
-       list_add_rcu(&found->list, &info->space_info);
-       return 0;
-}
+       ret = should_retry_reserve(trans, root, block_rsv, num_bytes, retries);
+       if (ret > 0)
+               goto again;
 
-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;
-       }
+       return ret;
 }
 
-static void set_block_group_readonly(struct btrfs_block_group_cache *cache)
+int btrfs_block_rsv_check(struct btrfs_trans_handle *trans,
+                         struct btrfs_root *root,
+                         struct btrfs_block_rsv *block_rsv,
+                         u64 min_reserved, int min_factor)
 {
-       spin_lock(&cache->space_info->lock);
-       spin_lock(&cache->lock);
-       if (!cache->ro) {
-               cache->space_info->bytes_readonly += cache->key.offset -
-                                       btrfs_block_group_used(&cache->item);
-               cache->ro = 1;
-       }
-       spin_unlock(&cache->lock);
-       spin_unlock(&cache->space_info->lock);
-}
+       u64 num_bytes = 0;
+       int commit_trans = 0;
+       int ret = -ENOSPC;
 
-u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags)
-{
-       u64 num_devices = root->fs_info->fs_devices->rw_devices;
+       if (!block_rsv)
+               return 0;
 
-       if (num_devices == 1)
-               flags &= ~(BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID0);
-       if (num_devices < 4)
-               flags &= ~BTRFS_BLOCK_GROUP_RAID10;
+       spin_lock(&block_rsv->lock);
+       if (min_factor > 0)
+               num_bytes = div_factor(block_rsv->size, min_factor);
+       if (min_reserved > num_bytes)
+               num_bytes = min_reserved;
 
-       if ((flags & BTRFS_BLOCK_GROUP_DUP) &&
-           (flags & (BTRFS_BLOCK_GROUP_RAID1 |
-                     BTRFS_BLOCK_GROUP_RAID10))) {
-               flags &= ~BTRFS_BLOCK_GROUP_DUP;
+       if (block_rsv->reserved >= num_bytes) {
+               ret = 0;
+       } else {
+               num_bytes -= block_rsv->reserved;
+               if (block_rsv->durable &&
+                   block_rsv->freed[0] + block_rsv->freed[1] >= num_bytes)
+                       commit_trans = 1;
        }
+       spin_unlock(&block_rsv->lock);
+       if (!ret)
+               return 0;
 
-       if ((flags & BTRFS_BLOCK_GROUP_RAID1) &&
-           (flags & BTRFS_BLOCK_GROUP_RAID10)) {
-               flags &= ~BTRFS_BLOCK_GROUP_RAID1;
+       if (block_rsv->refill_used) {
+               ret = reserve_metadata_bytes(block_rsv, num_bytes);
+               if (!ret) {
+                       block_rsv_add_bytes(block_rsv, num_bytes, 0);
+                       return 0;
+               }
        }
 
-       if ((flags & BTRFS_BLOCK_GROUP_RAID0) &&
-           ((flags & BTRFS_BLOCK_GROUP_RAID1) |
-            (flags & BTRFS_BLOCK_GROUP_RAID10) |
-            (flags & BTRFS_BLOCK_GROUP_DUP)))
-               flags &= ~BTRFS_BLOCK_GROUP_RAID0;
-       return flags;
-}
+       if (commit_trans) {
+               if (trans)
+                       return -EAGAIN;
 
-static u64 btrfs_get_alloc_profile(struct btrfs_root *root, u64 data)
-{
-       struct btrfs_fs_info *info = root->fs_info;
-       u64 alloc_profile;
-
-       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;
+               trans = btrfs_join_transaction(root, 1);
+               BUG_ON(IS_ERR(trans));
+               ret = btrfs_commit_transaction(trans, root);
+               return 0;
        }
 
-       return btrfs_reduce_alloc_profile(root, data);
+       WARN_ON(1);
+       printk(KERN_INFO"block_rsv size %llu reserved %llu freed %llu %llu\n",
+               block_rsv->size, block_rsv->reserved,
+               block_rsv->freed[0], block_rsv->freed[1]);
+
+       return -ENOSPC;
 }
 
-void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *inode)
+int btrfs_block_rsv_migrate(struct btrfs_block_rsv *src_rsv,
+                           struct btrfs_block_rsv *dst_rsv,
+                           u64 num_bytes)
 {
-       u64 alloc_target;
+       return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes);
+}
 
-       alloc_target = btrfs_get_alloc_profile(root, 1);
-       BTRFS_I(inode)->space_info = __find_space_info(root->fs_info,
-                                                      alloc_target);
+void btrfs_block_rsv_release(struct btrfs_root *root,
+                            struct btrfs_block_rsv *block_rsv,
+                            u64 num_bytes)
+{
+       struct btrfs_block_rsv *global_rsv = &root->fs_info->global_block_rsv;
+       if (global_rsv->full || global_rsv == block_rsv ||
+           block_rsv->space_info != global_rsv->space_info)
+               global_rsv = NULL;
+       block_rsv_release_bytes(block_rsv, global_rsv, num_bytes);
 }
 
 /*
- * for now this just makes sure we have at least 5% of our metadata space free
- * for use.
+ * helper to calculate size of global block reservation.
+ * the desired value is sum of space used by extent tree,
+ * checksum tree and root tree
  */
-int btrfs_check_metadata_free_space(struct btrfs_root *root)
+static u64 calc_global_metadata_size(struct btrfs_fs_info *fs_info)
 {
-       struct btrfs_fs_info *info = root->fs_info;
-       struct btrfs_space_info *meta_sinfo;
-       u64 alloc_target, thresh;
-       int committed = 0, ret;
+       struct btrfs_space_info *sinfo;
+       u64 num_bytes;
+       u64 meta_used;
+       u64 data_used;
+       int csum_size = btrfs_super_csum_size(&fs_info->super_copy);
+#if 0
+       /*
+        * per tree used space accounting can be inaccuracy, so we
+        * can't rely on it.
+        */
+       spin_lock(&fs_info->extent_root->accounting_lock);
+       num_bytes = btrfs_root_used(&fs_info->extent_root->root_item);
+       spin_unlock(&fs_info->extent_root->accounting_lock);
 
-       /* get the space info for where the metadata will live */
-       alloc_target = btrfs_get_alloc_profile(root, 0);
-       meta_sinfo = __find_space_info(info, alloc_target);
+       spin_lock(&fs_info->csum_root->accounting_lock);
+       num_bytes += btrfs_root_used(&fs_info->csum_root->root_item);
+       spin_unlock(&fs_info->csum_root->accounting_lock);
 
-again:
-       spin_lock(&meta_sinfo->lock);
-       if (!meta_sinfo->full)
-               thresh = meta_sinfo->total_bytes * 80;
-       else
-               thresh = meta_sinfo->total_bytes * 95;
+       spin_lock(&fs_info->tree_root->accounting_lock);
+       num_bytes += btrfs_root_used(&fs_info->tree_root->root_item);
+       spin_unlock(&fs_info->tree_root->accounting_lock);
+#endif
+       sinfo = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_DATA);
+       spin_lock(&sinfo->lock);
+       data_used = sinfo->bytes_used;
+       spin_unlock(&sinfo->lock);
 
-       do_div(thresh, 100);
+       sinfo = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA);
+       spin_lock(&sinfo->lock);
+       meta_used = sinfo->bytes_used;
+       spin_unlock(&sinfo->lock);
 
-       if (meta_sinfo->bytes_used + meta_sinfo->bytes_reserved +
-           meta_sinfo->bytes_pinned + meta_sinfo->bytes_readonly > thresh) {
-               struct btrfs_trans_handle *trans;
-               if (!meta_sinfo->full) {
-                       meta_sinfo->force_alloc = 1;
-                       spin_unlock(&meta_sinfo->lock);
+       num_bytes = (data_used >> fs_info->sb->s_blocksize_bits) *
+                   csum_size * 2;
+       num_bytes += div64_u64(data_used + meta_used, 50);
 
-                       trans = btrfs_start_transaction(root, 1);
-                       if (!trans)
-                               return -ENOMEM;
+       if (num_bytes * 3 > meta_used)
+               num_bytes = div64_u64(meta_used, 3);
 
-                       ret = do_chunk_alloc(trans, root->fs_info->extent_root,
-                                            2 * 1024 * 1024, alloc_target, 0);
-                       btrfs_end_transaction(trans, root);
-                       goto again;
-               }
-               spin_unlock(&meta_sinfo->lock);
+       return ALIGN(num_bytes, fs_info->extent_root->leafsize << 10);
+}
 
-               if (!committed) {
-                       committed = 1;
-                       trans = btrfs_join_transaction(root, 1);
-                       if (!trans)
-                               return -ENOMEM;
-                       ret = btrfs_commit_transaction(trans, root);
-                       if (ret)
-                               return ret;
-                       goto again;
-               }
-               return -ENOSPC;
+static void update_global_block_rsv(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
+       struct btrfs_space_info *sinfo = block_rsv->space_info;
+       u64 num_bytes;
+
+       num_bytes = calc_global_metadata_size(fs_info);
+
+       spin_lock(&block_rsv->lock);
+       spin_lock(&sinfo->lock);
+
+       block_rsv->size = num_bytes;
+
+       num_bytes = sinfo->bytes_used + sinfo->bytes_pinned +
+                   sinfo->bytes_reserved + sinfo->bytes_readonly;
+
+       if (sinfo->total_bytes > num_bytes) {
+               num_bytes = sinfo->total_bytes - num_bytes;
+               block_rsv->reserved += num_bytes;
+               sinfo->bytes_reserved += num_bytes;
        }
-       spin_unlock(&meta_sinfo->lock);
 
-       return 0;
+       if (block_rsv->reserved >= block_rsv->size) {
+               num_bytes = block_rsv->reserved - block_rsv->size;
+               sinfo->bytes_reserved -= num_bytes;
+               block_rsv->reserved = block_rsv->size;
+               block_rsv->full = 1;
+       }
+#if 0
+       printk(KERN_INFO"global block rsv size %llu reserved %llu\n",
+               block_rsv->size, block_rsv->reserved);
+#endif
+       spin_unlock(&sinfo->lock);
+       spin_unlock(&block_rsv->lock);
 }
 
-/*
- * This will check the space that the inode allocates from to make sure we have
- * enough space for bytes.
- */
-int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode,
-                               u64 bytes)
+static void init_global_block_rsv(struct btrfs_fs_info *fs_info)
 {
-       struct btrfs_space_info *data_sinfo;
-       int ret = 0, committed = 0;
-
-       /* make sure bytes are sectorsize aligned */
-       bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1);
+       struct btrfs_space_info *space_info;
 
-       data_sinfo = BTRFS_I(inode)->space_info;
-again:
-       /* make sure we have enough space to handle the data first */
-       spin_lock(&data_sinfo->lock);
-       if (data_sinfo->total_bytes - data_sinfo->bytes_used -
-           data_sinfo->bytes_delalloc - data_sinfo->bytes_reserved -
-           data_sinfo->bytes_pinned - data_sinfo->bytes_readonly -
-           data_sinfo->bytes_may_use < bytes) {
-               struct btrfs_trans_handle *trans;
+       space_info = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_SYSTEM);
+       fs_info->chunk_block_rsv.space_info = space_info;
+       fs_info->chunk_block_rsv.priority = 10;
 
-               /*
-                * if we don't have enough free bytes in this space then we need
-                * to alloc a new chunk.
-                */
-               if (!data_sinfo->full) {
-                       u64 alloc_target;
+       space_info = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA);
+       fs_info->global_block_rsv.space_info = space_info;
+       fs_info->global_block_rsv.priority = 10;
+       fs_info->global_block_rsv.refill_used = 1;
+       fs_info->delalloc_block_rsv.space_info = space_info;
+       fs_info->trans_block_rsv.space_info = space_info;
+       fs_info->empty_block_rsv.space_info = space_info;
+       fs_info->empty_block_rsv.priority = 10;
 
-                       data_sinfo->force_alloc = 1;
-                       spin_unlock(&data_sinfo->lock);
+       fs_info->extent_root->block_rsv = &fs_info->global_block_rsv;
+       fs_info->csum_root->block_rsv = &fs_info->global_block_rsv;
+       fs_info->dev_root->block_rsv = &fs_info->global_block_rsv;
+       fs_info->tree_root->block_rsv = &fs_info->global_block_rsv;
+       fs_info->chunk_root->block_rsv = &fs_info->chunk_block_rsv;
 
-                       alloc_target = btrfs_get_alloc_profile(root, 1);
-                       trans = btrfs_start_transaction(root, 1);
-                       if (!trans)
-                               return -ENOMEM;
+       btrfs_add_durable_block_rsv(fs_info, &fs_info->global_block_rsv);
 
-                       ret = do_chunk_alloc(trans, root->fs_info->extent_root,
-                                            bytes + 2 * 1024 * 1024,
-                                            alloc_target, 0);
-                       btrfs_end_transaction(trans, root);
-                       if (ret)
-                               return ret;
-                       goto again;
-               }
-               spin_unlock(&data_sinfo->lock);
+       btrfs_add_durable_block_rsv(fs_info, &fs_info->delalloc_block_rsv);
 
-               /* commit the current transaction and try again */
-               if (!committed) {
-                       committed = 1;
-                       trans = btrfs_join_transaction(root, 1);
-                       if (!trans)
-                               return -ENOMEM;
-                       ret = btrfs_commit_transaction(trans, root);
-                       if (ret)
-                               return ret;
-                       goto again;
-               }
+       update_global_block_rsv(fs_info);
+}
 
-               printk(KERN_ERR "no space left, need %llu, %llu delalloc bytes"
-                      ", %llu bytes_used, %llu bytes_reserved, "
-                      "%llu bytes_pinned, %llu bytes_readonly, %llu may use"
-                      "%llu total\n", bytes, data_sinfo->bytes_delalloc,
-                      data_sinfo->bytes_used, data_sinfo->bytes_reserved,
-                      data_sinfo->bytes_pinned, data_sinfo->bytes_readonly,
-                      data_sinfo->bytes_may_use, data_sinfo->total_bytes);
-               return -ENOSPC;
-       }
-       data_sinfo->bytes_may_use += bytes;
-       BTRFS_I(inode)->reserved_bytes += bytes;
-       spin_unlock(&data_sinfo->lock);
+static void release_global_block_rsv(struct btrfs_fs_info *fs_info)
+{
+       block_rsv_release_bytes(&fs_info->global_block_rsv, NULL, (u64)-1);
+       WARN_ON(fs_info->delalloc_block_rsv.size > 0);
+       WARN_ON(fs_info->delalloc_block_rsv.reserved > 0);
+       WARN_ON(fs_info->trans_block_rsv.size > 0);
+       WARN_ON(fs_info->trans_block_rsv.reserved > 0);
+       WARN_ON(fs_info->chunk_block_rsv.size > 0);
+       WARN_ON(fs_info->chunk_block_rsv.reserved > 0);
+}
 
-       return btrfs_check_metadata_free_space(root);
+static u64 calc_trans_metadata_size(struct btrfs_root *root, int num_items)
+{
+       return (root->leafsize + root->nodesize * (BTRFS_MAX_LEVEL - 1)) *
+               3 * num_items;
 }
 
-/*
- * if there was an error for whatever reason after calling
- * btrfs_check_data_free_space, call this so we can cleanup the counters.
- */
-void btrfs_free_reserved_data_space(struct btrfs_root *root,
-                                   struct inode *inode, u64 bytes)
+int btrfs_trans_reserve_metadata(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                int num_items, int *retries)
 {
-       struct btrfs_space_info *data_sinfo;
+       u64 num_bytes;
+       int ret;
 
-       /* make sure bytes are sectorsize aligned */
-       bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1);
+       if (num_items == 0 || root->fs_info->chunk_root == root)
+               return 0;
 
-       data_sinfo = BTRFS_I(inode)->space_info;
-       spin_lock(&data_sinfo->lock);
-       data_sinfo->bytes_may_use -= bytes;
-       BTRFS_I(inode)->reserved_bytes -= bytes;
-       spin_unlock(&data_sinfo->lock);
+       num_bytes = calc_trans_metadata_size(root, num_items);
+       ret = btrfs_block_rsv_add(trans, root, &root->fs_info->trans_block_rsv,
+                                 num_bytes, retries);
+       if (!ret) {
+               trans->bytes_reserved += num_bytes;
+               trans->block_rsv = &root->fs_info->trans_block_rsv;
+       }
+       return ret;
 }
 
-/* called when we are adding a delalloc extent to the inode's io_tree */
-void btrfs_delalloc_reserve_space(struct btrfs_root *root, struct inode *inode,
-                                 u64 bytes)
+void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans,
+                                 struct btrfs_root *root)
 {
-       struct btrfs_space_info *data_sinfo;
+       if (!trans->bytes_reserved)
+               return;
 
-       /* get the space info for where this inode will be storing its data */
-       data_sinfo = BTRFS_I(inode)->space_info;
+       BUG_ON(trans->block_rsv != &root->fs_info->trans_block_rsv);
+       btrfs_block_rsv_release(root, trans->block_rsv,
+                               trans->bytes_reserved);
+       trans->bytes_reserved = 0;
+}
 
-       /* make sure we have enough space to handle the data first */
-       spin_lock(&data_sinfo->lock);
-       data_sinfo->bytes_delalloc += bytes;
+int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans,
+                                 struct inode *inode)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_block_rsv *src_rsv = get_block_rsv(trans, root);
+       struct btrfs_block_rsv *dst_rsv = root->orphan_block_rsv;
 
        /*
-        * we are adding a delalloc extent without calling
-        * btrfs_check_data_free_space first.  This happens on a weird
-        * writepage condition, but shouldn't hurt our accounting
+        * one for deleting orphan item, one for updating inode and
+        * two for calling btrfs_truncate_inode_items.
+        *
+        * btrfs_truncate_inode_items is a delete operation, it frees
+        * more space than it uses in most cases. So two units of
+        * metadata space should be enough for calling it many times.
+        * If all of the metadata space is used, we can commit
+        * transaction and use space it freed.
         */
-       if (unlikely(bytes > BTRFS_I(inode)->reserved_bytes)) {
-               data_sinfo->bytes_may_use -= BTRFS_I(inode)->reserved_bytes;
-               BTRFS_I(inode)->reserved_bytes = 0;
-       } else {
-               data_sinfo->bytes_may_use -= bytes;
-               BTRFS_I(inode)->reserved_bytes -= bytes;
-       }
-
-       spin_unlock(&data_sinfo->lock);
+       u64 num_bytes = calc_trans_metadata_size(root, 4);
+       return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes);
 }
 
-/* called when we are clearing an delalloc extent from the inode's io_tree */
-void btrfs_delalloc_free_space(struct btrfs_root *root, struct inode *inode,
-                             u64 bytes)
+void btrfs_orphan_release_metadata(struct inode *inode)
 {
-       struct btrfs_space_info *info;
-
-       info = BTRFS_I(inode)->space_info;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       u64 num_bytes = calc_trans_metadata_size(root, 4);
+       btrfs_block_rsv_release(root, root->orphan_block_rsv, num_bytes);
+}
 
-       spin_lock(&info->lock);
-       info->bytes_delalloc -= bytes;
-       spin_unlock(&info->lock);
+int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans,
+                               struct btrfs_pending_snapshot *pending)
+{
+       struct btrfs_root *root = pending->root;
+       struct btrfs_block_rsv *src_rsv = get_block_rsv(trans, root);
+       struct btrfs_block_rsv *dst_rsv = &pending->block_rsv;
+       /*
+        * two for root back/forward refs, two for directory entries
+        * and one for root of the snapshot.
+        */
+       u64 num_bytes = calc_trans_metadata_size(root, 5);
+       dst_rsv->space_info = src_rsv->space_info;
+       return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes);
 }
 
-static int do_chunk_alloc(struct btrfs_trans_handle *trans,
-                         struct btrfs_root *extent_root, u64 alloc_bytes,
-                         u64 flags, int force)
+static u64 calc_csum_metadata_size(struct inode *inode, u64 num_bytes)
 {
-       struct btrfs_space_info *space_info;
-       u64 thresh;
-       int ret = 0;
+       return num_bytes >>= 3;
+}
 
-       mutex_lock(&extent_root->fs_info->chunk_mutex);
+int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_block_rsv *block_rsv = &root->fs_info->delalloc_block_rsv;
+       u64 to_reserve;
+       int nr_extents;
+       int retries = 0;
+       int ret;
 
-       flags = btrfs_reduce_alloc_profile(extent_root, flags);
+       if (btrfs_transaction_in_commit(root->fs_info))
+               schedule_timeout(1);
 
-       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);
+       num_bytes = ALIGN(num_bytes, root->sectorsize);
+again:
+       spin_lock(&BTRFS_I(inode)->accounting_lock);
+       nr_extents = atomic_read(&BTRFS_I(inode)->outstanding_extents) + 1;
+       if (nr_extents > BTRFS_I(inode)->reserved_extents) {
+               nr_extents -= BTRFS_I(inode)->reserved_extents;
+               to_reserve = calc_trans_metadata_size(root, nr_extents);
+       } else {
+               nr_extents = 0;
+               to_reserve = 0;
        }
-       BUG_ON(!space_info);
 
-       spin_lock(&space_info->lock);
-       if (space_info->force_alloc) {
-               force = 1;
-               space_info->force_alloc = 0;
-       }
-       if (space_info->full) {
-               spin_unlock(&space_info->lock);
-               goto out;
+       to_reserve += calc_csum_metadata_size(inode, num_bytes);
+       ret = reserve_metadata_bytes(block_rsv, to_reserve);
+       if (ret) {
+               spin_unlock(&BTRFS_I(inode)->accounting_lock);
+               ret = should_retry_reserve(NULL, root, block_rsv, to_reserve,
+                                          &retries);
+               if (ret > 0)
+                       goto again;
+               return ret;
        }
 
-       thresh = space_info->total_bytes - space_info->bytes_readonly;
-       thresh = div_factor(thresh, 6);
-       if (!force &&
-          (space_info->bytes_used + space_info->bytes_pinned +
-           space_info->bytes_reserved + alloc_bytes) < thresh) {
-               spin_unlock(&space_info->lock);
-               goto out;
+       BTRFS_I(inode)->reserved_extents += nr_extents;
+       atomic_inc(&BTRFS_I(inode)->outstanding_extents);
+       spin_unlock(&BTRFS_I(inode)->accounting_lock);
+
+       block_rsv_add_bytes(block_rsv, to_reserve, 1);
+
+       if (block_rsv->size > 512 * 1024 * 1024)
+               shrink_delalloc(NULL, root, to_reserve);
+
+       return 0;
+}
+
+void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       u64 to_free;
+       int nr_extents;
+
+       num_bytes = ALIGN(num_bytes, root->sectorsize);
+       atomic_dec(&BTRFS_I(inode)->outstanding_extents);
+
+       spin_lock(&BTRFS_I(inode)->accounting_lock);
+       nr_extents = atomic_read(&BTRFS_I(inode)->outstanding_extents);
+       if (nr_extents < BTRFS_I(inode)->reserved_extents) {
+               nr_extents = BTRFS_I(inode)->reserved_extents - nr_extents;
+               BTRFS_I(inode)->reserved_extents -= nr_extents;
+       } else {
+               nr_extents = 0;
        }
-       spin_unlock(&space_info->lock);
+       spin_unlock(&BTRFS_I(inode)->accounting_lock);
 
-       ret = btrfs_alloc_chunk(trans, extent_root, flags);
+       to_free = calc_csum_metadata_size(inode, num_bytes);
+       if (nr_extents > 0)
+               to_free += calc_trans_metadata_size(root, nr_extents);
+
+       btrfs_block_rsv_release(root, &root->fs_info->delalloc_block_rsv,
+                               to_free);
+}
+
+int btrfs_delalloc_reserve_space(struct inode *inode, u64 num_bytes)
+{
+       int ret;
+
+       ret = btrfs_check_data_free_space(inode, num_bytes);
        if (ret)
-               space_info->full = 1;
-out:
-       mutex_unlock(&extent_root->fs_info->chunk_mutex);
-       return ret;
+               return ret;
+
+       ret = btrfs_delalloc_reserve_metadata(inode, num_bytes);
+       if (ret) {
+               btrfs_free_reserved_data_space(inode, num_bytes);
+               return ret;
+       }
+
+       return 0;
+}
+
+void btrfs_delalloc_release_space(struct inode *inode, u64 num_bytes)
+{
+       btrfs_delalloc_release_metadata(inode, num_bytes);
+       btrfs_free_reserved_data_space(inode, num_bytes);
 }
 
 static int update_block_group(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root,
-                             u64 bytenr, u64 num_bytes, int alloc,
-                             int mark_free)
+                             u64 bytenr, u64 num_bytes, int alloc)
 {
-       struct btrfs_block_group_cache *cache;
+       struct btrfs_block_group_cache *cache = NULL;
        struct btrfs_fs_info *info = root->fs_info;
        u64 total = num_bytes;
        u64 old_val;
        u64 byte_in_group;
+       int factor;
+
+       /* block accounting for super block */
+       spin_lock(&info->delalloc_lock);
+       old_val = btrfs_super_bytes_used(&info->super_copy);
+       if (alloc)
+               old_val += num_bytes;
+       else
+               old_val -= num_bytes;
+       btrfs_set_super_bytes_used(&info->super_copy, old_val);
+       spin_unlock(&info->delalloc_lock);
 
        while (total) {
                cache = btrfs_lookup_block_group(info, bytenr);
                if (!cache)
                        return -1;
+               if (cache->flags & (BTRFS_BLOCK_GROUP_DUP |
+                                   BTRFS_BLOCK_GROUP_RAID1 |
+                                   BTRFS_BLOCK_GROUP_RAID10))
+                       factor = 2;
+               else
+                       factor = 1;
+               /*
+                * If this block group has free space cache written out, we
+                * need to make sure to load it if we are removing space.  This
+                * is because we need the unpinning stage to actually add the
+                * space back to the block group, otherwise we will leak space.
+                */
+               if (!alloc && cache->cached == BTRFS_CACHE_NO)
+                       cache_block_group(cache, trans, 1);
+
                byte_in_group = bytenr - cache->key.objectid;
                WARN_ON(byte_in_group > cache->key.offset);
 
                spin_lock(&cache->space_info->lock);
                spin_lock(&cache->lock);
+
+               if (btrfs_super_cache_generation(&info->super_copy) != 0 &&
+                   cache->disk_cache_state < BTRFS_DC_CLEAR)
+                       cache->disk_cache_state = BTRFS_DC_CLEAR;
+
                cache->dirty = 1;
                old_val = btrfs_block_group_used(&cache->item);
                num_bytes = min(total, cache->key.offset - byte_in_group);
                if (alloc) {
                        old_val += num_bytes;
-                       cache->space_info->bytes_used += num_bytes;
-                       if (cache->ro)
-                               cache->space_info->bytes_readonly -= num_bytes;
                        btrfs_set_block_group_used(&cache->item, old_val);
+                       cache->reserved -= num_bytes;
+                       cache->space_info->bytes_reserved -= num_bytes;
+                       cache->space_info->bytes_used += num_bytes;
+                       cache->space_info->disk_used += num_bytes * factor;
                        spin_unlock(&cache->lock);
                        spin_unlock(&cache->space_info->lock);
                } else {
                        old_val -= num_bytes;
-                       cache->space_info->bytes_used -= num_bytes;
-                       if (cache->ro)
-                               cache->space_info->bytes_readonly += num_bytes;
                        btrfs_set_block_group_used(&cache->item, old_val);
+                       cache->pinned += num_bytes;
+                       cache->space_info->bytes_pinned += num_bytes;
+                       cache->space_info->bytes_used -= num_bytes;
+                       cache->space_info->disk_used -= num_bytes * factor;
                        spin_unlock(&cache->lock);
                        spin_unlock(&cache->space_info->lock);
-                       if (mark_free) {
-                               int ret;
 
-                               ret = btrfs_discard_extent(root, bytenr,
-                                                          num_bytes);
-                               WARN_ON(ret);
-
-                               ret = btrfs_add_free_space(cache, bytenr,
-                                                          num_bytes);
-                               WARN_ON(ret);
-                       }
+                       set_extent_dirty(info->pinned_extents,
+                                        bytenr, bytenr + num_bytes - 1,
+                                        GFP_NOFS | __GFP_NOFAIL);
                }
-               put_block_group(cache);
+               btrfs_put_block_group(cache);
                total -= num_bytes;
                bytenr += num_bytes;
        }
@@ -2035,121 +4091,186 @@ static u64 first_logical_byte(struct btrfs_root *root, u64 search_start)
                return 0;
 
        bytenr = cache->key.objectid;
-       put_block_group(cache);
+       btrfs_put_block_group(cache);
 
        return bytenr;
 }
 
-int btrfs_update_pinned_extents(struct btrfs_root *root,
-                               u64 bytenr, u64 num, int pin)
+static int pin_down_extent(struct btrfs_root *root,
+                          struct btrfs_block_group_cache *cache,
+                          u64 bytenr, u64 num_bytes, int reserved)
+{
+       spin_lock(&cache->space_info->lock);
+       spin_lock(&cache->lock);
+       cache->pinned += num_bytes;
+       cache->space_info->bytes_pinned += num_bytes;
+       if (reserved) {
+               cache->reserved -= num_bytes;
+               cache->space_info->bytes_reserved -= num_bytes;
+       }
+       spin_unlock(&cache->lock);
+       spin_unlock(&cache->space_info->lock);
+
+       set_extent_dirty(root->fs_info->pinned_extents, bytenr,
+                        bytenr + num_bytes - 1, GFP_NOFS | __GFP_NOFAIL);
+       return 0;
+}
+
+/*
+ * this function must be called within transaction
+ */
+int btrfs_pin_extent(struct btrfs_root *root,
+                    u64 bytenr, u64 num_bytes, int reserved)
 {
-       u64 len;
        struct btrfs_block_group_cache *cache;
-       struct btrfs_fs_info *fs_info = root->fs_info;
 
-       WARN_ON(!mutex_is_locked(&root->fs_info->pinned_mutex));
-       if (pin) {
-               set_extent_dirty(&fs_info->pinned_extents,
-                               bytenr, bytenr + num - 1, GFP_NOFS);
+       cache = btrfs_lookup_block_group(root->fs_info, bytenr);
+       BUG_ON(!cache);
+
+       pin_down_extent(root, cache, bytenr, num_bytes, reserved);
+
+       btrfs_put_block_group(cache);
+       return 0;
+}
+
+/*
+ * update size of reserved extents. this function may return -EAGAIN
+ * if 'reserve' is true or 'sinfo' is false.
+ */
+static int update_reserved_bytes(struct btrfs_block_group_cache *cache,
+                                u64 num_bytes, int reserve, int sinfo)
+{
+       int ret = 0;
+       if (sinfo) {
+               struct btrfs_space_info *space_info = cache->space_info;
+               spin_lock(&space_info->lock);
+               spin_lock(&cache->lock);
+               if (reserve) {
+                       if (cache->ro) {
+                               ret = -EAGAIN;
+                       } else {
+                               cache->reserved += num_bytes;
+                               space_info->bytes_reserved += num_bytes;
+                       }
+               } else {
+                       if (cache->ro)
+                               space_info->bytes_readonly += num_bytes;
+                       cache->reserved -= num_bytes;
+                       space_info->bytes_reserved -= num_bytes;
+               }
+               spin_unlock(&cache->lock);
+               spin_unlock(&space_info->lock);
        } else {
-               clear_extent_dirty(&fs_info->pinned_extents,
-                               bytenr, bytenr + num - 1, GFP_NOFS);
+               spin_lock(&cache->lock);
+               if (cache->ro) {
+                       ret = -EAGAIN;
+               } else {
+                       if (reserve)
+                               cache->reserved += num_bytes;
+                       else
+                               cache->reserved -= num_bytes;
+               }
+               spin_unlock(&cache->lock);
        }
-       mutex_unlock(&root->fs_info->pinned_mutex);
+       return ret;
+}
 
-       while (num > 0) {
-               cache = btrfs_lookup_block_group(fs_info, bytenr);
-               BUG_ON(!cache);
-               len = min(num, cache->key.offset -
-                         (bytenr - cache->key.objectid));
-               if (pin) {
-                       spin_lock(&cache->space_info->lock);
-                       spin_lock(&cache->lock);
-                       cache->pinned += len;
-                       cache->space_info->bytes_pinned += len;
-                       spin_unlock(&cache->lock);
-                       spin_unlock(&cache->space_info->lock);
-                       fs_info->total_pinned += len;
+int btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root)
+{
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       struct btrfs_caching_control *next;
+       struct btrfs_caching_control *caching_ctl;
+       struct btrfs_block_group_cache *cache;
+
+       down_write(&fs_info->extent_commit_sem);
+
+       list_for_each_entry_safe(caching_ctl, next,
+                                &fs_info->caching_block_groups, list) {
+               cache = caching_ctl->block_group;
+               if (block_group_cache_done(cache)) {
+                       cache->last_byte_to_unpin = (u64)-1;
+                       list_del_init(&caching_ctl->list);
+                       put_caching_control(caching_ctl);
                } else {
-                       spin_lock(&cache->space_info->lock);
-                       spin_lock(&cache->lock);
-                       cache->pinned -= len;
-                       cache->space_info->bytes_pinned -= len;
-                       spin_unlock(&cache->lock);
-                       spin_unlock(&cache->space_info->lock);
-                       fs_info->total_pinned -= len;
-                       if (cache->cached)
-                               btrfs_add_free_space(cache, bytenr, len);
+                       cache->last_byte_to_unpin = caching_ctl->progress;
                }
-               put_block_group(cache);
-               bytenr += len;
-               num -= len;
        }
+
+       if (fs_info->pinned_extents == &fs_info->freed_extents[0])
+               fs_info->pinned_extents = &fs_info->freed_extents[1];
+       else
+               fs_info->pinned_extents = &fs_info->freed_extents[0];
+
+       up_write(&fs_info->extent_commit_sem);
+
+       update_global_block_rsv(fs_info);
        return 0;
 }
 
-static int update_reserved_extents(struct btrfs_root *root,
-                                  u64 bytenr, u64 num, int reserve)
+static int unpin_extent_range(struct btrfs_root *root, u64 start, u64 end)
 {
-       u64 len;
-       struct btrfs_block_group_cache *cache;
        struct btrfs_fs_info *fs_info = root->fs_info;
+       struct btrfs_block_group_cache *cache = NULL;
+       u64 len;
+
+       while (start <= end) {
+               if (!cache ||
+                   start >= cache->key.objectid + cache->key.offset) {
+                       if (cache)
+                               btrfs_put_block_group(cache);
+                       cache = btrfs_lookup_block_group(fs_info, start);
+                       BUG_ON(!cache);
+               }
 
-       while (num > 0) {
-               cache = btrfs_lookup_block_group(fs_info, bytenr);
-               BUG_ON(!cache);
-               len = min(num, cache->key.offset -
-                         (bytenr - cache->key.objectid));
+               len = cache->key.objectid + cache->key.offset - start;
+               len = min(len, end + 1 - start);
+
+               if (start < cache->last_byte_to_unpin) {
+                       len = min(len, cache->last_byte_to_unpin - start);
+                       btrfs_add_free_space(cache, start, len);
+               }
+
+               start += len;
 
                spin_lock(&cache->space_info->lock);
                spin_lock(&cache->lock);
-               if (reserve) {
-                       cache->reserved += len;
+               cache->pinned -= len;
+               cache->space_info->bytes_pinned -= len;
+               if (cache->ro) {
+                       cache->space_info->bytes_readonly += len;
+               } else if (cache->reserved_pinned > 0) {
+                       len = min(len, cache->reserved_pinned);
+                       cache->reserved_pinned -= len;
                        cache->space_info->bytes_reserved += len;
-               } else {
-                       cache->reserved -= len;
-                       cache->space_info->bytes_reserved -= len;
                }
                spin_unlock(&cache->lock);
                spin_unlock(&cache->space_info->lock);
-               put_block_group(cache);
-               bytenr += len;
-               num -= len;
        }
-       return 0;
-}
-
-int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy)
-{
-       u64 last = 0;
-       u64 start;
-       u64 end;
-       struct extent_io_tree *pinned_extents = &root->fs_info->pinned_extents;
-       int ret;
 
-       mutex_lock(&root->fs_info->pinned_mutex);
-       while (1) {
-               ret = find_first_extent_bit(pinned_extents, last,
-                                           &start, &end, EXTENT_DIRTY);
-               if (ret)
-                       break;
-               set_extent_dirty(copy, start, end, GFP_NOFS);
-               last = end + 1;
-       }
-       mutex_unlock(&root->fs_info->pinned_mutex);
+       if (cache)
+               btrfs_put_block_group(cache);
        return 0;
 }
 
 int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
-                              struct btrfs_root *root,
-                              struct extent_io_tree *unpin)
+                              struct btrfs_root *root)
 {
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       struct extent_io_tree *unpin;
+       struct btrfs_block_rsv *block_rsv;
+       struct btrfs_block_rsv *next_rsv;
        u64 start;
        u64 end;
+       int idx;
        int ret;
 
+       if (fs_info->pinned_extents == &fs_info->freed_extents[0])
+               unpin = &fs_info->freed_extents[1];
+       else
+               unpin = &fs_info->freed_extents[0];
+
        while (1) {
-               mutex_lock(&root->fs_info->pinned_mutex);
                ret = find_first_extent_bit(unpin, 0, &start, &end,
                                            EXTENT_DIRTY);
                if (ret)
@@ -2157,119 +4278,108 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
 
                ret = btrfs_discard_extent(root, start, end + 1 - start);
 
-               /* unlocks the pinned mutex */
-               btrfs_update_pinned_extents(root, start, end + 1 - start, 0);
                clear_extent_dirty(unpin, start, end, GFP_NOFS);
-
+               unpin_extent_range(root, start, end);
                cond_resched();
        }
-       mutex_unlock(&root->fs_info->pinned_mutex);
-       return ret;
-}
-
-static int pin_down_bytes(struct btrfs_trans_handle *trans,
-                         struct btrfs_root *root,
-                         struct btrfs_path *path,
-                         u64 bytenr, u64 num_bytes, int is_data,
-                         struct extent_buffer **must_clean)
-{
-       int err = 0;
-       struct extent_buffer *buf;
 
-       if (is_data)
-               goto pinit;
+       mutex_lock(&fs_info->durable_block_rsv_mutex);
+       list_for_each_entry_safe(block_rsv, next_rsv,
+                                &fs_info->durable_block_rsv_list, list) {
 
-       buf = btrfs_find_tree_block(root, bytenr, num_bytes);
-       if (!buf)
-               goto pinit;
+               idx = trans->transid & 0x1;
+               if (block_rsv->freed[idx] > 0) {
+                       block_rsv_add_bytes(block_rsv,
+                                           block_rsv->freed[idx], 0);
+                       block_rsv->freed[idx] = 0;
+               }
+               if (atomic_read(&block_rsv->usage) == 0) {
+                       btrfs_block_rsv_release(root, block_rsv, (u64)-1);
 
-       /* we can reuse a block if it hasn't been written
-        * and it is from this transaction.  We can't
-        * reuse anything from the tree log root because
-        * it has tiny sub-transactions.
-        */
-       if (btrfs_buffer_uptodate(buf, 0) &&
-           btrfs_try_tree_lock(buf)) {
-               u64 header_owner = btrfs_header_owner(buf);
-               u64 header_transid = btrfs_header_generation(buf);
-               if (header_owner != BTRFS_TREE_LOG_OBJECTID &&
-                   header_owner != BTRFS_TREE_RELOC_OBJECTID &&
-                   header_owner != BTRFS_DATA_RELOC_TREE_OBJECTID &&
-                   header_transid == trans->transid &&
-                   !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) {
-                       *must_clean = buf;
-                       return 1;
+                       if (block_rsv->freed[0] == 0 &&
+                           block_rsv->freed[1] == 0) {
+                               list_del_init(&block_rsv->list);
+                               kfree(block_rsv);
+                       }
+               } else {
+                       btrfs_block_rsv_release(root, block_rsv, 0);
                }
-               btrfs_tree_unlock(buf);
        }
-       free_extent_buffer(buf);
-pinit:
-       btrfs_set_path_blocking(path);
-       mutex_lock(&root->fs_info->pinned_mutex);
-       /* unlocks the pinned mutex */
-       btrfs_update_pinned_extents(root, bytenr, num_bytes, 1);
+       mutex_unlock(&fs_info->durable_block_rsv_mutex);
 
-       BUG_ON(err < 0);
        return 0;
 }
 
-/*
- * remove an extent from the root, returns 0 on success
- */
-static int __free_extent(struct btrfs_trans_handle *trans,
-                        struct btrfs_root *root,
-                        u64 bytenr, u64 num_bytes, u64 parent,
-                        u64 root_objectid, u64 ref_generation,
-                        u64 owner_objectid, int pin, int mark_free,
-                        int refs_to_drop)
+static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root,
+                               u64 bytenr, u64 num_bytes, u64 parent,
+                               u64 root_objectid, u64 owner_objectid,
+                               u64 owner_offset, int refs_to_drop,
+                               struct btrfs_delayed_extent_op *extent_op)
 {
-       struct btrfs_path *path;
        struct btrfs_key key;
+       struct btrfs_path *path;
        struct btrfs_fs_info *info = root->fs_info;
        struct btrfs_root *extent_root = info->extent_root;
        struct extent_buffer *leaf;
+       struct btrfs_extent_item *ei;
+       struct btrfs_extent_inline_ref *iref;
        int ret;
+       int is_data;
        int extent_slot = 0;
        int found_extent = 0;
        int num_to_del = 1;
-       struct btrfs_extent_item *ei;
-       u32 refs;
+       u32 item_size;
+       u64 refs;
 
-       key.objectid = bytenr;
-       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
-       key.offset = num_bytes;
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
 
        path->reada = 1;
        path->leave_spinning = 1;
-       ret = lookup_extent_backref(trans, extent_root, path,
-                                   bytenr, parent, root_objectid,
-                                   ref_generation, owner_objectid, 1);
+
+       is_data = owner_objectid >= BTRFS_FIRST_FREE_OBJECTID;
+       BUG_ON(!is_data && refs_to_drop != 1);
+
+       ret = lookup_extent_backref(trans, extent_root, path, &iref,
+                                   bytenr, num_bytes, parent,
+                                   root_objectid, owner_objectid,
+                                   owner_offset);
        if (ret == 0) {
-               struct btrfs_key found_key;
                extent_slot = path->slots[0];
-               while (extent_slot > 0) {
-                       extent_slot--;
-                       btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+               while (extent_slot >= 0) {
+                       btrfs_item_key_to_cpu(path->nodes[0], &key,
                                              extent_slot);
-                       if (found_key.objectid != bytenr)
+                       if (key.objectid != bytenr)
                                break;
-                       if (found_key.type == BTRFS_EXTENT_ITEM_KEY &&
-                           found_key.offset == num_bytes) {
+                       if (key.type == BTRFS_EXTENT_ITEM_KEY &&
+                           key.offset == num_bytes) {
                                found_extent = 1;
                                break;
                        }
                        if (path->slots[0] - extent_slot > 5)
                                break;
+                       extent_slot--;
                }
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+               item_size = btrfs_item_size_nr(path->nodes[0], extent_slot);
+               if (found_extent && item_size < sizeof(*ei))
+                       found_extent = 0;
+#endif
                if (!found_extent) {
+                       BUG_ON(iref);
                        ret = remove_extent_backref(trans, extent_root, path,
-                                                   refs_to_drop);
+                                                   NULL, refs_to_drop,
+                                                   is_data);
                        BUG_ON(ret);
                        btrfs_release_path(extent_root, path);
                        path->leave_spinning = 1;
+
+                       key.objectid = bytenr;
+                       key.type = BTRFS_EXTENT_ITEM_KEY;
+                       key.offset = num_bytes;
+
                        ret = btrfs_search_slot(trans, extent_root,
                                                &key, path, -1, 1);
                        if (ret) {
@@ -2285,103 +4395,96 @@ static int __free_extent(struct btrfs_trans_handle *trans,
                btrfs_print_leaf(extent_root, path->nodes[0]);
                WARN_ON(1);
                printk(KERN_ERR "btrfs unable to find ref byte nr %llu "
-                      "parent %llu root %llu gen %llu owner %llu\n",
+                      "parent %llu root %llu  owner %llu offset %llu\n",
                       (unsigned long long)bytenr,
                       (unsigned long long)parent,
                       (unsigned long long)root_objectid,
-                      (unsigned long long)ref_generation,
-                      (unsigned long long)owner_objectid);
+                      (unsigned long long)owner_objectid,
+                      (unsigned long long)owner_offset);
        }
 
        leaf = path->nodes[0];
+       item_size = btrfs_item_size_nr(leaf, extent_slot);
+#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
+       if (item_size < sizeof(*ei)) {
+               BUG_ON(found_extent || extent_slot != path->slots[0]);
+               ret = convert_extent_item_v0(trans, extent_root, path,
+                                            owner_objectid, 0);
+               BUG_ON(ret < 0);
+
+               btrfs_release_path(extent_root, path);
+               path->leave_spinning = 1;
+
+               key.objectid = bytenr;
+               key.type = BTRFS_EXTENT_ITEM_KEY;
+               key.offset = num_bytes;
+
+               ret = btrfs_search_slot(trans, extent_root, &key, path,
+                                       -1, 1);
+               if (ret) {
+                       printk(KERN_ERR "umm, got %d back from search"
+                              ", was looking for %llu\n", ret,
+                              (unsigned long long)bytenr);
+                       btrfs_print_leaf(extent_root, path->nodes[0]);
+               }
+               BUG_ON(ret);
+               extent_slot = path->slots[0];
+               leaf = path->nodes[0];
+               item_size = btrfs_item_size_nr(leaf, extent_slot);
+       }
+#endif
+       BUG_ON(item_size < sizeof(*ei));
        ei = btrfs_item_ptr(leaf, extent_slot,
                            struct btrfs_extent_item);
-       refs = btrfs_extent_refs(leaf, ei);
-
-       /*
-        * we're not allowed to delete the extent item if there
-        * are other delayed ref updates pending
-        */
+       if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID) {
+               struct btrfs_tree_block_info *bi;
+               BUG_ON(item_size < sizeof(*ei) + sizeof(*bi));
+               bi = (struct btrfs_tree_block_info *)(ei + 1);
+               WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi));
+       }
 
+       refs = btrfs_extent_refs(leaf, ei);
        BUG_ON(refs < refs_to_drop);
        refs -= refs_to_drop;
-       btrfs_set_extent_refs(leaf, ei, refs);
-       btrfs_mark_buffer_dirty(leaf);
 
-       if (refs == 0 && found_extent &&
-           path->slots[0] == extent_slot + 1) {
-               struct btrfs_extent_ref *ref;
-               ref = btrfs_item_ptr(leaf, path->slots[0],
-                                    struct btrfs_extent_ref);
-               BUG_ON(btrfs_ref_num_refs(leaf, ref) != refs_to_drop);
-               /* if the back ref and the extent are next to each other
-                * they get deleted below in one shot
+       if (refs > 0) {
+               if (extent_op)
+                       __run_delayed_extent_op(extent_op, leaf, ei);
+               /*
+                * In the case of inline back ref, reference count will
+                * be updated by remove_extent_backref
                 */
-               path->slots[0] = extent_slot;
-               num_to_del = 2;
-       } else if (found_extent) {
-               /* otherwise delete the extent back ref */
-               ret = remove_extent_backref(trans, extent_root, path,
-                                           refs_to_drop);
-               BUG_ON(ret);
-               /* if refs are 0, we need to setup the path for deletion */
-               if (refs == 0) {
-                       btrfs_release_path(extent_root, path);
-                       path->leave_spinning = 1;
-                       ret = btrfs_search_slot(trans, extent_root, &key, path,
-                                               -1, 1);
+               if (iref) {
+                       BUG_ON(!found_extent);
+               } else {
+                       btrfs_set_extent_refs(leaf, ei, refs);
+                       btrfs_mark_buffer_dirty(leaf);
+               }
+               if (found_extent) {
+                       ret = remove_extent_backref(trans, extent_root, path,
+                                                   iref, refs_to_drop,
+                                                   is_data);
                        BUG_ON(ret);
                }
-       }
-
-       if (refs == 0) {
-               u64 super_used;
-               u64 root_used;
-               struct extent_buffer *must_clean = NULL;
-
-               if (pin) {
-                       ret = pin_down_bytes(trans, root, path,
-                               bytenr, num_bytes,
-                               owner_objectid >= BTRFS_FIRST_FREE_OBJECTID,
-                               &must_clean);
-                       if (ret > 0)
-                               mark_free = 1;
-                       BUG_ON(ret < 0);
+       } else {
+               if (found_extent) {
+                       BUG_ON(is_data && refs_to_drop !=
+                              extent_data_ref_count(root, path, iref));
+                       if (iref) {
+                               BUG_ON(path->slots[0] != extent_slot);
+                       } else {
+                               BUG_ON(path->slots[0] != extent_slot + 1);
+                               path->slots[0] = extent_slot;
+                               num_to_del = 2;
+                       }
                }
 
-               /* block accounting for super block */
-               spin_lock(&info->delalloc_lock);
-               super_used = btrfs_super_bytes_used(&info->super_copy);
-               btrfs_set_super_bytes_used(&info->super_copy,
-                                          super_used - num_bytes);
-
-               /* block accounting for root item */
-               root_used = btrfs_root_used(&root->root_item);
-               btrfs_set_root_used(&root->root_item,
-                                          root_used - num_bytes);
-               spin_unlock(&info->delalloc_lock);
-
-               /*
-                * it is going to be very rare for someone to be waiting
-                * on the block we're freeing.  del_items might need to
-                * schedule, so rather than get fancy, just force it
-                * to blocking here
-                */
-               if (must_clean)
-                       btrfs_set_lock_blocking(must_clean);
-
                ret = btrfs_del_items(trans, extent_root, path, path->slots[0],
                                      num_to_del);
                BUG_ON(ret);
                btrfs_release_path(extent_root, path);
 
-               if (must_clean) {
-                       clean_tree_block(NULL, root, must_clean);
-                       btrfs_tree_unlock(must_clean);
-                       free_extent_buffer(must_clean);
-               }
-
-               if (owner_objectid >= BTRFS_FIRST_FREE_OBJECTID) {
+               if (is_data) {
                        ret = btrfs_del_csums(trans, root, bytenr, num_bytes);
                        BUG_ON(ret);
                } else {
@@ -2390,8 +4493,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
                             (bytenr + num_bytes - 1) >> PAGE_CACHE_SHIFT);
                }
 
-               ret = update_block_group(trans, root, bytenr, num_bytes, 0,
-                                        mark_free);
+               ret = update_block_group(trans, root, bytenr, num_bytes, 0);
                BUG_ON(ret);
        }
        btrfs_free_path(path);
@@ -2399,35 +4501,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
 }
 
 /*
- * remove an extent from the root, returns 0 on success
- */
-static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
-                                       struct btrfs_root *root,
-                                       u64 bytenr, u64 num_bytes, u64 parent,
-                                       u64 root_objectid, u64 ref_generation,
-                                       u64 owner_objectid, int pin,
-                                       int refs_to_drop)
-{
-       WARN_ON(num_bytes < root->sectorsize);
-
-       /*
-        * if metadata always pin
-        * if data pin when any transaction has committed this
-        */
-       if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID ||
-           ref_generation != trans->transid)
-               pin = 1;
-
-       if (ref_generation != trans->transid)
-               pin = 1;
-
-       return __free_extent(trans, root, bytenr, num_bytes, parent,
-                           root_objectid, ref_generation,
-                           owner_objectid, pin, pin == 0, refs_to_drop);
-}
-
-/*
- * when we free an extent, it is possible (and likely) that we free the last
+ * when we free an block, it is possible (and likely) that we free the last
  * delayed ref for that extent as well.  This searches the delayed ref tree for
  * a given extent, and if there are no other delayed refs to be processed, it
  * removes it from the tree.
@@ -2439,7 +4513,7 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
        struct btrfs_delayed_ref_root *delayed_refs;
        struct btrfs_delayed_ref_node *ref;
        struct rb_node *node;
-       int ret;
+       int ret = 0;
 
        delayed_refs = &trans->transaction->delayed_refs;
        spin_lock(&delayed_refs->lock);
@@ -2457,6 +4531,13 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
        if (ref->bytenr == bytenr)
                goto out;
 
+       if (head->extent_op) {
+               if (!head->must_insert_reserved)
+                       goto out;
+               kfree(head->extent_op);
+               head->extent_op = NULL;
+       }
+
        /*
         * waiting for the lock here would deadlock.  If someone else has it
         * locked they are already in the process of dropping it anyway
@@ -2484,46 +4565,125 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
        list_del_init(&head->cluster);
        spin_unlock(&delayed_refs->lock);
 
-       ret = run_one_delayed_ref(trans, root->fs_info->tree_root,
-                                 &head->node, head->must_insert_reserved);
-       BUG_ON(ret);
+       BUG_ON(head->extent_op);
+       if (head->must_insert_reserved)
+               ret = 1;
+
+       mutex_unlock(&head->mutex);
        btrfs_put_delayed_ref(&head->node);
-       return 0;
+       return ret;
 out:
        spin_unlock(&delayed_refs->lock);
        return 0;
 }
 
+void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *root,
+                          struct extent_buffer *buf,
+                          u64 parent, int last_ref)
+{
+       struct btrfs_block_rsv *block_rsv;
+       struct btrfs_block_group_cache *cache = NULL;
+       int ret;
+
+       if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) {
+               ret = btrfs_add_delayed_tree_ref(trans, buf->start, buf->len,
+                                               parent, root->root_key.objectid,
+                                               btrfs_header_level(buf),
+                                               BTRFS_DROP_DELAYED_REF, NULL);
+               BUG_ON(ret);
+       }
+
+       if (!last_ref)
+               return;
+
+       block_rsv = get_block_rsv(trans, root);
+       cache = btrfs_lookup_block_group(root->fs_info, buf->start);
+       if (block_rsv->space_info != cache->space_info)
+               goto out;
+
+       if (btrfs_header_generation(buf) == trans->transid) {
+               if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) {
+                       ret = check_ref_cleanup(trans, root, buf->start);
+                       if (!ret)
+                               goto pin;
+               }
+
+               if (btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) {
+                       pin_down_extent(root, cache, buf->start, buf->len, 1);
+                       goto pin;
+               }
+
+               WARN_ON(test_bit(EXTENT_BUFFER_DIRTY, &buf->bflags));
+
+               btrfs_add_free_space(cache, buf->start, buf->len);
+               ret = update_reserved_bytes(cache, buf->len, 0, 0);
+               if (ret == -EAGAIN) {
+                       /* block group became read-only */
+                       update_reserved_bytes(cache, buf->len, 0, 1);
+                       goto out;
+               }
+
+               ret = 1;
+               spin_lock(&block_rsv->lock);
+               if (block_rsv->reserved < block_rsv->size) {
+                       block_rsv->reserved += buf->len;
+                       ret = 0;
+               }
+               spin_unlock(&block_rsv->lock);
+
+               if (ret) {
+                       spin_lock(&cache->space_info->lock);
+                       cache->space_info->bytes_reserved -= buf->len;
+                       spin_unlock(&cache->space_info->lock);
+               }
+               goto out;
+       }
+pin:
+       if (block_rsv->durable && !cache->ro) {
+               ret = 0;
+               spin_lock(&cache->lock);
+               if (!cache->ro) {
+                       cache->reserved_pinned += buf->len;
+                       ret = 1;
+               }
+               spin_unlock(&cache->lock);
+
+               if (ret) {
+                       spin_lock(&block_rsv->lock);
+                       block_rsv->freed[trans->transid & 0x1] += buf->len;
+                       spin_unlock(&block_rsv->lock);
+               }
+       }
+out:
+       btrfs_put_block_group(cache);
+}
+
 int btrfs_free_extent(struct btrfs_trans_handle *trans,
                      struct btrfs_root *root,
                      u64 bytenr, u64 num_bytes, u64 parent,
-                     u64 root_objectid, u64 ref_generation,
-                     u64 owner_objectid, int pin)
+                     u64 root_objectid, u64 owner, u64 offset)
 {
        int ret;
 
        /*
         * tree log blocks never actually go into the extent allocation
         * tree, just update pinning info and exit early.
-        *
-        * data extents referenced by the tree log do need to have
-        * their reference counts bumped.
         */
-       if (root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID &&
-           owner_objectid < BTRFS_FIRST_FREE_OBJECTID) {
-               mutex_lock(&root->fs_info->pinned_mutex);
-
+       if (root_objectid == BTRFS_TREE_LOG_OBJECTID) {
+               WARN_ON(owner >= BTRFS_FIRST_FREE_OBJECTID);
                /* unlocks the pinned mutex */
-               btrfs_update_pinned_extents(root, bytenr, num_bytes, 1);
-               update_reserved_extents(root, bytenr, num_bytes, 0);
+               btrfs_pin_extent(root, bytenr, num_bytes, 1);
                ret = 0;
-       } else {
-               ret = btrfs_add_delayed_ref(trans, bytenr, num_bytes, parent,
-                                      root_objectid, ref_generation,
-                                      owner_objectid,
-                                      BTRFS_DROP_DELAYED_REF, 1);
+       } else if (owner < BTRFS_FIRST_FREE_OBJECTID) {
+               ret = btrfs_add_delayed_tree_ref(trans, bytenr, num_bytes,
+                                       parent, root_objectid, (int)owner,
+                                       BTRFS_DROP_DELAYED_REF, NULL);
                BUG_ON(ret);
-               ret = check_ref_cleanup(trans, root, bytenr);
+       } else {
+               ret = btrfs_add_delayed_data_ref(trans, bytenr, num_bytes,
+                                       parent, root_objectid, owner,
+                                       offset, BTRFS_DROP_DELAYED_REF, NULL);
                BUG_ON(ret);
        }
        return ret;
@@ -2536,6 +4696,75 @@ static u64 stripe_align(struct btrfs_root *root, u64 val)
        return ret;
 }
 
+/*
+ * when we wait for progress in the block group caching, its because
+ * our allocation attempt failed at least once.  So, we must sleep
+ * and let some progress happen before we try again.
+ *
+ * This function will sleep at least once waiting for new free space to
+ * show up, and then it will check the block group free space numbers
+ * for our min num_bytes.  Another option is to have it go ahead
+ * and look in the rbtree for a free extent of a given size, but this
+ * is a good start.
+ */
+static noinline int
+wait_block_group_cache_progress(struct btrfs_block_group_cache *cache,
+                               u64 num_bytes)
+{
+       struct btrfs_caching_control *caching_ctl;
+       DEFINE_WAIT(wait);
+
+       caching_ctl = get_caching_control(cache);
+       if (!caching_ctl)
+               return 0;
+
+       wait_event(caching_ctl->wait, block_group_cache_done(cache) ||
+                  (cache->free_space >= num_bytes));
+
+       put_caching_control(caching_ctl);
+       return 0;
+}
+
+static noinline int
+wait_block_group_cache_done(struct btrfs_block_group_cache *cache)
+{
+       struct btrfs_caching_control *caching_ctl;
+       DEFINE_WAIT(wait);
+
+       caching_ctl = get_caching_control(cache);
+       if (!caching_ctl)
+               return 0;
+
+       wait_event(caching_ctl->wait, block_group_cache_done(cache));
+
+       put_caching_control(caching_ctl);
+       return 0;
+}
+
+static int get_block_group_index(struct btrfs_block_group_cache *cache)
+{
+       int index;
+       if (cache->flags & BTRFS_BLOCK_GROUP_RAID10)
+               index = 0;
+       else if (cache->flags & BTRFS_BLOCK_GROUP_RAID1)
+               index = 1;
+       else if (cache->flags & BTRFS_BLOCK_GROUP_DUP)
+               index = 2;
+       else if (cache->flags & BTRFS_BLOCK_GROUP_RAID0)
+               index = 3;
+       else
+               index = 4;
+       return index;
+}
+
+enum btrfs_loop_type {
+       LOOP_FIND_IDEAL = 0,
+       LOOP_CACHING_NOWAIT = 1,
+       LOOP_CACHING_WAIT = 2,
+       LOOP_ALLOC_CHUNK = 3,
+       LOOP_NO_EMPTY_SIZE = 4,
+};
+
 /*
  * walks the btree of allocated extents and find a hole of a given size.
  * The key ins is changed to record the hole:
@@ -2549,233 +4778,415 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
                                     u64 num_bytes, u64 empty_size,
                                     u64 search_start, u64 search_end,
                                     u64 hint_byte, struct btrfs_key *ins,
-                                    u64 exclude_start, u64 exclude_nr,
                                     int data)
 {
        int ret = 0;
        struct btrfs_root *root = orig_root->fs_info->extent_root;
-       u64 total_needed = num_bytes;
-       u64 *last_ptr = NULL;
-       u64 last_wanted = 0;
+       struct btrfs_free_cluster *last_ptr = NULL;
        struct btrfs_block_group_cache *block_group = NULL;
-       int chunk_alloc_done = 0;
        int empty_cluster = 2 * 1024 * 1024;
        int allowed_chunk_alloc = 0;
-       struct list_head *head = NULL, *cur = NULL;
-       int loop = 0;
-       int extra_loop = 0;
+       int done_chunk_alloc = 0;
        struct btrfs_space_info *space_info;
+       int last_ptr_loop = 0;
+       int loop = 0;
+       int index = 0;
+       bool found_uncached_bg = false;
+       bool failed_cluster_refill = false;
+       bool failed_alloc = false;
+       bool use_cluster = true;
+       u64 ideal_cache_percent = 0;
+       u64 ideal_cache_offset = 0;
 
        WARN_ON(num_bytes < root->sectorsize);
        btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
        ins->objectid = 0;
        ins->offset = 0;
 
+       space_info = __find_space_info(root->fs_info, data);
+       if (!space_info) {
+               printk(KERN_ERR "No space info for %d\n", data);
+               return -ENOSPC;
+       }
+
+       /*
+        * If the space info is for both data and metadata it means we have a
+        * small filesystem and we can't use the clustering stuff.
+        */
+       if (btrfs_mixed_space_info(space_info))
+               use_cluster = false;
+
        if (orig_root->ref_cows || empty_size)
                allowed_chunk_alloc = 1;
 
-       if (data & BTRFS_BLOCK_GROUP_METADATA) {
-               last_ptr = &root->fs_info->last_alloc;
+       if (data & BTRFS_BLOCK_GROUP_METADATA && use_cluster) {
+               last_ptr = &root->fs_info->meta_alloc_cluster;
                if (!btrfs_test_opt(root, SSD))
                        empty_cluster = 64 * 1024;
        }
 
-       if ((data & BTRFS_BLOCK_GROUP_DATA) && btrfs_test_opt(root, SSD))
-               last_ptr = &root->fs_info->last_data_alloc;
+       if ((data & BTRFS_BLOCK_GROUP_DATA) && use_cluster &&
+           btrfs_test_opt(root, SSD)) {
+               last_ptr = &root->fs_info->data_alloc_cluster;
+       }
 
        if (last_ptr) {
-               if (*last_ptr) {
-                       hint_byte = *last_ptr;
-                       last_wanted = *last_ptr;
-               } else
-                       empty_size += empty_cluster;
-       } else {
-               empty_cluster = 0;
+               spin_lock(&last_ptr->lock);
+               if (last_ptr->block_group)
+                       hint_byte = last_ptr->window_start;
+               spin_unlock(&last_ptr->lock);
        }
+
        search_start = max(search_start, first_logical_byte(root, 0));
        search_start = max(search_start, hint_byte);
 
-       if (last_wanted && search_start != last_wanted) {
-               last_wanted = 0;
-               empty_size += empty_cluster;
-       }
-
-       total_needed += empty_size;
-       block_group = btrfs_lookup_block_group(root->fs_info, search_start);
-       if (!block_group)
-               block_group = btrfs_lookup_first_block_group(root->fs_info,
-                                                            search_start);
-       space_info = __find_space_info(root->fs_info, data);
+       if (!last_ptr)
+               empty_cluster = 0;
 
-       down_read(&space_info->groups_sem);
-       while (1) {
-               struct btrfs_free_space *free_space;
+       if (search_start == hint_byte) {
+ideal_cache:
+               block_group = btrfs_lookup_block_group(root->fs_info,
+                                                      search_start);
                /*
-                * the only way this happens if our hint points to a block
-                * group thats not of the proper type, while looping this
-                * should never happen
+                * we don't want to use the block group if it doesn't match our
+                * allocation bits, or if its not cached.
+                *
+                * However if we are re-searching with an ideal block group
+                * picked out then we don't care that the block group is cached.
                 */
-               if (empty_size)
-                       extra_loop = 1;
+               if (block_group && block_group_bits(block_group, data) &&
+                   (block_group->cached != BTRFS_CACHE_NO ||
+                    search_start == ideal_cache_offset)) {
+                       down_read(&space_info->groups_sem);
+                       if (list_empty(&block_group->list) ||
+                           block_group->ro) {
+                               /*
+                                * someone is removing this block group,
+                                * we can't jump into the have_block_group
+                                * target because our list pointers are not
+                                * valid
+                                */
+                               btrfs_put_block_group(block_group);
+                               up_read(&space_info->groups_sem);
+                       } else {
+                               index = get_block_group_index(block_group);
+                               goto have_block_group;
+                       }
+               } else if (block_group) {
+                       btrfs_put_block_group(block_group);
+               }
+       }
+search:
+       down_read(&space_info->groups_sem);
+       list_for_each_entry(block_group, &space_info->block_groups[index],
+                           list) {
+               u64 offset;
+               int cached;
 
-               if (!block_group)
-                       goto new_group_no_lock;
+               btrfs_get_block_group(block_group);
+               search_start = block_group->key.objectid;
 
-               if (unlikely(!block_group->cached)) {
-                       mutex_lock(&block_group->cache_mutex);
-                       ret = cache_block_group(root, block_group);
-                       mutex_unlock(&block_group->cache_mutex);
-                       if (ret)
-                               break;
+have_block_group:
+               if (unlikely(block_group->cached == BTRFS_CACHE_NO)) {
+                       u64 free_percent;
+
+                       ret = cache_block_group(block_group, trans, 1);
+                       if (block_group->cached == BTRFS_CACHE_FINISHED)
+                               goto have_block_group;
+
+                       free_percent = btrfs_block_group_used(&block_group->item);
+                       free_percent *= 100;
+                       free_percent = div64_u64(free_percent,
+                                                block_group->key.offset);
+                       free_percent = 100 - free_percent;
+                       if (free_percent > ideal_cache_percent &&
+                           likely(!block_group->ro)) {
+                               ideal_cache_offset = block_group->key.objectid;
+                               ideal_cache_percent = free_percent;
+                       }
+
+                       /*
+                        * We only want to start kthread caching if we are at
+                        * the point where we will wait for caching to make
+                        * progress, or if our ideal search is over and we've
+                        * found somebody to start caching.
+                        */
+                       if (loop > LOOP_CACHING_NOWAIT ||
+                           (loop > LOOP_FIND_IDEAL &&
+                            atomic_read(&space_info->caching_threads) < 2)) {
+                               ret = cache_block_group(block_group, trans, 0);
+                               BUG_ON(ret);
+                       }
+                       found_uncached_bg = true;
+
+                       /*
+                        * If loop is set for cached only, try the next block
+                        * group.
+                        */
+                       if (loop == LOOP_FIND_IDEAL)
+                               goto loop;
                }
 
-               mutex_lock(&block_group->alloc_mutex);
-               if (unlikely(!block_group_bits(block_group, data)))
-                       goto new_group;
+               cached = block_group_cache_done(block_group);
+               if (unlikely(!cached))
+                       found_uncached_bg = true;
 
                if (unlikely(block_group->ro))
-                       goto new_group;
+                       goto loop;
 
-               free_space = btrfs_find_free_space(block_group, search_start,
-                                                  total_needed);
-               if (free_space) {
-                       u64 start = block_group->key.objectid;
-                       u64 end = block_group->key.objectid +
-                               block_group->key.offset;
+               /*
+                * Ok we want to try and use the cluster allocator, so lets look
+                * there, unless we are on LOOP_NO_EMPTY_SIZE, since we will
+                * have tried the cluster allocator plenty of times at this
+                * point and not have found anything, so we are likely way too
+                * fragmented for the clustering stuff to find anything, so lets
+                * just skip it and let the allocator find whatever block it can
+                * find
+                */
+               if (last_ptr && loop < LOOP_NO_EMPTY_SIZE) {
+                       /*
+                        * the refill lock keeps out other
+                        * people trying to start a new cluster
+                        */
+                       spin_lock(&last_ptr->refill_lock);
+                       if (last_ptr->block_group &&
+                           (last_ptr->block_group->ro ||
+                           !block_group_bits(last_ptr->block_group, data))) {
+                               offset = 0;
+                               goto refill_cluster;
+                       }
 
-                       search_start = stripe_align(root, free_space->offset);
+                       offset = btrfs_alloc_from_cluster(block_group, last_ptr,
+                                                num_bytes, search_start);
+                       if (offset) {
+                               /* we have a block, we're done */
+                               spin_unlock(&last_ptr->refill_lock);
+                               goto checks;
+                       }
 
-                       /* move on to the next group */
-                       if (search_start + num_bytes >= search_end)
-                               goto new_group;
+                       spin_lock(&last_ptr->lock);
+                       /*
+                        * whoops, this cluster doesn't actually point to
+                        * this block group.  Get a ref on the block
+                        * group is does point to and try again
+                        */
+                       if (!last_ptr_loop && last_ptr->block_group &&
+                           last_ptr->block_group != block_group) {
 
-                       /* move on to the next group */
-                       if (search_start + num_bytes > end)
-                               goto new_group;
+                               btrfs_put_block_group(block_group);
+                               block_group = last_ptr->block_group;
+                               btrfs_get_block_group(block_group);
+                               spin_unlock(&last_ptr->lock);
+                               spin_unlock(&last_ptr->refill_lock);
 
-                       if (last_wanted && search_start != last_wanted) {
-                               total_needed += empty_cluster;
-                               empty_size += empty_cluster;
-                               last_wanted = 0;
+                               last_ptr_loop = 1;
+                               search_start = block_group->key.objectid;
                                /*
-                                * if search_start is still in this block group
-                                * then we just re-search this block group
+                                * we know this block group is properly
+                                * in the list because
+                                * btrfs_remove_block_group, drops the
+                                * cluster before it removes the block
+                                * group from the list
                                 */
-                               if (search_start >= start &&
-                                   search_start < end) {
-                                       mutex_unlock(&block_group->alloc_mutex);
-                                       continue;
-                               }
-
-                               /* else we go to the next block group */
-                               goto new_group;
+                               goto have_block_group;
                        }
+                       spin_unlock(&last_ptr->lock);
+refill_cluster:
+                       /*
+                        * this cluster didn't work out, free it and
+                        * start over
+                        */
+                       btrfs_return_cluster_to_free_space(NULL, last_ptr);
+
+                       last_ptr_loop = 0;
 
-                       if (exclude_nr > 0 &&
-                           (search_start + num_bytes > exclude_start &&
-                            search_start < exclude_start + exclude_nr)) {
-                               search_start = exclude_start + exclude_nr;
+                       /* allocate a cluster in this block group */
+                       ret = btrfs_find_space_cluster(trans, root,
+                                              block_group, last_ptr,
+                                              offset, num_bytes,
+                                              empty_cluster + empty_size);
+                       if (ret == 0) {
                                /*
-                                * if search_start is still in this block group
-                                * then we just re-search this block group
+                                * now pull our allocation out of this
+                                * cluster
                                 */
-                               if (search_start >= start &&
-                                   search_start < end) {
-                                       mutex_unlock(&block_group->alloc_mutex);
-                                       last_wanted = 0;
-                                       continue;
+                               offset = btrfs_alloc_from_cluster(block_group,
+                                                 last_ptr, num_bytes,
+                                                 search_start);
+                               if (offset) {
+                                       /* we found one, proceed */
+                                       spin_unlock(&last_ptr->refill_lock);
+                                       goto checks;
                                }
-
-                               /* else we go to the next block group */
-                               goto new_group;
+                       } else if (!cached && loop > LOOP_CACHING_NOWAIT
+                                  && !failed_cluster_refill) {
+                               spin_unlock(&last_ptr->refill_lock);
+
+                               failed_cluster_refill = true;
+                               wait_block_group_cache_progress(block_group,
+                                      num_bytes + empty_cluster + empty_size);
+                               goto have_block_group;
                        }
 
-                       ins->objectid = search_start;
-                       ins->offset = num_bytes;
-
-                       btrfs_remove_free_space_lock(block_group, search_start,
-                                                    num_bytes);
-                       /* we are all good, lets return */
-                       mutex_unlock(&block_group->alloc_mutex);
-                       break;
+                       /*
+                        * at this point we either didn't find a cluster
+                        * or we weren't able to allocate a block from our
+                        * cluster.  Free the cluster we've been trying
+                        * to use, and go to the next block group
+                        */
+                       btrfs_return_cluster_to_free_space(NULL, last_ptr);
+                       spin_unlock(&last_ptr->refill_lock);
+                       goto loop;
                }
-new_group:
-               mutex_unlock(&block_group->alloc_mutex);
-               put_block_group(block_group);
-               block_group = NULL;
-new_group_no_lock:
-               /* don't try to compare new allocations against the
-                * last allocation any more
-                */
-               last_wanted = 0;
 
+               offset = btrfs_find_space_for_alloc(block_group, search_start,
+                                                   num_bytes, empty_size);
                /*
-                * Here's how this works.
-                * loop == 0: we were searching a block group via a hint
-                *              and didn't find anything, so we start at
-                *              the head of the block groups and keep searching
-                * loop == 1: we're searching through all of the block groups
-                *              if we hit the head again we have searched
-                *              all of the block groups for this space and we
-                *              need to try and allocate, if we cant error out.
-                * loop == 2: we allocated more space and are looping through
-                *              all of the block groups again.
+                * If we didn't find a chunk, and we haven't failed on this
+                * block group before, and this block group is in the middle of
+                * caching and we are ok with waiting, then go ahead and wait
+                * for progress to be made, and set failed_alloc to true.
+                *
+                * If failed_alloc is true then we've already waited on this
+                * block group once and should move on to the next block group.
                 */
-               if (loop == 0) {
-                       head = &space_info->block_groups;
-                       cur = head->next;
+               if (!offset && !failed_alloc && !cached &&
+                   loop > LOOP_CACHING_NOWAIT) {
+                       wait_block_group_cache_progress(block_group,
+                                               num_bytes + empty_size);
+                       failed_alloc = true;
+                       goto have_block_group;
+               } else if (!offset) {
+                       goto loop;
+               }
+checks:
+               search_start = stripe_align(root, offset);
+               /* move on to the next group */
+               if (search_start + num_bytes >= search_end) {
+                       btrfs_add_free_space(block_group, offset, num_bytes);
+                       goto loop;
+               }
+
+               /* move on to the next group */
+               if (search_start + num_bytes >
+                   block_group->key.objectid + block_group->key.offset) {
+                       btrfs_add_free_space(block_group, offset, num_bytes);
+                       goto loop;
+               }
+
+               ins->objectid = search_start;
+               ins->offset = num_bytes;
+
+               if (offset < search_start)
+                       btrfs_add_free_space(block_group, offset,
+                                            search_start - offset);
+               BUG_ON(offset > search_start);
+
+               ret = update_reserved_bytes(block_group, num_bytes, 1,
+                                           (data & BTRFS_BLOCK_GROUP_DATA));
+               if (ret == -EAGAIN) {
+                       btrfs_add_free_space(block_group, offset, num_bytes);
+                       goto loop;
+               }
+
+               /* we are all good, lets return */
+               ins->objectid = search_start;
+               ins->offset = num_bytes;
+
+               if (offset < search_start)
+                       btrfs_add_free_space(block_group, offset,
+                                            search_start - offset);
+               BUG_ON(offset > search_start);
+               break;
+loop:
+               failed_cluster_refill = false;
+               failed_alloc = false;
+               BUG_ON(index != get_block_group_index(block_group));
+               btrfs_put_block_group(block_group);
+       }
+       up_read(&space_info->groups_sem);
+
+       if (!ins->objectid && ++index < BTRFS_NR_RAID_TYPES)
+               goto search;
+
+       /* LOOP_FIND_IDEAL, only search caching/cached bg's, and don't wait for
+        *                      for them to make caching progress.  Also
+        *                      determine the best possible bg to cache
+        * LOOP_CACHING_NOWAIT, search partially cached block groups, kicking
+        *                      caching kthreads as we move along
+        * LOOP_CACHING_WAIT, search everything, and wait if our bg is caching
+        * LOOP_ALLOC_CHUNK, force a chunk allocation and try again
+        * LOOP_NO_EMPTY_SIZE, set empty_size and empty_cluster to 0 and try
+        *                      again
+        */
+       if (!ins->objectid && loop < LOOP_NO_EMPTY_SIZE &&
+           (found_uncached_bg || empty_size || empty_cluster ||
+            allowed_chunk_alloc)) {
+               index = 0;
+               if (loop == LOOP_FIND_IDEAL && found_uncached_bg) {
+                       found_uncached_bg = false;
                        loop++;
-               } else if (loop == 1 && cur == head) {
-                       int keep_going;
+                       if (!ideal_cache_percent &&
+                           atomic_read(&space_info->caching_threads))
+                               goto search;
 
-                       /* at this point we give up on the empty_size
-                        * allocations and just try to allocate the min
-                        * space.
+                       /*
+                        * 1 of the following 2 things have happened so far
                         *
-                        * The extra_loop field was set if an empty_size
-                        * allocation was attempted above, and if this
-                        * is try we need to try the loop again without
-                        * the additional empty_size.
+                        * 1) We found an ideal block group for caching that
+                        * is mostly full and will cache quickly, so we might
+                        * as well wait for it.
+                        *
+                        * 2) We searched for cached only and we didn't find
+                        * anything, and we didn't start any caching kthreads
+                        * either, so chances are we will loop through and
+                        * start a couple caching kthreads, and then come back
+                        * around and just wait for them.  This will be slower
+                        * because we will have 2 caching kthreads reading at
+                        * the same time when we could have just started one
+                        * and waited for it to get far enough to give us an
+                        * allocation, so go ahead and go to the wait caching
+                        * loop.
                         */
-                       total_needed -= empty_size;
-                       empty_size = 0;
-                       keep_going = extra_loop;
+                       loop = LOOP_CACHING_WAIT;
+                       search_start = ideal_cache_offset;
+                       ideal_cache_percent = 0;
+                       goto ideal_cache;
+               } else if (loop == LOOP_FIND_IDEAL) {
+                       /*
+                        * Didn't find a uncached bg, wait on anything we find
+                        * next.
+                        */
+                       loop = LOOP_CACHING_WAIT;
+                       goto search;
+               }
+
+               if (loop < LOOP_CACHING_WAIT) {
                        loop++;
+                       goto search;
+               }
 
-                       if (allowed_chunk_alloc && !chunk_alloc_done) {
-                               up_read(&space_info->groups_sem);
-                               ret = do_chunk_alloc(trans, root, num_bytes +
-                                                    2 * 1024 * 1024, data, 1);
-                               down_read(&space_info->groups_sem);
-                               if (ret < 0)
-                                       goto loop_check;
-                               head = &space_info->block_groups;
-                               /*
-                                * we've allocated a new chunk, keep
-                                * trying
-                                */
-                               keep_going = 1;
-                               chunk_alloc_done = 1;
-                       } else if (!allowed_chunk_alloc) {
-                               space_info->force_alloc = 1;
-                       }
-loop_check:
-                       if (keep_going) {
-                               cur = head->next;
-                               extra_loop = 0;
-                       } else {
-                               break;
-                       }
-               } else if (cur == head) {
-                       break;
+               if (loop == LOOP_ALLOC_CHUNK) {
+                       empty_size = 0;
+                       empty_cluster = 0;
                }
 
-               block_group = list_entry(cur, struct btrfs_block_group_cache,
-                                        list);
-               atomic_inc(&block_group->count);
+               if (allowed_chunk_alloc) {
+                       ret = do_chunk_alloc(trans, root, num_bytes +
+                                            2 * 1024 * 1024, data, 1);
+                       allowed_chunk_alloc = 0;
+                       done_chunk_alloc = 1;
+               } else if (!done_chunk_alloc) {
+                       space_info->force_alloc = 1;
+               }
 
-               search_start = block_group->key.objectid;
-               cur = cur->next;
+               if (loop < LOOP_NO_EMPTY_SIZE) {
+                       loop++;
+                       goto search;
+               }
+               ret = -ENOSPC;
+       } else if (!ins->objectid) {
+               ret = -ENOSPC;
        }
 
        /* we found what we needed */
@@ -2783,39 +5194,41 @@ loop_check:
                if (!(data & BTRFS_BLOCK_GROUP_DATA))
                        trans->block_group = block_group->key.objectid;
 
-               if (last_ptr)
-                       *last_ptr = ins->objectid + ins->offset;
+               btrfs_put_block_group(block_group);
                ret = 0;
-       } else if (!ret) {
-               printk(KERN_ERR "btrfs searching for %llu bytes, "
-                      "num_bytes %llu, loop %d, allowed_alloc %d\n",
-                      (unsigned long long)total_needed,
-                      (unsigned long long)num_bytes,
-                      loop, allowed_chunk_alloc);
-               ret = -ENOSPC;
        }
-       if (block_group)
-               put_block_group(block_group);
 
-       up_read(&space_info->groups_sem);
        return ret;
 }
 
-static void dump_space_info(struct btrfs_space_info *info, u64 bytes)
+static void dump_space_info(struct btrfs_space_info *info, u64 bytes,
+                           int dump_block_groups)
 {
        struct btrfs_block_group_cache *cache;
+       int index = 0;
 
+       spin_lock(&info->lock);
        printk(KERN_INFO "space_info has %llu free, is %sfull\n",
               (unsigned long long)(info->total_bytes - info->bytes_used -
-                                   info->bytes_pinned - info->bytes_reserved),
+                                   info->bytes_pinned - info->bytes_reserved -
+                                   info->bytes_readonly),
               (info->full) ? "" : "not ");
-       printk(KERN_INFO "space_info total=%llu, pinned=%llu, delalloc=%llu,"
-              " may_use=%llu, used=%llu\n", info->total_bytes,
-              info->bytes_pinned, info->bytes_delalloc, info->bytes_may_use,
-              info->bytes_used);
+       printk(KERN_INFO "space_info total=%llu, used=%llu, pinned=%llu, "
+              "reserved=%llu, may_use=%llu, readonly=%llu\n",
+              (unsigned long long)info->total_bytes,
+              (unsigned long long)info->bytes_used,
+              (unsigned long long)info->bytes_pinned,
+              (unsigned long long)info->bytes_reserved,
+              (unsigned long long)info->bytes_may_use,
+              (unsigned long long)info->bytes_readonly);
+       spin_unlock(&info->lock);
+
+       if (!dump_block_groups)
+               return;
 
        down_read(&info->groups_sem);
-       list_for_each_entry(cache, &info->block_groups, list) {
+again:
+       list_for_each_entry(cache, &info->block_groups[index], list) {
                spin_lock(&cache->lock);
                printk(KERN_INFO "block group %llu has %llu bytes, %llu used "
                       "%llu pinned %llu reserved\n",
@@ -2827,19 +5240,20 @@ static void dump_space_info(struct btrfs_space_info *info, u64 bytes)
                btrfs_dump_free_space(cache, bytes);
                spin_unlock(&cache->lock);
        }
+       if (++index < BTRFS_NR_RAID_TYPES)
+               goto again;
        up_read(&info->groups_sem);
 }
 
-static int __btrfs_reserve_extent(struct btrfs_trans_handle *trans,
-                                 struct btrfs_root *root,
-                                 u64 num_bytes, u64 min_alloc_size,
-                                 u64 empty_size, u64 hint_byte,
-                                 u64 search_end, struct btrfs_key *ins,
-                                 u64 data)
+int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root,
+                        u64 num_bytes, u64 min_alloc_size,
+                        u64 empty_size, u64 hint_byte,
+                        u64 search_end, struct btrfs_key *ins,
+                        u64 data)
 {
        int ret;
        u64 search_start = 0;
-       struct btrfs_fs_info *info = root->fs_info;
 
        data = btrfs_get_alloc_profile(root, data);
 again:
@@ -2847,23 +5261,14 @@ again:
         * the only place that sets empty_size is btrfs_realloc_node, which
         * is not called recursively on allocations
         */
-       if (empty_size || 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), 0);
-               }
+       if (empty_size || root->ref_cows)
                ret = do_chunk_alloc(trans, root->fs_info->extent_root,
                                     num_bytes + 2 * 1024 * 1024, data, 0);
-       }
 
        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);
+                              search_start, search_end, hint_byte,
+                              ins, data);
 
        if (ret == -ENOSPC && num_bytes > min_alloc_size) {
                num_bytes = num_bytes >> 1;
@@ -2873,15 +5278,14 @@ again:
                               num_bytes, data, 1);
                goto again;
        }
-       if (ret) {
+       if (ret == -ENOSPC) {
                struct btrfs_space_info *sinfo;
 
                sinfo = __find_space_info(root->fs_info, data);
                printk(KERN_ERR "btrfs allocation failed flags %llu, "
                       "wanted %llu\n", (unsigned long long)data,
                       (unsigned long long)num_bytes);
-               dump_space_info(sinfo, num_bytes);
-               BUG();
+               dump_space_info(sinfo, num_bytes, 1);
        }
 
        return ret;
@@ -2902,120 +5306,151 @@ int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len)
        ret = btrfs_discard_extent(root, start, len);
 
        btrfs_add_free_space(cache, start, len);
-       put_block_group(cache);
-       update_reserved_extents(root, start, len, 0);
+       update_reserved_bytes(cache, len, 0, 1);
+       btrfs_put_block_group(cache);
+
+       return ret;
+}
+
+static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
+                                     struct btrfs_root *root,
+                                     u64 parent, u64 root_objectid,
+                                     u64 flags, u64 owner, u64 offset,
+                                     struct btrfs_key *ins, int ref_mod)
+{
+       int ret;
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       struct btrfs_extent_item *extent_item;
+       struct btrfs_extent_inline_ref *iref;
+       struct btrfs_path *path;
+       struct extent_buffer *leaf;
+       int type;
+       u32 size;
+
+       if (parent > 0)
+               type = BTRFS_SHARED_DATA_REF_KEY;
+       else
+               type = BTRFS_EXTENT_DATA_REF_KEY;
+
+       size = sizeof(*extent_item) + btrfs_extent_inline_ref_size(type);
+
+       path = btrfs_alloc_path();
+       BUG_ON(!path);
+
+       path->leave_spinning = 1;
+       ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
+                                     ins, size);
+       BUG_ON(ret);
+
+       leaf = path->nodes[0];
+       extent_item = btrfs_item_ptr(leaf, path->slots[0],
+                                    struct btrfs_extent_item);
+       btrfs_set_extent_refs(leaf, extent_item, ref_mod);
+       btrfs_set_extent_generation(leaf, extent_item, trans->transid);
+       btrfs_set_extent_flags(leaf, extent_item,
+                              flags | BTRFS_EXTENT_FLAG_DATA);
+
+       iref = (struct btrfs_extent_inline_ref *)(extent_item + 1);
+       btrfs_set_extent_inline_ref_type(leaf, iref, type);
+       if (parent > 0) {
+               struct btrfs_shared_data_ref *ref;
+               ref = (struct btrfs_shared_data_ref *)(iref + 1);
+               btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
+               btrfs_set_shared_data_ref_count(leaf, ref, ref_mod);
+       } else {
+               struct btrfs_extent_data_ref *ref;
+               ref = (struct btrfs_extent_data_ref *)(&iref->offset);
+               btrfs_set_extent_data_ref_root(leaf, ref, root_objectid);
+               btrfs_set_extent_data_ref_objectid(leaf, ref, owner);
+               btrfs_set_extent_data_ref_offset(leaf, ref, offset);
+               btrfs_set_extent_data_ref_count(leaf, ref, ref_mod);
+       }
 
-       return ret;
-}
+       btrfs_mark_buffer_dirty(path->nodes[0]);
+       btrfs_free_path(path);
 
-int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
-                                 struct btrfs_root *root,
-                                 u64 num_bytes, u64 min_alloc_size,
-                                 u64 empty_size, u64 hint_byte,
-                                 u64 search_end, struct btrfs_key *ins,
-                                 u64 data)
-{
-       int ret;
-       ret = __btrfs_reserve_extent(trans, root, num_bytes, min_alloc_size,
-                                    empty_size, hint_byte, search_end, ins,
-                                    data);
-       update_reserved_extents(root, ins->objectid, ins->offset, 1);
+       ret = update_block_group(trans, root, ins->objectid, ins->offset, 1);
+       if (ret) {
+               printk(KERN_ERR "btrfs update block group failed for %llu "
+                      "%llu\n", (unsigned long long)ins->objectid,
+                      (unsigned long long)ins->offset);
+               BUG();
+       }
        return ret;
 }
 
-static int __btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
-                                        struct btrfs_root *root, u64 parent,
-                                        u64 root_objectid, u64 ref_generation,
-                                        u64 owner, struct btrfs_key *ins,
-                                        int ref_mod)
+static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
+                                    struct btrfs_root *root,
+                                    u64 parent, u64 root_objectid,
+                                    u64 flags, struct btrfs_disk_key *key,
+                                    int level, struct btrfs_key *ins)
 {
        int ret;
-       u64 super_used;
-       u64 root_used;
-       u64 num_bytes = ins->offset;
-       u32 sizes[2];
-       struct btrfs_fs_info *info = root->fs_info;
-       struct btrfs_root *extent_root = info->extent_root;
+       struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_extent_item *extent_item;
-       struct btrfs_extent_ref *ref;
+       struct btrfs_tree_block_info *block_info;
+       struct btrfs_extent_inline_ref *iref;
        struct btrfs_path *path;
-       struct btrfs_key keys[2];
-
-       if (parent == 0)
-               parent = ins->objectid;
-
-       /* block accounting for super block */
-       spin_lock(&info->delalloc_lock);
-       super_used = btrfs_super_bytes_used(&info->super_copy);
-       btrfs_set_super_bytes_used(&info->super_copy, super_used + num_bytes);
-
-       /* block accounting for root item */
-       root_used = btrfs_root_used(&root->root_item);
-       btrfs_set_root_used(&root->root_item, root_used + num_bytes);
-       spin_unlock(&info->delalloc_lock);
-
-       memcpy(&keys[0], ins, sizeof(*ins));
-       keys[1].objectid = ins->objectid;
-       keys[1].type = BTRFS_EXTENT_REF_KEY;
-       keys[1].offset = parent;
-       sizes[0] = sizeof(*extent_item);
-       sizes[1] = sizeof(*ref);
+       struct extent_buffer *leaf;
+       u32 size = sizeof(*extent_item) + sizeof(*block_info) + sizeof(*iref);
 
        path = btrfs_alloc_path();
        BUG_ON(!path);
 
        path->leave_spinning = 1;
-       ret = btrfs_insert_empty_items(trans, extent_root, path, keys,
-                                      sizes, 2);
+       ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
+                                     ins, size);
        BUG_ON(ret);
 
-       extent_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+       leaf = path->nodes[0];
+       extent_item = btrfs_item_ptr(leaf, path->slots[0],
                                     struct btrfs_extent_item);
-       btrfs_set_extent_refs(path->nodes[0], extent_item, ref_mod);
-       ref = btrfs_item_ptr(path->nodes[0], path->slots[0] + 1,
-                            struct btrfs_extent_ref);
-
-       btrfs_set_ref_root(path->nodes[0], ref, root_objectid);
-       btrfs_set_ref_generation(path->nodes[0], ref, ref_generation);
-       btrfs_set_ref_objectid(path->nodes[0], ref, owner);
-       btrfs_set_ref_num_refs(path->nodes[0], ref, ref_mod);
-
-       btrfs_mark_buffer_dirty(path->nodes[0]);
+       btrfs_set_extent_refs(leaf, extent_item, 1);
+       btrfs_set_extent_generation(leaf, extent_item, trans->transid);
+       btrfs_set_extent_flags(leaf, extent_item,
+                              flags | BTRFS_EXTENT_FLAG_TREE_BLOCK);
+       block_info = (struct btrfs_tree_block_info *)(extent_item + 1);
+
+       btrfs_set_tree_block_key(leaf, block_info, key);
+       btrfs_set_tree_block_level(leaf, block_info, level);
+
+       iref = (struct btrfs_extent_inline_ref *)(block_info + 1);
+       if (parent > 0) {
+               BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF));
+               btrfs_set_extent_inline_ref_type(leaf, iref,
+                                                BTRFS_SHARED_BLOCK_REF_KEY);
+               btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
+       } else {
+               btrfs_set_extent_inline_ref_type(leaf, iref,
+                                                BTRFS_TREE_BLOCK_REF_KEY);
+               btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
+       }
 
-       trans->alloc_exclude_start = 0;
-       trans->alloc_exclude_nr = 0;
+       btrfs_mark_buffer_dirty(leaf);
        btrfs_free_path(path);
 
-       if (ret)
-               goto out;
-
-       ret = update_block_group(trans, root, ins->objectid,
-                                ins->offset, 1, 0);
+       ret = update_block_group(trans, root, ins->objectid, ins->offset, 1);
        if (ret) {
                printk(KERN_ERR "btrfs update block group failed for %llu "
                       "%llu\n", (unsigned long long)ins->objectid,
                       (unsigned long long)ins->offset);
                BUG();
        }
-out:
        return ret;
 }
 
-int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
-                               struct btrfs_root *root, u64 parent,
-                               u64 root_objectid, u64 ref_generation,
-                               u64 owner, struct btrfs_key *ins)
+int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
+                                    struct btrfs_root *root,
+                                    u64 root_objectid, u64 owner,
+                                    u64 offset, struct btrfs_key *ins)
 {
        int ret;
 
-       if (root_objectid == BTRFS_TREE_LOG_OBJECTID)
-               return 0;
+       BUG_ON(root_objectid == BTRFS_TREE_LOG_OBJECTID);
 
-       ret = btrfs_add_delayed_ref(trans, ins->objectid,
-                                   ins->offset, parent, root_objectid,
-                                   ref_generation, owner,
-                                   BTRFS_ADD_DELAYED_EXTENT, 0);
-       BUG_ON(ret);
+       ret = btrfs_add_delayed_data_ref(trans, ins->objectid, ins->offset,
+                                        0, root_objectid, owner, offset,
+                                        BTRFS_ADD_DELAYED_EXTENT, NULL);
        return ret;
 }
 
@@ -3024,55 +5459,57 @@ int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
  * an extent has been allocated and makes sure to clear the free
  * space cache bits as well
  */
-int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans,
-                               struct btrfs_root *root, u64 parent,
-                               u64 root_objectid, u64 ref_generation,
-                               u64 owner, struct btrfs_key *ins)
+int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
+                                  struct btrfs_root *root,
+                                  u64 root_objectid, u64 owner, u64 offset,
+                                  struct btrfs_key *ins)
 {
        int ret;
        struct btrfs_block_group_cache *block_group;
+       struct btrfs_caching_control *caching_ctl;
+       u64 start = ins->objectid;
+       u64 num_bytes = ins->offset;
 
        block_group = btrfs_lookup_block_group(root->fs_info, ins->objectid);
-       mutex_lock(&block_group->cache_mutex);
-       cache_block_group(root, block_group);
-       mutex_unlock(&block_group->cache_mutex);
-
-       ret = btrfs_remove_free_space(block_group, ins->objectid,
-                                     ins->offset);
-       BUG_ON(ret);
-       put_block_group(block_group);
-       ret = __btrfs_alloc_reserved_extent(trans, root, parent, root_objectid,
-                                           ref_generation, owner, ins, 1);
-       return ret;
-}
+       cache_block_group(block_group, trans, 0);
+       caching_ctl = get_caching_control(block_group);
 
-/*
- * finds a free extent and does all the dirty work required for allocation
- * returns the key for the extent through ins, and a tree buffer for
- * the first block of the extent through buf.
- *
- * returns 0 if everything worked, non-zero otherwise.
- */
-int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
-                      struct btrfs_root *root,
-                      u64 num_bytes, u64 parent, u64 min_alloc_size,
-                      u64 root_objectid, u64 ref_generation,
-                      u64 owner_objectid, u64 empty_size, u64 hint_byte,
-                      u64 search_end, struct btrfs_key *ins, u64 data)
-{
-       int ret;
-       ret = __btrfs_reserve_extent(trans, root, num_bytes,
-                                    min_alloc_size, empty_size, hint_byte,
-                                    search_end, ins, data);
-       BUG_ON(ret);
-       if (root_objectid != BTRFS_TREE_LOG_OBJECTID) {
-               ret = btrfs_add_delayed_ref(trans, ins->objectid,
-                                           ins->offset, parent, root_objectid,
-                                           ref_generation, owner_objectid,
-                                           BTRFS_ADD_DELAYED_EXTENT, 0);
+       if (!caching_ctl) {
+               BUG_ON(!block_group_cache_done(block_group));
+               ret = btrfs_remove_free_space(block_group, start, num_bytes);
                BUG_ON(ret);
+       } else {
+               mutex_lock(&caching_ctl->mutex);
+
+               if (start >= caching_ctl->progress) {
+                       ret = add_excluded_extent(root, start, num_bytes);
+                       BUG_ON(ret);
+               } else if (start + num_bytes <= caching_ctl->progress) {
+                       ret = btrfs_remove_free_space(block_group,
+                                                     start, num_bytes);
+                       BUG_ON(ret);
+               } else {
+                       num_bytes = caching_ctl->progress - start;
+                       ret = btrfs_remove_free_space(block_group,
+                                                     start, num_bytes);
+                       BUG_ON(ret);
+
+                       start = caching_ctl->progress;
+                       num_bytes = ins->objectid + ins->offset -
+                                   caching_ctl->progress;
+                       ret = add_excluded_extent(root, start, num_bytes);
+                       BUG_ON(ret);
+               }
+
+               mutex_unlock(&caching_ctl->mutex);
+               put_caching_control(caching_ctl);
        }
-       update_reserved_extents(root, ins->objectid, ins->offset, 1);
+
+       ret = update_reserved_bytes(block_group, ins->offset, 1, 1);
+       BUG_ON(ret);
+       btrfs_put_block_group(block_group);
+       ret = alloc_reserved_file_extent(trans, root, 0, root_objectid,
+                                        0, owner, offset, ins, 1);
        return ret;
 }
 
@@ -3095,8 +5532,16 @@ struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans,
        btrfs_set_buffer_uptodate(buf);
 
        if (root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID) {
-               set_extent_dirty(&root->dirty_log_pages, buf->start,
-                        buf->start + buf->len - 1, GFP_NOFS);
+               /*
+                * we allow two log transactions at a time, use different
+                * EXENT bit to differentiate dirty pages.
+                */
+               if (root->log_transid % 2 == 0)
+                       set_extent_dirty(&root->dirty_log_pages, buf->start,
+                                       buf->start + buf->len - 1, GFP_NOFS);
+               else
+                       set_extent_new(&root->dirty_log_pages, buf->start,
+                                       buf->start + buf->len - 1, GFP_NOFS);
        } else {
                set_extent_dirty(&trans->transaction->dirty_pages, buf->start,
                         buf->start + buf->len - 1, GFP_NOFS);
@@ -3106,772 +5551,762 @@ struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans,
        return buf;
 }
 
+static struct btrfs_block_rsv *
+use_block_rsv(struct btrfs_trans_handle *trans,
+             struct btrfs_root *root, u32 blocksize)
+{
+       struct btrfs_block_rsv *block_rsv;
+       int ret;
+
+       block_rsv = get_block_rsv(trans, root);
+
+       if (block_rsv->size == 0) {
+               ret = reserve_metadata_bytes(block_rsv, blocksize);
+               if (ret)
+                       return ERR_PTR(ret);
+               return block_rsv;
+       }
+
+       ret = block_rsv_use_bytes(block_rsv, blocksize);
+       if (!ret)
+               return block_rsv;
+
+       WARN_ON(1);
+       printk(KERN_INFO"block_rsv size %llu reserved %llu freed %llu %llu\n",
+               block_rsv->size, block_rsv->reserved,
+               block_rsv->freed[0], block_rsv->freed[1]);
+
+       return ERR_PTR(-ENOSPC);
+}
+
+static void unuse_block_rsv(struct btrfs_block_rsv *block_rsv, u32 blocksize)
+{
+       block_rsv_add_bytes(block_rsv, blocksize, 0);
+       block_rsv_release_bytes(block_rsv, NULL, 0);
+}
+
 /*
- * helper function to allocate a block for a given tree
+ * finds a free extent and does all the dirty work required for allocation
+ * returns the key for the extent through ins, and a tree buffer for
+ * the first block of the extent through buf.
+ *
  * returns the tree buffer or NULL.
  */
 struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
-                                            struct btrfs_root *root,
-                                            u32 blocksize, u64 parent,
-                                            u64 root_objectid,
-                                            u64 ref_generation,
-                                            int level,
-                                            u64 hint,
-                                            u64 empty_size)
+                                       struct btrfs_root *root, u32 blocksize,
+                                       u64 parent, u64 root_objectid,
+                                       struct btrfs_disk_key *key, int level,
+                                       u64 hint, u64 empty_size)
 {
        struct btrfs_key ins;
-       int ret;
+       struct btrfs_block_rsv *block_rsv;
        struct extent_buffer *buf;
+       u64 flags = 0;
+       int ret;
+
 
-       ret = btrfs_alloc_extent(trans, root, blocksize, parent, blocksize,
-                                root_objectid, ref_generation, level,
-                                empty_size, hint, (u64)-1, &ins, 0);
+       block_rsv = use_block_rsv(trans, root, blocksize);
+       if (IS_ERR(block_rsv))
+               return ERR_CAST(block_rsv);
+
+       ret = btrfs_reserve_extent(trans, root, blocksize, blocksize,
+                                  empty_size, hint, (u64)-1, &ins, 0);
        if (ret) {
-               BUG_ON(ret > 0);
+               unuse_block_rsv(block_rsv, blocksize);
                return ERR_PTR(ret);
        }
 
        buf = btrfs_init_new_buffer(trans, root, ins.objectid,
                                    blocksize, level);
+       BUG_ON(IS_ERR(buf));
+
+       if (root_objectid == BTRFS_TREE_RELOC_OBJECTID) {
+               if (parent == 0)
+                       parent = ins.objectid;
+               flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
+       } else
+               BUG_ON(parent > 0);
+
+       if (root_objectid != BTRFS_TREE_LOG_OBJECTID) {
+               struct btrfs_delayed_extent_op *extent_op;
+               extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS);
+               BUG_ON(!extent_op);
+               if (key)
+                       memcpy(&extent_op->key, key, sizeof(extent_op->key));
+               else
+                       memset(&extent_op->key, 0, sizeof(extent_op->key));
+               extent_op->flags_to_set = flags;
+               extent_op->update_key = 1;
+               extent_op->update_flags = 1;
+               extent_op->is_data = 0;
+
+               ret = btrfs_add_delayed_tree_ref(trans, ins.objectid,
+                                       ins.offset, parent, root_objectid,
+                                       level, BTRFS_ADD_DELAYED_EXTENT,
+                                       extent_op);
+               BUG_ON(ret);
+       }
        return buf;
 }
 
-int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
-                       struct btrfs_root *root, struct extent_buffer *leaf)
+struct walk_control {
+       u64 refs[BTRFS_MAX_LEVEL];
+       u64 flags[BTRFS_MAX_LEVEL];
+       struct btrfs_key update_progress;
+       int stage;
+       int level;
+       int shared_level;
+       int update_ref;
+       int keep_locks;
+       int reada_slot;
+       int reada_count;
+};
+
+#define DROP_REFERENCE 1
+#define UPDATE_BACKREF 2
+
+static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
+                                    struct btrfs_root *root,
+                                    struct walk_control *wc,
+                                    struct btrfs_path *path)
 {
-       u64 leaf_owner;
-       u64 leaf_generation;
-       struct refsort *sorted;
+       u64 bytenr;
+       u64 generation;
+       u64 refs;
+       u64 flags;
+       u64 last = 0;
+       u32 nritems;
+       u32 blocksize;
        struct btrfs_key key;
-       struct btrfs_file_extent_item *fi;
-       int i;
-       int nritems;
+       struct extent_buffer *eb;
        int ret;
-       int refi = 0;
        int slot;
+       int nread = 0;
 
-       BUG_ON(!btrfs_is_leaf(leaf));
-       nritems = btrfs_header_nritems(leaf);
-       leaf_owner = btrfs_header_owner(leaf);
-       leaf_generation = btrfs_header_generation(leaf);
-
-       sorted = kmalloc(sizeof(*sorted) * nritems, GFP_NOFS);
-       /* we do this loop twice.  The first time we build a list
-        * of the extents we have a reference on, then we sort the list
-        * by bytenr.  The second time around we actually do the
-        * extent freeing.
-        */
-       for (i = 0; i < nritems; i++) {
-               u64 disk_bytenr;
-               cond_resched();
-
-               btrfs_item_key_to_cpu(leaf, &key, i);
-
-               /* only extents have references, skip everything else */
-               if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
-                       continue;
-
-               fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
-
-               /* inline extents live in the btree, they don't have refs */
-               if (btrfs_file_extent_type(leaf, fi) ==
-                   BTRFS_FILE_EXTENT_INLINE)
-                       continue;
-
-               disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
-
-               /* holes don't have refs */
-               if (disk_bytenr == 0)
-                       continue;
-
-               sorted[refi].bytenr = disk_bytenr;
-               sorted[refi].slot = i;
-               refi++;
+       if (path->slots[wc->level] < wc->reada_slot) {
+               wc->reada_count = wc->reada_count * 2 / 3;
+               wc->reada_count = max(wc->reada_count, 2);
+       } else {
+               wc->reada_count = wc->reada_count * 3 / 2;
+               wc->reada_count = min_t(int, wc->reada_count,
+                                       BTRFS_NODEPTRS_PER_BLOCK(root));
        }
 
-       if (refi == 0)
-               goto out;
-
-       sort(sorted, refi, sizeof(struct refsort), refsort_cmp, NULL);
-
-       for (i = 0; i < refi; i++) {
-               u64 disk_bytenr;
+       eb = path->nodes[wc->level];
+       nritems = btrfs_header_nritems(eb);
+       blocksize = btrfs_level_size(root, wc->level - 1);
 
-               disk_bytenr = sorted[i].bytenr;
-               slot = sorted[i].slot;
+       for (slot = path->slots[wc->level]; slot < nritems; slot++) {
+               if (nread >= wc->reada_count)
+                       break;
 
                cond_resched();
+               bytenr = btrfs_node_blockptr(eb, slot);
+               generation = btrfs_node_ptr_generation(eb, slot);
 
-               btrfs_item_key_to_cpu(leaf, &key, slot);
-               if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
-                       continue;
+               if (slot == path->slots[wc->level])
+                       goto reada;
 
-               fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+               if (wc->stage == UPDATE_BACKREF &&
+                   generation <= root->root_key.offset)
+                       continue;
 
-               ret = btrfs_free_extent(trans, root, disk_bytenr,
-                               btrfs_file_extent_disk_num_bytes(leaf, fi),
-                               leaf->start, leaf_owner, leaf_generation,
-                               key.objectid, 0);
+               /* We don't lock the tree block, it's OK to be racy here */
+               ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize,
+                                              &refs, &flags);
                BUG_ON(ret);
+               BUG_ON(refs == 0);
 
-               atomic_inc(&root->fs_info->throttle_gen);
-               wake_up(&root->fs_info->transaction_throttle);
-               cond_resched();
+               if (wc->stage == DROP_REFERENCE) {
+                       if (refs == 1)
+                               goto reada;
+
+                       if (wc->level == 1 &&
+                           (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF))
+                               continue;
+                       if (!wc->update_ref ||
+                           generation <= root->root_key.offset)
+                               continue;
+                       btrfs_node_key_to_cpu(eb, &key, slot);
+                       ret = btrfs_comp_cpu_keys(&key,
+                                                 &wc->update_progress);
+                       if (ret < 0)
+                               continue;
+               } else {
+                       if (wc->level == 1 &&
+                           (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF))
+                               continue;
+               }
+reada:
+               ret = readahead_tree_block(root, bytenr, blocksize,
+                                          generation);
+               if (ret)
+                       break;
+               last = bytenr + blocksize;
+               nread++;
        }
-out:
-       kfree(sorted);
-       return 0;
+       wc->reada_slot = slot;
 }
 
-static noinline int cache_drop_leaf_ref(struct btrfs_trans_handle *trans,
-                                       struct btrfs_root *root,
-                                       struct btrfs_leaf_ref *ref)
+/*
+ * hepler to process tree block while walking down the tree.
+ *
+ * when wc->stage == UPDATE_BACKREF, this function updates
+ * back refs for pointers in the block.
+ *
+ * NOTE: return value 1 means we should stop walking down.
+ */
+static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
+                                  struct btrfs_root *root,
+                                  struct btrfs_path *path,
+                                  struct walk_control *wc, int lookup_info)
 {
-       int i;
+       int level = wc->level;
+       struct extent_buffer *eb = path->nodes[level];
+       u64 flag = BTRFS_BLOCK_FLAG_FULL_BACKREF;
        int ret;
-       struct btrfs_extent_info *info;
-       struct refsort *sorted;
 
-       if (ref->nritems == 0)
-               return 0;
-
-       sorted = kmalloc(sizeof(*sorted) * ref->nritems, GFP_NOFS);
-       for (i = 0; i < ref->nritems; i++) {
-               sorted[i].bytenr = ref->extents[i].bytenr;
-               sorted[i].slot = i;
-       }
-       sort(sorted, ref->nritems, sizeof(struct refsort), refsort_cmp, NULL);
+       if (wc->stage == UPDATE_BACKREF &&
+           btrfs_header_owner(eb) != root->root_key.objectid)
+               return 1;
 
        /*
-        * the items in the ref were sorted when the ref was inserted
-        * into the ref cache, so this is already in order
+        * when reference count of tree block is 1, it won't increase
+        * again. once full backref flag is set, we never clear it.
         */
-       for (i = 0; i < ref->nritems; i++) {
-               info = ref->extents + sorted[i].slot;
-               ret = btrfs_free_extent(trans, root, info->bytenr,
-                                         info->num_bytes, ref->bytenr,
-                                         ref->owner, ref->generation,
-                                         info->objectid, 0);
-
-               atomic_inc(&root->fs_info->throttle_gen);
-               wake_up(&root->fs_info->transaction_throttle);
-               cond_resched();
-
+       if (lookup_info &&
+           ((wc->stage == DROP_REFERENCE && wc->refs[level] != 1) ||
+            (wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag)))) {
+               BUG_ON(!path->locks[level]);
+               ret = btrfs_lookup_extent_info(trans, root,
+                                              eb->start, eb->len,
+                                              &wc->refs[level],
+                                              &wc->flags[level]);
                BUG_ON(ret);
-               info++;
+               BUG_ON(wc->refs[level] == 0);
        }
 
-       kfree(sorted);
-       return 0;
-}
-
-static int drop_snap_lookup_refcount(struct btrfs_trans_handle *trans,
-                                    struct btrfs_root *root, u64 start,
-                                    u64 len, u32 *refs)
-{
-       int ret;
-
-       ret = btrfs_lookup_extent_ref(trans, root, start, len, refs);
-       BUG_ON(ret);
-
-#if 0 /* some debugging code in case we see problems here */
-       /* if the refs count is one, it won't get increased again.  But
-        * if the ref count is > 1, someone may be decreasing it at
-        * the same time we are.
-        */
-       if (*refs != 1) {
-               struct extent_buffer *eb = NULL;
-               eb = btrfs_find_create_tree_block(root, start, len);
-               if (eb)
-                       btrfs_tree_lock(eb);
-
-               mutex_lock(&root->fs_info->alloc_mutex);
-               ret = lookup_extent_ref(NULL, root, start, len, refs);
-               BUG_ON(ret);
-               mutex_unlock(&root->fs_info->alloc_mutex);
+       if (wc->stage == DROP_REFERENCE) {
+               if (wc->refs[level] > 1)
+                       return 1;
 
-               if (eb) {
+               if (path->locks[level] && !wc->keep_locks) {
                        btrfs_tree_unlock(eb);
-                       free_extent_buffer(eb);
-               }
-               if (*refs == 1) {
-                       printk(KERN_ERR "btrfs block %llu went down to one "
-                              "during drop_snap\n", (unsigned long long)start);
+                       path->locks[level] = 0;
                }
+               return 0;
+       }
 
+       /* wc->stage == UPDATE_BACKREF */
+       if (!(wc->flags[level] & flag)) {
+               BUG_ON(!path->locks[level]);
+               ret = btrfs_inc_ref(trans, root, eb, 1);
+               BUG_ON(ret);
+               ret = btrfs_dec_ref(trans, root, eb, 0);
+               BUG_ON(ret);
+               ret = btrfs_set_disk_extent_flags(trans, root, eb->start,
+                                                 eb->len, flag, 0);
+               BUG_ON(ret);
+               wc->flags[level] |= flag;
        }
-#endif
 
-       cond_resched();
-       return ret;
+       /*
+        * the block is shared by multiple trees, so it's not good to
+        * keep the tree lock
+        */
+       if (path->locks[level] && level > 0) {
+               btrfs_tree_unlock(eb);
+               path->locks[level] = 0;
+       }
+       return 0;
 }
 
 /*
- * this is used while deleting old snapshots, and it drops the refs
- * on a whole subtree starting from a level 1 node.
+ * hepler to process tree block pointer.
  *
- * The idea is to sort all the leaf pointers, and then drop the
- * ref on all the leaves in order.  Most of the time the leaves
- * will have ref cache entries, so no leaf IOs will be required to
- * find the extents they have references on.
+ * when wc->stage == DROP_REFERENCE, this function checks
+ * reference count of the block pointed to. if the block
+ * is shared and we need update back refs for the subtree
+ * rooted at the block, this function changes wc->stage to
+ * UPDATE_BACKREF. if the block is shared and there is no
+ * need to update back, this function drops the reference
+ * to the block.
  *
- * For each leaf, any references it has are also dropped in order
- *
- * This ends up dropping the references in something close to optimal
- * order for reading and modifying the extent allocation tree.
+ * NOTE: return value 1 means we should stop walking down.
  */
-static noinline int drop_level_one_refs(struct btrfs_trans_handle *trans,
-                                       struct btrfs_root *root,
-                                       struct btrfs_path *path)
+static noinline int do_walk_down(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                struct walk_control *wc, int *lookup_info)
 {
        u64 bytenr;
-       u64 root_owner;
-       u64 root_gen;
-       struct extent_buffer *eb = path->nodes[1];
-       struct extent_buffer *leaf;
-       struct btrfs_leaf_ref *ref;
-       struct refsort *sorted = NULL;
-       int nritems = btrfs_header_nritems(eb);
-       int ret;
-       int i;
-       int refi = 0;
-       int slot = path->slots[1];
-       u32 blocksize = btrfs_level_size(root, 0);
-       u32 refs;
-
-       if (nritems == 0)
-               goto out;
-
-       root_owner = btrfs_header_owner(eb);
-       root_gen = btrfs_header_generation(eb);
-       sorted = kmalloc(sizeof(*sorted) * nritems, GFP_NOFS);
+       u64 generation;
+       u64 parent;
+       u32 blocksize;
+       struct btrfs_key key;
+       struct extent_buffer *next;
+       int level = wc->level;
+       int reada = 0;
+       int ret = 0;
 
+       generation = btrfs_node_ptr_generation(path->nodes[level],
+                                              path->slots[level]);
        /*
-        * step one, sort all the leaf pointers so we don't scribble
-        * randomly into the extent allocation tree
+        * if the lower level block was created before the snapshot
+        * was created, we know there is no need to update back refs
+        * for the subtree
         */
-       for (i = slot; i < nritems; i++) {
-               sorted[refi].bytenr = btrfs_node_blockptr(eb, i);
-               sorted[refi].slot = i;
-               refi++;
+       if (wc->stage == UPDATE_BACKREF &&
+           generation <= root->root_key.offset) {
+               *lookup_info = 1;
+               return 1;
        }
 
-       /*
-        * nritems won't be zero, but if we're picking up drop_snapshot
-        * after a crash, slot might be > 0, so double check things
-        * just in case.
-        */
-       if (refi == 0)
-               goto out;
+       bytenr = btrfs_node_blockptr(path->nodes[level], path->slots[level]);
+       blocksize = btrfs_level_size(root, level - 1);
 
-       sort(sorted, refi, sizeof(struct refsort), refsort_cmp, NULL);
+       next = btrfs_find_tree_block(root, bytenr, blocksize);
+       if (!next) {
+               next = btrfs_find_create_tree_block(root, bytenr, blocksize);
+               if (!next)
+                       return -ENOMEM;
+               reada = 1;
+       }
+       btrfs_tree_lock(next);
+       btrfs_set_lock_blocking(next);
 
-       /*
-        * the first loop frees everything the leaves point to
-        */
-       for (i = 0; i < refi; i++) {
-               u64 ptr_gen;
+       ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize,
+                                      &wc->refs[level - 1],
+                                      &wc->flags[level - 1]);
+       BUG_ON(ret);
+       BUG_ON(wc->refs[level - 1] == 0);
+       *lookup_info = 0;
 
-               bytenr = sorted[i].bytenr;
+       if (wc->stage == DROP_REFERENCE) {
+               if (wc->refs[level - 1] > 1) {
+                       if (level == 1 &&
+                           (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF))
+                               goto skip;
 
-               /*
-                * check the reference count on this leaf.  If it is > 1
-                * we just decrement it below and don't update any
-                * of the refs the leaf points to.
-                */
-               ret = drop_snap_lookup_refcount(trans, root, bytenr,
-                                               blocksize, &refs);
-               BUG_ON(ret);
-               if (refs != 1)
-                       continue;
+                       if (!wc->update_ref ||
+                           generation <= root->root_key.offset)
+                               goto skip;
 
-               ptr_gen = btrfs_node_ptr_generation(eb, sorted[i].slot);
+                       btrfs_node_key_to_cpu(path->nodes[level], &key,
+                                             path->slots[level]);
+                       ret = btrfs_comp_cpu_keys(&key, &wc->update_progress);
+                       if (ret < 0)
+                               goto skip;
 
-               /*
-                * the leaf only had one reference, which means the
-                * only thing pointing to this leaf is the snapshot
-                * we're deleting.  It isn't possible for the reference
-                * count to increase again later
-                *
-                * The reference cache is checked for the leaf,
-                * and if found we'll be able to drop any refs held by
-                * the leaf without needing to read it in.
-                */
-               ref = btrfs_lookup_leaf_ref(root, bytenr);
-               if (ref && ref->generation != ptr_gen) {
-                       btrfs_free_leaf_ref(root, ref);
-                       ref = NULL;
-               }
-               if (ref) {
-                       ret = cache_drop_leaf_ref(trans, root, ref);
-                       BUG_ON(ret);
-                       btrfs_remove_leaf_ref(root, ref);
-                       btrfs_free_leaf_ref(root, ref);
-               } else {
-                       /*
-                        * the leaf wasn't in the reference cache, so
-                        * we have to read it.
-                        */
-                       leaf = read_tree_block(root, bytenr, blocksize,
-                                              ptr_gen);
-                       ret = btrfs_drop_leaf_ref(trans, root, leaf);
-                       BUG_ON(ret);
-                       free_extent_buffer(leaf);
+                       wc->stage = UPDATE_BACKREF;
+                       wc->shared_level = level - 1;
                }
-               atomic_inc(&root->fs_info->throttle_gen);
-               wake_up(&root->fs_info->transaction_throttle);
-               cond_resched();
+       } else {
+               if (level == 1 &&
+                   (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF))
+                       goto skip;
        }
 
-       /*
-        * run through the loop again to free the refs on the leaves.
-        * This is faster than doing it in the loop above because
-        * the leaves are likely to be clustered together.  We end up
-        * working in nice chunks on the extent allocation tree.
-        */
-       for (i = 0; i < refi; i++) {
-               bytenr = sorted[i].bytenr;
-               ret = btrfs_free_extent(trans, root, bytenr,
-                                       blocksize, eb->start,
-                                       root_owner, root_gen, 0, 1);
-               BUG_ON(ret);
+       if (!btrfs_buffer_uptodate(next, generation)) {
+               btrfs_tree_unlock(next);
+               free_extent_buffer(next);
+               next = NULL;
+               *lookup_info = 1;
+       }
 
-               atomic_inc(&root->fs_info->throttle_gen);
-               wake_up(&root->fs_info->transaction_throttle);
-               cond_resched();
+       if (!next) {
+               if (reada && level == 1)
+                       reada_walk_down(trans, root, wc, path);
+               next = read_tree_block(root, bytenr, blocksize, generation);
+               btrfs_tree_lock(next);
+               btrfs_set_lock_blocking(next);
        }
-out:
-       kfree(sorted);
 
-       /*
-        * update the path to show we've processed the entire level 1
-        * node.  This will get saved into the root's drop_snapshot_progress
-        * field so these drops are not repeated again if this transaction
-        * commits.
-        */
-       path->slots[1] = nritems;
+       level--;
+       BUG_ON(level != btrfs_header_level(next));
+       path->nodes[level] = next;
+       path->slots[level] = 0;
+       path->locks[level] = 1;
+       wc->level = level;
+       if (wc->level == 1)
+               wc->reada_slot = 0;
        return 0;
+skip:
+       wc->refs[level - 1] = 0;
+       wc->flags[level - 1] = 0;
+       if (wc->stage == DROP_REFERENCE) {
+               if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
+                       parent = path->nodes[level]->start;
+               } else {
+                       BUG_ON(root->root_key.objectid !=
+                              btrfs_header_owner(path->nodes[level]));
+                       parent = 0;
+               }
+
+               ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent,
+                                       root->root_key.objectid, level - 1, 0);
+               BUG_ON(ret);
+       }
+       btrfs_tree_unlock(next);
+       free_extent_buffer(next);
+       *lookup_info = 1;
+       return 1;
 }
 
 /*
- * helper function for drop_snapshot, this walks down the tree dropping ref
- * counts as it goes.
+ * hepler to process tree block while walking up the tree.
+ *
+ * when wc->stage == DROP_REFERENCE, this function drops
+ * reference count on the block.
+ *
+ * when wc->stage == UPDATE_BACKREF, this function changes
+ * wc->stage back to DROP_REFERENCE if we changed wc->stage
+ * to UPDATE_BACKREF previously while processing the block.
+ *
+ * NOTE: return value 1 means we should stop walking up.
  */
-static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
-                                  struct btrfs_root *root,
-                                  struct btrfs_path *path, int *level)
+static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                struct walk_control *wc)
 {
-       u64 root_owner;
-       u64 root_gen;
-       u64 bytenr;
-       u64 ptr_gen;
-       struct extent_buffer *next;
-       struct extent_buffer *cur;
-       struct extent_buffer *parent;
-       u32 blocksize;
        int ret;
-       u32 refs;
-
-       WARN_ON(*level < 0);
-       WARN_ON(*level >= BTRFS_MAX_LEVEL);
-       ret = drop_snap_lookup_refcount(trans, root, path->nodes[*level]->start,
-                               path->nodes[*level]->len, &refs);
-       BUG_ON(ret);
-       if (refs > 1)
-               goto out;
-
-       /*
-        * walk down to the last node level and free all the leaves
-        */
-       while (*level >= 0) {
-               WARN_ON(*level < 0);
-               WARN_ON(*level >= BTRFS_MAX_LEVEL);
-               cur = path->nodes[*level];
+       int level = wc->level;
+       struct extent_buffer *eb = path->nodes[level];
+       u64 parent = 0;
 
-               if (btrfs_header_level(cur) != *level)
-                       WARN_ON(1);
+       if (wc->stage == UPDATE_BACKREF) {
+               BUG_ON(wc->shared_level < level);
+               if (level < wc->shared_level)
+                       goto out;
 
-               if (path->slots[*level] >=
-                   btrfs_header_nritems(cur))
-                       break;
+               ret = find_next_key(path, level + 1, &wc->update_progress);
+               if (ret > 0)
+                       wc->update_ref = 0;
 
-               /* the new code goes down to level 1 and does all the
-                * leaves pointed to that node in bulk.  So, this check
-                * for level 0 will always be false.
-                *
-                * But, the disk format allows the drop_snapshot_progress
-                * field in the root to leave things in a state where
-                * a leaf will need cleaning up here.  If someone crashes
-                * with the old code and then boots with the new code,
-                * we might find a leaf here.
-                */
-               if (*level == 0) {
-                       ret = btrfs_drop_leaf_ref(trans, root, cur);
-                       BUG_ON(ret);
-                       break;
-               }
+               wc->stage = DROP_REFERENCE;
+               wc->shared_level = -1;
+               path->slots[level] = 0;
 
                /*
-                * once we get to level one, process the whole node
-                * at once, including everything below it.
+                * check reference count again if the block isn't locked.
+                * we should start walking down the tree again if reference
+                * count is one.
                 */
-               if (*level == 1) {
-                       ret = drop_level_one_refs(trans, root, path);
+               if (!path->locks[level]) {
+                       BUG_ON(level == 0);
+                       btrfs_tree_lock(eb);
+                       btrfs_set_lock_blocking(eb);
+                       path->locks[level] = 1;
+
+                       ret = btrfs_lookup_extent_info(trans, root,
+                                                      eb->start, eb->len,
+                                                      &wc->refs[level],
+                                                      &wc->flags[level]);
                        BUG_ON(ret);
-                       break;
+                       BUG_ON(wc->refs[level] == 0);
+                       if (wc->refs[level] == 1) {
+                               btrfs_tree_unlock(eb);
+                               path->locks[level] = 0;
+                               return 1;
+                       }
                }
+       }
 
-               bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
-               ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
-               blocksize = btrfs_level_size(root, *level - 1);
-
-               ret = drop_snap_lookup_refcount(trans, root, bytenr,
-                                               blocksize, &refs);
-               BUG_ON(ret);
+       /* wc->stage == DROP_REFERENCE */
+       BUG_ON(wc->refs[level] > 1 && !path->locks[level]);
 
-               /*
-                * if there is more than one reference, we don't need
-                * to read that node to drop any references it has.  We
-                * just drop the ref we hold on that node and move on to the
-                * next slot in this level.
-                */
-               if (refs != 1) {
-                       parent = path->nodes[*level];
-                       root_owner = btrfs_header_owner(parent);
-                       root_gen = btrfs_header_generation(parent);
-                       path->slots[*level]++;
-
-                       ret = btrfs_free_extent(trans, root, bytenr,
-                                               blocksize, parent->start,
-                                               root_owner, root_gen,
-                                               *level - 1, 1);
+       if (wc->refs[level] == 1) {
+               if (level == 0) {
+                       if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
+                               ret = btrfs_dec_ref(trans, root, eb, 1);
+                       else
+                               ret = btrfs_dec_ref(trans, root, eb, 0);
                        BUG_ON(ret);
-
-                       atomic_inc(&root->fs_info->throttle_gen);
-                       wake_up(&root->fs_info->transaction_throttle);
-                       cond_resched();
-
-                       continue;
                }
-
-               /*
-                * we need to keep freeing things in the next level down.
-                * read the block and loop around to process it
-                */
-               next = read_tree_block(root, bytenr, blocksize, ptr_gen);
-               WARN_ON(*level <= 0);
-               if (path->nodes[*level-1])
-                       free_extent_buffer(path->nodes[*level-1]);
-               path->nodes[*level-1] = next;
-               *level = btrfs_header_level(next);
-               path->slots[*level] = 0;
-               cond_resched();
+               /* make block locked assertion in clean_tree_block happy */
+               if (!path->locks[level] &&
+                   btrfs_header_generation(eb) == trans->transid) {
+                       btrfs_tree_lock(eb);
+                       btrfs_set_lock_blocking(eb);
+                       path->locks[level] = 1;
+               }
+               clean_tree_block(trans, root, eb);
        }
-out:
-       WARN_ON(*level < 0);
-       WARN_ON(*level >= BTRFS_MAX_LEVEL);
 
-       if (path->nodes[*level] == root->node) {
-               parent = path->nodes[*level];
-               bytenr = path->nodes[*level]->start;
+       if (eb == root->node) {
+               if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
+                       parent = eb->start;
+               else
+                       BUG_ON(root->root_key.objectid !=
+                              btrfs_header_owner(eb));
        } else {
-               parent = path->nodes[*level + 1];
-               bytenr = btrfs_node_blockptr(parent, path->slots[*level + 1]);
+               if (wc->flags[level + 1] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
+                       parent = path->nodes[level + 1]->start;
+               else
+                       BUG_ON(root->root_key.objectid !=
+                              btrfs_header_owner(path->nodes[level + 1]));
        }
 
-       blocksize = btrfs_level_size(root, *level);
-       root_owner = btrfs_header_owner(parent);
-       root_gen = btrfs_header_generation(parent);
-
-       /*
-        * cleanup and free the reference on the last node
-        * we processed
-        */
-       ret = btrfs_free_extent(trans, root, bytenr, blocksize,
-                                 parent->start, root_owner, root_gen,
-                                 *level, 1);
-       free_extent_buffer(path->nodes[*level]);
-       path->nodes[*level] = NULL;
-
-       *level += 1;
-       BUG_ON(ret);
-
-       cond_resched();
+       btrfs_free_tree_block(trans, root, eb, parent, wc->refs[level] == 1);
+out:
+       wc->refs[level] = 0;
+       wc->flags[level] = 0;
        return 0;
 }
 
-/*
- * helper function for drop_subtree, this function is similar to
- * walk_down_tree. The main difference is that it checks reference
- * counts while tree blocks are locked.
- */
-static noinline int walk_down_subtree(struct btrfs_trans_handle *trans,
-                                     struct btrfs_root *root,
-                                     struct btrfs_path *path, int *level)
+static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
+                                  struct btrfs_root *root,
+                                  struct btrfs_path *path,
+                                  struct walk_control *wc)
 {
-       struct extent_buffer *next;
-       struct extent_buffer *cur;
-       struct extent_buffer *parent;
-       u64 bytenr;
-       u64 ptr_gen;
-       u32 blocksize;
-       u32 refs;
+       int level = wc->level;
+       int lookup_info = 1;
        int ret;
 
-       cur = path->nodes[*level];
-       ret = btrfs_lookup_extent_ref(trans, root, cur->start, cur->len,
-                                     &refs);
-       BUG_ON(ret);
-       if (refs > 1)
-               goto out;
-
-       while (*level >= 0) {
-               cur = path->nodes[*level];
-               if (*level == 0) {
-                       ret = btrfs_drop_leaf_ref(trans, root, cur);
-                       BUG_ON(ret);
-                       clean_tree_block(trans, root, cur);
-                       break;
-               }
-               if (path->slots[*level] >= btrfs_header_nritems(cur)) {
-                       clean_tree_block(trans, root, cur);
+       while (level >= 0) {
+               ret = walk_down_proc(trans, root, path, wc, lookup_info);
+               if (ret > 0)
                        break;
-               }
 
-               bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
-               blocksize = btrfs_level_size(root, *level - 1);
-               ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
+               if (level == 0)
+                       break;
 
-               next = read_tree_block(root, bytenr, blocksize, ptr_gen);
-               btrfs_tree_lock(next);
-               btrfs_set_lock_blocking(next);
+               if (path->slots[level] >=
+                   btrfs_header_nritems(path->nodes[level]))
+                       break;
 
-               ret = btrfs_lookup_extent_ref(trans, root, bytenr, blocksize,
-                                             &refs);
-               BUG_ON(ret);
-               if (refs > 1) {
-                       parent = path->nodes[*level];
-                       ret = btrfs_free_extent(trans, root, bytenr,
-                                       blocksize, parent->start,
-                                       btrfs_header_owner(parent),
-                                       btrfs_header_generation(parent),
-                                       *level - 1, 1);
-                       BUG_ON(ret);
-                       path->slots[*level]++;
-                       btrfs_tree_unlock(next);
-                       free_extent_buffer(next);
+               ret = do_walk_down(trans, root, path, wc, &lookup_info);
+               if (ret > 0) {
+                       path->slots[level]++;
                        continue;
-               }
-
-               *level = btrfs_header_level(next);
-               path->nodes[*level] = next;
-               path->slots[*level] = 0;
-               path->locks[*level] = 1;
-               cond_resched();
+               } else if (ret < 0)
+                       return ret;
+               level = wc->level;
        }
-out:
-       parent = path->nodes[*level + 1];
-       bytenr = path->nodes[*level]->start;
-       blocksize = path->nodes[*level]->len;
-
-       ret = btrfs_free_extent(trans, root, bytenr, blocksize,
-                       parent->start, btrfs_header_owner(parent),
-                       btrfs_header_generation(parent), *level, 1);
-       BUG_ON(ret);
-
-       if (path->locks[*level]) {
-               btrfs_tree_unlock(path->nodes[*level]);
-               path->locks[*level] = 0;
-       }
-       free_extent_buffer(path->nodes[*level]);
-       path->nodes[*level] = NULL;
-       *level += 1;
-       cond_resched();
        return 0;
 }
 
-/*
- * helper for dropping snapshots.  This walks back up the tree in the path
- * to find the first node higher up where we haven't yet gone through
- * all the slots
- */
 static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
                                 struct btrfs_root *root,
                                 struct btrfs_path *path,
-                                int *level, int max_level)
+                                struct walk_control *wc, int max_level)
 {
-       u64 root_owner;
-       u64 root_gen;
-       struct btrfs_root_item *root_item = &root->root_item;
-       int i;
-       int slot;
+       int level = wc->level;
        int ret;
 
-       for (i = *level; i < max_level && path->nodes[i]; i++) {
-               slot = path->slots[i];
-               if (slot < btrfs_header_nritems(path->nodes[i]) - 1) {
-                       struct extent_buffer *node;
-                       struct btrfs_disk_key disk_key;
-
-                       /*
-                        * there is more work to do in this level.
-                        * Update the drop_progress marker to reflect
-                        * the work we've done so far, and then bump
-                        * the slot number
-                        */
-                       node = path->nodes[i];
-                       path->slots[i]++;
-                       *level = i;
-                       WARN_ON(*level == 0);
-                       btrfs_node_key(node, &disk_key, path->slots[i]);
-                       memcpy(&root_item->drop_progress,
-                              &disk_key, sizeof(disk_key));
-                       root_item->drop_level = i;
+       path->slots[level] = btrfs_header_nritems(path->nodes[level]);
+       while (level < max_level && path->nodes[level]) {
+               wc->level = level;
+               if (path->slots[level] + 1 <
+                   btrfs_header_nritems(path->nodes[level])) {
+                       path->slots[level]++;
                        return 0;
                } else {
-                       struct extent_buffer *parent;
-
-                       /*
-                        * this whole node is done, free our reference
-                        * on it and go up one level
-                        */
-                       if (path->nodes[*level] == root->node)
-                               parent = path->nodes[*level];
-                       else
-                               parent = path->nodes[*level + 1];
-
-                       root_owner = btrfs_header_owner(parent);
-                       root_gen = btrfs_header_generation(parent);
+                       ret = walk_up_proc(trans, root, path, wc);
+                       if (ret > 0)
+                               return 0;
 
-                       clean_tree_block(trans, root, path->nodes[*level]);
-                       ret = btrfs_free_extent(trans, root,
-                                               path->nodes[*level]->start,
-                                               path->nodes[*level]->len,
-                                               parent->start, root_owner,
-                                               root_gen, *level, 1);
-                       BUG_ON(ret);
-                       if (path->locks[*level]) {
-                               btrfs_tree_unlock(path->nodes[*level]);
-                               path->locks[*level] = 0;
+                       if (path->locks[level]) {
+                               btrfs_tree_unlock(path->nodes[level]);
+                               path->locks[level] = 0;
                        }
-                       free_extent_buffer(path->nodes[*level]);
-                       path->nodes[*level] = NULL;
-                       *level = i + 1;
+                       free_extent_buffer(path->nodes[level]);
+                       path->nodes[level] = NULL;
+                       level++;
                }
        }
        return 1;
 }
 
 /*
- * drop the reference count on the tree rooted at 'snap'.  This traverses
- * the tree freeing any blocks that have a ref count of zero after being
- * decremented.
+ * drop a subvolume tree.
+ *
+ * this function traverses the tree freeing any blocks that only
+ * referenced by the tree.
+ *
+ * when a shared tree block is found. this function decreases its
+ * reference count by one. if update_ref is true, this function
+ * also make sure backrefs for the shared block and all lower level
+ * blocks are properly updated.
  */
-int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
-                       *root)
+int btrfs_drop_snapshot(struct btrfs_root *root,
+                       struct btrfs_block_rsv *block_rsv, int update_ref)
 {
-       int ret = 0;
-       int wret;
-       int level;
        struct btrfs_path *path;
-       int i;
-       int orig_level;
-       int update_count;
+       struct btrfs_trans_handle *trans;
+       struct btrfs_root *tree_root = root->fs_info->tree_root;
        struct btrfs_root_item *root_item = &root->root_item;
+       struct walk_control *wc;
+       struct btrfs_key key;
+       int err = 0;
+       int ret;
+       int level;
 
-       WARN_ON(!mutex_is_locked(&root->fs_info->drop_mutex));
        path = btrfs_alloc_path();
        BUG_ON(!path);
 
-       level = btrfs_header_level(root->node);
-       orig_level = level;
+       wc = kzalloc(sizeof(*wc), GFP_NOFS);
+       BUG_ON(!wc);
+
+       trans = btrfs_start_transaction(tree_root, 0);
+       if (block_rsv)
+               trans->block_rsv = block_rsv;
+
        if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
-               path->nodes[level] = root->node;
-               extent_buffer_get(root->node);
+               level = btrfs_header_level(root->node);
+               path->nodes[level] = btrfs_lock_root_node(root);
+               btrfs_set_lock_blocking(path->nodes[level]);
                path->slots[level] = 0;
+               path->locks[level] = 1;
+               memset(&wc->update_progress, 0,
+                      sizeof(wc->update_progress));
        } else {
-               struct btrfs_key key;
-               struct btrfs_disk_key found_key;
-               struct extent_buffer *node;
-
                btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
+               memcpy(&wc->update_progress, &key,
+                      sizeof(wc->update_progress));
+
                level = root_item->drop_level;
+               BUG_ON(level == 0);
                path->lowest_level = level;
-               wret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
-               if (wret < 0) {
-                       ret = wret;
+               ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+               path->lowest_level = 0;
+               if (ret < 0) {
+                       err = ret;
                        goto out;
                }
-               node = path->nodes[level];
-               btrfs_node_key(node, &found_key, path->slots[level]);
-               WARN_ON(memcmp(&found_key, &root_item->drop_progress,
-                              sizeof(found_key)));
+               WARN_ON(ret > 0);
+
                /*
                 * unlock our path, this is safe because only this
                 * function is allowed to delete this snapshot
                 */
-               for (i = 0; i < BTRFS_MAX_LEVEL; i++) {
-                       if (path->nodes[i] && path->locks[i]) {
-                               path->locks[i] = 0;
-                               btrfs_tree_unlock(path->nodes[i]);
-                       }
+               btrfs_unlock_up_safe(path, 0);
+
+               level = btrfs_header_level(root->node);
+               while (1) {
+                       btrfs_tree_lock(path->nodes[level]);
+                       btrfs_set_lock_blocking(path->nodes[level]);
+
+                       ret = btrfs_lookup_extent_info(trans, root,
+                                               path->nodes[level]->start,
+                                               path->nodes[level]->len,
+                                               &wc->refs[level],
+                                               &wc->flags[level]);
+                       BUG_ON(ret);
+                       BUG_ON(wc->refs[level] == 0);
+
+                       if (level == root_item->drop_level)
+                               break;
+
+                       btrfs_tree_unlock(path->nodes[level]);
+                       WARN_ON(wc->refs[level] != 1);
+                       level--;
                }
        }
+
+       wc->level = level;
+       wc->shared_level = -1;
+       wc->stage = DROP_REFERENCE;
+       wc->update_ref = update_ref;
+       wc->keep_locks = 0;
+       wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root);
+
        while (1) {
-               unsigned long update;
-               wret = walk_down_tree(trans, root, path, &level);
-               if (wret > 0)
+               ret = walk_down_tree(trans, root, path, wc);
+               if (ret < 0) {
+                       err = ret;
                        break;
-               if (wret < 0)
-                       ret = wret;
+               }
 
-               wret = walk_up_tree(trans, root, path, &level,
-                                   BTRFS_MAX_LEVEL);
-               if (wret > 0)
+               ret = walk_up_tree(trans, root, path, wc, BTRFS_MAX_LEVEL);
+               if (ret < 0) {
+                       err = ret;
                        break;
-               if (wret < 0)
-                       ret = wret;
-               if (trans->transaction->in_commit ||
-                   trans->transaction->delayed_refs.flushing) {
-                       ret = -EAGAIN;
+               }
+
+               if (ret > 0) {
+                       BUG_ON(wc->stage != DROP_REFERENCE);
                        break;
                }
-               atomic_inc(&root->fs_info->throttle_gen);
-               wake_up(&root->fs_info->transaction_throttle);
-               for (update_count = 0; update_count < 16; update_count++) {
-                       update = trans->delayed_ref_updates;
-                       trans->delayed_ref_updates = 0;
-                       if (update)
-                               btrfs_run_delayed_refs(trans, root, update);
-                       else
-                               break;
+
+               if (wc->stage == DROP_REFERENCE) {
+                       level = wc->level;
+                       btrfs_node_key(path->nodes[level],
+                                      &root_item->drop_progress,
+                                      path->slots[level]);
+                       root_item->drop_level = level;
+               }
+
+               BUG_ON(wc->level == 0);
+               if (btrfs_should_end_transaction(trans, tree_root)) {
+                       ret = btrfs_update_root(trans, tree_root,
+                                               &root->root_key,
+                                               root_item);
+                       BUG_ON(ret);
+
+                       btrfs_end_transaction_throttle(trans, tree_root);
+                       trans = btrfs_start_transaction(tree_root, 0);
+                       if (block_rsv)
+                               trans->block_rsv = block_rsv;
                }
        }
-       for (i = 0; i <= orig_level; i++) {
-               if (path->nodes[i]) {
-                       free_extent_buffer(path->nodes[i]);
-                       path->nodes[i] = NULL;
+       btrfs_release_path(root, path);
+       BUG_ON(err);
+
+       ret = btrfs_del_root(trans, tree_root, &root->root_key);
+       BUG_ON(ret);
+
+       if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
+               ret = btrfs_find_last_root(tree_root, root->root_key.objectid,
+                                          NULL, NULL);
+               BUG_ON(ret < 0);
+               if (ret > 0) {
+                       ret = btrfs_del_orphan_item(trans, tree_root,
+                                                   root->root_key.objectid);
+                       BUG_ON(ret);
                }
        }
+
+       if (root->in_radix) {
+               btrfs_free_fs_root(tree_root->fs_info, root);
+       } else {
+               free_extent_buffer(root->node);
+               free_extent_buffer(root->commit_root);
+               kfree(root);
+       }
 out:
+       btrfs_end_transaction_throttle(trans, tree_root);
+       kfree(wc);
        btrfs_free_path(path);
-       return ret;
+       return err;
 }
 
+/*
+ * drop subtree rooted at tree block 'node'.
+ *
+ * NOTE: this function will unlock and release tree block 'node'
+ */
 int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
                        struct btrfs_root *root,
                        struct extent_buffer *node,
                        struct extent_buffer *parent)
 {
        struct btrfs_path *path;
+       struct walk_control *wc;
        int level;
        int parent_level;
        int ret = 0;
        int wret;
 
+       BUG_ON(root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID);
+
        path = btrfs_alloc_path();
        BUG_ON(!path);
 
+       wc = kzalloc(sizeof(*wc), GFP_NOFS);
+       BUG_ON(!wc);
+
        btrfs_assert_tree_locked(parent);
        parent_level = btrfs_header_level(parent);
        extent_buffer_get(parent);
@@ -3880,28 +6315,39 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
 
        btrfs_assert_tree_locked(node);
        level = btrfs_header_level(node);
-       extent_buffer_get(node);
        path->nodes[level] = node;
        path->slots[level] = 0;
+       path->locks[level] = 1;
+
+       wc->refs[parent_level] = 1;
+       wc->flags[parent_level] = BTRFS_BLOCK_FLAG_FULL_BACKREF;
+       wc->level = level;
+       wc->shared_level = -1;
+       wc->stage = DROP_REFERENCE;
+       wc->update_ref = 0;
+       wc->keep_locks = 1;
+       wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root);
 
        while (1) {
-               wret = walk_down_subtree(trans, root, path, &level);
-               if (wret < 0)
+               wret = walk_down_tree(trans, root, path, wc);
+               if (wret < 0) {
                        ret = wret;
-               if (wret != 0)
                        break;
+               }
 
-               wret = walk_up_tree(trans, root, path, &level, parent_level);
+               wret = walk_up_tree(trans, root, path, wc, parent_level);
                if (wret < 0)
                        ret = wret;
                if (wret != 0)
                        break;
        }
 
+       kfree(wc);
        btrfs_free_path(path);
        return ret;
 }
 
+#if 0
 static unsigned long calc_ra(unsigned long start, unsigned long last,
                             unsigned long nr)
 {
@@ -4023,9 +6469,9 @@ static noinline int relocate_data_extent(struct inode *reloc_inode,
        lock_extent(&BTRFS_I(reloc_inode)->io_tree, start, end, GFP_NOFS);
        while (1) {
                int ret;
-               spin_lock(&em_tree->lock);
+               write_lock(&em_tree->lock);
                ret = add_extent_mapping(em_tree, em);
-               spin_unlock(&em_tree->lock);
+               write_unlock(&em_tree->lock);
                if (ret != -EEXIST) {
                        free_extent_map(em);
                        break;
@@ -4776,6 +7222,7 @@ static noinline int invalidate_extent_cache(struct btrfs_root *root,
        struct btrfs_key key;
        struct inode *inode = NULL;
        struct btrfs_file_extent_item *fi;
+       struct extent_state *cached_state = NULL;
        u64 num_bytes;
        u64 skip_objectid = 0;
        u32 nritems;
@@ -4804,12 +7251,14 @@ static noinline int invalidate_extent_cache(struct btrfs_root *root,
                }
                num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
 
-               lock_extent(&BTRFS_I(inode)->io_tree, key.offset,
-                           key.offset + num_bytes - 1, GFP_NOFS);
+               lock_extent_bits(&BTRFS_I(inode)->io_tree, key.offset,
+                                key.offset + num_bytes - 1, 0, &cached_state,
+                                GFP_NOFS);
                btrfs_drop_extent_cache(inode, key.offset,
                                        key.offset + num_bytes - 1, 1);
-               unlock_extent(&BTRFS_I(inode)->io_tree, key.offset,
-                             key.offset + num_bytes - 1, GFP_NOFS);
+               unlock_extent_cached(&BTRFS_I(inode)->io_tree, key.offset,
+                                    key.offset + num_bytes - 1, &cached_state,
+                                    GFP_NOFS);
                cond_resched();
        }
        iput(inode);
@@ -5383,356 +7832,201 @@ out:
        kfree(ref_path);
        return ret;
 }
+#endif
 
-static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
-{
-       u64 num_devices;
-       u64 stripped = BTRFS_BLOCK_GROUP_RAID0 |
-               BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10;
-
-       num_devices = root->fs_info->fs_devices->rw_devices;
-       if (num_devices == 1) {
-               stripped |= BTRFS_BLOCK_GROUP_DUP;
-               stripped = flags & ~stripped;
-
-               /* turn raid0 into single device chunks */
-               if (flags & BTRFS_BLOCK_GROUP_RAID0)
-                       return stripped;
-
-               /* turn mirroring into duplication */
-               if (flags & (BTRFS_BLOCK_GROUP_RAID1 |
-                            BTRFS_BLOCK_GROUP_RAID10))
-                       return stripped | BTRFS_BLOCK_GROUP_DUP;
-               return flags;
-       } else {
-               /* they already had raid on here, just return */
-               if (flags & stripped)
-                       return flags;
-
-               stripped |= BTRFS_BLOCK_GROUP_DUP;
-               stripped = flags & ~stripped;
-
-               /* switch duplicated blocks with raid1 */
-               if (flags & BTRFS_BLOCK_GROUP_DUP)
-                       return stripped | BTRFS_BLOCK_GROUP_RAID1;
-
-               /* turn single device chunks into raid0 */
-               return stripped | BTRFS_BLOCK_GROUP_RAID0;
-       }
-       return flags;
-}
-
-static int __alloc_chunk_for_shrink(struct btrfs_root *root,
-                    struct btrfs_block_group_cache *shrink_block_group,
-                    int force)
-{
-       struct btrfs_trans_handle *trans;
-       u64 new_alloc_flags;
-       u64 calc;
-
-       spin_lock(&shrink_block_group->lock);
-       if (btrfs_block_group_used(&shrink_block_group->item) > 0) {
-               spin_unlock(&shrink_block_group->lock);
-
-               trans = btrfs_start_transaction(root, 1);
-               spin_lock(&shrink_block_group->lock);
-
-               new_alloc_flags = update_block_group_flags(root,
-                                                  shrink_block_group->flags);
-               if (new_alloc_flags != shrink_block_group->flags) {
-                       calc =
-                            btrfs_block_group_used(&shrink_block_group->item);
-               } else {
-                       calc = shrink_block_group->key.offset;
-               }
-               spin_unlock(&shrink_block_group->lock);
-
-               do_chunk_alloc(trans, root->fs_info->extent_root,
-                              calc + 2 * 1024 * 1024, new_alloc_flags, force);
-
-               btrfs_end_transaction(trans, root);
-       } else
-               spin_unlock(&shrink_block_group->lock);
-       return 0;
-}
-
-static int __insert_orphan_inode(struct btrfs_trans_handle *trans,
-                                struct btrfs_root *root,
-                                u64 objectid, u64 size)
-{
-       struct btrfs_path *path;
-       struct btrfs_inode_item *item;
-       struct extent_buffer *leaf;
-       int ret;
-
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-
-       path->leave_spinning = 1;
-       ret = btrfs_insert_empty_inode(trans, root, path, objectid);
-       if (ret)
-               goto out;
-
-       leaf = path->nodes[0];
-       item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_inode_item);
-       memset_extent_buffer(leaf, 0, (unsigned long)item, sizeof(*item));
-       btrfs_set_inode_generation(leaf, item, 1);
-       btrfs_set_inode_size(leaf, item, size);
-       btrfs_set_inode_mode(leaf, item, S_IFREG | 0600);
-       btrfs_set_inode_flags(leaf, item, BTRFS_INODE_NOCOMPRESS);
-       btrfs_mark_buffer_dirty(leaf);
-       btrfs_release_path(root, path);
-out:
-       btrfs_free_path(path);
-       return ret;
-}
-
-static noinline struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info,
-                                       struct btrfs_block_group_cache *group)
+static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
 {
-       struct inode *inode = NULL;
-       struct btrfs_trans_handle *trans;
-       struct btrfs_root *root;
-       struct btrfs_key root_key;
-       u64 objectid = BTRFS_FIRST_FREE_OBJECTID;
-       int err = 0;
-
-       root_key.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID;
-       root_key.type = BTRFS_ROOT_ITEM_KEY;
-       root_key.offset = (u64)-1;
-       root = btrfs_read_fs_root_no_name(fs_info, &root_key);
-       if (IS_ERR(root))
-               return ERR_CAST(root);
+       u64 num_devices;
+       u64 stripped = BTRFS_BLOCK_GROUP_RAID0 |
+               BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10;
 
-       trans = btrfs_start_transaction(root, 1);
-       BUG_ON(!trans);
+       num_devices = root->fs_info->fs_devices->rw_devices;
+       if (num_devices == 1) {
+               stripped |= BTRFS_BLOCK_GROUP_DUP;
+               stripped = flags & ~stripped;
 
-       err = btrfs_find_free_objectid(trans, root, objectid, &objectid);
-       if (err)
-               goto out;
+               /* turn raid0 into single device chunks */
+               if (flags & BTRFS_BLOCK_GROUP_RAID0)
+                       return stripped;
 
-       err = __insert_orphan_inode(trans, root, objectid, group->key.offset);
-       BUG_ON(err);
+               /* turn mirroring into duplication */
+               if (flags & (BTRFS_BLOCK_GROUP_RAID1 |
+                            BTRFS_BLOCK_GROUP_RAID10))
+                       return stripped | BTRFS_BLOCK_GROUP_DUP;
+               return flags;
+       } else {
+               /* they already had raid on here, just return */
+               if (flags & stripped)
+                       return flags;
 
-       err = btrfs_insert_file_extent(trans, root, objectid, 0, 0, 0,
-                                      group->key.offset, 0, group->key.offset,
-                                      0, 0, 0);
-       BUG_ON(err);
+               stripped |= BTRFS_BLOCK_GROUP_DUP;
+               stripped = flags & ~stripped;
 
-       inode = btrfs_iget_locked(root->fs_info->sb, objectid, root);
-       if (inode->i_state & I_NEW) {
-               BTRFS_I(inode)->root = root;
-               BTRFS_I(inode)->location.objectid = objectid;
-               BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
-               BTRFS_I(inode)->location.offset = 0;
-               btrfs_read_locked_inode(inode);
-               unlock_new_inode(inode);
-               BUG_ON(is_bad_inode(inode));
-       } else {
-               BUG_ON(1);
-       }
-       BTRFS_I(inode)->index_cnt = group->key.objectid;
+               /* switch duplicated blocks with raid1 */
+               if (flags & BTRFS_BLOCK_GROUP_DUP)
+                       return stripped | BTRFS_BLOCK_GROUP_RAID1;
 
-       err = btrfs_orphan_add(trans, inode);
-out:
-       btrfs_end_transaction(trans, root);
-       if (err) {
-               if (inode)
-                       iput(inode);
-               inode = ERR_PTR(err);
+               /* turn single device chunks into raid0 */
+               return stripped | BTRFS_BLOCK_GROUP_RAID0;
        }
-       return inode;
+       return flags;
 }
 
-int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
+static int set_block_group_ro(struct btrfs_block_group_cache *cache)
 {
+       struct btrfs_space_info *sinfo = cache->space_info;
+       u64 num_bytes;
+       int ret = -ENOSPC;
 
-       struct btrfs_ordered_sum *sums;
-       struct btrfs_sector_sum *sector_sum;
-       struct btrfs_ordered_extent *ordered;
-       struct btrfs_root *root = BTRFS_I(inode)->root;
-       struct list_head list;
-       size_t offset;
-       int ret;
-       u64 disk_bytenr;
-
-       INIT_LIST_HEAD(&list);
-
-       ordered = btrfs_lookup_ordered_extent(inode, file_pos);
-       BUG_ON(ordered->file_offset != file_pos || ordered->len != len);
-
-       disk_bytenr = file_pos + BTRFS_I(inode)->index_cnt;
-       ret = btrfs_lookup_csums_range(root->fs_info->csum_root, disk_bytenr,
-                                      disk_bytenr + len - 1, &list);
-
-       while (!list_empty(&list)) {
-               sums = list_entry(list.next, struct btrfs_ordered_sum, list);
-               list_del_init(&sums->list);
-
-               sector_sum = sums->sums;
-               sums->bytenr = ordered->start;
-
-               offset = 0;
-               while (offset < sums->len) {
-                       sector_sum->bytenr += ordered->start - disk_bytenr;
-                       sector_sum++;
-                       offset += root->sectorsize;
-               }
+       if (cache->ro)
+               return 0;
 
-               btrfs_add_ordered_sum(inode, ordered, sums);
+       spin_lock(&sinfo->lock);
+       spin_lock(&cache->lock);
+       num_bytes = cache->key.offset - cache->reserved - cache->pinned -
+                   cache->bytes_super - btrfs_block_group_used(&cache->item);
+
+       if (sinfo->bytes_used + sinfo->bytes_reserved + sinfo->bytes_pinned +
+           sinfo->bytes_may_use + sinfo->bytes_readonly +
+           cache->reserved_pinned + num_bytes < sinfo->total_bytes) {
+               sinfo->bytes_readonly += num_bytes;
+               sinfo->bytes_reserved += cache->reserved_pinned;
+               cache->reserved_pinned = 0;
+               cache->ro = 1;
+               ret = 0;
        }
-       btrfs_put_ordered_extent(ordered);
-       return 0;
+       spin_unlock(&cache->lock);
+       spin_unlock(&sinfo->lock);
+       return ret;
 }
 
-int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start)
+int btrfs_set_block_group_ro(struct btrfs_root *root,
+                            struct btrfs_block_group_cache *cache)
+
 {
        struct btrfs_trans_handle *trans;
-       struct btrfs_path *path;
-       struct btrfs_fs_info *info = root->fs_info;
-       struct extent_buffer *leaf;
-       struct inode *reloc_inode;
-       struct btrfs_block_group_cache *block_group;
-       struct btrfs_key key;
-       u64 skipped;
-       u64 cur_byte;
-       u64 total_found;
-       u32 nritems;
+       u64 alloc_flags;
        int ret;
-       int progress;
-       int pass = 0;
-
-       root = root->fs_info->extent_root;
 
-       block_group = btrfs_lookup_block_group(info, group_start);
-       BUG_ON(!block_group);
-
-       printk(KERN_INFO "btrfs relocating block group %llu flags %llu\n",
-              (unsigned long long)block_group->key.objectid,
-              (unsigned long long)block_group->flags);
-
-       path = btrfs_alloc_path();
-       BUG_ON(!path);
-
-       reloc_inode = create_reloc_inode(info, block_group);
-       BUG_ON(IS_ERR(reloc_inode));
+       BUG_ON(cache->ro);
 
-       __alloc_chunk_for_shrink(root, block_group, 1);
-       set_block_group_readonly(block_group);
+       trans = btrfs_join_transaction(root, 1);
+       BUG_ON(IS_ERR(trans));
 
-       btrfs_start_delalloc_inodes(info->tree_root);
-       btrfs_wait_ordered_extents(info->tree_root, 0);
-again:
-       skipped = 0;
-       total_found = 0;
-       progress = 0;
-       key.objectid = block_group->key.objectid;
-       key.offset = 0;
-       key.type = 0;
-       cur_byte = key.objectid;
+       alloc_flags = update_block_group_flags(root, cache->flags);
+       if (alloc_flags != cache->flags)
+               do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags, 1);
 
-       trans = btrfs_start_transaction(info->tree_root, 1);
-       btrfs_commit_transaction(trans, info->tree_root);
+       ret = set_block_group_ro(cache);
+       if (!ret)
+               goto out;
+       alloc_flags = get_alloc_profile(root, cache->space_info->flags);
+       ret = do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags, 1);
+       if (ret < 0)
+               goto out;
+       ret = set_block_group_ro(cache);
+out:
+       btrfs_end_transaction(trans, root);
+       return ret;
+}
 
-       mutex_lock(&root->fs_info->cleaner_mutex);
-       btrfs_clean_old_snapshots(info->tree_root);
-       btrfs_remove_leaf_refs(info->tree_root, (u64)-1, 1);
-       mutex_unlock(&root->fs_info->cleaner_mutex);
+int btrfs_set_block_group_rw(struct btrfs_root *root,
+                             struct btrfs_block_group_cache *cache)
+{
+       struct btrfs_space_info *sinfo = cache->space_info;
+       u64 num_bytes;
 
-       trans = btrfs_start_transaction(info->tree_root, 1);
-       btrfs_commit_transaction(trans, info->tree_root);
+       BUG_ON(!cache->ro);
 
-       while (1) {
-               ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
-               if (ret < 0)
-                       goto out;
-next:
-               leaf = path->nodes[0];
-               nritems = btrfs_header_nritems(leaf);
-               if (path->slots[0] >= nritems) {
-                       ret = btrfs_next_leaf(root, path);
-                       if (ret < 0)
-                               goto out;
-                       if (ret == 1) {
-                               ret = 0;
-                               break;
-                       }
-                       leaf = path->nodes[0];
-                       nritems = btrfs_header_nritems(leaf);
-               }
+       spin_lock(&sinfo->lock);
+       spin_lock(&cache->lock);
+       num_bytes = cache->key.offset - cache->reserved - cache->pinned -
+                   cache->bytes_super - btrfs_block_group_used(&cache->item);
+       sinfo->bytes_readonly -= num_bytes;
+       cache->ro = 0;
+       spin_unlock(&cache->lock);
+       spin_unlock(&sinfo->lock);
+       return 0;
+}
 
-               btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+/*
+ * checks to see if its even possible to relocate this block group.
+ *
+ * @return - -1 if it's not a good idea to relocate this block group, 0 if its
+ * ok to go ahead and try.
+ */
+int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
+{
+       struct btrfs_block_group_cache *block_group;
+       struct btrfs_space_info *space_info;
+       struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
+       struct btrfs_device *device;
+       int full = 0;
+       int ret = 0;
 
-               if (key.objectid >= block_group->key.objectid +
-                   block_group->key.offset)
-                       break;
+       block_group = btrfs_lookup_block_group(root->fs_info, bytenr);
 
-               if (progress && need_resched()) {
-                       btrfs_release_path(root, path);
-                       cond_resched();
-                       progress = 0;
-                       continue;
-               }
-               progress = 1;
+       /* odd, couldn't find the block group, leave it alone */
+       if (!block_group)
+               return -1;
 
-               if (btrfs_key_type(&key) != BTRFS_EXTENT_ITEM_KEY ||
-                   key.objectid + key.offset <= cur_byte) {
-                       path->slots[0]++;
-                       goto next;
-               }
+       /* no bytes used, we're good */
+       if (!btrfs_block_group_used(&block_group->item))
+               goto out;
 
-               total_found++;
-               cur_byte = key.objectid + key.offset;
-               btrfs_release_path(root, path);
+       space_info = block_group->space_info;
+       spin_lock(&space_info->lock);
 
-               __alloc_chunk_for_shrink(root, block_group, 0);
-               ret = relocate_one_extent(root, path, &key, block_group,
-                                         reloc_inode, pass);
-               BUG_ON(ret < 0);
-               if (ret > 0)
-                       skipped++;
+       full = space_info->full;
 
-               key.objectid = cur_byte;
-               key.type = 0;
-               key.offset = 0;
+       /*
+        * if this is the last block group we have in this space, we can't
+        * relocate it unless we're able to allocate a new chunk below.
+        *
+        * Otherwise, we need to make sure we have room in the space to handle
+        * all of the extents from this block group.  If we can, we're good
+        */
+       if ((space_info->total_bytes != block_group->key.offset) &&
+          (space_info->bytes_used + space_info->bytes_reserved +
+           space_info->bytes_pinned + space_info->bytes_readonly +
+           btrfs_block_group_used(&block_group->item) <
+           space_info->total_bytes)) {
+               spin_unlock(&space_info->lock);
+               goto out;
        }
+       spin_unlock(&space_info->lock);
 
-       btrfs_release_path(root, path);
+       /*
+        * ok we don't have enough space, but maybe we have free space on our
+        * devices to allocate new chunks for relocation, so loop through our
+        * alloc devices and guess if we have enough space.  However, if we
+        * were marked as full, then we know there aren't enough chunks, and we
+        * can just return.
+        */
+       ret = -1;
+       if (full)
+               goto out;
 
-       if (pass == 0) {
-               btrfs_wait_ordered_range(reloc_inode, 0, (u64)-1);
-               invalidate_mapping_pages(reloc_inode->i_mapping, 0, -1);
-       }
+       mutex_lock(&root->fs_info->chunk_mutex);
+       list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) {
+               u64 min_free = btrfs_block_group_used(&block_group->item);
+               u64 dev_offset, max_avail;
 
-       if (total_found > 0) {
-               printk(KERN_INFO "btrfs found %llu extents in pass %d\n",
-                      (unsigned long long)total_found, pass);
-               pass++;
-               if (total_found == skipped && pass > 2) {
-                       iput(reloc_inode);
-                       reloc_inode = create_reloc_inode(info, block_group);
-                       pass = 0;
+               /*
+                * check to make sure we can actually find a chunk with enough
+                * space to fit our block group in.
+                */
+               if (device->total_bytes > device->bytes_used + min_free) {
+                       ret = find_free_dev_extent(NULL, device, min_free,
+                                                  &dev_offset, &max_avail);
+                       if (!ret)
+                               break;
+                       ret = -1;
                }
-               goto again;
        }
-
-       /* delete reloc_inode */
-       iput(reloc_inode);
-
-       /* unpin extents in this range */
-       trans = btrfs_start_transaction(info->tree_root, 1);
-       btrfs_commit_transaction(trans, info->tree_root);
-
-       spin_lock(&block_group->lock);
-       WARN_ON(block_group->pinned > 0);
-       WARN_ON(block_group->reserved > 0);
-       WARN_ON(btrfs_block_group_used(&block_group->item) > 0);
-       spin_unlock(&block_group->lock);
-       put_block_group(block_group);
-       ret = 0;
+       mutex_unlock(&root->fs_info->chunk_mutex);
 out:
-       btrfs_free_path(path);
+       btrfs_put_block_group(block_group);
        return ret;
 }
 
@@ -5768,17 +8062,60 @@ static int find_first_block_group(struct btrfs_root *root,
                }
                path->slots[0]++;
        }
-       ret = -ENOENT;
 out:
        return ret;
 }
 
+void btrfs_put_block_group_cache(struct btrfs_fs_info *info)
+{
+       struct btrfs_block_group_cache *block_group;
+       u64 last = 0;
+
+       while (1) {
+               struct inode *inode;
+
+               block_group = btrfs_lookup_first_block_group(info, last);
+               while (block_group) {
+                       spin_lock(&block_group->lock);
+                       if (block_group->iref)
+                               break;
+                       spin_unlock(&block_group->lock);
+                       block_group = next_block_group(info->tree_root,
+                                                      block_group);
+               }
+               if (!block_group) {
+                       if (last == 0)
+                               break;
+                       last = 0;
+                       continue;
+               }
+
+               inode = block_group->inode;
+               block_group->iref = 0;
+               block_group->inode = NULL;
+               spin_unlock(&block_group->lock);
+               iput(inode);
+               last = block_group->key.objectid + block_group->key.offset;
+               btrfs_put_block_group(block_group);
+       }
+}
+
 int btrfs_free_block_groups(struct btrfs_fs_info *info)
 {
        struct btrfs_block_group_cache *block_group;
        struct btrfs_space_info *space_info;
+       struct btrfs_caching_control *caching_ctl;
        struct rb_node *n;
 
+       down_write(&info->extent_commit_sem);
+       while (!list_empty(&info->caching_block_groups)) {
+               caching_ctl = list_entry(info->caching_block_groups.next,
+                                        struct btrfs_caching_control, list);
+               list_del(&caching_ctl->list);
+               put_caching_control(caching_ctl);
+       }
+       up_write(&info->extent_commit_sem);
+
        spin_lock(&info->block_group_cache_lock);
        while ((n = rb_last(&info->block_group_cache_tree)) != NULL) {
                block_group = rb_entry(n, struct btrfs_block_group_cache,
@@ -5787,13 +8124,15 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
                         &info->block_group_cache_tree);
                spin_unlock(&info->block_group_cache_lock);
 
-               btrfs_remove_free_space_cache(block_group);
                down_write(&block_group->space_info->groups_sem);
                list_del(&block_group->list);
                up_write(&block_group->space_info->groups_sem);
 
-               WARN_ON(atomic_read(&block_group->count) != 1);
-               kfree(block_group);
+               if (block_group->cached == BTRFS_CACHE_STARTED)
+                       wait_block_group_cache_done(block_group);
+
+               btrfs_remove_free_space_cache(block_group);
+               btrfs_put_block_group(block_group);
 
                spin_lock(&info->block_group_cache_lock);
        }
@@ -5807,17 +8146,33 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
         */
        synchronize_rcu();
 
+       release_global_block_rsv(info);
+
        while(!list_empty(&info->space_info)) {
                space_info = list_entry(info->space_info.next,
                                        struct btrfs_space_info,
                                        list);
-
+               if (space_info->bytes_pinned > 0 ||
+                   space_info->bytes_reserved > 0) {
+                       WARN_ON(1);
+                       dump_space_info(space_info, 0, 0);
+               }
                list_del(&space_info->list);
                kfree(space_info);
        }
        return 0;
 }
 
+static void __link_block_group(struct btrfs_space_info *space_info,
+                              struct btrfs_block_group_cache *cache)
+{
+       int index = get_block_group_index(cache);
+
+       down_write(&space_info->groups_sem);
+       list_add_tail(&cache->list, &space_info->block_groups[index]);
+       up_write(&space_info->groups_sem);
+}
+
 int btrfs_read_block_groups(struct btrfs_root *root)
 {
        struct btrfs_path *path;
@@ -5828,6 +8183,8 @@ int btrfs_read_block_groups(struct btrfs_root *root)
        struct btrfs_key key;
        struct btrfs_key found_key;
        struct extent_buffer *leaf;
+       int need_clear = 0;
+       u64 cache_gen;
 
        root = info->extent_root;
        key.objectid = 0;
@@ -5837,12 +8194,17 @@ int btrfs_read_block_groups(struct btrfs_root *root)
        if (!path)
                return -ENOMEM;
 
+       cache_gen = btrfs_super_cache_generation(&root->fs_info->super_copy);
+       if (cache_gen != 0 &&
+           btrfs_super_generation(&root->fs_info->super_copy) != cache_gen)
+               need_clear = 1;
+       if (btrfs_test_opt(root, CLEAR_CACHE))
+               need_clear = 1;
+
        while (1) {
                ret = find_first_block_group(root, path, &key);
-               if (ret > 0) {
-                       ret = 0;
-                       goto error;
-               }
+               if (ret > 0)
+                       break;
                if (ret != 0)
                        goto error;
 
@@ -5851,14 +8213,27 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                cache = kzalloc(sizeof(*cache), GFP_NOFS);
                if (!cache) {
                        ret = -ENOMEM;
-                       break;
+                       goto error;
                }
 
                atomic_set(&cache->count, 1);
                spin_lock_init(&cache->lock);
-               mutex_init(&cache->alloc_mutex);
-               mutex_init(&cache->cache_mutex);
+               spin_lock_init(&cache->tree_lock);
+               cache->fs_info = info;
                INIT_LIST_HEAD(&cache->list);
+               INIT_LIST_HEAD(&cache->cluster_list);
+
+               if (need_clear)
+                       cache->disk_cache_state = BTRFS_DC_CLEAR;
+
+               /*
+                * we only want to have 32k of ram per block group for keeping
+                * track of free space, and if we pass 1/2 of that we want to
+                * start converting things over to using bitmaps
+                */
+               cache->extents_thresh = ((1024 * 32) / 2) /
+                       sizeof(struct btrfs_free_space);
+
                read_extent_buffer(leaf, &cache->item,
                                   btrfs_item_ptr_offset(leaf, path->slots[0]),
                                   sizeof(cache->item));
@@ -5867,23 +8242,67 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                key.objectid = found_key.objectid + found_key.offset;
                btrfs_release_path(root, path);
                cache->flags = btrfs_block_group_flags(&cache->item);
+               cache->sectorsize = root->sectorsize;
+
+               /*
+                * check for two cases, either we are full, and therefore
+                * don't need to bother with the caching work since we won't
+                * find any space, or we are empty, and we can just add all
+                * the space in and be done with it.  This saves us _alot_ of
+                * time, particularly in the full case.
+                */
+               if (found_key.offset == btrfs_block_group_used(&cache->item)) {
+                       exclude_super_stripes(root, cache);
+                       cache->last_byte_to_unpin = (u64)-1;
+                       cache->cached = BTRFS_CACHE_FINISHED;
+                       free_excluded_extents(root, cache);
+               } else if (btrfs_block_group_used(&cache->item) == 0) {
+                       exclude_super_stripes(root, cache);
+                       cache->last_byte_to_unpin = (u64)-1;
+                       cache->cached = BTRFS_CACHE_FINISHED;
+                       add_new_free_space(cache, root->fs_info,
+                                          found_key.objectid,
+                                          found_key.objectid +
+                                          found_key.offset);
+                       free_excluded_extents(root, cache);
+               }
 
                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;
-               down_write(&space_info->groups_sem);
-               list_add_tail(&cache->list, &space_info->block_groups);
-               up_write(&space_info->groups_sem);
+               spin_lock(&cache->space_info->lock);
+               cache->space_info->bytes_readonly += cache->bytes_super;
+               spin_unlock(&cache->space_info->lock);
+
+               __link_block_group(space_info, cache);
 
                ret = btrfs_add_block_group_cache(root->fs_info, cache);
                BUG_ON(ret);
 
                set_avail_alloc_bits(root->fs_info, cache->flags);
                if (btrfs_chunk_readonly(root, cache->key.objectid))
-                       set_block_group_readonly(cache);
+                       set_block_group_ro(cache);
+       }
+
+       list_for_each_entry_rcu(space_info, &root->fs_info->space_info, list) {
+               if (!(get_alloc_profile(root, space_info->flags) &
+                     (BTRFS_BLOCK_GROUP_RAID10 |
+                      BTRFS_BLOCK_GROUP_RAID1 |
+                      BTRFS_BLOCK_GROUP_DUP)))
+                       continue;
+               /*
+                * avoid allocating from un-mirrored block group if there are
+                * mirrored block groups.
+                */
+               list_for_each_entry(cache, &space_info->block_groups[3], list)
+                       set_block_group_ro(cache);
+               list_for_each_entry(cache, &space_info->block_groups[4], list)
+                       set_block_group_ro(cache);
        }
+
+       init_global_block_rsv(info);
        ret = 0;
 error:
        btrfs_free_path(path);
@@ -5910,23 +8329,45 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
        cache->key.objectid = chunk_offset;
        cache->key.offset = size;
        cache->key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+       cache->sectorsize = root->sectorsize;
+       cache->fs_info = root->fs_info;
+
+       /*
+        * we only want to have 32k of ram per block group for keeping track
+        * of free space, and if we pass 1/2 of that we want to start
+        * converting things over to using bitmaps
+        */
+       cache->extents_thresh = ((1024 * 32) / 2) /
+               sizeof(struct btrfs_free_space);
        atomic_set(&cache->count, 1);
        spin_lock_init(&cache->lock);
-       mutex_init(&cache->alloc_mutex);
-       mutex_init(&cache->cache_mutex);
+       spin_lock_init(&cache->tree_lock);
        INIT_LIST_HEAD(&cache->list);
+       INIT_LIST_HEAD(&cache->cluster_list);
 
        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);
 
+       cache->last_byte_to_unpin = (u64)-1;
+       cache->cached = BTRFS_CACHE_FINISHED;
+       exclude_super_stripes(root, cache);
+
+       add_new_free_space(cache, root->fs_info, chunk_offset,
+                          chunk_offset + size);
+
+       free_excluded_extents(root, cache);
+
        ret = update_space_info(root->fs_info, cache->flags, size, bytes_used,
                                &cache->space_info);
        BUG_ON(ret);
-       down_write(&cache->space_info->groups_sem);
-       list_add_tail(&cache->list, &cache->space_info->block_groups);
-       up_write(&cache->space_info->groups_sem);
+
+       spin_lock(&cache->space_info->lock);
+       cache->space_info->bytes_readonly += cache->bytes_super;
+       spin_unlock(&cache->space_info->lock);
+
+       __link_block_group(cache->space_info, cache);
 
        ret = btrfs_add_block_group_cache(root->fs_info, cache);
        BUG_ON(ret);
@@ -5945,7 +8386,10 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
 {
        struct btrfs_path *path;
        struct btrfs_block_group_cache *block_group;
+       struct btrfs_free_cluster *cluster;
+       struct btrfs_root *tree_root = root->fs_info->tree_root;
        struct btrfs_key key;
+       struct inode *inode;
        int ret;
 
        root = root->fs_info->extent_root;
@@ -5954,28 +8398,87 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
        BUG_ON(!block_group);
        BUG_ON(!block_group->ro);
 
-       memcpy(&key, &block_group->key, sizeof(key));
+       /* make sure this block group isn't part of an allocation cluster */
+       cluster = &root->fs_info->data_alloc_cluster;
+       spin_lock(&cluster->refill_lock);
+       btrfs_return_cluster_to_free_space(block_group, cluster);
+       spin_unlock(&cluster->refill_lock);
+
+       /*
+        * make sure this block group isn't part of a metadata
+        * allocation cluster
+        */
+       cluster = &root->fs_info->meta_alloc_cluster;
+       spin_lock(&cluster->refill_lock);
+       btrfs_return_cluster_to_free_space(block_group, cluster);
+       spin_unlock(&cluster->refill_lock);
 
        path = btrfs_alloc_path();
        BUG_ON(!path);
 
+       inode = lookup_free_space_inode(root, block_group, path);
+       if (!IS_ERR(inode)) {
+               btrfs_orphan_add(trans, inode);
+               clear_nlink(inode);
+               /* One for the block groups ref */
+               spin_lock(&block_group->lock);
+               if (block_group->iref) {
+                       block_group->iref = 0;
+                       block_group->inode = NULL;
+                       spin_unlock(&block_group->lock);
+                       iput(inode);
+               } else {
+                       spin_unlock(&block_group->lock);
+               }
+               /* One for our lookup ref */
+               iput(inode);
+       }
+
+       key.objectid = BTRFS_FREE_SPACE_OBJECTID;
+       key.offset = block_group->key.objectid;
+       key.type = 0;
+
+       ret = btrfs_search_slot(trans, tree_root, &key, path, -1, 1);
+       if (ret < 0)
+               goto out;
+       if (ret > 0)
+               btrfs_release_path(tree_root, path);
+       if (ret == 0) {
+               ret = btrfs_del_item(trans, tree_root, path);
+               if (ret)
+                       goto out;
+               btrfs_release_path(tree_root, path);
+       }
+
        spin_lock(&root->fs_info->block_group_cache_lock);
        rb_erase(&block_group->cache_node,
                 &root->fs_info->block_group_cache_tree);
        spin_unlock(&root->fs_info->block_group_cache_lock);
-       btrfs_remove_free_space_cache(block_group);
+
        down_write(&block_group->space_info->groups_sem);
-       list_del(&block_group->list);
+       /*
+        * we must use list_del_init so people can check to see if they
+        * are still on the list after taking the semaphore
+        */
+       list_del_init(&block_group->list);
        up_write(&block_group->space_info->groups_sem);
 
+       if (block_group->cached == BTRFS_CACHE_STARTED)
+               wait_block_group_cache_done(block_group);
+
+       btrfs_remove_free_space_cache(block_group);
+
        spin_lock(&block_group->space_info->lock);
        block_group->space_info->total_bytes -= block_group->key.offset;
        block_group->space_info->bytes_readonly -= block_group->key.offset;
        spin_unlock(&block_group->space_info->lock);
-       block_group->space_info->full = 0;
 
-       put_block_group(block_group);
-       put_block_group(block_group);
+       memcpy(&key, &block_group->key, sizeof(key));
+
+       btrfs_clear_space_info_full(root->fs_info);
+
+       btrfs_put_block_group(block_group);
+       btrfs_put_block_group(block_group);
 
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
        if (ret > 0)