Skip to content
Snippets Groups Projects
extent-tree.c 72.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Chris Mason's avatar
    Chris Mason committed
    /*
     * Copyright (C) 2007 Oracle.  All rights reserved.
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public
     * License v2 as published by the Free Software Foundation.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * General Public License for more details.
     *
     * You should have received a copy of the GNU General Public
     * License along with this program; if not, write to the
     * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     * Boston, MA 021110-1307, USA.
     */
    
    #include <linux/sched.h>
    
    #include <linux/pagemap.h>
    
    #include "crc32c.h"
    
    #include "ctree.h"
    #include "disk-io.h"
    #include "print-tree.h"
    
    #include "transaction.h"
    
    #define BLOCK_GROUP_DATA     EXTENT_WRITEBACK
    
    #define BLOCK_GROUP_METADATA EXTENT_UPTODATE
    
    #define BLOCK_GROUP_SYSTEM   EXTENT_NEW
    
    
    #define BLOCK_GROUP_DIRTY EXTENT_DIRTY
    
    
    static int finish_current_insert(struct btrfs_trans_handle *trans, struct
    				 btrfs_root *extent_root);
    
    static int del_pending_extents(struct btrfs_trans_handle *trans, struct
    			       btrfs_root *extent_root);
    
    static int cache_block_group(struct btrfs_root *root,
    			     struct btrfs_block_group_cache *block_group)
    {
    	struct btrfs_path *path;
    	int ret;
    	struct btrfs_key key;
    
    	struct extent_buffer *leaf;
    
    	struct extent_io_tree *free_space_cache;
    
    	int slot;
    	u64 last = 0;
    	u64 hole_size;
    
    	root = root->fs_info->extent_root;
    
    	free_space_cache = &root->fs_info->free_space_cache;
    
    
    	if (block_group->cached)
    		return 0;
    
    	path = btrfs_alloc_path();
    	if (!path)
    		return -ENOMEM;
    
    	first_free = block_group->key.objectid;
    
    	key.objectid = block_group->key.objectid;
    	key.offset = 0;
    	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
    	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
    	if (ret < 0)
    		return ret;
    
    	ret = btrfs_previous_item(root, path, 0, BTRFS_EXTENT_ITEM_KEY);
    
    	if (ret < 0)
    		return ret;
    	if (ret == 0) {
    		leaf = path->nodes[0];
    		btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
    		if (key.objectid + key.offset > first_free)
    			first_free = key.objectid + key.offset;
    	}
    
    		slot = path->slots[0];
    
    		if (slot >= btrfs_header_nritems(leaf)) {
    
    			ret = btrfs_next_leaf(root, path);
    
    		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) {
    			if (!found) {
    
    			if (key.objectid > last) {
    				hole_size = key.objectid - last;
    				set_extent_dirty(free_space_cache, last,
    						 last + hole_size - 1,
    						 GFP_NOFS);
    
    			}
    			last = key.objectid + key.offset;
    
    	if (!found)
    		last = first_free;
    	if (block_group->key.objectid +
    	    block_group->key.offset > last) {
    		hole_size = block_group->key.objectid +
    			block_group->key.offset - last;
    
    		set_extent_dirty(free_space_cache, last,
    				 last + hole_size - 1, GFP_NOFS);
    
    	block_group->cached = 1;
    
    	btrfs_free_path(path);
    	return 0;
    }
    
    
    struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
    							 btrfs_fs_info *info,
    
    	struct extent_io_tree *block_group_cache;
    
    	struct btrfs_block_group_cache *block_group = NULL;
    	u64 ptr;
    	u64 start;
    	u64 end;
    
    	block_group_cache = &info->block_group_cache;
    	ret = find_first_extent_bit(block_group_cache,
    
    				    bytenr, &start, &end,
    
    				    BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA |
    				    BLOCK_GROUP_SYSTEM);
    
    	if (ret) {
    
    	ret = get_state_private(block_group_cache, start, &ptr);
    	if (ret)
    		return NULL;
    
    
    Jens Axboe's avatar
    Jens Axboe committed
    	block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr;
    
    	if (block_group->key.objectid <= bytenr && bytenr <
    
    	    block_group->key.objectid + block_group->key.offset)
    		return block_group;
    
    	return NULL;
    }
    
    
    static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
    {
    
    	return (cache->flags & bits) == bits;
    
    }
    
    static int noinline find_search_start(struct btrfs_root *root,
    
    			      struct btrfs_block_group_cache **cache_ret,
    
    			      u64 *start_ret, int num, int data)
    
    {
    	int ret;
    	struct btrfs_block_group_cache *cache = *cache_ret;
    
    	struct extent_io_tree *free_space_cache;
    
    	struct extent_state *state;
    
    	u64 total_fs_bytes;
    
    	u64 search_start = *start_ret;
    
    	total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
    
    	free_space_cache = &root->fs_info->free_space_cache;
    
    
    	ret = cache_block_group(root, cache);
    	if (ret)
    		goto out;
    
    	last = max(search_start, cache->key.objectid);
    
    	if (!block_group_bits(cache, data) || cache->ro) {
    
    	spin_lock_irq(&free_space_cache->lock);
    	state = find_first_extent_bit_state(free_space_cache, last, EXTENT_DIRTY);
    
    			spin_unlock_irq(&free_space_cache->lock);
    
    		start = max(last, state->start);
    		last = state->end + 1;
    
    		if (last - start < num) {
    			if (last == cache->key.objectid + cache->key.offset)
    				cache_miss = start;
    
    			do {
    				state = extent_state_next(state);
    			} while(state && !(state->state & EXTENT_DIRTY));
    
    		spin_unlock_irq(&free_space_cache->lock);
    
    		if (start + num > cache->key.objectid + cache->key.offset)
    
    			goto new_group;
    
    		if (start + num  > total_fs_bytes)
    			goto new_group;
    
    		if (!block_group_bits(cache, data)) {
    
    			printk("block group bits don't match %Lu %d\n", cache->flags, data);
    
    		*start_ret = start;
    		return 0;
    
    	cache = btrfs_lookup_block_group(root->fs_info, search_start);
    	if (!cache) {
    
    		printk("Unable to find block group for %Lu\n", search_start);
    
    	last = cache->key.objectid + cache->key.offset;
    
    	cache = btrfs_lookup_block_group(root->fs_info, last);
    
    	if (!cache || cache->key.objectid >= total_fs_bytes) {
    
    		if (!wrapped) {
    			wrapped = 1;
    			last = search_start;
    			goto wrapped;
    		}
    
    	if (cache_miss && !cache->cached) {
    		cache_block_group(root, cache);
    		last = cache_miss;
    		cache = btrfs_lookup_block_group(root->fs_info, last);
    	}
    
    	cache = btrfs_find_block_group(root, cache, last, data, 0);
    
    	*cache_ret = cache;
    
    Chris Mason's avatar
    Chris Mason committed
    static u64 div_factor(u64 num, int factor)
    {
    
    Chris Mason's avatar
    Chris Mason committed
    	num *= factor;
    	do_div(num, 10);
    	return num;
    }
    
    
    static int block_group_state_bits(u64 flags)
    {
    	int bits = 0;
    	if (flags & BTRFS_BLOCK_GROUP_DATA)
    		bits |= BLOCK_GROUP_DATA;
    	if (flags & BTRFS_BLOCK_GROUP_METADATA)
    		bits |= BLOCK_GROUP_METADATA;
    	if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
    		bits |= BLOCK_GROUP_SYSTEM;
    	return bits;
    }
    
    
    struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
    						 struct btrfs_block_group_cache
    
    						 *hint, u64 search_start,
    
    						 int data, int owner)
    
    	struct btrfs_block_group_cache *cache;
    
    	struct extent_io_tree *block_group_cache;
    
    	struct btrfs_block_group_cache *found_group = NULL;
    
    	struct btrfs_fs_info *info = root->fs_info;
    	u64 used;
    
    	u64 last = 0;
    	u64 hint_last;
    
    	u64 start;
    	u64 end;
    	u64 free_check;
    	u64 ptr;
    
    	u64 total_fs_bytes;
    
    	int ret;
    
    	int full_search = 0;
    
    	block_group_cache = &info->block_group_cache;
    
    	total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
    
    	bit = block_group_state_bits(data);
    
    	if (search_start && search_start < total_fs_bytes) {
    
    		struct btrfs_block_group_cache *shint;
    
    		shint = btrfs_lookup_block_group(info, search_start);
    
    		if (shint && block_group_bits(shint, data) && !shint->ro) {
    
    			used = btrfs_block_group_used(&shint->item);
    
    			if (used + shint->pinned <
    			    div_factor(shint->key.offset, factor)) {
    
    	if (hint && !hint->ro && block_group_bits(hint, data) &&
    
    	    hint->key.objectid < total_fs_bytes) {
    
    		used = btrfs_block_group_used(&hint->item);
    
    		if (used + hint->pinned <
    		    div_factor(hint->key.offset, factor)) {
    
    		last = hint->key.objectid + hint->key.offset;
    
    		hint_last = last;
    	} else {
    
    		if (hint)
    			hint_last = max(hint->key.objectid, search_start);
    		else
    			hint_last = search_start;
    
    
    		if (hint_last >= total_fs_bytes)
    			hint_last = search_start;
    
    		last = hint_last;
    
    	while(1) {
    
    		ret = find_first_extent_bit(block_group_cache, last,
    					    &start, &end, bit);
    		if (ret)
    
    
    		ret = get_state_private(block_group_cache, start, &ptr);
    		if (ret)
    			break;
    
    
    Jens Axboe's avatar
    Jens Axboe committed
    		cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
    
    		last = cache->key.objectid + cache->key.offset;
    		used = btrfs_block_group_used(&cache->item);
    
    
    		if (cache->key.objectid > total_fs_bytes)
    			break;
    
    
    		if (!cache->ro && block_group_bits(cache, data)) {
    
    			if (full_search)
    				free_check = cache->key.offset;
    			else
    				free_check = div_factor(cache->key.offset,
    							factor);
    
    			if (used + cache->pinned < free_check) {
    				found_group = cache;
    				goto found;
    			}
    
    	if (!full_search) {
    
    		last = search_start;
    
    		full_search = 1;
    		goto again;
    	}
    
    	return found_group;
    
    static u64 hash_extent_ref(u64 root_objectid, u64 ref_generation,
    
    			   u64 owner, u64 owner_offset)
    {
    	u32 high_crc = ~(u32)0;
    	u32 low_crc = ~(u32)0;
    	__le64 lenum;
    	lenum = cpu_to_le64(root_objectid);
    
    	high_crc = btrfs_crc32c(high_crc, &lenum, sizeof(lenum));
    
    	lenum = cpu_to_le64(ref_generation);
    
    	low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
    
    	if (owner >= BTRFS_FIRST_FREE_OBJECTID) {
    		lenum = cpu_to_le64(owner);
    
    		low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
    
    		lenum = cpu_to_le64(owner_offset);
    
    		low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
    
    	return ((u64)high_crc << 32) | (u64)low_crc;
    }
    
    
    static int match_extent_ref(struct extent_buffer *leaf,
    			    struct btrfs_extent_ref *disk_ref,
    			    struct btrfs_extent_ref *cpu_ref)
    {
    	int ret;
    	int len;
    
    	if (cpu_ref->objectid)
    		len = sizeof(*cpu_ref);
    	else
    		len = 2 * sizeof(u64);
    	ret = memcmp_extent_buffer(leaf, cpu_ref, (unsigned long)disk_ref,
    				   len);
    	return ret == 0;
    }
    
    
    static int noinline lookup_extent_backref(struct btrfs_trans_handle *trans,
    					  struct btrfs_root *root,
    					  struct btrfs_path *path, u64 bytenr,
    					  u64 root_objectid,
    					  u64 ref_generation, u64 owner,
    					  u64 owner_offset, int del)
    
    {
    	u64 hash;
    	struct btrfs_key key;
    
    	struct btrfs_extent_ref ref;
    
    	struct extent_buffer *leaf;
    	struct btrfs_extent_ref *disk_ref;
    	int ret;
    	int ret2;
    
    	btrfs_set_stack_ref_root(&ref, root_objectid);
    	btrfs_set_stack_ref_generation(&ref, ref_generation);
    	btrfs_set_stack_ref_objectid(&ref, owner);
    	btrfs_set_stack_ref_offset(&ref, owner_offset);
    
    	hash = hash_extent_ref(root_objectid, ref_generation, owner,
    			       owner_offset);
    	key.offset = hash;
    	key.objectid = bytenr;
    	key.type = BTRFS_EXTENT_REF_KEY;
    
    	while (1) {
    		ret = btrfs_search_slot(trans, root, &key, path,
    					del ? -1 : 0, del);
    		if (ret < 0)
    			goto out;
    		leaf = path->nodes[0];
    		if (ret != 0) {
    			u32 nritems = btrfs_header_nritems(leaf);
    			if (path->slots[0] >= nritems) {
    				ret2 = btrfs_next_leaf(root, path);
    				if (ret2)
    					goto out;
    				leaf = path->nodes[0];
    			}
    			btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
    			if (found_key.objectid != bytenr ||
    			    found_key.type != BTRFS_EXTENT_REF_KEY)
    				goto out;
    			key.offset = found_key.offset;
    			if (del) {
    				btrfs_release_path(root, path);
    				continue;
    			}
    		}
    		disk_ref = btrfs_item_ptr(path->nodes[0],
    					  path->slots[0],
    					  struct btrfs_extent_ref);
    		if (match_extent_ref(path->nodes[0], disk_ref, &ref)) {
    			ret = 0;
    			goto out;
    		}
    		btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
    		key.offset = found_key.offset + 1;
    		btrfs_release_path(root, path);
    	}
    out:
    	return ret;
    }
    
    
    /*
     * Back reference rules.  Back refs have three main goals:
     *
     * 1) differentiate between all holders of references to an extent so that
     *    when a reference is dropped we can make sure it was a valid reference
     *    before freeing the extent.
     *
     * 2) Provide enough information to quickly find the holders of an extent
     *    if we notice a given block is corrupted or bad.
     *
     * 3) Make it easy to migrate blocks for FS shrinking or storage pool
     *    maintenance.  This is actually the same as #2, but with a slightly
     *    different use case.
     *
     * File extents can be referenced by:
     *
     * - multiple snapshots, subvolumes, or different generations in one subvol
     * - different files inside a single subvolume (in theory, not implemented yet)
     * - different offsets inside a file (bookend extents in file.c)
     *
     * The extent ref structure has fields for:
     *
     * - Objectid of the subvolume root
     * - Generation number of the tree holding the reference
     * - objectid of the file holding the reference
     * - offset in the file corresponding to the key holding the reference
     *
     * When a file extent is allocated the fields are filled in:
     *     (root_key.objectid, trans->transid, inode objectid, offset in file)
     *
     * When a leaf is cow'd new references are added for every file extent found
     * in the leaf.  It looks the same as the create case, but trans->transid
     * will be different when the block is cow'd.
     *
     *     (root_key.objectid, trans->transid, inode objectid, offset in file)
     *
     * When a file extent is removed either during snapshot deletion or file
     * truncation, the corresponding back reference is found
     * by searching for:
     *
     *     (btrfs_header_owner(leaf), btrfs_header_generation(leaf),
     *      inode objectid, offset in file)
     *
     * Btree extents can be referenced by:
     *
     * - Different subvolumes
     * - Different generations of the same subvolume
     *
     * Storing sufficient information for a full reverse mapping of a btree
     * block would require storing the lowest key of the block in the backref,
     * and it would require updating that lowest key either before write out or
     * every time it changed.  Instead, the objectid of the lowest key is stored
     * along with the level of the tree block.  This provides a hint
     * about where in the btree the block can be found.  Searches through the
     * btree only need to look for a pointer to that block, so they stop one
     * level higher than the level recorded in the backref.
     *
     * Some btrees do not do reference counting on their extents.  These
     * include the extent tree and the tree of tree roots.  Backrefs for these
     * trees always have a generation of zero.
     *
     * When a tree block is created, back references are inserted:
     *
    
     * (root->root_key.objectid, trans->transid or zero, level, lowest_key_objectid)
    
     *
     * When a tree block is cow'd in a reference counted root,
     * new back references are added for all the blocks it points to.
     * These are of the form (trans->transid will have increased since creation):
     *
    
     * (root->root_key.objectid, trans->transid, level, lowest_key_objectid)
    
     *
     * Because the lowest_key_objectid and the level are just hints
     * they are not used when backrefs are deleted.  When a backref is deleted:
     *
     * if backref was for a tree root:
     *     root_objectid = root->root_key.objectid
     * else
     *     root_objectid = btrfs_header_owner(parent)
     *
     * (root_objectid, btrfs_header_generation(parent) or zero, 0, 0)
     *
     * Back Reference Key hashing:
     *
     * Back references have four fields, each 64 bits long.  Unfortunately,
     * This is hashed into a single 64 bit number and placed into the key offset.
     * The key objectid corresponds to the first byte in the extent, and the
     * key type is set to BTRFS_EXTENT_REF_KEY
     */
    
    int btrfs_insert_extent_backref(struct btrfs_trans_handle *trans,
    				 struct btrfs_root *root,
    				 struct btrfs_path *path, u64 bytenr,
    				 u64 root_objectid, u64 ref_generation,
    				 u64 owner, u64 owner_offset)
    {
    	u64 hash;
    	struct btrfs_key key;
    	struct btrfs_extent_ref ref;
    	struct btrfs_extent_ref *disk_ref;
    
    	int ret;
    
    	btrfs_set_stack_ref_root(&ref, root_objectid);
    
    	btrfs_set_stack_ref_generation(&ref, ref_generation);
    
    	btrfs_set_stack_ref_objectid(&ref, owner);
    	btrfs_set_stack_ref_offset(&ref, owner_offset);
    
    
    	hash = hash_extent_ref(root_objectid, ref_generation, owner,
    			       owner_offset);
    
    	key.offset = hash;
    	key.objectid = bytenr;
    	key.type = BTRFS_EXTENT_REF_KEY;
    
    	ret = btrfs_insert_empty_item(trans, root, path, &key, sizeof(ref));
    	while (ret == -EEXIST) {
    
    		disk_ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
    					  struct btrfs_extent_ref);
    		if (match_extent_ref(path->nodes[0], disk_ref, &ref))
    			goto out;
    		key.offset++;
    		btrfs_release_path(root, path);
    		ret = btrfs_insert_empty_item(trans, root, path, &key,
    					      sizeof(ref));
    
    	if (ret)
    		goto out;
    	disk_ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
    				  struct btrfs_extent_ref);
    	write_extent_buffer(path->nodes[0], &ref, (unsigned long)disk_ref,
    			    sizeof(ref));
    	btrfs_mark_buffer_dirty(path->nodes[0]);
    out:
    	btrfs_release_path(root, path);
    	return ret;
    
    int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
    				struct btrfs_root *root,
    
    				u64 bytenr, u64 num_bytes,
    
    				u64 root_objectid, u64 ref_generation,
    
    				u64 owner, u64 owner_offset)
    
    	struct btrfs_path *path;
    
    	int ret;
    
    	struct btrfs_key key;
    
    	struct extent_buffer *l;
    
    	struct btrfs_extent_item *item;
    
    	u32 refs;
    
    	WARN_ON(num_bytes < root->sectorsize);
    
    	path = btrfs_alloc_path();
    
    	key.objectid = bytenr;
    
    	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
    
    	key.offset = num_bytes;
    
    	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
    
    	BUG_ON(ret != 0);
    
    	item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
    
    	refs = btrfs_extent_refs(l, item);
    	btrfs_set_extent_refs(l, item, refs + 1);
    
    	btrfs_mark_buffer_dirty(path->nodes[0]);
    
    	btrfs_release_path(root->fs_info->extent_root, path);
    
    	ret = btrfs_insert_extent_backref(trans, root->fs_info->extent_root,
    					  path, bytenr, root_objectid,
    					  ref_generation, owner, owner_offset);
    	BUG_ON(ret);
    
    	finish_current_insert(trans, root->fs_info->extent_root);
    
    	del_pending_extents(trans, root->fs_info->extent_root);
    
    int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
    			 struct btrfs_root *root)
    {
    	finish_current_insert(trans, root->fs_info->extent_root);
    	del_pending_extents(trans, root->fs_info->extent_root);
    	return 0;
    }
    
    
    static int lookup_extent_ref(struct btrfs_trans_handle *trans,
    
    			     struct btrfs_root *root, u64 bytenr,
    			     u64 num_bytes, u32 *refs)
    
    	struct btrfs_path *path;
    
    	struct btrfs_key key;
    
    	struct extent_buffer *l;
    
    	struct btrfs_extent_item *item;
    
    	WARN_ON(num_bytes < root->sectorsize);
    
    	path = btrfs_alloc_path();
    
    	key.objectid = bytenr;
    	key.offset = num_bytes;
    
    	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
    
    	ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
    
    	if (ret != 0) {
    		btrfs_print_leaf(root, path->nodes[0]);
    
    		printk("failed to find block number %Lu\n", bytenr);
    
    	item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
    
    	*refs = btrfs_extent_refs(l, item);
    
    	btrfs_free_path(path);
    
    u32 btrfs_count_snapshots_in_path(struct btrfs_root *root,
    				  struct btrfs_path *count_path,
    				  u64 first_extent)
    {
    	struct btrfs_root *extent_root = root->fs_info->extent_root;
    	struct btrfs_path *path;
    	u64 bytenr;
    	u64 found_objectid;
    
    	u64 root_objectid = root->root_key.objectid;
    
    	u32 total_count = 0;
    	u32 cur_count;
    	u32 nritems;
    	int ret;
    	struct btrfs_key key;
    	struct btrfs_key found_key;
    	struct extent_buffer *l;
    	struct btrfs_extent_item *item;
    	struct btrfs_extent_ref *ref_item;
    	int level = -1;
    
    	path = btrfs_alloc_path();
    again:
    	if (level == -1)
    		bytenr = first_extent;
    	else
    		bytenr = count_path->nodes[level]->start;
    
    	cur_count = 0;
    	key.objectid = bytenr;
    	key.offset = 0;
    
    	btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
    	ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
    	if (ret < 0)
    		goto out;
    	BUG_ON(ret == 0);
    
    	l = path->nodes[0];
    	btrfs_item_key_to_cpu(l, &found_key, path->slots[0]);
    
    	if (found_key.objectid != bytenr ||
    	    found_key.type != BTRFS_EXTENT_ITEM_KEY) {
    		goto out;
    	}
    
    	item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
    	while (1) {
    
    		nritems = btrfs_header_nritems(l);
    		if (path->slots[0] >= nritems) {
    			ret = btrfs_next_leaf(extent_root, path);
    			if (ret == 0)
    				continue;
    			break;
    		}
    		btrfs_item_key_to_cpu(l, &found_key, path->slots[0]);
    		if (found_key.objectid != bytenr)
    			break;
    
    		if (found_key.type != BTRFS_EXTENT_REF_KEY) {
    			path->slots[0]++;
    			continue;
    		}
    
    		cur_count++;
    		ref_item = btrfs_item_ptr(l, path->slots[0],
    					  struct btrfs_extent_ref);
    		found_objectid = btrfs_ref_root(l, ref_item);
    
    
    		if (found_objectid != root_objectid) {
    			total_count = 2;
    
    		path->slots[0]++;
    	}
    	if (cur_count == 0) {
    		total_count = 0;
    		goto out;
    	}
    	if (level >= 0 && root->node == count_path->nodes[level])
    		goto out;
    	level++;
    	btrfs_release_path(root, path);
    	goto again;
    
    out:
    	btrfs_free_path(path);
    	return total_count;
    }
    
    Chris Mason's avatar
    Chris Mason committed
    int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
    
    		       struct btrfs_root *root, u64 owner_objectid)
    
    Chris Mason's avatar
    Chris Mason committed
    {
    
    	u64 generation;
    	u64 key_objectid;
    	u64 level;
    	u32 nritems;
    	struct btrfs_disk_key disk_key;
    
    	level = btrfs_header_level(root->node);
    	generation = trans->transid;
    	nritems = btrfs_header_nritems(root->node);
    	if (nritems > 0) {
    		if (level == 0)
    			btrfs_item_key(root->node, &disk_key, 0);
    		else
    			btrfs_node_key(root->node, &disk_key, 0);
    		key_objectid = btrfs_disk_key_objectid(&disk_key);
    	} else {
    		key_objectid = 0;
    	}
    
    	return btrfs_inc_extent_ref(trans, root, root->node->start,
    
    int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
    
    		  struct extent_buffer *buf)
    
    	u32 nritems;
    	struct btrfs_key key;
    
    	struct btrfs_file_extent_item *fi;
    
    	int i;
    
    	level = btrfs_header_level(buf);
    
    	nritems = btrfs_header_nritems(buf);
    	for (i = 0; i < nritems; i++) {
    
    		if (level == 0) {
    			u64 disk_bytenr;
    
    			btrfs_item_key_to_cpu(buf, &key, i);
    			if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
    
    			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)
    
    Chris Mason's avatar
    Chris Mason committed
    				continue;
    
    			ret = btrfs_inc_extent_ref(trans, root, disk_bytenr,
    
    				    btrfs_file_extent_disk_num_bytes(buf, fi),
    				    root->root_key.objectid, trans->transid,
    				    key.objectid, key.offset);
    
    			bytenr = btrfs_node_blockptr(buf, i);
    
    			btrfs_node_key_to_cpu(buf, &key, i);
    
    			ret = btrfs_inc_extent_ref(trans, root, bytenr,
    
    					   btrfs_level_size(root, level - 1),
    					   root->root_key.objectid,
    
    					   trans->transid,
    					   level - 1, key.objectid);
    
    	}
    	return 0;
    
    Chris Mason's avatar
    Chris Mason committed
    	WARN_ON(1);
    
    		if (level == 0) {
    			u64 disk_bytenr;
    
    			btrfs_item_key_to_cpu(buf, &key, i);
    			if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
    
    			fi = btrfs_item_ptr(buf, i,
    
    			if (btrfs_file_extent_type(buf, fi) ==
    
    			disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
    			if (disk_bytenr == 0)
    
    			err = btrfs_free_extent(trans, root, disk_bytenr,
    				    btrfs_file_extent_disk_num_bytes(buf,
    
    			bytenr = btrfs_node_blockptr(buf, i);
    			err = btrfs_free_extent(trans, root, bytenr,
    					btrfs_level_size(root, level - 1), 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;
    	int pending_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);
    
    	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);
    
    	finish_current_insert(trans, extent_root);
    	pending_ret = del_pending_extents(trans, extent_root);
    	if (ret)
    		return ret;
    	if (pending_ret)
    		return pending_ret;
    	return 0;
    
    }
    
    
    int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
    				   struct btrfs_root *root)
    
    	struct extent_io_tree *block_group_cache;
    
    	struct btrfs_block_group_cache *cache;
    
    	int ret;
    	int err = 0;
    	int werr = 0;
    	struct btrfs_path *path;
    
    	u64 last = 0;
    	u64 start;
    	u64 end;
    	u64 ptr;
    
    	block_group_cache = &root->fs_info->block_group_cache;
    
    	path = btrfs_alloc_path();
    	if (!path)
    		return -ENOMEM;
    
    	while(1) {
    
    		ret = find_first_extent_bit(block_group_cache, last,
    					    &start, &end, BLOCK_GROUP_DIRTY);
    		if (ret)
    
    		last = end + 1;
    		ret = get_state_private(block_group_cache, start, &ptr);
    		if (ret)
    			break;
    
    Jens Axboe's avatar
    Jens Axboe committed
    		cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
    
    		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;
    
    		clear_extent_bits(block_group_cache, start, end,
    				  BLOCK_GROUP_DIRTY, GFP_NOFS);
    
    	}
    	btrfs_free_path(path);
    	return werr;
    }