Newer
Older
/*
* 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 "ctree.h"
#include "disk-io.h"
#include "print-tree.h"
#define BLOCK_GROUP_DATA EXTENT_WRITEBACK
#define BLOCK_GROUP_METADATA EXTENT_UPTODATE
#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_map_tree *free_space_cache;
int slot;
u64 last = 0;
u64 hole_size;
u64 first_free;
if (!block_group)
return 0;
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 && path->slots[0] > 0)
path->slots[0]--;
leaf = path->nodes[0];
if (slot >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(root, path);
if (ret < 0)
goto err;
btrfs_item_key_to_cpu(leaf, &key, slot);
if (key.objectid < block_group->key.objectid) {
if (key.objectid + key.offset > first_free)
first_free = key.objectid + key.offset;
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) {
last = first_free;
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);
err:
btrfs_free_path(path);
return 0;
}
struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
btrfs_fs_info *info,
struct extent_map_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,
BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA);
ret = get_state_private(block_group_cache, start, &ptr);
if (ret)
return NULL;
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;
static u64 find_search_start(struct btrfs_root *root,
struct btrfs_block_group_cache **cache_ret,
u64 search_start, int num,
int data, int full_scan)
{
int ret;
struct btrfs_block_group_cache *cache = *cache_ret;

Chris Mason
committed
u64 last;
u64 start = 0;
u64 end = 0;

Chris Mason
committed
u64 cache_miss = 0;

Chris Mason
committed
int wrapped = 0;
if (!cache) {
}
ret = cache_block_group(root, cache);
if (ret)
goto out;

Chris Mason
committed

Chris Mason
committed
last = max(search_start, cache->key.objectid);
ret = find_first_extent_bit(&root->fs_info->free_space_cache,
last, &start, &end, EXTENT_DIRTY);

Chris Mason
committed
if (ret) {

Chris Mason
committed
if (!cache_miss)
cache_miss = last;

Chris Mason
committed
goto new_group;
}
start = max(last, start);
last = end + 1;

Chris Mason
committed
if (last - start < num) {
if (last == cache->key.objectid + cache->key.offset)
cache_miss = start;

Chris Mason
committed
}
if (data != BTRFS_BLOCK_GROUP_MIXED &&
start + num > cache->key.objectid + cache->key.offset)
cache = btrfs_lookup_block_group(root->fs_info, search_start);
if (!cache) {
printk("Unable to find block group for %Lu\n",
search_start);
WARN_ON(1);
return search_start;
}

Chris Mason
committed
return search_start;

Chris Mason
committed
last = cache->key.objectid + cache->key.offset;

Chris Mason
committed
wrapped:

Chris Mason
committed
cache = btrfs_lookup_block_group(root->fs_info, last);

Chris Mason
committed
if (!wrapped) {
wrapped = 1;
last = search_start;
data = BTRFS_BLOCK_GROUP_MIXED;
goto wrapped;
}

Chris Mason
committed
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);
if (!cache)
goto no_cache;

Chris Mason
committed
cache_miss = 0;

Chris Mason
committed
if (factor == 10)
return num;
num *= factor;
do_div(num, 10);
return num;
}
struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
struct btrfs_block_group_cache
struct btrfs_block_group_cache *cache;
struct extent_map_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;
int bit;
block_group_cache = &info->block_group_cache;

Chris Mason
committed
factor = 8;

Chris Mason
committed
if (data == BTRFS_BLOCK_GROUP_MIXED) {

Chris Mason
committed
bit = BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA;

Chris Mason
committed
factor = 10;
} else if (data)
bit = BLOCK_GROUP_DATA;
else
bit = BLOCK_GROUP_METADATA;
if (search_start) {
struct btrfs_block_group_cache *shint;
shint = btrfs_lookup_block_group(info, search_start);

Chris Mason
committed
if (shint && (shint->data == data ||
shint->data == BTRFS_BLOCK_GROUP_MIXED)) {
used = btrfs_block_group_used(&shint->item);
if (used + shint->pinned <
div_factor(shint->key.offset, factor)) {

Chris Mason
committed
if (hint && (hint->data == data ||
hint->data == BTRFS_BLOCK_GROUP_MIXED)) {
used = btrfs_block_group_used(&hint->item);
if (used + hint->pinned <
div_factor(hint->key.offset, factor)) {

Chris Mason
committed
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;
last = hint_last;
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;
cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
last = cache->key.objectid + cache->key.offset;
used = btrfs_block_group_used(&cache->item);
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;
full_search = 1;
goto again;
}
bit = BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA;
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes)
struct extent_buffer *l;
WARN_ON(num_bytes < root->sectorsize);
if (!path)
return -ENOMEM;
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,
if (ret < 0)
return ret;
l = path->nodes[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);
btrfs_free_path(path);
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 extent_buffer *l;
WARN_ON(num_bytes < root->sectorsize);
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)
goto out;
if (ret != 0) {
btrfs_print_leaf(root, path->nodes[0]);
printk("failed to find block number %Lu\n", bytenr);
}
l = path->nodes[0];
item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
*refs = btrfs_extent_refs(l, item);
out:
int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
return btrfs_inc_extent_ref(trans, root, root->node->start,
root->node->len);
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 faili;
int err;

Chris Mason
committed
if (!root->ref_cows)
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,
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)
ret = btrfs_inc_extent_ref(trans, root, disk_bytenr,
btrfs_file_extent_disk_num_bytes(buf, fi));
if (ret) {
faili = i;
goto fail;
}
bytenr = btrfs_node_blockptr(buf, i);
ret = btrfs_inc_extent_ref(trans, root, bytenr,
btrfs_level_size(root, level - 1));
if (ret) {
faili = i;
goto fail;
}
fail:
for (i =0; i < faili; i++) {
if (level == 0) {
u64 disk_bytenr;
btrfs_item_key_to_cpu(buf, &key, i);
if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(buf, i,
struct btrfs_file_extent_item);
if (btrfs_file_extent_type(buf, fi) ==
BTRFS_FILE_EXTENT_INLINE)
continue;
disk_bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
if (disk_bytenr == 0)
continue;
err = btrfs_free_extent(trans, root, disk_bytenr,
btrfs_file_extent_disk_num_bytes(buf,
BUG_ON(err);
} else {
bytenr = btrfs_node_blockptr(buf, i);
err = btrfs_free_extent(trans, root, bytenr,
btrfs_level_size(root, level - 1), 0);
BUG_ON(err);
}
}
return ret;
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);
if (ret < 0)
goto fail;
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);
fail:
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_map_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;
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;
}
static int update_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes, int alloc,
int mark_free, int data)
{
struct btrfs_block_group_cache *cache;
struct btrfs_fs_info *info = root->fs_info;
u64 start;
u64 end;
cache = btrfs_lookup_block_group(info, bytenr);
byte_in_group = bytenr - cache->key.objectid;
WARN_ON(byte_in_group > cache->key.offset);
start = cache->key.objectid;
end = start + cache->key.offset - 1;
set_extent_bits(&info->block_group_cache, start, end,
BLOCK_GROUP_DIRTY, GFP_NOFS);
old_val = btrfs_block_group_used(&cache->item);
num_bytes = min(total, cache->key.offset - byte_in_group);
int bit_to_clear;
int bit_to_set;
cache->data = data;
bit_to_clear = BLOCK_GROUP_METADATA;
bit_to_set = BLOCK_GROUP_DATA;

Chris Mason
committed
cache->item.flags &=
~BTRFS_BLOCK_GROUP_MIXED;
cache->item.flags |=
BTRFS_BLOCK_GROUP_DATA;
} else {
bit_to_clear = BLOCK_GROUP_DATA;
bit_to_set = BLOCK_GROUP_METADATA;

Chris Mason
committed
cache->item.flags &=
~BTRFS_BLOCK_GROUP_MIXED;
cache->item.flags &=
~BTRFS_BLOCK_GROUP_DATA;
}
clear_extent_bits(&info->block_group_cache,
start, end, bit_to_clear,
GFP_NOFS);
set_extent_bits(&info->block_group_cache,
start, end, bit_to_set,
GFP_NOFS);

Chris Mason
committed
} else if (cache->data != data &&
cache->data != BTRFS_BLOCK_GROUP_MIXED) {
cache->data = BTRFS_BLOCK_GROUP_MIXED;
set_extent_bits(&info->block_group_cache,
start, end,
BLOCK_GROUP_DATA |
BLOCK_GROUP_METADATA,
GFP_NOFS);
if (mark_free) {
set_extent_dirty(&info->free_space_cache,
bytenr, bytenr + num_bytes - 1,
btrfs_set_block_group_used(&cache->item, old_val);
total -= num_bytes;
bytenr += num_bytes;
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
static int update_pinned_extents(struct btrfs_root *root,
u64 bytenr, u64 num, int pin)
{
u64 len;
struct btrfs_block_group_cache *cache;
struct btrfs_fs_info *fs_info = root->fs_info;
if (pin) {
set_extent_dirty(&fs_info->pinned_extents,
bytenr, bytenr + num - 1, GFP_NOFS);
} else {
clear_extent_dirty(&fs_info->pinned_extents,
bytenr, bytenr + num - 1, GFP_NOFS);
}
while (num > 0) {
cache = btrfs_lookup_block_group(fs_info, bytenr);
WARN_ON(!cache);
len = min(num, cache->key.offset -
(bytenr - cache->key.objectid));
if (pin) {
cache->pinned += len;
fs_info->total_pinned += len;
} else {
cache->pinned -= len;
fs_info->total_pinned -= len;
}
bytenr += len;
num -= len;
}
return 0;
}

Chris Mason
committed
int btrfs_copy_pinned(struct btrfs_root *root, struct extent_map_tree *copy)

Chris Mason
committed
u64 start;
u64 end;
struct extent_map_tree *pinned_extents = &root->fs_info->pinned_extents;

Chris Mason
committed
ret = find_first_extent_bit(pinned_extents, last,
&start, &end, EXTENT_DIRTY);
if (ret)

Chris Mason
committed
set_extent_dirty(copy, start, end, GFP_NOFS);
last = end + 1;
}
return 0;
}
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
struct btrfs_root *root,

Chris Mason
committed
struct extent_map_tree *unpin)

Chris Mason
committed
u64 start;
u64 end;
struct extent_map_tree *free_space_cache;
free_space_cache = &root->fs_info->free_space_cache;

Chris Mason
committed
ret = find_first_extent_bit(unpin, 0, &start, &end,
EXTENT_DIRTY);
if (ret)
update_pinned_extents(root, start, end + 1 - start, 0);

Chris Mason
committed
clear_extent_dirty(unpin, start, end, GFP_NOFS);
set_extent_dirty(free_space_cache, start, end, GFP_NOFS);
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
btrfs_root *extent_root)

Chris Mason
committed
int err = 0;
u64 start;
u64 end;
struct btrfs_fs_info *info = extent_root->fs_info;
btrfs_set_stack_extent_refs(&extent_item, 1);
btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
btrfs_set_stack_extent_owner(&extent_item,
extent_root->root_key.objectid);
while(1) {

Chris Mason
committed
ret = find_first_extent_bit(&info->extent_ins, 0, &start,
&end, EXTENT_LOCKED);
if (ret)
break;

Chris Mason
committed
ins.objectid = start;
ins.offset = end + 1 - start;
err = btrfs_insert_item(trans, extent_root, &ins,
&extent_item, sizeof(extent_item));
clear_extent_bits(&info->extent_ins, start, end, EXTENT_LOCKED,
GFP_NOFS);
static int pin_down_bytes(struct btrfs_root *root, u64 bytenr, u32 num_bytes,
int pending)

Chris Mason
committed
int err = 0;
struct extent_buffer *buf;
buf = btrfs_find_tree_block(root, bytenr, num_bytes);
if (buf) {
if (btrfs_buffer_uptodate(buf)) {
u64 transid =
root->fs_info->running_transaction->transid;
if (btrfs_header_generation(buf) == transid) {
free_extent_buffer(buf);
free_extent_buffer(buf);
update_pinned_extents(root, bytenr, num_bytes, 1);

Chris Mason
committed
set_extent_bits(&root->fs_info->pending_del,
bytenr, bytenr + num_bytes - 1,
EXTENT_LOCKED, GFP_NOFS);
* remove an extent from the root, returns 0 on success
static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
*root, u64 bytenr, u64 num_bytes, int pin,
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root;
struct extent_buffer *leaf;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
key.offset = num_bytes;
if (!path)
return -ENOMEM;
ret = btrfs_search_slot(trans, extent_root, &key, path, -1, 1);
if (ret < 0)
return ret;
BUG_ON(ret);
leaf = path->nodes[0];
ei = btrfs_item_ptr(leaf, path->slots[0],
refs = btrfs_extent_refs(leaf, ei);
BUG_ON(refs == 0);
refs -= 1;
btrfs_set_extent_refs(leaf, ei, refs);
btrfs_mark_buffer_dirty(leaf);
u64 super_used;
u64 root_used;
ret = pin_down_bytes(root, bytenr, num_bytes, 0);
if (ret > 0)
mark_free = 1;
BUG_ON(ret < 0);
/* block accounting for super block */
super_used = btrfs_super_bytes_used(&info->super_copy);
btrfs_set_super_bytes_used(&info->super_copy,
super_used - num_bytes);
/* block accounting for root item */
root_used = btrfs_root_used(&root->root_item);
btrfs_set_root_used(&root->root_item,
root_used - num_bytes);
ret = btrfs_del_item(trans, extent_root, path);
if (ret) {
return ret;
}
ret = update_block_group(trans, root, bytenr, num_bytes, 0,
finish_current_insert(trans, extent_root);
return ret;
}
/*
* find all the blocks marked as pending in the radix tree and remove
* them from the extent map
*/
static int del_pending_extents(struct btrfs_trans_handle *trans, struct
btrfs_root *extent_root)

Chris Mason
committed
u64 start;
u64 end;
struct extent_map_tree *pending_del;
struct extent_map_tree *pinned_extents;

Chris Mason
committed
pending_del = &extent_root->fs_info->pending_del;
pinned_extents = &extent_root->fs_info->pinned_extents;

Chris Mason
committed
ret = find_first_extent_bit(pending_del, 0, &start, &end,
EXTENT_LOCKED);
if (ret)
update_pinned_extents(extent_root, start, end + 1 - start, 1);

Chris Mason
committed
clear_extent_bits(pending_del, start, end, EXTENT_LOCKED,
GFP_NOFS);
ret = __free_extent(trans, extent_root,
start, end + 1 - start, 0, 0);
if (ret)
err = ret;
}
/*
* remove an extent from the root, returns 0 on success
*/
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
*root, u64 bytenr, u64 num_bytes, int pin)
struct btrfs_root *extent_root = root->fs_info->extent_root;
WARN_ON(num_bytes < root->sectorsize);
pin_down_bytes(root, bytenr, num_bytes, 1);
ret = __free_extent(trans, root, bytenr, num_bytes, pin, pin == 0);
pending_ret = del_pending_extents(trans, root->fs_info->extent_root);
return ret ? ret : pending_ret;
}
static u64 stripe_align(struct btrfs_root *root, u64 val)
{
u64 mask = ((u64)root->stripesize - 1);
u64 ret = (val + mask) & ~mask;
return ret;
}
/*
* walks the btree of allocated extents and find a hole of a given size.
* The key ins is changed to record the hole:
* ins->objectid == block start
* ins->flags = BTRFS_EXTENT_ITEM_KEY
* ins->offset == number of blocks
* Any available blocks before search_start are skipped.
*/
static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
*orig_root, u64 num_bytes, u64 empty_size,
u64 search_start, u64 search_end, u64 hint_byte,
struct btrfs_key *ins, u64 exclude_start,
u64 exclude_nr, int data)
struct extent_buffer *l;
struct btrfs_root * root = orig_root->fs_info->extent_root;
struct btrfs_fs_info *info = root->fs_info;
u64 total_needed = num_bytes;
struct btrfs_block_group_cache *block_group;
int wrapped = 0;

Chris Mason
committed
u64 cached_start;
WARN_ON(num_bytes < root->sectorsize);
btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
level = btrfs_header_level(root->node);
if (num_bytes >= 32 * 1024 * 1024 && hint_byte) {

Chris Mason
committed
data = BTRFS_BLOCK_GROUP_MIXED;
}
search_end = btrfs_super_total_bytes(&info->super_copy);
if (hint_byte) {
block_group = btrfs_lookup_block_group(info, hint_byte);
if (!block_group)
hint_byte = search_start;
block_group = btrfs_find_block_group(root, block_group,
} else {
block_group = btrfs_find_block_group(root,
trans->block_group,
search_start, data, 1);