diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-10-15 16:18:25 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:03:56 -0400 |
commit | 3326d1b07c0cb6a2ff5b835b7a2cffa54124d074 (patch) | |
tree | 16db275e3e8edb35e46fdd9f9a0f3a1c377ada5f /fs/btrfs/file.c | |
parent | 4dc119046d0d8501afa4346472917fb05586ad9c (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.c | 152 |
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 | ||
83 | static int insert_inline_extent(struct btrfs_trans_handle *trans, | 83 | static 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 { | ||
169 | insert: | ||
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); |
127 | fail: | 203 | fail: |
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 | */ |
286 | int btrfs_drop_extents(struct btrfs_trans_handle *trans, | 360 | int 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 */ |