diff options
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r-- | fs/btrfs/inode.c | 126 |
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 | ||
5285 | struct 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 | } | ||
5397 | out: | ||
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 | |||
5285 | static struct extent_map *btrfs_new_extent_direct(struct inode *inode, | 5407 | static 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: | |||
6104 | static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, | 6226 | static 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 | ||
6110 | int btrfs_readpage(struct file *file, struct page *page) | 6232 | int btrfs_readpage(struct file *file, struct page *page) |