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;
 
        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
  */
 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;
        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;
        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]);
        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;
                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;
        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;
                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,
        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,
                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;
        }
        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)
        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,
        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)
        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,
                                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--;
                        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,
        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);
        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)
        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,
        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,
        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))
                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;
 
                        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;
             (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);
        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) {
                        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) {
 
                        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;
 
 int find_inline_backref(struct extent_buffer *leaf, int slot,
                        unsigned long *ptr, unsigned long *end)
 {
+       struct btrfs_key key;
        struct btrfs_extent_item *ei;
        struct btrfs_tree_block_info *bi;
        u32 item_size;
 
+       btrfs_item_key_to_cpu(leaf, &key, slot);
+
        item_size = btrfs_item_size_nr(leaf, slot);
 #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
        if (item_size < sizeof(*ei)) {
        WARN_ON(!(btrfs_extent_flags(leaf, ei) &
                  BTRFS_EXTENT_FLAG_TREE_BLOCK));
 
-       if (item_size <= sizeof(*ei) + sizeof(*bi)) {
+       if (key.type == BTRFS_EXTENT_ITEM_KEY &&
+           item_size <= sizeof(*ei) + sizeof(*bi)) {
                WARN_ON(item_size < sizeof(*ei) + sizeof(*bi));
                return 1;
        }
 
-       bi = (struct btrfs_tree_block_info *)(ei + 1);
-       *ptr = (unsigned long)(bi + 1);
+       if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+               bi = (struct btrfs_tree_block_info *)(ei + 1);
+               *ptr = (unsigned long)(bi + 1);
+       } else {
+               *ptr = (unsigned long)(ei + 1);
+       }
        *end = (unsigned long)ei + item_size;
        return 0;
 }
        end = 0;
        ptr = 0;
        key.objectid = cur->bytenr;
-       key.type = BTRFS_EXTENT_ITEM_KEY;
+       key.type = BTRFS_METADATA_ITEM_KEY;
        key.offset = (u64)-1;
 
        path1->search_commit_root = 1;
                                break;
                        }
 
-                       if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+                       if (key.type == BTRFS_EXTENT_ITEM_KEY ||
+                           key.type == BTRFS_METADATA_ITEM_KEY) {
                                ret = find_inline_backref(eb, path1->slots[0],
                                                          &ptr, &end);
                                if (ret)
                            struct tree_block *block)
 {
        BUG_ON(block->key_ready);
-       readahead_tree_block(rc->extent_root, block->bytenr,
-                            block->key.objectid, block->key.offset);
+       if (block->key.type == BTRFS_METADATA_ITEM_KEY)
+               readahead_tree_block(rc->extent_root, block->bytenr,
+                                    block->key.objectid,
+                                    rc->extent_root->leafsize);
+       else
+               readahead_tree_block(rc->extent_root, block->bytenr,
+                                    block->key.objectid, block->key.offset);
        return 0;
 }
 
        eb =  path->nodes[0];
        item_size = btrfs_item_size_nr(eb, path->slots[0]);
 
-       if (item_size >= sizeof(*ei) + sizeof(*bi)) {
+       if (extent_key->type == BTRFS_METADATA_ITEM_KEY ||
+           item_size >= sizeof(*ei) + sizeof(*bi)) {
                ei = btrfs_item_ptr(eb, path->slots[0],
                                struct btrfs_extent_item);
-               bi = (struct btrfs_tree_block_info *)(ei + 1);
+               if (extent_key->type == BTRFS_EXTENT_ITEM_KEY) {
+                       bi = (struct btrfs_tree_block_info *)(ei + 1);
+                       level = btrfs_tree_block_level(eb, bi);
+               } else {
+                       level = (int)extent_key->offset;
+               }
                generation = btrfs_extent_generation(eb, ei);
-               level = btrfs_tree_block_level(eb, bi);
        } else {
 #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
                u64 ref_owner;
                return -ENOMEM;
 
        block->bytenr = extent_key->objectid;
-       block->key.objectid = extent_key->offset;
+       block->key.objectid = rc->extent_root->leafsize;
        block->key.offset = generation;
        block->level = level;
        block->key_ready = 0;
        ret = btrfs_search_slot(NULL, rc->extent_root, &key, path, 0, 0);
        if (ret < 0)
                goto out;
-       BUG_ON(ret);
 
        btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+       if (ret > 0) {
+               if (key.objectid == bytenr &&
+                   key.type == BTRFS_METADATA_ITEM_KEY)
+                       ret = 0;
+       }
+       BUG_ON(ret);
+
        ret = add_tree_block(rc, &key, path, blocks);
 out:
        btrfs_free_path(path);
                return 1;
 
        ret = btrfs_lookup_extent_info(NULL, rc->extent_root,
-                                      eb->start, eb->len, NULL, &flags);
+                                      eb->start, btrfs_header_level(eb), 1,
+                                      NULL, &flags);
        BUG_ON(ret);
 
        if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
                        break;
                }
 
-               if (key.type != BTRFS_EXTENT_ITEM_KEY ||
+               if (key.type != BTRFS_EXTENT_ITEM_KEY &&
+                   key.type != BTRFS_METADATA_ITEM_KEY) {
+                       path->slots[0]++;
+                       goto next;
+               }
+
+               if (key.type == BTRFS_EXTENT_ITEM_KEY &&
                    key.objectid + key.offset <= rc->search_start) {
                        path->slots[0]++;
                        goto next;
                }
 
+               if (key.type == BTRFS_METADATA_ITEM_KEY &&
+                   key.objectid + rc->extent_root->leafsize <=
+                   rc->search_start) {
+                       path->slots[0]++;
+                       goto next;
+               }
+
                ret = find_first_extent_bit(&rc->processed_blocks,
                                            key.objectid, &start, &end,
                                            EXTENT_DIRTY, NULL);
                        btrfs_release_path(path);
                        rc->search_start = end + 1;
                } else {
-                       rc->search_start = key.objectid + key.offset;
+                       if (key.type == BTRFS_EXTENT_ITEM_KEY)
+                               rc->search_start = key.objectid + key.offset;
+                       else
+                               rc->search_start = key.objectid +
+                                       rc->extent_root->leafsize;
                        memcpy(extent_key, &key, sizeof(key));
                        return 0;
                }