diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index b20021c501d7c3d9d9e83e090304752fcbe42ded..7d608dbae970d4e24521c53c4281798a527c64e2 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4332,12 +4332,31 @@ static struct extent_map *get_extent_skip_holes(struct inode *inode, return NULL; while (1) { + struct extent_state *cached_state = NULL; + u64 lockstart; + u64 lockend; + len = last - offset; if (len == 0) break; len = ALIGN(len, sectorsize); + lockstart = round_down(offset, sectorsize); + lockend = round_up(offset + len, sectorsize) - 1; + + /* + * We are only locking for the delalloc range because that's the + * only thing that can change here. With fiemap we have a lock + * on the inode, so no buffered or direct writes can happen. + * + * However mmaps and normal page writeback will cause this to + * change arbitrarily. We have to lock the extent lock here to + * make sure that nobody messes with the tree while we're doing + * btrfs_find_delalloc_in_range. + */ + lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, &cached_state); em = btrfs_get_extent_fiemap(BTRFS_I(inode), NULL, 0, offset, len, 0); + unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend, &cached_state); if (IS_ERR_OR_NULL(em)) return em; @@ -4481,7 +4500,6 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 isize = i_size_read(inode); struct btrfs_key found_key; struct extent_map *em = NULL; - struct extent_state *cached_state = NULL; struct btrfs_path *path; struct btrfs_root *root = BTRFS_I(inode)->root; struct fiemap_cache cache = { 0 }; @@ -4547,9 +4565,6 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, last_for_get_extent = isize; } - lock_extent_bits(&BTRFS_I(inode)->io_tree, start, start + len - 1, - &cached_state); - em = get_extent_skip_holes(inode, start, last_for_get_extent); if (!em) goto out; @@ -4662,8 +4677,6 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, free_extent_map(em); out: btrfs_free_path(path); - unlock_extent_cached(&BTRFS_I(inode)->io_tree, start, start + len - 1, - &cached_state); return ret; }