aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorYan, Zheng <zheng.yan@oracle.com>2009-11-12 04:33:58 -0500
committerChris Mason <chris.mason@oracle.com>2009-12-15 21:24:25 -0500
commitad48fd754676bfae4139be1a897b1ea58f9aaf21 (patch)
treea48a051fa8716ba4be8f148f6d7d8ca47d93ab07 /fs/btrfs
parent8cef4e160d74920ad1725f58c89fd75ec4c4ac38 (diff)
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: Yan Zheng <zheng.yan@oracle.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ctree.c198
-rw-r--r--fs/btrfs/ctree.h4
2 files changed, 143 insertions, 59 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index ec96f3a6d536..9d4ba3470c17 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -37,6 +37,11 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
37 struct extent_buffer *src_buf); 37 struct extent_buffer *src_buf);
38static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, 38static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
39 struct btrfs_path *path, int level, int slot); 39 struct btrfs_path *path, int level, int slot);
40static int setup_items_for_insert(struct btrfs_trans_handle *trans,
41 struct btrfs_root *root, struct btrfs_path *path,
42 struct btrfs_key *cpu_key, u32 *data_size,
43 u32 total_data, u32 total_size, int nr);
44
40 45
41struct btrfs_path *btrfs_alloc_path(void) 46struct btrfs_path *btrfs_alloc_path(void)
42{ 47{
@@ -2997,75 +3002,85 @@ again:
2997 return ret; 3002 return ret;
2998} 3003}
2999 3004
3000/* 3005static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans,
3001 * This function splits a single item into two items, 3006 struct btrfs_root *root,
3002 * giving 'new_key' to the new item and splitting the 3007 struct btrfs_path *path, int ins_len)
3003 * old one at split_offset (from the start of the item).
3004 *
3005 * The path may be released by this operation. After
3006 * the split, the path is pointing to the old item. The
3007 * new item is going to be in the same node as the old one.
3008 *
3009 * Note, the item being split must be smaller enough to live alone on
3010 * a tree block with room for one extra struct btrfs_item
3011 *
3012 * This allows us to split the item in place, keeping a lock on the
3013 * leaf the entire time.
3014 */
3015int btrfs_split_item(struct btrfs_trans_handle *trans,
3016 struct btrfs_root *root,
3017 struct btrfs_path *path,
3018 struct btrfs_key *new_key,
3019 unsigned long split_offset)
3020{ 3008{
3021 u32 item_size; 3009 struct btrfs_key key;
3022 struct extent_buffer *leaf; 3010 struct extent_buffer *leaf;
3023 struct btrfs_key orig_key; 3011 struct btrfs_file_extent_item *fi;
3024 struct btrfs_item *item; 3012 u64 extent_len = 0;
3025 struct btrfs_item *new_item; 3013 u32 item_size;
3026 int ret = 0; 3014 int ret;
3027 int slot;
3028 u32 nritems;
3029 u32 orig_offset;
3030 struct btrfs_disk_key disk_key;
3031 char *buf;
3032 3015
3033 leaf = path->nodes[0]; 3016 leaf = path->nodes[0];
3034 btrfs_item_key_to_cpu(leaf, &orig_key, path->slots[0]); 3017 btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
3035 if (btrfs_leaf_free_space(root, leaf) >= sizeof(struct btrfs_item)) 3018
3036 goto split; 3019 BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY &&
3020 key.type != BTRFS_EXTENT_CSUM_KEY);
3021
3022 if (btrfs_leaf_free_space(root, leaf) >= ins_len)
3023 return 0;
3037 3024
3038 item_size = btrfs_item_size_nr(leaf, path->slots[0]); 3025 item_size = btrfs_item_size_nr(leaf, path->slots[0]);
3026 if (key.type == BTRFS_EXTENT_DATA_KEY) {
3027 fi = btrfs_item_ptr(leaf, path->slots[0],
3028 struct btrfs_file_extent_item);
3029 extent_len = btrfs_file_extent_num_bytes(leaf, fi);
3030 }
3039 btrfs_release_path(root, path); 3031 btrfs_release_path(root, path);
3040 3032
3041 path->search_for_split = 1;
3042 path->keep_locks = 1; 3033 path->keep_locks = 1;
3043 3034 path->search_for_split = 1;
3044 ret = btrfs_search_slot(trans, root, &orig_key, path, 0, 1); 3035 ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
3045 path->search_for_split = 0; 3036 path->search_for_split = 0;
3037 if (ret < 0)
3038 goto err;
3046 3039
3040 ret = -EAGAIN;
3041 leaf = path->nodes[0];
3047 /* if our item isn't there or got smaller, return now */ 3042 /* if our item isn't there or got smaller, return now */
3048 if (ret != 0 || item_size != btrfs_item_size_nr(path->nodes[0], 3043 if (ret > 0 || item_size != btrfs_item_size_nr(leaf, path->slots[0]))
3049 path->slots[0])) { 3044 goto err;
3050 path->keep_locks = 0; 3045
3051 return -EAGAIN; 3046 if (key.type == BTRFS_EXTENT_DATA_KEY) {
3047 fi = btrfs_item_ptr(leaf, path->slots[0],
3048 struct btrfs_file_extent_item);
3049 if (extent_len != btrfs_file_extent_num_bytes(leaf, fi))
3050 goto err;
3052 } 3051 }
3053 3052
3054 btrfs_set_path_blocking(path); 3053 btrfs_set_path_blocking(path);
3055 ret = split_leaf(trans, root, &orig_key, path, 3054 ret = split_leaf(trans, root, &key, path, ins_len, 1);
3056 sizeof(struct btrfs_item), 1);
3057 path->keep_locks = 0;
3058 BUG_ON(ret); 3055 BUG_ON(ret);
3059 3056
3057 path->keep_locks = 0;
3060 btrfs_unlock_up_safe(path, 1); 3058 btrfs_unlock_up_safe(path, 1);
3059 return 0;
3060err:
3061 path->keep_locks = 0;
3062 return ret;
3063}
3064
3065static noinline int split_item(struct btrfs_trans_handle *trans,
3066 struct btrfs_root *root,
3067 struct btrfs_path *path,
3068 struct btrfs_key *new_key,
3069 unsigned long split_offset)
3070{
3071 struct extent_buffer *leaf;
3072 struct btrfs_item *item;
3073 struct btrfs_item *new_item;
3074 int slot;
3075 char *buf;
3076 u32 nritems;
3077 u32 item_size;
3078 u32 orig_offset;
3079 struct btrfs_disk_key disk_key;
3080
3061 leaf = path->nodes[0]; 3081 leaf = path->nodes[0];
3062 BUG_ON(btrfs_leaf_free_space(root, leaf) < sizeof(struct btrfs_item)); 3082 BUG_ON(btrfs_leaf_free_space(root, leaf) < sizeof(struct btrfs_item));
3063 3083
3064split:
3065 /*
3066 * make sure any changes to the path from split_leaf leave it
3067 * in a blocking state
3068 */
3069 btrfs_set_path_blocking(path); 3084 btrfs_set_path_blocking(path);
3070 3085
3071 item = btrfs_item_nr(leaf, path->slots[0]); 3086 item = btrfs_item_nr(leaf, path->slots[0]);
@@ -3073,19 +3088,19 @@ split:
3073 item_size = btrfs_item_size(leaf, item); 3088 item_size = btrfs_item_size(leaf, item);
3074 3089
3075 buf = kmalloc(item_size, GFP_NOFS); 3090 buf = kmalloc(item_size, GFP_NOFS);
3091 if (!buf)
3092 return -ENOMEM;
3093
3076 read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf, 3094 read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf,
3077 path->slots[0]), item_size); 3095 path->slots[0]), item_size);
3078 slot = path->slots[0] + 1;
3079 leaf = path->nodes[0];
3080 3096
3097 slot = path->slots[0] + 1;
3081 nritems = btrfs_header_nritems(leaf); 3098 nritems = btrfs_header_nritems(leaf);
3082
3083 if (slot != nritems) { 3099 if (slot != nritems) {
3084 /* shift the items */ 3100 /* shift the items */
3085 memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + 1), 3101 memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + 1),
3086 btrfs_item_nr_offset(slot), 3102 btrfs_item_nr_offset(slot),
3087 (nritems - slot) * sizeof(struct btrfs_item)); 3103 (nritems - slot) * sizeof(struct btrfs_item));
3088
3089 } 3104 }
3090 3105
3091 btrfs_cpu_key_to_disk(&disk_key, new_key); 3106 btrfs_cpu_key_to_disk(&disk_key, new_key);
@@ -3113,16 +3128,81 @@ split:
3113 item_size - split_offset); 3128 item_size - split_offset);
3114 btrfs_mark_buffer_dirty(leaf); 3129 btrfs_mark_buffer_dirty(leaf);
3115 3130
3116 ret = 0; 3131 BUG_ON(btrfs_leaf_free_space(root, leaf) < 0);
3117 if (btrfs_leaf_free_space(root, leaf) < 0) {
3118 btrfs_print_leaf(root, leaf);
3119 BUG();
3120 }
3121 kfree(buf); 3132 kfree(buf);
3133 return 0;
3134}
3135
3136/*
3137 * This function splits a single item into two items,
3138 * giving 'new_key' to the new item and splitting the
3139 * old one at split_offset (from the start of the item).
3140 *
3141 * The path may be released by this operation. After
3142 * the split, the path is pointing to the old item. The
3143 * new item is going to be in the same node as the old one.
3144 *
3145 * Note, the item being split must be smaller enough to live alone on
3146 * a tree block with room for one extra struct btrfs_item
3147 *
3148 * This allows us to split the item in place, keeping a lock on the
3149 * leaf the entire time.
3150 */
3151int btrfs_split_item(struct btrfs_trans_handle *trans,
3152 struct btrfs_root *root,
3153 struct btrfs_path *path,
3154 struct btrfs_key *new_key,
3155 unsigned long split_offset)
3156{
3157 int ret;
3158 ret = setup_leaf_for_split(trans, root, path,
3159 sizeof(struct btrfs_item));
3160 if (ret)
3161 return ret;
3162
3163 ret = split_item(trans, root, path, new_key, split_offset);
3122 return ret; 3164 return ret;
3123} 3165}
3124 3166
3125/* 3167/*
3168 * This function duplicate a item, giving 'new_key' to the new item.
3169 * It guarantees both items live in the same tree leaf and the new item
3170 * is contiguous with the original item.
3171 *
3172 * This allows us to split file extent in place, keeping a lock on the
3173 * leaf the entire time.
3174 */
3175int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
3176 struct btrfs_root *root,
3177 struct btrfs_path *path,
3178 struct btrfs_key *new_key)
3179{
3180 struct extent_buffer *leaf;
3181 int ret;
3182 u32 item_size;
3183
3184 leaf = path->nodes[0];
3185 item_size = btrfs_item_size_nr(leaf, path->slots[0]);
3186 ret = setup_leaf_for_split(trans, root, path,
3187 item_size + sizeof(struct btrfs_item));
3188 if (ret)
3189 return ret;
3190
3191 path->slots[0]++;
3192 ret = setup_items_for_insert(trans, root, path, new_key, &item_size,
3193 item_size, item_size +
3194 sizeof(struct btrfs_item), 1);
3195 BUG_ON(ret);
3196
3197 leaf = path->nodes[0];
3198 memcpy_extent_buffer(leaf,
3199 btrfs_item_ptr_offset(leaf, path->slots[0]),
3200 btrfs_item_ptr_offset(leaf, path->slots[0] - 1),
3201 item_size);
3202 return 0;
3203}
3204
3205/*
3126 * make the item pointed to by the path smaller. new_size indicates 3206 * make the item pointed to by the path smaller. new_size indicates
3127 * how small to make it, and from_end tells us if we just chop bytes 3207 * how small to make it, and from_end tells us if we just chop bytes
3128 * off the end of the item or if we shift the item to chop bytes off 3208 * off the end of the item or if we shift the item to chop bytes off
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 444b3e9b92a4..e3b58001162d 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2089,6 +2089,10 @@ int btrfs_split_item(struct btrfs_trans_handle *trans,
2089 struct btrfs_path *path, 2089 struct btrfs_path *path,
2090 struct btrfs_key *new_key, 2090 struct btrfs_key *new_key,
2091 unsigned long split_offset); 2091 unsigned long split_offset);
2092int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
2093 struct btrfs_root *root,
2094 struct btrfs_path *path,
2095 struct btrfs_key *new_key);
2092int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root 2096int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
2093 *root, struct btrfs_key *key, struct btrfs_path *p, int 2097 *root, struct btrfs_key *key, struct btrfs_path *p, int
2094 ins_len, int cow); 2098 ins_len, int cow);