aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/file.c
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2007-10-15 16:18:25 -0400
committerChris Mason <chris.mason@oracle.com>2008-09-25 11:03:56 -0400
commit3326d1b07c0cb6a2ff5b835b7a2cffa54124d074 (patch)
tree16db275e3e8edb35e46fdd9f9a0f3a1c377ada5f /fs/btrfs/file.c
parent4dc119046d0d8501afa4346472917fb05586ad9c (diff)
Btrfs: Allow tails larger than one page
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/file.c')
-rw-r--r--fs/btrfs/file.c152
1 files changed, 116 insertions, 36 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 844d8807e44a..1af2b6534dad 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -82,8 +82,9 @@ static void btrfs_drop_pages(struct page **pages, size_t num_pages)
82 82
83static int insert_inline_extent(struct btrfs_trans_handle *trans, 83static int insert_inline_extent(struct btrfs_trans_handle *trans,
84 struct btrfs_root *root, struct inode *inode, 84 struct btrfs_root *root, struct inode *inode,
85 u64 offset, ssize_t size, 85 u64 offset, size_t size,
86 struct page *page, size_t page_offset) 86 struct page **pages, size_t page_offset,
87 int num_pages)
87{ 88{
88 struct btrfs_key key; 89 struct btrfs_key key;
89 struct btrfs_path *path; 90 struct btrfs_path *path;
@@ -91,9 +92,12 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
91 char *kaddr; 92 char *kaddr;
92 unsigned long ptr; 93 unsigned long ptr;
93 struct btrfs_file_extent_item *ei; 94 struct btrfs_file_extent_item *ei;
95 struct page *page;
94 u32 datasize; 96 u32 datasize;
95 int err = 0; 97 int err = 0;
96 int ret; 98 int ret;
99 int i;
100 ssize_t cur_size;
97 101
98 path = btrfs_alloc_path(); 102 path = btrfs_alloc_path();
99 if (!path) 103 if (!path)
@@ -104,25 +108,97 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
104 key.objectid = inode->i_ino; 108 key.objectid = inode->i_ino;
105 key.offset = offset; 109 key.offset = offset;
106 btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); 110 btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
107 BUG_ON(size >= PAGE_CACHE_SIZE); 111 datasize = btrfs_file_extent_calc_inline_size(offset + size);
108 datasize = btrfs_file_extent_calc_inline_size(size);
109 112
110 ret = btrfs_insert_empty_item(trans, root, path, &key, 113 ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
111 datasize); 114 if (ret < 0) {
112 if (ret) {
113 err = ret; 115 err = ret;
114 goto fail; 116 goto fail;
115 } 117 }
116 leaf = path->nodes[0]; 118 if (ret == 1) {
117 ei = btrfs_item_ptr(leaf, path->slots[0], 119 path->slots[0]--;
118 struct btrfs_file_extent_item); 120 leaf = path->nodes[0];
119 btrfs_set_file_extent_generation(leaf, ei, trans->transid); 121 ei = btrfs_item_ptr(leaf, path->slots[0],
120 btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE); 122 struct btrfs_file_extent_item);
121 ptr = btrfs_file_extent_inline_start(ei); 123
122 124 if (btrfs_file_extent_type(leaf, ei) !=
123 kaddr = kmap_atomic(page, KM_USER1); 125 BTRFS_FILE_EXTENT_INLINE) {
124 write_extent_buffer(leaf, kaddr + page_offset, ptr, size); 126 goto insert;
125 kunmap_atomic(kaddr, KM_USER1); 127 }
128 btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
129 ret = 0;
130 }
131 if (ret == 0) {
132 u32 found_size;
133 u64 found_start;
134
135 leaf = path->nodes[0];
136 ei = btrfs_item_ptr(leaf, path->slots[0],
137 struct btrfs_file_extent_item);
138
139 if (btrfs_file_extent_type(leaf, ei) !=
140 BTRFS_FILE_EXTENT_INLINE) {
141 err = ret;
142 btrfs_print_leaf(root, leaf);
143 printk("found wasn't inline offset %Lu inode %lu\n",
144 offset, inode->i_ino);
145 goto fail;
146 }
147 found_start = key.offset;
148 found_size = btrfs_file_extent_inline_len(leaf,
149 btrfs_item_nr(leaf, path->slots[0]));
150
151 if (found_size < offset + size) {
152 btrfs_release_path(root, path);
153 ret = btrfs_search_slot(trans, root, &key, path,
154 offset + size - found_size -
155 found_start, 1);
156 BUG_ON(ret != 0);
157 ret = btrfs_extend_item(trans, root, path,
158 offset + size - found_size -
159 found_start);
160 if (ret) {
161 err = ret;
162 goto fail;
163 }
164 leaf = path->nodes[0];
165 ei = btrfs_item_ptr(leaf, path->slots[0],
166 struct btrfs_file_extent_item);
167 }
168 } else {
169insert:
170 btrfs_release_path(root, path);
171 ret = btrfs_insert_empty_item(trans, root, path, &key,
172 datasize);
173 if (ret) {
174 err = ret;
175 printk("got bad ret %d\n", ret);
176 goto fail;
177 }
178 leaf = path->nodes[0];
179 ei = btrfs_item_ptr(leaf, path->slots[0],
180 struct btrfs_file_extent_item);
181 btrfs_set_file_extent_generation(leaf, ei, trans->transid);
182 btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE);
183 }
184 ptr = btrfs_file_extent_inline_start(ei) + offset;
185
186 cur_size = size;
187 i = 0;
188 while (size > 0) {
189 page = pages[i];
190 kaddr = kmap_atomic(page, KM_USER0);
191 cur_size = min(PAGE_CACHE_SIZE - page_offset, size);
192 write_extent_buffer(leaf, kaddr + page_offset, ptr, cur_size);
193 kunmap_atomic(kaddr, KM_USER0);
194 page_offset = 0;
195 ptr += cur_size;
196 size -= cur_size;
197 if (i >= num_pages) {
198 printk("i %d num_pages %d\n", i, num_pages);
199 }
200 i++;
201 }
126 btrfs_mark_buffer_dirty(leaf); 202 btrfs_mark_buffer_dirty(leaf);
127fail: 203fail:
128 btrfs_free_path(path); 204 btrfs_free_path(path);
@@ -193,6 +269,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
193 err = btrfs_drop_extents(trans, root, inode, 269 err = btrfs_drop_extents(trans, root, inode,
194 last_pos_in_file, 270 last_pos_in_file,
195 last_pos_in_file + hole_size, 271 last_pos_in_file + hole_size,
272 last_pos_in_file,
196 &hint_byte); 273 &hint_byte);
197 if (err) 274 if (err)
198 goto failed; 275 goto failed;
@@ -210,11 +287,12 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
210 * either allocate an extent for the new bytes or setup the key 287 * either allocate an extent for the new bytes or setup the key
211 * to show we are doing inline data in the extent 288 * to show we are doing inline data in the extent
212 */ 289 */
213 inline_size = end_pos - start_pos; 290 inline_size = end_pos;
214 if (isize >= PAGE_CACHE_SIZE || pos + write_bytes < inode->i_size || 291 if (isize >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
215 inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root) || 292 inline_size > 16384 ||
216 inline_size >= PAGE_CACHE_SIZE) { 293 inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
217 u64 last_end; 294 u64 last_end;
295
218 for (i = 0; i < num_pages; i++) { 296 for (i = 0; i < num_pages; i++) {
219 struct page *p = pages[i]; 297 struct page *p = pages[i];
220 SetPageUptodate(p); 298 SetPageUptodate(p);
@@ -225,22 +303,18 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
225 set_extent_delalloc(em_tree, start_pos, end_of_last_block, 303 set_extent_delalloc(em_tree, start_pos, end_of_last_block,
226 GFP_NOFS); 304 GFP_NOFS);
227 } else { 305 } else {
228 struct page *p = pages[0]; 306 u64 aligned_end;
229 /* step one, delete the existing extents in this range */ 307 /* step one, delete the existing extents in this range */
308 aligned_end = (pos + write_bytes + root->sectorsize - 1) &
309 ~((u64)root->sectorsize - 1);
230 err = btrfs_drop_extents(trans, root, inode, start_pos, 310 err = btrfs_drop_extents(trans, root, inode, start_pos,
231 (pos + write_bytes + root->sectorsize -1) & 311 aligned_end, end_pos, &hint_byte);
232 ~((u64)root->sectorsize - 1), &hint_byte);
233 if (err) 312 if (err)
234 goto failed; 313 goto failed;
235
236 err = insert_inline_extent(trans, root, inode, start_pos, 314 err = insert_inline_extent(trans, root, inode, start_pos,
237 end_pos - start_pos, p, 0); 315 end_pos - start_pos, pages, 0,
316 num_pages);
238 BUG_ON(err); 317 BUG_ON(err);
239 em->start = start_pos;
240 em->end = end_pos - 1;
241 em->block_start = EXTENT_MAP_INLINE;
242 em->block_end = EXTENT_MAP_INLINE;
243 add_extent_mapping(em_tree, em);
244 } 318 }
245 if (end_pos > isize) { 319 if (end_pos > isize) {
246 i_size_write(inode, end_pos); 320 i_size_write(inode, end_pos);
@@ -285,7 +359,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
285 */ 359 */
286int btrfs_drop_extents(struct btrfs_trans_handle *trans, 360int btrfs_drop_extents(struct btrfs_trans_handle *trans,
287 struct btrfs_root *root, struct inode *inode, 361 struct btrfs_root *root, struct inode *inode,
288 u64 start, u64 end, u64 *hint_byte) 362 u64 start, u64 end, u64 inline_end, u64 *hint_byte)
289{ 363{
290 int ret; 364 int ret;
291 struct btrfs_key key; 365 struct btrfs_key key;
@@ -401,8 +475,8 @@ next_slot:
401 BUG_ON(ret); 475 BUG_ON(ret);
402 } 476 }
403 } 477 }
404 WARN_ON(found_inline); 478 if (!found_inline)
405 bookend = 1; 479 bookend = 1;
406 } 480 }
407 /* truncate existing extent */ 481 /* truncate existing extent */
408 if (start > key.offset) { 482 if (start > key.offset) {
@@ -425,8 +499,14 @@ next_slot:
425 btrfs_set_file_extent_num_bytes(leaf, extent, 499 btrfs_set_file_extent_num_bytes(leaf, extent,
426 new_num); 500 new_num);
427 btrfs_mark_buffer_dirty(leaf); 501 btrfs_mark_buffer_dirty(leaf);
428 } else { 502 } else if (end > extent_end &&
429 WARN_ON(1); 503 key.offset < inline_end &&
504 inline_end < extent_end) {
505 u32 new_size;
506 new_size = btrfs_file_extent_calc_inline_size(
507 inline_end - key.offset);
508 btrfs_truncate_item(trans, root, path,
509 new_size);
430 } 510 }
431 } 511 }
432 /* delete the entire extent */ 512 /* delete the entire extent */