* this uses that block instead of allocating a new one. btrfs_alloc_reserved_extent
* is used to finish the allocation.
*/
-int noinline __btrfs_cow_block(struct btrfs_trans_handle *trans,
+static int noinline __btrfs_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *buf,
struct extent_buffer *parent, int parent_slot,
unmap_extent_buffer(eb, map_token, KM_USER0);
map_token = NULL;
}
- err = map_extent_buffer(eb, offset,
+
+ err = map_private_extent_buffer(eb, offset,
sizeof(struct btrfs_disk_key),
&map_token, &kaddr,
&map_start, &map_len, KM_USER0);
if (ret && slot > 0)
slot -= 1;
p->slots[level] = slot;
- if (ins_len > 0 && btrfs_header_nritems(b) >=
+ if ((p->search_for_split || ins_len > 0) &&
+ btrfs_header_nritems(b) >=
BTRFS_NODEPTRS_PER_BLOCK(root) - 3) {
int sret = split_node(trans, root, p, level);
BUG_ON(sret > 0);
btrfs_tree_lock(b);
} else {
p->slots[level] = slot;
- if (ins_len > 0 && btrfs_leaf_free_space(root, b) <
- sizeof(struct btrfs_item) + ins_len) {
+ if (ins_len > 0 &&
+ btrfs_leaf_free_space(root, b) < ins_len) {
int sret = split_leaf(trans, root, key,
p, ins_len, ret == 0);
BUG_ON(sret > 0);
goto done;
}
}
- unlock_up(p, level, lowest_unlock);
+ if (!p->search_for_split)
+ unlock_up(p, level, lowest_unlock);
goto done;
}
}
right = read_node_slot(root, upper, slot + 1);
btrfs_tree_lock(right);
free_space = btrfs_leaf_free_space(root, right);
- if (free_space < data_size + sizeof(struct btrfs_item))
+ if (free_space < data_size)
goto out_unlock;
/* cow and double check */
goto out_unlock;
free_space = btrfs_leaf_free_space(root, right);
- if (free_space < data_size + sizeof(struct btrfs_item))
+ if (free_space < data_size)
goto out_unlock;
left_nritems = btrfs_header_nritems(left);
nr = 1;
if (path->slots[0] >= left_nritems)
- push_space += data_size + sizeof(*item);
+ push_space += data_size;
i = left_nritems - 1;
while (i >= nr) {
}
if (path->slots[0] == i)
- push_space += data_size + sizeof(*item);
+ push_space += data_size;
if (!left->map_token) {
map_extent_buffer(left, (unsigned long)item,
left = read_node_slot(root, path->nodes[1], slot - 1);
btrfs_tree_lock(left);
free_space = btrfs_leaf_free_space(root, left);
- if (free_space < data_size + sizeof(struct btrfs_item)) {
+ if (free_space < data_size) {
ret = 1;
goto out;
}
}
free_space = btrfs_leaf_free_space(root, left);
- if (free_space < data_size + sizeof(struct btrfs_item)) {
+ if (free_space < data_size) {
ret = 1;
goto out;
}
}
if (path->slots[0] == i)
- push_space += data_size + sizeof(*item);
+ push_space += data_size;
this_item_size = btrfs_item_size(right, item);
if (this_item_size + sizeof(*item) + push_space > free_space)
btrfs_item_offset_nr(right, push_items - 1),
push_space);
old_left_nritems = btrfs_header_nritems(left);
- BUG_ON(old_left_nritems < 0);
+ BUG_ON(old_left_nritems <= 0);
old_left_item_size = btrfs_item_offset_nr(left, old_left_nritems - 1);
for (i = old_left_nritems; i < old_left_nritems + push_items; i++) {
int mid;
int slot;
struct extent_buffer *right;
- int space_needed = data_size + sizeof(struct btrfs_item);
int data_copy_size;
int rt_data_off;
int i;
int num_doubles = 0;
struct btrfs_disk_key disk_key;
- if (extend)
- space_needed = data_size;
-
/* first try to make some room by pushing left and right */
- if (ins_key->type != BTRFS_DIR_ITEM_KEY) {
+ if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY) {
wret = push_leaf_right(trans, root, path, data_size, 0);
if (wret < 0) {
return wret;
l = path->nodes[0];
/* did the pushes work? */
- if (btrfs_leaf_free_space(root, l) >= space_needed)
+ if (btrfs_leaf_free_space(root, l) >= data_size)
return 0;
}
BTRFS_UUID_SIZE);
if (mid <= slot) {
if (nritems == 1 ||
- leaf_space_used(l, mid, nritems - mid) + space_needed >
+ leaf_space_used(l, mid, nritems - mid) + data_size >
BTRFS_LEAF_DATA_SIZE(root)) {
if (slot >= nritems) {
btrfs_cpu_key_to_disk(&disk_key, ins_key);
mid = slot;
if (mid != nritems &&
leaf_space_used(l, mid, nritems - mid) +
- space_needed > BTRFS_LEAF_DATA_SIZE(root)) {
+ data_size > BTRFS_LEAF_DATA_SIZE(root)) {
double_split = 1;
}
}
} else {
- if (leaf_space_used(l, 0, mid + 1) + space_needed >
+ if (leaf_space_used(l, 0, mid) + data_size >
BTRFS_LEAF_DATA_SIZE(root)) {
- if (!extend && slot == 0) {
+ if (!extend && data_size && slot == 0) {
btrfs_cpu_key_to_disk(&disk_key, ins_key);
btrfs_set_header_nritems(right, 0);
wret = insert_ptr(trans, root, path,
}
btrfs_mark_buffer_dirty(right);
return ret;
- } else if (extend && slot == 0) {
+ } else if ((extend || !data_size) && slot == 0) {
mid = 1;
} else {
mid = slot;
if (mid != nritems &&
leaf_space_used(l, mid, nritems - mid) +
- space_needed > BTRFS_LEAF_DATA_SIZE(root)) {
+ data_size > BTRFS_LEAF_DATA_SIZE(root)) {
double_split = 1;
}
}
}
/*
+ * This function splits a single item into two items,
+ * giving 'new_key' to the new item and splitting the
+ * old one at split_offset (from the start of the item).
+ *
+ * The path may be released by this operation. After
+ * the split, the path is pointing to the old item. The
+ * new item is going to be in the same node as the old one.
+ *
+ * Note, the item being split must be smaller enough to live alone on
+ * a tree block with room for one extra struct btrfs_item
+ *
+ * This allows us to split the item in place, keeping a lock on the
+ * leaf the entire time.
+ */
+int btrfs_split_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+ struct btrfs_key *new_key,
+ unsigned long split_offset)
+{
+ u32 item_size;
+ struct extent_buffer *leaf;
+ struct btrfs_key orig_key;
+ struct btrfs_item *item;
+ struct btrfs_item *new_item;
+ int ret = 0;
+ int slot;
+ u32 nritems;
+ u32 orig_offset;
+ struct btrfs_disk_key disk_key;
+ char *buf;
+
+ leaf = path->nodes[0];
+ btrfs_item_key_to_cpu(leaf, &orig_key, path->slots[0]);
+ if (btrfs_leaf_free_space(root, leaf) >= sizeof(struct btrfs_item))
+ goto split;
+
+ item_size = btrfs_item_size_nr(leaf, path->slots[0]);
+ btrfs_release_path(root, path);
+
+ path->search_for_split = 1;
+ path->keep_locks = 1;
+
+ ret = btrfs_search_slot(trans, root, &orig_key, path, 0, 1);
+ path->search_for_split = 0;
+
+ /* if our item isn't there or got smaller, return now */
+ if (ret != 0 || item_size != btrfs_item_size_nr(path->nodes[0],
+ path->slots[0])) {
+ path->keep_locks = 0;
+ return -EAGAIN;
+ }
+
+ ret = split_leaf(trans, root, &orig_key, path,
+ sizeof(struct btrfs_item), 1);
+ path->keep_locks = 0;
+ BUG_ON(ret);
+
+ leaf = path->nodes[0];
+ BUG_ON(btrfs_leaf_free_space(root, leaf) < sizeof(struct btrfs_item));
+
+split:
+ item = btrfs_item_nr(leaf, path->slots[0]);
+ orig_offset = btrfs_item_offset(leaf, item);
+ item_size = btrfs_item_size(leaf, item);
+
+
+ buf = kmalloc(item_size, GFP_NOFS);
+ read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf,
+ path->slots[0]), item_size);
+ slot = path->slots[0] + 1;
+ leaf = path->nodes[0];
+
+ nritems = btrfs_header_nritems(leaf);
+
+ if (slot != nritems) {
+ /* shift the items */
+ memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + 1),
+ btrfs_item_nr_offset(slot),
+ (nritems - slot) * sizeof(struct btrfs_item));
+
+ }
+
+ btrfs_cpu_key_to_disk(&disk_key, new_key);
+ btrfs_set_item_key(leaf, &disk_key, slot);
+
+ new_item = btrfs_item_nr(leaf, slot);
+
+ btrfs_set_item_offset(leaf, new_item, orig_offset);
+ btrfs_set_item_size(leaf, new_item, item_size - split_offset);
+
+ btrfs_set_item_offset(leaf, item,
+ orig_offset + item_size - split_offset);
+ btrfs_set_item_size(leaf, item, split_offset);
+
+ btrfs_set_header_nritems(leaf, nritems + 1);
+
+ /* write the data for the start of the original item */
+ write_extent_buffer(leaf, buf,
+ btrfs_item_ptr_offset(leaf, path->slots[0]),
+ split_offset);
+
+ /* write the data for the new item */
+ write_extent_buffer(leaf, buf + split_offset,
+ btrfs_item_ptr_offset(leaf, slot),
+ item_size - split_offset);
+ btrfs_mark_buffer_dirty(leaf);
+
+ ret = 0;
+ if (btrfs_leaf_free_space(root, leaf) < 0) {
+ btrfs_print_leaf(root, leaf);
+ BUG();
+ }
+ kfree(buf);
+ return ret;
+}
+
+/*
* make the item pointed to by the path smaller. new_size indicates
* how small to make it, and from_end tells us if we just chop bytes
* off the end of the item or if we shift the item to chop bytes off
struct btrfs_item *item;
int ret = 0;
int slot;
- int slot_orig;
int i;
u32 nritems;
u32 total_data = 0;
struct btrfs_disk_key disk_key;
struct btrfs_key found_key;
- found_key.objectid = 0;
- nr = min_t(int, nr, BTRFS_NODEPTRS_PER_BLOCK(root));
-
- for (i = 0; i < nr; i++)
+ for (i = 0; i < nr; i++) {
+ if (total_size + data_size[i] + sizeof(struct btrfs_item) >
+ BTRFS_LEAF_DATA_SIZE(root)) {
+ break;
+ nr = i;
+ }
total_data += data_size[i];
+ total_size += data_size[i] + sizeof(struct btrfs_item);
+ }
+ BUG_ON(nr == 0);
- total_data = min_t(u32, total_data, BTRFS_LEAF_DATA_SIZE(root));
- total_size = total_data + (nr * sizeof(struct btrfs_item));
ret = btrfs_search_slot(trans, root, cpu_key, path, total_size, 1);
if (ret == 0)
return -EEXIST;
if (ret < 0)
goto out;
- slot_orig = path->slots[0];
leaf = path->nodes[0];
nritems = btrfs_header_nritems(leaf);
int level;
int ret = 1;
+ WARN_ON(!path->keep_locks);
again:
cur = btrfs_lock_root_node(root);
level = btrfs_header_level(cur);
int slot;
struct extent_buffer *c;
+ WARN_ON(!path->keep_locks);
while(level < BTRFS_MAX_LEVEL) {
if (!path->nodes[level])
return 1;