aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/inode.c
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2011-02-23 16:23:20 -0500
committerChris Mason <chris.mason@oracle.com>2011-02-23 16:23:20 -0500
commitec29ed5b407d618a8128f5942aade9e1758aa14b (patch)
tree19b3c13f15504cf9de116f0bd55045ebcf0d0cfc /fs/btrfs/inode.c
parentfb01aa85b8b29c1a4e1f4a28ea54175de6bf7559 (diff)
Btrfs: fix fiemap bugs with delalloc
The Btrfs fiemap code wasn't properly returning delalloc extents, so applications that trust fiemap to decide if there are holes in the file see holes instead of delalloc. This reworks the btrfs fiemap code, adding a get_extent helper that searches for delalloc ranges and also adding a helper for extent_fiemap that skips past holes in the file. Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r--fs/btrfs/inode.c126
1 files changed, 124 insertions, 2 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8d392ed73d57..44b926646e33 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1913,7 +1913,7 @@ static int btrfs_clean_io_failures(struct inode *inode, u64 start)
1913 1913
1914 private = 0; 1914 private = 0;
1915 if (count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private, 1915 if (count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private,
1916 (u64)-1, 1, EXTENT_DIRTY)) { 1916 (u64)-1, 1, EXTENT_DIRTY, 0)) {
1917 ret = get_state_private(&BTRFS_I(inode)->io_failure_tree, 1917 ret = get_state_private(&BTRFS_I(inode)->io_failure_tree,
1918 start, &private_failure); 1918 start, &private_failure);
1919 if (ret == 0) { 1919 if (ret == 0) {
@@ -5282,6 +5282,128 @@ out:
5282 return em; 5282 return em;
5283} 5283}
5284 5284
5285struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page,
5286 size_t pg_offset, u64 start, u64 len,
5287 int create)
5288{
5289 struct extent_map *em;
5290 struct extent_map *hole_em = NULL;
5291 u64 range_start = start;
5292 u64 end;
5293 u64 found;
5294 u64 found_end;
5295 int err = 0;
5296
5297 em = btrfs_get_extent(inode, page, pg_offset, start, len, create);
5298 if (IS_ERR(em))
5299 return em;
5300 if (em) {
5301 /*
5302 * if our em maps to a hole, there might
5303 * actually be delalloc bytes behind it
5304 */
5305 if (em->block_start != EXTENT_MAP_HOLE)
5306 return em;
5307 else
5308 hole_em = em;
5309 }
5310
5311 /* check to see if we've wrapped (len == -1 or similar) */
5312 end = start + len;
5313 if (end < start)
5314 end = (u64)-1;
5315 else
5316 end -= 1;
5317
5318 em = NULL;
5319
5320 /* ok, we didn't find anything, lets look for delalloc */
5321 found = count_range_bits(&BTRFS_I(inode)->io_tree, &range_start,
5322 end, len, EXTENT_DELALLOC, 1);
5323 found_end = range_start + found;
5324 if (found_end < range_start)
5325 found_end = (u64)-1;
5326
5327 /*
5328 * we didn't find anything useful, return
5329 * the original results from get_extent()
5330 */
5331 if (range_start > end || found_end <= start) {
5332 em = hole_em;
5333 hole_em = NULL;
5334 goto out;
5335 }
5336
5337 /* adjust the range_start to make sure it doesn't
5338 * go backwards from the start they passed in
5339 */
5340 range_start = max(start,range_start);
5341 found = found_end - range_start;
5342
5343 if (found > 0) {
5344 u64 hole_start = start;
5345 u64 hole_len = len;
5346
5347 em = alloc_extent_map(GFP_NOFS);
5348 if (!em) {
5349 err = -ENOMEM;
5350 goto out;
5351 }
5352 /*
5353 * when btrfs_get_extent can't find anything it
5354 * returns one huge hole
5355 *
5356 * make sure what it found really fits our range, and
5357 * adjust to make sure it is based on the start from
5358 * the caller
5359 */
5360 if (hole_em) {
5361 u64 calc_end = extent_map_end(hole_em);
5362
5363 if (calc_end <= start || (hole_em->start > end)) {
5364 free_extent_map(hole_em);
5365 hole_em = NULL;
5366 } else {
5367 hole_start = max(hole_em->start, start);
5368 hole_len = calc_end - hole_start;
5369 }
5370 }
5371 em->bdev = NULL;
5372 if (hole_em && range_start > hole_start) {
5373 /* our hole starts before our delalloc, so we
5374 * have to return just the parts of the hole
5375 * that go until the delalloc starts
5376 */
5377 em->len = min(hole_len,
5378 range_start - hole_start);
5379 em->start = hole_start;
5380 em->orig_start = hole_start;
5381 /*
5382 * don't adjust block start at all,
5383 * it is fixed at EXTENT_MAP_HOLE
5384 */
5385 em->block_start = hole_em->block_start;
5386 em->block_len = hole_len;
5387 } else {
5388 em->start = range_start;
5389 em->len = found;
5390 em->orig_start = range_start;
5391 em->block_start = EXTENT_MAP_DELALLOC;
5392 em->block_len = found;
5393 }
5394 } else if (hole_em) {
5395 return hole_em;
5396 }
5397out:
5398
5399 free_extent_map(hole_em);
5400 if (err) {
5401 free_extent_map(em);
5402 return ERR_PTR(err);
5403 }
5404 return em;
5405}
5406
5285static struct extent_map *btrfs_new_extent_direct(struct inode *inode, 5407static struct extent_map *btrfs_new_extent_direct(struct inode *inode,
5286 u64 start, u64 len) 5408 u64 start, u64 len)
5287{ 5409{
@@ -6104,7 +6226,7 @@ out:
6104static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, 6226static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
6105 __u64 start, __u64 len) 6227 __u64 start, __u64 len)
6106{ 6228{
6107 return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent); 6229 return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent_fiemap);
6108} 6230}
6109 6231
6110int btrfs_readpage(struct file *file, struct page *page) 6232int btrfs_readpage(struct file *file, struct page *page)