Btrfs: allocate new chunks if the space is not enough for global rsv
[linux-2.6-block.git] / fs / btrfs / extent-tree.c
index 3d551231cabae9e43c7a1e90385f22fd25891239..ef4ce2c026d6a69a8d98bfa7ea3eae7fa9615382 100644 (file)
@@ -270,9 +270,27 @@ static int exclude_super_stripes(struct btrfs_root *root,
                        return ret;
 
                while (nr--) {
-                       cache->bytes_super += stripe_len;
-                       ret = add_excluded_extent(root, logical[nr],
-                                                 stripe_len);
+                       u64 start, len;
+
+                       if (logical[nr] > cache->key.objectid +
+                           cache->key.offset)
+                               continue;
+
+                       if (logical[nr] + stripe_len <= cache->key.objectid)
+                               continue;
+
+                       start = logical[nr];
+                       if (start < cache->key.objectid) {
+                               start = cache->key.objectid;
+                               len = (logical[nr] + stripe_len) - start;
+                       } else {
+                               len = min_t(u64, stripe_len,
+                                           cache->key.objectid +
+                                           cache->key.offset - start);
+                       }
+
+                       cache->bytes_super += len;
+                       ret = add_excluded_extent(root, start, len);
                        if (ret) {
                                kfree(logical);
                                return ret;
@@ -419,8 +437,7 @@ again:
                        if (ret)
                                break;
 
-                       if (need_resched() ||
-                           btrfs_next_leaf(extent_root, path)) {
+                       if (need_resched()) {
                                caching_ctl->progress = last;
                                btrfs_release_path(path);
                                up_read(&fs_info->extent_commit_sem);
@@ -428,6 +445,12 @@ again:
                                cond_resched();
                                goto again;
                        }
+
+                       ret = btrfs_next_leaf(extent_root, path);
+                       if (ret < 0)
+                               goto err;
+                       if (ret)
+                               break;
                        leaf = path->nodes[0];
                        nritems = btrfs_header_nritems(leaf);
                        continue;
@@ -442,11 +465,16 @@ again:
                    block_group->key.offset)
                        break;
 
-               if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+               if (key.type == BTRFS_EXTENT_ITEM_KEY ||
+                   key.type == BTRFS_METADATA_ITEM_KEY) {
                        total_found += add_new_free_space(block_group,
                                                          fs_info, last,
                                                          key.objectid);
-                       last = key.objectid + key.offset;
+                       if (key.type == BTRFS_METADATA_ITEM_KEY)
+                               last = key.objectid +
+                                       fs_info->tree_root->leafsize;
+                       else
+                               last = key.objectid + key.offset;
 
                        if (total_found > (1024 * 1024 * 2)) {
                                total_found = 0;
@@ -718,15 +746,21 @@ int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len)
 
        key.objectid = start;
        key.offset = len;
-       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+       key.type = BTRFS_EXTENT_ITEM_KEY;
        ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, path,
                                0, 0);
+       if (ret > 0) {
+               btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+               if (key.objectid == start &&
+                   key.type == BTRFS_METADATA_ITEM_KEY)
+                       ret = 0;
+       }
        btrfs_free_path(path);
        return ret;
 }
 
 /*
- * helper function to lookup reference count and flags of extent.
+ * helper function to lookup reference count and flags of a tree block.
  *
  * 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
@@ -736,7 +770,7 @@ int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len)
  */
 int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root, u64 bytenr,
-                            u64 num_bytes, u64 *refs, u64 *flags)
+                            u64 offset, int metadata, u64 *refs, u64 *flags)
 {
        struct btrfs_delayed_ref_head *head;
        struct btrfs_delayed_ref_root *delayed_refs;
@@ -749,13 +783,29 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
        u64 extent_flags;
        int ret;
 
+       /*
+        * If we don't have skinny metadata, don't bother doing anything
+        * different
+        */
+       if (metadata && !btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) {
+               offset = root->leafsize;
+               metadata = 0;
+       }
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
 
-       key.objectid = bytenr;
-       key.type = BTRFS_EXTENT_ITEM_KEY;
-       key.offset = num_bytes;
+       if (metadata) {
+               key.objectid = bytenr;
+               key.type = BTRFS_METADATA_ITEM_KEY;
+               key.offset = offset;
+       } else {
+               key.objectid = bytenr;
+               key.type = BTRFS_EXTENT_ITEM_KEY;
+               key.offset = offset;
+       }
+
        if (!trans) {
                path->skip_locking = 1;
                path->search_commit_root = 1;
@@ -766,6 +816,13 @@ again:
        if (ret < 0)
                goto out_free;
 
+       if (ret > 0 && metadata && key.type == BTRFS_METADATA_ITEM_KEY) {
+               key.type = BTRFS_EXTENT_ITEM_KEY;
+               key.offset = root->leafsize;
+               btrfs_release_path(path);
+               goto again;
+       }
+
        if (ret == 0) {
                leaf = path->nodes[0];
                item_size = btrfs_item_size_nr(leaf, path->slots[0]);
@@ -1001,7 +1058,7 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
                return ret;
        BUG_ON(ret); /* Corruption */
 
-       btrfs_extend_item(trans, root, path, new_size);
+       btrfs_extend_item(root, path, new_size);
 
        leaf = path->nodes[0];
        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
@@ -1453,6 +1510,8 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
        int want;
        int ret;
        int err = 0;
+       bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
+                                                SKINNY_METADATA);
 
        key.objectid = bytenr;
        key.type = BTRFS_EXTENT_ITEM_KEY;
@@ -1464,11 +1523,46 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
                path->keep_locks = 1;
        } else
                extra_size = -1;
+
+       /*
+        * Owner is our parent level, so we can just add one to get the level
+        * for the block we are interested in.
+        */
+       if (skinny_metadata && owner < BTRFS_FIRST_FREE_OBJECTID) {
+               key.type = BTRFS_METADATA_ITEM_KEY;
+               key.offset = owner;
+       }
+
+again:
        ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1);
        if (ret < 0) {
                err = ret;
                goto out;
        }
+
+       /*
+        * We may be a newly converted file system which still has the old fat
+        * extent entries for metadata, so try and see if we have one of those.
+        */
+       if (ret > 0 && skinny_metadata) {
+               skinny_metadata = false;
+               if (path->slots[0]) {
+                       path->slots[0]--;
+                       btrfs_item_key_to_cpu(path->nodes[0], &key,
+                                             path->slots[0]);
+                       if (key.objectid == bytenr &&
+                           key.type == BTRFS_EXTENT_ITEM_KEY &&
+                           key.offset == num_bytes)
+                               ret = 0;
+               }
+               if (ret) {
+                       key.type = BTRFS_EXTENT_ITEM_KEY;
+                       key.offset = num_bytes;
+                       btrfs_release_path(path);
+                       goto again;
+               }
+       }
+
        if (ret && !insert) {
                err = -ENOENT;
                goto out;
@@ -1504,11 +1598,9 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
        ptr = (unsigned long)(ei + 1);
        end = (unsigned long)ei + item_size;
 
-       if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+       if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK && !skinny_metadata) {
                ptr += sizeof(struct btrfs_tree_block_info);
                BUG_ON(ptr > end);
-       } else {
-               BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA));
        }
 
        err = -ENOENT;
@@ -1590,8 +1682,7 @@ out:
  * helper to add new inline back ref
  */
 static noinline_for_stack
-void setup_inline_extent_backref(struct btrfs_trans_handle *trans,
-                                struct btrfs_root *root,
+void setup_inline_extent_backref(struct btrfs_root *root,
                                 struct btrfs_path *path,
                                 struct btrfs_extent_inline_ref *iref,
                                 u64 parent, u64 root_objectid,
@@ -1614,7 +1705,7 @@ void setup_inline_extent_backref(struct btrfs_trans_handle *trans,
        type = extent_ref_type(parent, owner);
        size = btrfs_extent_inline_ref_size(type);
 
-       btrfs_extend_item(trans, root, path, size);
+       btrfs_extend_item(root, path, size);
 
        ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
        refs = btrfs_extent_refs(leaf, ei);
@@ -1683,8 +1774,7 @@ static int lookup_extent_backref(struct btrfs_trans_handle *trans,
  * helper to update/remove inline back ref
  */
 static noinline_for_stack
-void update_inline_extent_backref(struct btrfs_trans_handle *trans,
-                                 struct btrfs_root *root,
+void update_inline_extent_backref(struct btrfs_root *root,
                                  struct btrfs_path *path,
                                  struct btrfs_extent_inline_ref *iref,
                                  int refs_to_mod,
@@ -1740,7 +1830,7 @@ void update_inline_extent_backref(struct btrfs_trans_handle *trans,
                        memmove_extent_buffer(leaf, ptr, ptr + size,
                                              end - ptr - size);
                item_size -= size;
-               btrfs_truncate_item(trans, root, path, item_size, 1);
+               btrfs_truncate_item(root, path, item_size, 1);
        }
        btrfs_mark_buffer_dirty(leaf);
 }
@@ -1762,10 +1852,10 @@ int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
                                           root_objectid, owner, offset, 1);
        if (ret == 0) {
                BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID);
-               update_inline_extent_backref(trans, root, path, iref,
+               update_inline_extent_backref(root, path, iref,
                                             refs_to_add, extent_op);
        } else if (ret == -ENOENT) {
-               setup_inline_extent_backref(trans, root, path, iref, parent,
+               setup_inline_extent_backref(root, path, iref, parent,
                                            root_objectid, owner, offset,
                                            refs_to_add, extent_op);
                ret = 0;
@@ -1802,7 +1892,7 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans,
 
        BUG_ON(!is_data && refs_to_drop != 1);
        if (iref) {
-               update_inline_extent_backref(trans, root, path, iref,
+               update_inline_extent_backref(root, path, iref,
                                             -refs_to_drop, NULL);
        } else if (is_data) {
                ret = remove_extent_data_ref(trans, root, path, refs_to_drop);
@@ -1973,10 +2063,8 @@ static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
                ref_root = ref->root;
 
        if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
-               if (extent_op) {
-                       BUG_ON(extent_op->update_key);
+               if (extent_op)
                        flags |= extent_op->flags_to_set;
-               }
                ret = alloc_reserved_file_extent(trans, root,
                                                 parent, ref_root, flags,
                                                 ref->objectid, ref->offset,
@@ -2029,18 +2117,33 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
        u32 item_size;
        int ret;
        int err = 0;
+       int metadata = (node->type == BTRFS_TREE_BLOCK_REF_KEY ||
+                       node->type == BTRFS_SHARED_BLOCK_REF_KEY);
 
        if (trans->aborted)
                return 0;
 
+       if (metadata && !btrfs_fs_incompat(root->fs_info, SKINNY_METADATA))
+               metadata = 0;
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
 
        key.objectid = node->bytenr;
-       key.type = BTRFS_EXTENT_ITEM_KEY;
-       key.offset = node->num_bytes;
 
+       if (metadata) {
+               struct btrfs_delayed_tree_ref *tree_ref;
+
+               tree_ref = btrfs_delayed_node_to_tree_ref(node);
+               key.type = BTRFS_METADATA_ITEM_KEY;
+               key.offset = tree_ref->level;
+       } else {
+               key.type = BTRFS_EXTENT_ITEM_KEY;
+               key.offset = node->num_bytes;
+       }
+
+again:
        path->reada = 1;
        path->leave_spinning = 1;
        ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key,
@@ -2050,6 +2153,14 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
                goto out;
        }
        if (ret > 0) {
+               if (metadata) {
+                       btrfs_release_path(path);
+                       metadata = 0;
+
+                       key.offset = node->num_bytes;
+                       key.type = BTRFS_EXTENT_ITEM_KEY;
+                       goto again;
+               }
                err = -EIO;
                goto out;
        }
@@ -2089,10 +2200,8 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
        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;
+       bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
+                                                SKINNY_METADATA);
 
        ref = btrfs_delayed_node_to_tree_ref(node);
        if (node->type == BTRFS_SHARED_BLOCK_REF_KEY)
@@ -2100,10 +2209,18 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
        else
                ref_root = ref->root;
 
+       ins.objectid = node->bytenr;
+       if (skinny_metadata) {
+               ins.offset = ref->level;
+               ins.type = BTRFS_METADATA_ITEM_KEY;
+       } else {
+               ins.offset = node->num_bytes;
+               ins.type = BTRFS_EXTENT_ITEM_KEY;
+       }
+
        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);
+               BUG_ON(!extent_op || !extent_op->update_flags);
                ret = alloc_reserved_tree_block(trans, root,
                                                parent, ref_root,
                                                extent_op->flags_to_set,
@@ -2307,9 +2424,7 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
                                btrfs_free_delayed_extent_op(extent_op);
 
                                if (ret) {
-                                       printk(KERN_DEBUG
-                                              "btrfs: run_delayed_extent_op "
-                                              "returned %d\n", ret);
+                                       btrfs_debug(fs_info, "run_delayed_extent_op returned %d", ret);
                                        spin_lock(&delayed_refs->lock);
                                        btrfs_delayed_ref_unlock(locked_ref);
                                        return ret;
@@ -2348,8 +2463,7 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
                if (ret) {
                        btrfs_delayed_ref_unlock(locked_ref);
                        btrfs_put_delayed_ref(ref);
-                       printk(KERN_DEBUG
-                              "btrfs: run_one_delayed_ref returned %d\n", ret);
+                       btrfs_debug(fs_info, "run_one_delayed_ref returned %d", ret);
                        spin_lock(&delayed_refs->lock);
                        return ret;
                }
@@ -2426,9 +2540,11 @@ int btrfs_delayed_refs_qgroup_accounting(struct btrfs_trans_handle *trans,
        if (list_empty(&trans->qgroup_ref_list) !=
            !trans->delayed_ref_elem.seq) {
                /* list without seq or seq without list */
-               printk(KERN_ERR "btrfs: qgroup accounting update error, list is%s empty, seq is %llu\n",
+               btrfs_err(fs_info,
+                       "qgroup accounting update error, list is%s empty, seq is %#x.%x",
                        list_empty(&trans->qgroup_ref_list) ? "" : " not",
-                       trans->delayed_ref_elem.seq);
+                       (u32)(trans->delayed_ref_elem.seq >> 32),
+                       (u32)trans->delayed_ref_elem.seq);
                BUG();
        }
 
@@ -3557,6 +3673,11 @@ static void force_metadata_allocation(struct btrfs_fs_info *info)
        rcu_read_unlock();
 }
 
+static inline u64 calc_global_rsv_need_space(struct btrfs_block_rsv *global)
+{
+       return (global->size << 1);
+}
+
 static int should_alloc_chunk(struct btrfs_root *root,
                              struct btrfs_space_info *sinfo, int force)
 {
@@ -3574,7 +3695,7 @@ static int should_alloc_chunk(struct btrfs_root *root,
         * global_rsv, it doesn't change except when the transaction commits.
         */
        if (sinfo->flags & BTRFS_BLOCK_GROUP_METADATA)
-               num_allocated += global_rsv->size;
+               num_allocated += calc_global_rsv_need_space(global_rsv);
 
        /*
         * in limited mode, we want to have some free space up to
@@ -3627,8 +3748,8 @@ static void check_system_chunk(struct btrfs_trans_handle *trans,
 
        thresh = get_system_chunk_thresh(root, type);
        if (left < thresh && btrfs_test_opt(root, ENOSPC_DEBUG)) {
-               printk(KERN_INFO "left=%llu, need=%llu, flags=%llu\n",
-                      left, thresh, type);
+               btrfs_info(root->fs_info, "left=%llu, need=%llu, flags=%llu",
+                       left, thresh, type);
                dump_space_info(info, 0, 0);
        }
 
@@ -3746,7 +3867,7 @@ static int can_overcommit(struct btrfs_root *root,
 {
        struct btrfs_block_rsv *global_rsv = &root->fs_info->global_block_rsv;
        u64 profile = btrfs_get_alloc_profile(root, 0);
-       u64 rsv_size = 0;
+       u64 space_size;
        u64 avail;
        u64 used;
        u64 to_add;
@@ -3754,18 +3875,16 @@ static int can_overcommit(struct btrfs_root *root,
        used = space_info->bytes_used + space_info->bytes_reserved +
                space_info->bytes_pinned + space_info->bytes_readonly;
 
-       spin_lock(&global_rsv->lock);
-       rsv_size = global_rsv->size;
-       spin_unlock(&global_rsv->lock);
-
        /*
         * We only want to allow over committing if we have lots of actual space
         * free, but if we don't have enough space to handle the global reserve
         * space then we could end up having a real enospc problem when trying
         * to allocate a chunk or some other such important allocation.
         */
-       rsv_size <<= 1;
-       if (used + rsv_size >= space_info->total_bytes)
+       spin_lock(&global_rsv->lock);
+       space_size = calc_global_rsv_need_space(global_rsv);
+       spin_unlock(&global_rsv->lock);
+       if (used + space_size >= space_info->total_bytes)
                return 0;
 
        used += space_info->bytes_may_use;
@@ -3826,7 +3945,8 @@ void btrfs_writeback_inodes_sb_nr(struct btrfs_root *root,
                 * the disk).
                 */
                btrfs_start_delalloc_inodes(root, 0);
-               btrfs_wait_ordered_extents(root, 0);
+               if (!current->journal_info)
+                       btrfs_wait_ordered_extents(root, 0);
        }
 }
 
@@ -5312,6 +5432,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        int num_to_del = 1;
        u32 item_size;
        u64 refs;
+       bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
+                                                SKINNY_METADATA);
 
        path = btrfs_alloc_path();
        if (!path)
@@ -5323,6 +5445,9 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        is_data = owner_objectid >= BTRFS_FIRST_FREE_OBJECTID;
        BUG_ON(!is_data && refs_to_drop != 1);
 
+       if (is_data)
+               skinny_metadata = 0;
+
        ret = lookup_extent_backref(trans, extent_root, path, &iref,
                                    bytenr, num_bytes, parent,
                                    root_objectid, owner_objectid,
@@ -5339,6 +5464,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                found_extent = 1;
                                break;
                        }
+                       if (key.type == BTRFS_METADATA_ITEM_KEY &&
+                           key.offset == owner_objectid) {
+                               found_extent = 1;
+                               break;
+                       }
                        if (path->slots[0] - extent_slot > 5)
                                break;
                        extent_slot--;
@@ -5364,12 +5494,39 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        key.type = BTRFS_EXTENT_ITEM_KEY;
                        key.offset = num_bytes;
 
+                       if (!is_data && skinny_metadata) {
+                               key.type = BTRFS_METADATA_ITEM_KEY;
+                               key.offset = owner_objectid;
+                       }
+
                        ret = btrfs_search_slot(trans, extent_root,
                                                &key, path, -1, 1);
+                       if (ret > 0 && skinny_metadata && path->slots[0]) {
+                               /*
+                                * Couldn't find our skinny metadata item,
+                                * see if we have ye olde extent item.
+                                */
+                               path->slots[0]--;
+                               btrfs_item_key_to_cpu(path->nodes[0], &key,
+                                                     path->slots[0]);
+                               if (key.objectid == bytenr &&
+                                   key.type == BTRFS_EXTENT_ITEM_KEY &&
+                                   key.offset == num_bytes)
+                                       ret = 0;
+                       }
+
+                       if (ret > 0 && skinny_metadata) {
+                               skinny_metadata = false;
+                               key.type = BTRFS_EXTENT_ITEM_KEY;
+                               key.offset = num_bytes;
+                               btrfs_release_path(path);
+                               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_err(info, "umm, got %d back from search, was looking for %llu",
+                                       ret, (unsigned long long)bytenr);
                                if (ret > 0)
                                        btrfs_print_leaf(extent_root,
                                                         path->nodes[0]);
@@ -5383,13 +5540,13 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        } else if (ret == -ENOENT) {
                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  owner %llu offset %llu\n",
-                      (unsigned long long)bytenr,
-                      (unsigned long long)parent,
-                      (unsigned long long)root_objectid,
-                      (unsigned long long)owner_objectid,
-                      (unsigned long long)owner_offset);
+               btrfs_err(info,
+                       "unable to find ref byte nr %llu parent %llu root %llu  owner %llu offset %llu",
+                       (unsigned long long)bytenr,
+                       (unsigned long long)parent,
+                       (unsigned long long)root_objectid,
+                       (unsigned long long)owner_objectid,
+                       (unsigned long long)owner_offset);
        } else {
                btrfs_abort_transaction(trans, extent_root, ret);
                goto out;
@@ -5417,9 +5574,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                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_err(info, "umm, got %d back from search, was looking for %llu",
+                               ret, (unsigned long long)bytenr);
                        btrfs_print_leaf(extent_root, path->nodes[0]);
                }
                if (ret < 0) {
@@ -5435,7 +5591,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        BUG_ON(item_size < sizeof(*ei));
        ei = btrfs_item_ptr(leaf, extent_slot,
                            struct btrfs_extent_item);
-       if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID) {
+       if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID &&
+           key.type == BTRFS_EXTENT_ITEM_KEY) {
                struct btrfs_tree_block_info *bi;
                BUG_ON(item_size < sizeof(*ei) + sizeof(*bi));
                bi = (struct btrfs_tree_block_info *)(ei + 1);
@@ -5443,7 +5600,13 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        }
 
        refs = btrfs_extent_refs(leaf, ei);
-       BUG_ON(refs < refs_to_drop);
+       if (refs < refs_to_drop) {
+               btrfs_err(info, "trying to drop %d refs but we only have %Lu "
+                         "for bytenr %Lu\n", refs_to_drop, refs, bytenr);
+               ret = -EINVAL;
+               btrfs_abort_transaction(trans, extent_root, ret);
+               goto out;
+       }
        refs -= refs_to_drop;
 
        if (refs > 0) {
@@ -5787,7 +5950,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
 
        space_info = __find_space_info(root->fs_info, data);
        if (!space_info) {
-               printk(KERN_ERR "No space info for %llu\n", data);
+               btrfs_err(root->fs_info, "No space info for %llu", data);
                return -ENOSPC;
        }
 
@@ -6211,9 +6374,9 @@ again:
                        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);
+                       btrfs_err(root->fs_info, "allocation failed flags %llu, wanted %llu",
+                               (unsigned long long)data,
+                               (unsigned long long)num_bytes);
                        if (sinfo)
                                dump_space_info(sinfo, num_bytes, 1);
                }
@@ -6232,8 +6395,8 @@ static int __btrfs_free_reserved_extent(struct btrfs_root *root,
 
        cache = btrfs_lookup_block_group(root->fs_info, start);
        if (!cache) {
-               printk(KERN_ERR "Unable to find block group for %llu\n",
-                      (unsigned long long)start);
+               btrfs_err(root->fs_info, "Unable to find block group for %llu",
+                       (unsigned long long)start);
                return -ENOSPC;
        }
 
@@ -6328,9 +6491,9 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
 
        ret = update_block_group(root, ins->objectid, ins->offset, 1);
        if (ret) { /* -ENOENT, logic error */
-               printk(KERN_ERR "btrfs update block group failed for %llu "
-                      "%llu\n", (unsigned long long)ins->objectid,
-                      (unsigned long long)ins->offset);
+               btrfs_err(fs_info, "update block group failed for %llu %llu",
+                       (unsigned long long)ins->objectid,
+                       (unsigned long long)ins->offset);
                BUG();
        }
        return ret;
@@ -6349,7 +6512,12 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        struct btrfs_extent_inline_ref *iref;
        struct btrfs_path *path;
        struct extent_buffer *leaf;
-       u32 size = sizeof(*extent_item) + sizeof(*block_info) + sizeof(*iref);
+       u32 size = sizeof(*extent_item) + sizeof(*iref);
+       bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
+                                                SKINNY_METADATA);
+
+       if (!skinny_metadata)
+               size += sizeof(*block_info);
 
        path = btrfs_alloc_path();
        if (!path)
@@ -6370,12 +6538,16 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        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);
+       if (skinny_metadata) {
+               iref = (struct btrfs_extent_inline_ref *)(extent_item + 1);
+       } else {
+               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);
+       }
 
-       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,
@@ -6390,11 +6562,11 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(leaf);
        btrfs_free_path(path);
 
-       ret = update_block_group(root, ins->objectid, ins->offset, 1);
+       ret = update_block_group(root, ins->objectid, root->leafsize, 1);
        if (ret) { /* -ENOENT, logic error */
-               printk(KERN_ERR "btrfs update block group failed for %llu "
-                      "%llu\n", (unsigned long long)ins->objectid,
-                      (unsigned long long)ins->offset);
+               btrfs_err(fs_info, "update block group failed for %llu %llu",
+                       (unsigned long long)ins->objectid,
+                       (unsigned long long)ins->offset);
                BUG();
        }
        return ret;
@@ -6594,7 +6766,8 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
        struct extent_buffer *buf;
        u64 flags = 0;
        int ret;
-
+       bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
+                                                SKINNY_METADATA);
 
        block_rsv = use_block_rsv(trans, root, blocksize);
        if (IS_ERR(block_rsv))
@@ -6627,7 +6800,10 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
                else
                        memset(&extent_op->key, 0, sizeof(extent_op->key));
                extent_op->flags_to_set = flags;
-               extent_op->update_key = 1;
+               if (skinny_metadata)
+                       extent_op->update_key = 0;
+               else
+                       extent_op->update_key = 1;
                extent_op->update_flags = 1;
                extent_op->is_data = 0;
 
@@ -6704,8 +6880,9 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
                        continue;
 
                /* 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);
+               ret = btrfs_lookup_extent_info(trans, root, bytenr,
+                                              wc->level - 1, 1, &refs,
+                                              &flags);
                /* We don't care about errors in readahead. */
                if (ret < 0)
                        continue;
@@ -6772,7 +6949,7 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
             (wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag)))) {
                BUG_ON(!path->locks[level]);
                ret = btrfs_lookup_extent_info(trans, root,
-                                              eb->start, eb->len,
+                                              eb->start, level, 1,
                                               &wc->refs[level],
                                               &wc->flags[level]);
                BUG_ON(ret == -ENOMEM);
@@ -6870,7 +7047,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
        btrfs_tree_lock(next);
        btrfs_set_lock_blocking(next);
 
-       ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize,
+       ret = btrfs_lookup_extent_info(trans, root, bytenr, level - 1, 1,
                                       &wc->refs[level - 1],
                                       &wc->flags[level - 1]);
        if (ret < 0) {
@@ -6878,7 +7055,10 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
                return ret;
        }
 
-       BUG_ON(wc->refs[level - 1] == 0);
+       if (unlikely(wc->refs[level - 1] == 0)) {
+               btrfs_err(root->fs_info, "Missing references.");
+               BUG();
+       }
        *lookup_info = 0;
 
        if (wc->stage == DROP_REFERENCE) {
@@ -6917,8 +7097,10 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
                if (reada && level == 1)
                        reada_walk_down(trans, root, wc, path);
                next = read_tree_block(root, bytenr, blocksize, generation);
-               if (!next)
+               if (!next || !extent_buffer_uptodate(next)) {
+                       free_extent_buffer(next);
                        return -EIO;
+               }
                btrfs_tree_lock(next);
                btrfs_set_lock_blocking(next);
        }
@@ -7001,7 +7183,7 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
                        path->locks[level] = BTRFS_WRITE_LOCK_BLOCKING;
 
                        ret = btrfs_lookup_extent_info(trans, root,
-                                                      eb->start, eb->len,
+                                                      eb->start, level, 1,
                                                       &wc->refs[level],
                                                       &wc->flags[level]);
                        if (ret < 0) {
@@ -7137,6 +7319,8 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
  * 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.
+ *
+ * If called with for_reloc == 0, may exit early with -EAGAIN
  */
 int btrfs_drop_snapshot(struct btrfs_root *root,
                         struct btrfs_block_rsv *block_rsv, int update_ref,
@@ -7211,8 +7395,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
 
                        ret = btrfs_lookup_extent_info(trans, root,
                                                path->nodes[level]->start,
-                                               path->nodes[level]->len,
-                                               &wc->refs[level],
+                                               level, 1, &wc->refs[level],
                                                &wc->flags[level]);
                        if (ret < 0) {
                                err = ret;
@@ -7238,6 +7421,12 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
        wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root);
 
        while (1) {
+               if (!for_reloc && btrfs_fs_closing(root->fs_info)) {
+                       pr_debug("btrfs: drop snapshot early exit\n");
+                       err = -EAGAIN;
+                       goto out_end_trans;
+               }
+
                ret = walk_down_tree(trans, root, path, wc);
                if (ret < 0) {
                        err = ret;
@@ -8020,10 +8209,26 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                        free_excluded_extents(root, cache);
                }
 
+               ret = btrfs_add_block_group_cache(root->fs_info, cache);
+               if (ret) {
+                       btrfs_remove_free_space_cache(cache);
+                       btrfs_put_block_group(cache);
+                       goto error;
+               }
+
                ret = update_space_info(info, cache->flags, found_key.offset,
                                        btrfs_block_group_used(&cache->item),
                                        &space_info);
-               BUG_ON(ret); /* -ENOMEM */
+               if (ret) {
+                       btrfs_remove_free_space_cache(cache);
+                       spin_lock(&info->block_group_cache_lock);
+                       rb_erase(&cache->cache_node,
+                                &info->block_group_cache_tree);
+                       spin_unlock(&info->block_group_cache_lock);
+                       btrfs_put_block_group(cache);
+                       goto error;
+               }
+
                cache->space_info = space_info;
                spin_lock(&cache->space_info->lock);
                cache->space_info->bytes_readonly += cache->bytes_super;
@@ -8031,9 +8236,6 @@ int btrfs_read_block_groups(struct btrfs_root *root)
 
                __link_block_group(space_info, cache);
 
-               ret = btrfs_add_block_group_cache(root->fs_info, cache);
-               BUG_ON(ret); /* Logic error */
-
                set_avail_alloc_bits(root->fs_info, cache->flags);
                if (btrfs_chunk_readonly(root, cache->key.objectid))
                        set_block_group_ro(cache, 1);
@@ -8156,9 +8358,24 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
 
        free_excluded_extents(root, cache);
 
+       ret = btrfs_add_block_group_cache(root->fs_info, cache);
+       if (ret) {
+               btrfs_remove_free_space_cache(cache);
+               btrfs_put_block_group(cache);
+               return ret;
+       }
+
        ret = update_space_info(root->fs_info, cache->flags, size, bytes_used,
                                &cache->space_info);
-       BUG_ON(ret); /* -ENOMEM */
+       if (ret) {
+               btrfs_remove_free_space_cache(cache);
+               spin_lock(&root->fs_info->block_group_cache_lock);
+               rb_erase(&cache->cache_node,
+                        &root->fs_info->block_group_cache_tree);
+               spin_unlock(&root->fs_info->block_group_cache_lock);
+               btrfs_put_block_group(cache);
+               return ret;
+       }
        update_global_block_rsv(root->fs_info);
 
        spin_lock(&cache->space_info->lock);
@@ -8167,9 +8384,6 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
 
        __link_block_group(cache->space_info, cache);
 
-       ret = btrfs_add_block_group_cache(root->fs_info, cache);
-       BUG_ON(ret); /* Logic error */
-
        list_add_tail(&cache->new_bg_list, &trans->new_bgs);
 
        set_avail_alloc_bits(extent_root->fs_info, type);