Commit ad48fd75 authored by Yan, Zheng's avatar Yan, Zheng Committed by Chris Mason

Btrfs: Add btrfs_duplicate_item

btrfs_duplicate_item duplicates item with new key, guaranteeing
the source item and the new items are in the same tree leaf and
contiguous. It allows us to split file extent in place, without
using lock_extent to prevent bookend extent race.
Signed-off-by: default avatarYan Zheng <zheng.yan@oracle.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 8cef4e16
...@@ -37,6 +37,11 @@ static int balance_node_right(struct btrfs_trans_handle *trans, ...@@ -37,6 +37,11 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
struct extent_buffer *src_buf); struct extent_buffer *src_buf);
static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_path *path, int level, int slot); struct btrfs_path *path, int level, int slot);
static int setup_items_for_insert(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_key *cpu_key, u32 *data_size,
u32 total_data, u32 total_size, int nr);
struct btrfs_path *btrfs_alloc_path(void) struct btrfs_path *btrfs_alloc_path(void)
{ {
...@@ -2997,75 +3002,85 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, ...@@ -2997,75 +3002,85 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
/* static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans,
* This function splits a single item into two items, struct btrfs_root *root,
* giving 'new_key' to the new item and splitting the struct btrfs_path *path, int ins_len)
* 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 btrfs_key key;
struct extent_buffer *leaf; struct extent_buffer *leaf;
struct btrfs_key orig_key; struct btrfs_file_extent_item *fi;
struct btrfs_item *item; u64 extent_len = 0;
struct btrfs_item *new_item; u32 item_size;
int ret = 0; int ret;
int slot;
u32 nritems;
u32 orig_offset;
struct btrfs_disk_key disk_key;
char *buf;
leaf = path->nodes[0]; leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &orig_key, path->slots[0]); btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (btrfs_leaf_free_space(root, leaf) >= sizeof(struct btrfs_item))
goto split; BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY &&
key.type != BTRFS_EXTENT_CSUM_KEY);
if (btrfs_leaf_free_space(root, leaf) >= ins_len)
return 0;
item_size = btrfs_item_size_nr(leaf, path->slots[0]); item_size = btrfs_item_size_nr(leaf, path->slots[0]);
if (key.type == BTRFS_EXTENT_DATA_KEY) {
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
extent_len = btrfs_file_extent_num_bytes(leaf, fi);
}
btrfs_release_path(root, path); btrfs_release_path(root, path);
path->search_for_split = 1;
path->keep_locks = 1; path->keep_locks = 1;
path->search_for_split = 1;
ret = btrfs_search_slot(trans, root, &orig_key, path, 0, 1); ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
path->search_for_split = 0; path->search_for_split = 0;
if (ret < 0)
goto err;
ret = -EAGAIN;
leaf = path->nodes[0];
/* if our item isn't there or got smaller, return now */ /* if our item isn't there or got smaller, return now */
if (ret != 0 || item_size != btrfs_item_size_nr(path->nodes[0], if (ret > 0 || item_size != btrfs_item_size_nr(leaf, path->slots[0]))
path->slots[0])) { goto err;
path->keep_locks = 0;
return -EAGAIN; if (key.type == BTRFS_EXTENT_DATA_KEY) {
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
if (extent_len != btrfs_file_extent_num_bytes(leaf, fi))
goto err;
} }
btrfs_set_path_blocking(path); btrfs_set_path_blocking(path);
ret = split_leaf(trans, root, &orig_key, path, ret = split_leaf(trans, root, &key, path, ins_len, 1);
sizeof(struct btrfs_item), 1);
path->keep_locks = 0;
BUG_ON(ret); BUG_ON(ret);
path->keep_locks = 0;
btrfs_unlock_up_safe(path, 1); btrfs_unlock_up_safe(path, 1);
return 0;
err:
path->keep_locks = 0;
return ret;
}
static noinline int split_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_key *new_key,
unsigned long split_offset)
{
struct extent_buffer *leaf;
struct btrfs_item *item;
struct btrfs_item *new_item;
int slot;
char *buf;
u32 nritems;
u32 item_size;
u32 orig_offset;
struct btrfs_disk_key disk_key;
leaf = path->nodes[0]; leaf = path->nodes[0];
BUG_ON(btrfs_leaf_free_space(root, leaf) < sizeof(struct btrfs_item)); BUG_ON(btrfs_leaf_free_space(root, leaf) < sizeof(struct btrfs_item));
split:
/*
* make sure any changes to the path from split_leaf leave it
* in a blocking state
*/
btrfs_set_path_blocking(path); btrfs_set_path_blocking(path);
item = btrfs_item_nr(leaf, path->slots[0]); item = btrfs_item_nr(leaf, path->slots[0]);
...@@ -3073,19 +3088,19 @@ int btrfs_split_item(struct btrfs_trans_handle *trans, ...@@ -3073,19 +3088,19 @@ int btrfs_split_item(struct btrfs_trans_handle *trans,
item_size = btrfs_item_size(leaf, item); item_size = btrfs_item_size(leaf, item);
buf = kmalloc(item_size, GFP_NOFS); buf = kmalloc(item_size, GFP_NOFS);
if (!buf)
return -ENOMEM;
read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf, read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf,
path->slots[0]), item_size); path->slots[0]), item_size);
slot = path->slots[0] + 1;
leaf = path->nodes[0];
slot = path->slots[0] + 1;
nritems = btrfs_header_nritems(leaf); nritems = btrfs_header_nritems(leaf);
if (slot != nritems) { if (slot != nritems) {
/* shift the items */ /* shift the items */
memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + 1), memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + 1),
btrfs_item_nr_offset(slot), btrfs_item_nr_offset(slot),
(nritems - slot) * sizeof(struct btrfs_item)); (nritems - slot) * sizeof(struct btrfs_item));
} }
btrfs_cpu_key_to_disk(&disk_key, new_key); btrfs_cpu_key_to_disk(&disk_key, new_key);
...@@ -3113,15 +3128,80 @@ int btrfs_split_item(struct btrfs_trans_handle *trans, ...@@ -3113,15 +3128,80 @@ int btrfs_split_item(struct btrfs_trans_handle *trans,
item_size - split_offset); item_size - split_offset);
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
ret = 0; BUG_ON(btrfs_leaf_free_space(root, leaf) < 0);
if (btrfs_leaf_free_space(root, leaf) < 0) {
btrfs_print_leaf(root, leaf);
BUG();
}
kfree(buf); kfree(buf);
return 0;
}
/*
* 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)
{
int ret;
ret = setup_leaf_for_split(trans, root, path,
sizeof(struct btrfs_item));
if (ret)
return ret;
ret = split_item(trans, root, path, new_key, split_offset);
return ret; return ret;
} }
/*
* This function duplicate a item, giving 'new_key' to the new item.
* It guarantees both items live in the same tree leaf and the new item
* is contiguous with the original item.
*
* This allows us to split file extent in place, keeping a lock on the
* leaf the entire time.
*/
int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_key *new_key)
{
struct extent_buffer *leaf;
int ret;
u32 item_size;
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
ret = setup_leaf_for_split(trans, root, path,
item_size + sizeof(struct btrfs_item));
if (ret)
return ret;
path->slots[0]++;
ret = setup_items_for_insert(trans, root, path, new_key, &item_size,
item_size, item_size +
sizeof(struct btrfs_item), 1);
BUG_ON(ret);
leaf = path->nodes[0];
memcpy_extent_buffer(leaf,
btrfs_item_ptr_offset(leaf, path->slots[0]),
btrfs_item_ptr_offset(leaf, path->slots[0] - 1),
item_size);
return 0;
}
/* /*
* make the item pointed to by the path smaller. new_size indicates * 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 * how small to make it, and from_end tells us if we just chop bytes
......
...@@ -2089,6 +2089,10 @@ int btrfs_split_item(struct btrfs_trans_handle *trans, ...@@ -2089,6 +2089,10 @@ int btrfs_split_item(struct btrfs_trans_handle *trans,
struct btrfs_path *path, struct btrfs_path *path,
struct btrfs_key *new_key, struct btrfs_key *new_key,
unsigned long split_offset); unsigned long split_offset);
int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_key *new_key);
int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_key *key, struct btrfs_path *p, int *root, struct btrfs_key *key, struct btrfs_path *p, int
ins_len, int cow); ins_len, int cow);
......
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