Newer
Older
#include "ctree.h"
#include "disk-io.h"
#include "print-tree.h"
static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
*orig_root, u64 num_blocks, u64 search_start, u64
search_end, struct btrfs_key *ins);
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);
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static int find_search_start(struct btrfs_root *root, int data)
{
struct btrfs_block_group_cache *cache[8];
struct btrfs_fs_info *info = root->fs_info;
u64 used;
u64 last;
int i;
int ret;
cache[0] = info->block_group_cache;
if (!cache[0])
goto find_new;
used = btrfs_block_group_used(&cache[0]->item);
if (used < (cache[0]->key.offset * 3 / 2))
return 0;
find_new:
last = 0;
while(1) {
ret = radix_tree_gang_lookup_tag(&info->block_group_radix,
(void **)cache,
last, ARRAY_SIZE(cache),
BTRFS_BLOCK_GROUP_DIRTY);
if (!ret)
break;
for (i = 0; i < ret; i++) {
used = btrfs_block_group_used(&cache[i]->item);
if (used < (cache[i]->key.offset * 3 / 2)) {
info->block_group_cache = cache[i];
cache[i]->last_alloc = cache[i]->first_free;
return 0;
}
last = cache[i]->key.objectid +
cache[i]->key.offset - 1;
}
}
last = 0;
while(1) {
ret = radix_tree_gang_lookup(&info->block_group_radix,
(void **)cache,
last, ARRAY_SIZE(cache));
if (!ret)
break;
for (i = 0; i < ret; i++) {
used = btrfs_block_group_used(&cache[i]->item);
if (used < (cache[i]->key.offset * 3 / 2)) {
info->block_group_cache = cache[i];
cache[i]->last_alloc = cache[i]->first_free;
return 0;
}
last = cache[i]->key.objectid +
cache[i]->key.offset - 1;
}
}
info->block_group_cache = NULL;
return 0;
}
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 blocknr, u64 num_blocks)
struct btrfs_leaf *l;
struct btrfs_extent_item *item;
find_free_extent(trans, root->fs_info->extent_root, 0, 0, (u64)-1,
&ins);
path = btrfs_alloc_path();
BUG_ON(!path);
btrfs_init_path(path);
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
if (ret != 0) {
printk("can't find block %Lu %Lu\n", blocknr, num_blocks);
l = btrfs_buffer_leaf(path->nodes[0]);
item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
refs = btrfs_extent_refs(item);
btrfs_set_extent_refs(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);
static int lookup_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 blocknr,
u64 num_blocks, u32 *refs)
struct btrfs_leaf *l;
struct btrfs_extent_item *item;
path = btrfs_alloc_path();
btrfs_init_path(path);
key.flags = 0;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
l = btrfs_buffer_leaf(path->nodes[0]);
item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
btrfs_release_path(root->fs_info->extent_root, path);
btrfs_free_path(path);
int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
return btrfs_inc_extent_ref(trans, root, bh_blocknr(root->node), 1);
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_leaf *buf_leaf;
struct btrfs_disk_key *key;
struct btrfs_file_extent_item *fi;

Chris Mason
committed
if (!root->ref_cows)
leaf = btrfs_is_leaf(buf_node);
buf_leaf = btrfs_buffer_leaf(buf);
for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) {
if (leaf) {
key = &buf_leaf->items[i].key;
if (btrfs_disk_key_type(key) != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(buf_leaf, i,
struct btrfs_file_extent_item);
if (btrfs_file_extent_type(fi) ==
BTRFS_FILE_EXTENT_INLINE)
continue;
btrfs_file_extent_disk_blocknr(fi),
btrfs_file_extent_disk_num_blocks(fi));
BUG_ON(ret);
} else {
blocknr = btrfs_node_blockptr(buf_node, i);
ret = btrfs_inc_extent_ref(trans, root, blocknr, 1);
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
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;
struct btrfs_block_group_item *bi;
struct btrfs_key ins;
find_free_extent(trans, extent_root, 0, 0, (u64)-1, &ins);
ret = btrfs_search_slot(trans, extent_root, &cache->key, path, 0, 1);
BUG_ON(ret);
bi = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
struct btrfs_block_group_item);
memcpy(bi, &cache->item, sizeof(*bi));
mark_buffer_dirty(path->nodes[0]);
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 btrfs_block_group_cache *cache[8];
int ret;
int err = 0;
int werr = 0;
struct radix_tree_root *radix = &root->fs_info->block_group_radix;
int i;
struct btrfs_path *path;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
while(1) {
ret = radix_tree_gang_lookup_tag(radix, (void **)cache,
0, ARRAY_SIZE(cache),
BTRFS_BLOCK_GROUP_DIRTY);
if (!ret)
break;
for (i = 0; i < ret; i++) {
radix_tree_tag_clear(radix, cache[i]->key.objectid +
cache[i]->key.offset - 1,
BTRFS_BLOCK_GROUP_DIRTY);
err = write_one_cache_group(trans, root,
path, cache[i]);
if (err)
werr = err;
}
}
btrfs_free_path(path);
return werr;
}
static int update_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 blocknr, u64 num, int alloc)
{
struct btrfs_block_group_cache *cache;
struct btrfs_fs_info *info = root->fs_info;
u64 total = num;
u64 old_val;
u64 block_in_group;
int ret;
while(total) {
ret = radix_tree_gang_lookup(&info->block_group_radix,
(void **)&cache, blocknr, 1);
if (!ret) {
printk(KERN_CRIT "blocknr %Lu lookup failed\n",
blocknr);
block_in_group = blocknr - cache->key.objectid;
WARN_ON(block_in_group > cache->key.offset);
radix_tree_tag_set(&info->block_group_radix,
cache->key.objectid + cache->key.offset - 1,
BTRFS_BLOCK_GROUP_DIRTY);
old_val = btrfs_block_group_used(&cache->item);
num = min(total, cache->key.offset - block_in_group);
total -= num;
blocknr += num;
if (blocknr > cache->last_alloc)
cache->last_alloc = blocknr;
} else {
if (blocknr < cache->first_free)
cache->first_free = blocknr;
}
btrfs_set_block_group_used(&cache->item, old_val);
}
return 0;
}
static int try_remove_page(struct address_space *mapping, unsigned long index)
{
int ret;
ret = invalidate_mapping_pages(mapping, index, index);
return ret;
}
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
btrfs_root *root)
struct inode *btree_inode = root->fs_info->btree_inode;
struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix;
ret = find_first_radix_bit(pinned_radix, gang,
ARRAY_SIZE(gang));
try_remove_page(btree_inode->i_mapping,
gang[i] << (PAGE_CACHE_SHIFT -
btree_inode->i_blkbits));
if (root->fs_info->block_group_cache) {
root->fs_info->block_group_cache->last_alloc =
root->fs_info->block_group_cache->first_free;
}
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
btrfs_root *extent_root)
u64 super_blocks_used;
struct btrfs_fs_info *info = extent_root->fs_info;
btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
btrfs_set_extent_owner(&extent_item, extent_root->root_key.objectid);
for (i = 0; i < extent_root->fs_info->extent_tree_insert_nr; i++) {
ins.objectid = extent_root->fs_info->extent_tree_insert[i];
super_blocks_used = btrfs_super_blocks_used(info->disk_super);
btrfs_set_super_blocks_used(info->disk_super,
super_blocks_used + 1);
ret = btrfs_insert_item(trans, extent_root, &ins, &extent_item,
sizeof(extent_item));
extent_root->fs_info->extent_tree_insert_nr = 0;
extent_root->fs_info->extent_tree_prealloc_nr = 0;
static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
bh = btrfs_find_tree_block(root, blocknr);
if (bh) {
if (buffer_uptodate(bh)) {
u64 transid =
root->fs_info->running_transaction->transid;
header = btrfs_buffer_header(bh);
if (btrfs_header_generation(header) ==
transid) {
btrfs_block_release(root, bh);
return 0;
}
}
err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
} else {
err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
}
* remove an extent from the root, returns 0 on success
static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
*root, u64 blocknr, u64 num_blocks, int pin)
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root;
key.objectid = blocknr;
key.flags = 0;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
path = btrfs_alloc_path();
BUG_ON(!path);
btrfs_init_path(path);
ret = btrfs_search_slot(trans, extent_root, &key, path, -1, 1);
printk("failed to find %Lu\n", key.objectid);
btrfs_print_tree(extent_root, extent_root->node);
printk("failed to find %Lu\n", key.objectid);
ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
refs = btrfs_extent_refs(ei) - 1;
btrfs_set_extent_refs(ei, refs);
btrfs_mark_buffer_dirty(path->nodes[0]);
super_blocks_used = btrfs_super_blocks_used(info->disk_super);
btrfs_set_super_blocks_used(info->disk_super,
super_blocks_used - num_blocks);
ret = btrfs_del_item(trans, extent_root, path);
ret = update_block_group(trans, root, blocknr, num_blocks, 0);
BUG_ON(ret);
btrfs_release_path(extent_root, path);
btrfs_free_path(path);
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)
struct radix_tree_root *pending_radix;
struct radix_tree_root *pinned_radix;
pending_radix = &extent_root->fs_info->pending_del_radix;
pinned_radix = &extent_root->fs_info->pinned_radix;
ret = find_first_radix_bit(pending_radix, gang,
ARRAY_SIZE(gang));
if (!ret)
break;
for (i = 0; i < ret; i++) {
wret = set_radix_bit(pinned_radix, gang[i]);
BUG_ON(wret);
wret = clear_radix_bit(pending_radix, gang[i]);
BUG_ON(wret);
wret = __free_extent(trans, extent_root,
}
/*
* remove an extent from the root, returns 0 on success
*/
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
*root, u64 blocknr, u64 num_blocks, int pin)
struct btrfs_root *extent_root = root->fs_info->extent_root;
ret = __free_extent(trans, root, blocknr, num_blocks, pin);
pending_ret = del_pending_extents(trans, root->fs_info->extent_root);
return ret ? ret : pending_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_blocks, u64 search_start, u64
search_end, struct btrfs_key *ins)
int ret;
u64 hole_size = 0;
int slot = 0;
struct btrfs_root * root = orig_root->fs_info->extent_root;
struct btrfs_fs_info *info = root->fs_info;
int total_found = 0;
int fill_prealloc = 0;
path = btrfs_alloc_path();
ins->flags = 0;
btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
level = btrfs_header_level(btrfs_buffer_header(root->node));
if (num_blocks == 0) {
fill_prealloc = 1;
num_blocks = 1;
total_needed = (min(level + 1, BTRFS_MAX_LEVEL) + 2) * 3;
find_search_start(root, 0);
if (info->block_group_cache &&
info->block_group_cache->last_alloc > search_start)
search_start = info->block_group_cache->last_alloc;
ins->objectid = search_start;
ins->offset = 0;
start_found = 0;
ret = btrfs_search_slot(trans, root, ins, path, 0, 0);
if (path->slots[0] > 0)
path->slots[0]--;
l = btrfs_buffer_leaf(path->nodes[0]);
slot = path->slots[0];
if (slot >= btrfs_header_nritems(&l->header)) {
if (fill_prealloc) {
info->extent_tree_prealloc_nr = 0;
total_found = 0;
}
ret = btrfs_next_leaf(root, path);
if (!start_found) {
ins->objectid = search_start;
ins->offset = (u64)-1 - search_start;
start_found = 1;
goto check_pending;
}
ins->objectid = last_block > search_start ?
last_block : search_start;
ins->offset = (u64)-1 - ins->objectid;
btrfs_disk_key_to_cpu(&key, &l->items[slot].key);
if (btrfs_key_type(&key) != BTRFS_EXTENT_ITEM_KEY)
goto next;
if (last_block < search_start)
last_block = search_start;
}
// FIXME -ENOSPC
check_pending:
/* we have to make sure we didn't find an extent that has already
* been allocated by the map tree or the original allocation
*/
BUG_ON(ins->objectid < search_start);
if (ins->objectid >= btrfs_super_total_blocks(info->disk_super)) {
if (search_start == 0)
return -ENOSPC;
search_start = 0;
goto check_failed;
}
test_block < ins->objectid + num_blocks; test_block++) {
if (test_radix_bit(&info->pinned_radix, test_block)) {
goto check_failed;
}
}
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
if (!fill_prealloc && info->extent_tree_insert_nr) {
u64 last =
info->extent_tree_insert[info->extent_tree_insert_nr - 1];
if (ins->objectid + num_blocks >
info->extent_tree_insert[0] &&
ins->objectid <= last) {
search_start = last + 1;
WARN_ON(1);
goto check_failed;
}
}
if (!fill_prealloc && info->extent_tree_prealloc_nr) {
u64 first =
info->extent_tree_prealloc[info->extent_tree_prealloc_nr - 1];
if (ins->objectid + num_blocks > first &&
ins->objectid <= info->extent_tree_prealloc[0]) {
search_start = info->extent_tree_prealloc[0] + 1;
WARN_ON(1);
goto check_failed;
}
}
if (fill_prealloc) {
int nr;
test_block = ins->objectid;
while(test_block < ins->objectid + ins->offset &&
total_found < total_needed) {
nr = total_needed - total_found - 1;
BUG_ON(nr < 0);
info->extent_tree_prealloc[nr] = test_block;
total_found++;
test_block++;
}
if (total_found < total_needed) {
search_start = test_block;
goto check_failed;
}
info->extent_tree_prealloc_nr = total_found;
}
ret = radix_tree_gang_lookup(&info->block_group_radix,
(void **)&info->block_group_cache,
ins->objectid, 1);
if (ret) {
info->block_group_cache->last_alloc = ins->objectid;
btrfs_release_path(root, path);
btrfs_free_path(path);
}
/*
* finds a free extent and does all the dirty work required for allocation
* returns the key for the extent through ins, and a tree buffer for
* the first block of the extent through buf.
*
* returns 0 if everything worked, non-zero otherwise.
*/
int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 owner,
u64 num_blocks, u64 search_start,
u64 search_end, struct btrfs_key *ins)
{
int ret;
int pending_ret;
u64 super_blocks_used;
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root;
btrfs_set_extent_owner(&extent_item, owner);
int nr;
BUG_ON(info->extent_tree_prealloc_nr == 0);
BUG_ON(num_blocks != 1);
ins->offset = 1;
info->extent_tree_prealloc_nr--;
nr = info->extent_tree_prealloc_nr;
ins->objectid = info->extent_tree_prealloc[nr];
info->extent_tree_insert[info->extent_tree_insert_nr++] =
ins->objectid;
ret = update_block_group(trans, root,
ins->objectid, ins->offset, 1);
BUG_ON(ret);
ret = find_free_extent(trans, root, num_blocks, search_start,
search_end, ins);
if (ret)
return ret;
/* then do prealloc for the extent tree */
ret = find_free_extent(trans, root, 0, ins->objectid + ins->offset,
search_end, &prealloc_key);
if (ret)
return ret;
super_blocks_used = btrfs_super_blocks_used(info->disk_super);
btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
num_blocks);
ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
sizeof(extent_item));
finish_current_insert(trans, extent_root);
pending_ret = del_pending_extents(trans, extent_root);
if (ret)
return ret;
if (pending_ret)
return pending_ret;
ret = update_block_group(trans, root, ins->objectid, ins->offset, 1);
}
/*
* helper function to allocate a block for a given tree
* returns the tree buffer or NULL.
*/
struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
ret = btrfs_alloc_extent(trans, root, root->root_key.objectid,
1, 0, (unsigned long)-1, &ins);
if (ret) {
BUG();
return NULL;
}
buf = btrfs_find_create_tree_block(root, ins.objectid);
set_buffer_uptodate(buf);
set_radix_bit(&trans->transaction->dirty_pages, buf->b_page->index);
static int drop_leaf_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct buffer_head *cur)
{
struct btrfs_disk_key *key;
struct btrfs_leaf *leaf;
struct btrfs_file_extent_item *fi;
int i;
int nritems;
int ret;
BUG_ON(!btrfs_is_leaf(btrfs_buffer_node(cur)));
leaf = btrfs_buffer_leaf(cur);
nritems = btrfs_header_nritems(&leaf->header);
for (i = 0; i < nritems; i++) {
key = &leaf->items[i].key;
if (btrfs_disk_key_type(key) != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
if (btrfs_file_extent_type(fi) == BTRFS_FILE_EXTENT_INLINE)
continue;
/*
* FIXME make sure to insert a trans record that
* repeats the snapshot del on crash
*/
ret = btrfs_free_extent(trans, root,
btrfs_file_extent_disk_blocknr(fi),
btrfs_file_extent_disk_num_blocks(fi),
0);
BUG_ON(ret);
}
return 0;
}
/*
* helper function for drop_snapshot, this walks down the tree dropping ref
* counts as it goes.
*/
static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, int *level)
struct buffer_head *next;
struct buffer_head *cur;
u64 blocknr;
int ret;
u32 refs;
WARN_ON(*level < 0);
WARN_ON(*level >= BTRFS_MAX_LEVEL);
ret = lookup_extent_ref(trans, root, bh_blocknr(path->nodes[*level]),
BUG_ON(ret);
if (refs > 1)
goto out;
/*
* walk down to the last node level and free all the leaves
*/
WARN_ON(*level < 0);
WARN_ON(*level >= BTRFS_MAX_LEVEL);
if (btrfs_header_level(btrfs_buffer_header(cur)) != *level)
WARN_ON(1);
btrfs_header_nritems(btrfs_buffer_header(cur)))
if (*level == 0) {
ret = drop_leaf_ref(trans, root, cur);
BUG_ON(ret);
break;
}
blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur),
path->slots[*level]);
ret = lookup_extent_ref(trans, root, blocknr, 1, &refs);
ret = btrfs_free_extent(trans, root, blocknr, 1, 1);
BUG_ON(ret);
continue;
}
next = read_tree_block(root, blocknr);
btrfs_block_release(root, path->nodes[*level-1]);
*level = btrfs_header_level(btrfs_buffer_header(next));
WARN_ON(*level < 0);
WARN_ON(*level >= BTRFS_MAX_LEVEL);
ret = btrfs_free_extent(trans, root,
bh_blocknr(path->nodes[*level]), 1, 1);
btrfs_block_release(root, path->nodes[*level]);
path->nodes[*level] = NULL;
*level += 1;
BUG_ON(ret);
return 0;
}
/*
* helper for dropping snapshots. This walks back up the tree in the path
* to find the first node higher up where we haven't yet gone through
* all the slots
*/
static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, int *level)
for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
if (slot < btrfs_header_nritems(
btrfs_buffer_header(path->nodes[i])) - 1) {
path->slots[i]++;
*level = i;
return 0;
} else {
ret = btrfs_free_extent(trans, root,
bh_blocknr(path->nodes[*level]),
btrfs_block_release(root, path->nodes[*level]);
*level = i + 1;
}
}
return 1;
}
/*
* drop the reference count on the tree rooted at 'snap'. This traverses
* the tree freeing any blocks that have a ref count of zero after being
* decremented.
*/
int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root

Chris Mason
committed
int ret = 0;
path = btrfs_alloc_path();
BUG_ON(!path);
btrfs_init_path(path);
level = btrfs_header_level(btrfs_buffer_header(snap));
path->nodes[level] = snap;
path->slots[level] = 0;
wret = walk_down_tree(trans, root, path, &level);
wret = walk_up_tree(trans, root, path, &level);
if (path->nodes[i]) {
btrfs_block_release(root, path->nodes[i]);
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
int btrfs_free_block_groups(struct btrfs_fs_info *info)
{
int ret;
struct btrfs_block_group_cache *cache[8];
int i;
while(1) {
ret = radix_tree_gang_lookup(&info->block_group_radix,
(void **)cache, 0,
ARRAY_SIZE(cache));
if (!ret)
break;
for (i = 0; i < ret; i++) {
radix_tree_delete(&info->block_group_radix,
cache[i]->key.objectid +
cache[i]->key.offset - 1);
kfree(cache[i]);
}
}
return 0;
}
int btrfs_read_block_groups(struct btrfs_root *root)
{
struct btrfs_path *path;
int ret;
int err = 0;
struct btrfs_block_group_item *bi;
struct btrfs_block_group_cache *cache;
struct btrfs_key key;
struct btrfs_key found_key;
struct btrfs_leaf *leaf;
u64 group_size_blocks = BTRFS_BLOCK_GROUP_SIZE / root->blocksize;
root = root->fs_info->extent_root;
key.objectid = 0;
key.offset = group_size_blocks;
key.flags = 0;
btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY);
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
while(1) {
ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
&key, path, 0, 0);
if (ret != 0) {
err = ret;
break;
}
leaf = btrfs_buffer_leaf(path->nodes[0]);
btrfs_disk_key_to_cpu(&found_key,
&leaf->items[path->slots[0]].key);
cache = kmalloc(sizeof(*cache), GFP_NOFS);
if (!cache) {