Commit cc0c5538 authored by Chris Mason's avatar Chris Mason

Btrfs: Fix split_leaf to detect when it is extending an item

When making room for a new item, it is ok to create an empty leaf, but
when making room to extend an item, split_leaf needs to make sure it
keeps the item we're extending in the path and make sure we don't end up
with an empty leaf.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 65555a06
...@@ -26,7 +26,7 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -26,7 +26,7 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, int level); *root, struct btrfs_path *path, int level);
static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_key *ins_key, *root, struct btrfs_key *ins_key,
struct btrfs_path *path, int data_size); struct btrfs_path *path, int data_size, int extend);
static int push_node_left(struct btrfs_trans_handle *trans, static int push_node_left(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *dst, struct btrfs_root *root, struct extent_buffer *dst,
struct extent_buffer *src); struct extent_buffer *src);
...@@ -1049,7 +1049,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1049,7 +1049,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
if (ins_len > 0 && btrfs_leaf_free_space(root, b) < if (ins_len > 0 && btrfs_leaf_free_space(root, b) <
sizeof(struct btrfs_item) + ins_len) { sizeof(struct btrfs_item) + ins_len) {
int sret = split_leaf(trans, root, key, int sret = split_leaf(trans, root, key,
p, ins_len); p, ins_len, ret == 0);
BUG_ON(sret > 0); BUG_ON(sret > 0);
if (sret) if (sret)
return sret; return sret;
...@@ -1755,7 +1755,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1755,7 +1755,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
*/ */
static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_key *ins_key, *root, struct btrfs_key *ins_key,
struct btrfs_path *path, int data_size) struct btrfs_path *path, int data_size, int extend)
{ {
struct extent_buffer *l; struct extent_buffer *l;
u32 nritems; u32 nritems;
...@@ -1768,9 +1768,13 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1768,9 +1768,13 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
int i; int i;
int ret = 0; int ret = 0;
int wret; int wret;
int double_split = 0; int double_split;
int num_doubles = 0;
struct btrfs_disk_key disk_key; struct btrfs_disk_key disk_key;
if (extend)
space_needed = data_size;
/* first try to make some room by pushing left and right */ /* first try to make some room by pushing left and right */
if (ins_key->type != BTRFS_DIR_ITEM_KEY) { if (ins_key->type != BTRFS_DIR_ITEM_KEY) {
wret = push_leaf_right(trans, root, path, data_size); wret = push_leaf_right(trans, root, path, data_size);
...@@ -1785,19 +1789,18 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1785,19 +1789,18 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
l = path->nodes[0]; l = path->nodes[0];
/* did the pushes work? */ /* did the pushes work? */
if (btrfs_leaf_free_space(root, l) >= if (btrfs_leaf_free_space(root, l) >= space_needed)
sizeof(struct btrfs_item) + data_size) {
return 0; return 0;
} }
} else {
l = path->nodes[0];
}
if (!path->nodes[1]) { if (!path->nodes[1]) {
ret = insert_new_root(trans, root, path, 1); ret = insert_new_root(trans, root, path, 1);
if (ret) if (ret)
return ret; return ret;
} }
again:
double_split = 0;
l = path->nodes[0];
slot = path->slots[0]; slot = path->slots[0];
nritems = btrfs_header_nritems(l); nritems = btrfs_header_nritems(l);
mid = (nritems + 1)/ 2; mid = (nritems + 1)/ 2;
...@@ -1815,7 +1818,6 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1815,7 +1818,6 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
write_extent_buffer(right, root->fs_info->fsid, write_extent_buffer(right, root->fs_info->fsid,
(unsigned long)btrfs_header_fsid(right), (unsigned long)btrfs_header_fsid(right),
BTRFS_FSID_SIZE); BTRFS_FSID_SIZE);
if (mid <= slot) { if (mid <= slot) {
if (nritems == 1 || if (nritems == 1 ||
leaf_space_used(l, mid, nritems - mid) + space_needed > leaf_space_used(l, mid, nritems - mid) + space_needed >
...@@ -1844,7 +1846,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1844,7 +1846,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
} else { } else {
if (leaf_space_used(l, 0, mid + 1) + space_needed > if (leaf_space_used(l, 0, mid + 1) + space_needed >
BTRFS_LEAF_DATA_SIZE(root)) { BTRFS_LEAF_DATA_SIZE(root)) {
if (slot == 0) { if (!extend && slot == 0) {
btrfs_cpu_key_to_disk(&disk_key, ins_key); btrfs_cpu_key_to_disk(&disk_key, ins_key);
btrfs_set_header_nritems(right, 0); btrfs_set_header_nritems(right, 0);
wret = insert_ptr(trans, root, path, wret = insert_ptr(trans, root, path,
...@@ -1863,7 +1865,9 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1863,7 +1865,9 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
ret = wret; ret = wret;
} }
return ret; return ret;
} } else if (extend && slot == 0) {
mid = 1;
} else {
mid = slot; mid = slot;
if (mid != nritems && if (mid != nritems &&
leaf_space_used(l, mid, nritems - mid) + leaf_space_used(l, mid, nritems - mid) +
...@@ -1872,6 +1876,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1872,6 +1876,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
} }
} }
} }
}
nritems = nritems - mid; nritems = nritems - mid;
btrfs_set_header_nritems(right, nritems); btrfs_set_header_nritems(right, nritems);
data_copy_size = btrfs_item_end_nr(l, mid) - leaf_data_end(root, l); data_copy_size = btrfs_item_end_nr(l, mid) - leaf_data_end(root, l);
...@@ -1931,39 +1936,11 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1931,39 +1936,11 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
BUG_ON(path->slots[0] < 0); BUG_ON(path->slots[0] < 0);
if (!double_split) { if (double_split) {
return ret; BUG_ON(num_doubles != 0);
} num_doubles++;
goto again;
right = btrfs_alloc_free_block(trans, root, root->leafsize,
l->start, 0);
if (IS_ERR(right))
return PTR_ERR(right);
memset_extent_buffer(right, 0, 0, sizeof(struct btrfs_header));
btrfs_set_header_bytenr(right, right->start);
btrfs_set_header_generation(right, trans->transid);
btrfs_set_header_owner(right, root->root_key.objectid);
btrfs_set_header_level(right, 0);
write_extent_buffer(right, root->fs_info->fsid,
(unsigned long)btrfs_header_fsid(right),
BTRFS_FSID_SIZE);
btrfs_cpu_key_to_disk(&disk_key, ins_key);
btrfs_set_header_nritems(right, 0);
wret = insert_ptr(trans, root, path,
&disk_key, right->start,
path->slots[1], 1);
if (wret)
ret = wret;
if (path->slots[1] == 0) {
wret = fixup_low_keys(trans, root, path, &disk_key, 1);
if (wret)
ret = wret;
} }
free_extent_buffer(path->nodes[0]);
path->nodes[0] = right;
path->slots[0] = 0;
return ret; return ret;
} }
...@@ -1992,8 +1969,7 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans, ...@@ -1992,8 +1969,7 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
slot = path->slots[0]; slot = path->slots[0];
old_data_start = btrfs_item_offset_nr(leaf, slot); old_data_start = btrfs_item_offset_nr(leaf, slot);
old_size = btrfs_item_size_nr(leaf, slot); old_size = btrfs_item_size_nr(leaf, slot); BUG_ON(old_size <= new_size);
BUG_ON(old_size <= new_size);
size_diff = old_size - new_size; size_diff = old_size - new_size;
BUG_ON(slot < 0); BUG_ON(slot < 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment