summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ctree.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r--fs/btrfs/ctree.c195
1 files changed, 153 insertions, 42 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 7a08491e208e..c7e47e77723f 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -65,44 +65,44 @@ void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p)
memset(p, 0, sizeof(*p));
}
-static int btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root
+static int __btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct buffer_head *buf, struct buffer_head
*parent, int parent_slot, struct buffer_head
- **cow_ret)
+ **cow_ret, u64 search_start, u64 empty_size)
{
struct buffer_head *cow;
struct btrfs_node *cow_node;
- int ret;
+ int ret = 0;
+ int different_trans = 0;
+ WARN_ON(root->ref_cows && trans->transid != root->last_trans);
WARN_ON(!buffer_uptodate(buf));
- if (trans->transaction != root->fs_info->running_transaction) {
- printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid,
- root->fs_info->running_transaction->transid);
- WARN_ON(1);
- }
- if (trans->transid != root->fs_info->generation) {
- printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid,
- root->fs_info->generation);
- WARN_ON(1);
- }
- if (btrfs_header_generation(btrfs_buffer_header(buf)) ==
- trans->transid) {
- *cow_ret = buf;
- return 0;
- }
- cow = btrfs_alloc_free_block(trans, root, buf->b_blocknr);
+ cow = btrfs_alloc_free_block(trans, root, search_start, empty_size);
if (IS_ERR(cow))
return PTR_ERR(cow);
+
cow_node = btrfs_buffer_node(cow);
if (buf->b_size != root->blocksize || cow->b_size != root->blocksize)
WARN_ON(1);
+
memcpy(cow_node, btrfs_buffer_node(buf), root->blocksize);
btrfs_set_header_blocknr(&cow_node->header, bh_blocknr(cow));
btrfs_set_header_generation(&cow_node->header, trans->transid);
btrfs_set_header_owner(&cow_node->header, root->root_key.objectid);
- ret = btrfs_inc_ref(trans, root, buf);
- if (ret)
- return ret;
+
+ WARN_ON(btrfs_header_generation(btrfs_buffer_header(buf)) >
+ trans->transid);
+ if (btrfs_header_generation(btrfs_buffer_header(buf)) !=
+ trans->transid) {
+ different_trans = 1;
+ ret = btrfs_inc_ref(trans, root, buf);
+ if (ret)
+ return ret;
+ } else {
+ WARN_ON(!root->ref_cows);
+ clean_tree_block(trans, root, buf);
+ }
+
if (buf == root->node) {
root->node = cow;
get_bh(cow);
@@ -114,6 +114,8 @@ static int btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root
btrfs_set_node_blockptr(btrfs_buffer_node(parent), parent_slot,
bh_blocknr(cow));
btrfs_mark_buffer_dirty(parent);
+ WARN_ON(btrfs_header_generation(btrfs_buffer_header(parent)) !=
+ trans->transid);
btrfs_free_extent(trans, root, bh_blocknr(buf), 1, 1);
}
btrfs_block_release(root, buf);
@@ -122,6 +124,115 @@ static int btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root
return 0;
}
+int btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root
+ *root, struct buffer_head *buf, struct buffer_head
+ *parent, int parent_slot, struct buffer_head
+ **cow_ret)
+{
+ u64 search_start;
+ if (trans->transaction != root->fs_info->running_transaction) {
+ printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid,
+ root->fs_info->running_transaction->transid);
+ WARN_ON(1);
+ }
+ if (trans->transid != root->fs_info->generation) {
+ printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid,
+ root->fs_info->generation);
+ WARN_ON(1);
+ }
+ if (btrfs_header_generation(btrfs_buffer_header(buf)) ==
+ trans->transid) {
+ *cow_ret = buf;
+ return 0;
+ }
+
+ search_start = bh_blocknr(buf) & ~((u64)65535);
+ return __btrfs_cow_block(trans, root, buf, parent,
+ parent_slot, cow_ret, search_start, 0);
+}
+
+static int close_blocks(u64 blocknr, u64 other)
+{
+ if (blocknr < other && other - blocknr < 8)
+ return 1;
+ if (blocknr > other && blocknr - other < 8)
+ return 1;
+ return 0;
+}
+
+int btrfs_realloc_node(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, struct buffer_head *parent,
+ int cache_only)
+{
+ struct btrfs_node *parent_node;
+ struct buffer_head *cur_bh;
+ struct buffer_head *tmp_bh;
+ u64 blocknr;
+ u64 search_start = 0;
+ u64 other;
+ u32 parent_nritems;
+ int start_slot;
+ int end_slot;
+ int i;
+ int err = 0;
+
+ if (trans->transaction != root->fs_info->running_transaction) {
+ printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid,
+ root->fs_info->running_transaction->transid);
+ WARN_ON(1);
+ }
+ if (trans->transid != root->fs_info->generation) {
+ printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid,
+ root->fs_info->generation);
+ WARN_ON(1);
+ }
+ parent_node = btrfs_buffer_node(parent);
+ parent_nritems = btrfs_header_nritems(&parent_node->header);
+
+ start_slot = 0;
+ end_slot = parent_nritems;
+
+ if (parent_nritems == 1)
+ return 0;
+
+ for (i = start_slot; i < end_slot; i++) {
+ int close = 1;
+ blocknr = btrfs_node_blockptr(parent_node, i);
+ if (i > 0) {
+ other = btrfs_node_blockptr(parent_node, i - 1);
+ close = close_blocks(blocknr, other);
+ }
+ if (close && i < end_slot - 1) {
+ other = btrfs_node_blockptr(parent_node, i + 1);
+ close = close_blocks(blocknr, other);
+ }
+ if (close)
+ continue;
+
+ cur_bh = btrfs_find_tree_block(root, blocknr);
+ if (!cur_bh || !buffer_uptodate(cur_bh) ||
+ buffer_locked(cur_bh)) {
+ if (cache_only) {
+ brelse(cur_bh);
+ continue;
+ }
+ brelse(cur_bh);
+ cur_bh = read_tree_block(root, blocknr);
+ }
+ if (search_start == 0) {
+ search_start = bh_blocknr(cur_bh) & ~((u64)65535);
+ }
+ err = __btrfs_cow_block(trans, root, cur_bh, parent, i,
+ &tmp_bh, search_start,
+ min(8, end_slot - i));
+ if (err)
+ break;
+ search_start = bh_blocknr(tmp_bh);
+ brelse(tmp_bh);
+ }
+ return err;
+}
+
/*
* The leaf data grows from end-to-front in the node.
* this returns the address of the start of the last item,
@@ -221,6 +332,7 @@ static int check_leaf(struct btrfs_root *root, struct btrfs_path *path,
parent_slot = path->slots[level + 1];
parent_key = &parent->ptrs[parent_slot].key;
+
BUG_ON(memcmp(parent_key, &leaf->items[0].key,
sizeof(struct btrfs_disk_key)));
BUG_ON(btrfs_node_blockptr(parent, parent_slot) !=
@@ -643,7 +755,7 @@ static int push_nodes_for_insert(struct btrfs_trans_handle *trans,
* readahead one full node of leaves
*/
static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
- int slot)
+ int level, int slot)
{
struct btrfs_node *node;
int i;
@@ -659,10 +771,13 @@ static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
unsigned long gang[8];
struct buffer_head *bh;
- if (!path->nodes[1])
+ if (level == 0)
+ return;
+
+ if (!path->nodes[level])
return;
- node = btrfs_buffer_node(path->nodes[1]);
+ node = btrfs_buffer_node(path->nodes[level]);
search = btrfs_node_blockptr(node, slot);
bh = btrfs_find_tree_block(root, search);
if (bh) {
@@ -690,7 +805,7 @@ static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
for (i = 0; i < ret; i++) {
blocknr = gang[i];
clear_radix_bit(&found, blocknr);
- if (nread > 64)
+ if (nread > 32)
continue;
if (direction > 0 && cluster_start <= blocknr &&
cluster_start + 8 > blocknr) {
@@ -726,7 +841,6 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
struct buffer_head *b;
struct buffer_head *cow_buf;
struct btrfs_node *c;
- struct btrfs_root_item *root_item = &root->root_item;
u64 blocknr;
int slot;
int ret;
@@ -734,11 +848,8 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
int should_reada = p->reada;
u8 lowest_level = 0;
- if (btrfs_root_refs(root_item) == 0 && root->ref_cows) {
- lowest_level = root_item->drop_level;
- WARN_ON(ins_len || cow);
- }
-
+ lowest_level = p->lowest_level;
+ WARN_ON(lowest_level && ins_len);
WARN_ON(p->nodes[0] != NULL);
WARN_ON(!mutex_is_locked(&root->fs_info->fs_mutex));
again:
@@ -798,8 +909,8 @@ again:
if (level == lowest_level)
break;
blocknr = btrfs_node_blockptr(c, slot);
- if (level == 1 && should_reada)
- reada_for_search(root, p, slot);
+ if (should_reada)
+ reada_for_search(root, p, level, slot);
b = read_tree_block(root, btrfs_node_blockptr(c, slot));
} else {
@@ -960,7 +1071,7 @@ static int insert_new_root(struct btrfs_trans_handle *trans, struct btrfs_root
BUG_ON(path->nodes[level]);
BUG_ON(path->nodes[level-1] != root->node);
- t = btrfs_alloc_free_block(trans, root, root->node->b_blocknr);
+ t = btrfs_alloc_free_block(trans, root, root->node->b_blocknr, 0);
if (IS_ERR(t))
return PTR_ERR(t);
c = btrfs_buffer_node(t);
@@ -1070,7 +1181,7 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
}
c_nritems = btrfs_header_nritems(&c->header);
- split_buffer = btrfs_alloc_free_block(trans, root, t->b_blocknr);
+ split_buffer = btrfs_alloc_free_block(trans, root, t->b_blocknr, 0);
if (IS_ERR(split_buffer))
return PTR_ERR(split_buffer);
@@ -1461,7 +1572,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
nritems = btrfs_header_nritems(&l->header);
mid = (nritems + 1)/ 2;
- right_buffer = btrfs_alloc_free_block(trans, root, l_buf->b_blocknr);
+ right_buffer = btrfs_alloc_free_block(trans, root, l_buf->b_blocknr, 0);
if (IS_ERR(right_buffer))
return PTR_ERR(right_buffer);
@@ -1560,7 +1671,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
if (!double_split)
return ret;
- right_buffer = btrfs_alloc_free_block(trans, root, l_buf->b_blocknr);
+ right_buffer = btrfs_alloc_free_block(trans, root, l_buf->b_blocknr, 0);
if (IS_ERR(right_buffer))
return PTR_ERR(right_buffer);
@@ -1988,8 +2099,8 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
blocknr = btrfs_node_blockptr(c_node, slot);
if (next)
btrfs_block_release(root, next);
- if (level == 1 && path->reada)
- reada_for_search(root, path, slot);
+ if (path->reada)
+ reada_for_search(root, path, level, slot);
next = read_tree_block(root, blocknr);
break;
}
@@ -2002,8 +2113,8 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
path->slots[level] = 0;
if (!level)
break;
- if (level == 1 && path->reada)
- reada_for_search(root, path, slot);
+ if (path->reada)
+ reada_for_search(root, path, level, slot);
next = read_tree_block(root,
btrfs_node_blockptr(btrfs_buffer_node(next), 0));
}