diff options
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r-- | fs/btrfs/inode.c | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 48f1d1b96450..32e778fb014e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -2265,6 +2265,70 @@ out: | |||
2265 | return em; | 2265 | return em; |
2266 | } | 2266 | } |
2267 | 2267 | ||
2268 | static int btrfs_get_block(struct inode *inode, sector_t iblock, | ||
2269 | struct buffer_head *bh_result, int create) | ||
2270 | { | ||
2271 | struct extent_map *em; | ||
2272 | u64 start = (u64)iblock << inode->i_blkbits; | ||
2273 | struct btrfs_multi_bio *multi = NULL; | ||
2274 | struct btrfs_root *root = BTRFS_I(inode)->root; | ||
2275 | u64 len; | ||
2276 | u64 logical; | ||
2277 | u64 map_length; | ||
2278 | int ret = 0; | ||
2279 | |||
2280 | em = btrfs_get_extent(inode, NULL, 0, start, bh_result->b_size, 0); | ||
2281 | |||
2282 | if (!em || IS_ERR(em)) | ||
2283 | goto out; | ||
2284 | |||
2285 | if (em->start > start || em->start + em->len <= start) | ||
2286 | goto out; | ||
2287 | |||
2288 | if (em->block_start == EXTENT_MAP_INLINE) { | ||
2289 | ret = -EINVAL; | ||
2290 | goto out; | ||
2291 | } | ||
2292 | |||
2293 | if (em->block_start == EXTENT_MAP_HOLE || | ||
2294 | em->block_start == EXTENT_MAP_DELALLOC) { | ||
2295 | goto out; | ||
2296 | } | ||
2297 | |||
2298 | len = em->start + em->len - start; | ||
2299 | len = min_t(u64, len, INT_LIMIT(typeof(bh_result->b_size))); | ||
2300 | |||
2301 | logical = start - em->start; | ||
2302 | logical = em->block_start + logical; | ||
2303 | |||
2304 | map_length = len; | ||
2305 | ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, | ||
2306 | logical, &map_length, &multi, 0); | ||
2307 | BUG_ON(ret); | ||
2308 | bh_result->b_blocknr = multi->stripes[0].physical >> inode->i_blkbits; | ||
2309 | bh_result->b_size = min(map_length, len); | ||
2310 | bh_result->b_bdev = multi->stripes[0].dev->bdev; | ||
2311 | set_buffer_mapped(bh_result); | ||
2312 | kfree(multi); | ||
2313 | out: | ||
2314 | free_extent_map(em); | ||
2315 | return ret; | ||
2316 | } | ||
2317 | |||
2318 | static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, | ||
2319 | const struct iovec *iov, loff_t offset, | ||
2320 | unsigned long nr_segs) | ||
2321 | { | ||
2322 | struct file *file = iocb->ki_filp; | ||
2323 | struct inode *inode = file->f_mapping->host; | ||
2324 | |||
2325 | if (rw == WRITE) | ||
2326 | return -EINVAL; | ||
2327 | |||
2328 | return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, | ||
2329 | offset, nr_segs, btrfs_get_block, NULL); | ||
2330 | } | ||
2331 | |||
2268 | static sector_t btrfs_bmap(struct address_space *mapping, sector_t iblock) | 2332 | static sector_t btrfs_bmap(struct address_space *mapping, sector_t iblock) |
2269 | { | 2333 | { |
2270 | return extent_bmap(mapping, iblock, btrfs_get_extent); | 2334 | return extent_bmap(mapping, iblock, btrfs_get_extent); |
@@ -3136,6 +3200,7 @@ out_fail: | |||
3136 | btrfs_throttle(root); | 3200 | btrfs_throttle(root); |
3137 | return err; | 3201 | return err; |
3138 | } | 3202 | } |
3203 | |||
3139 | static int btrfs_permission(struct inode *inode, int mask, | 3204 | static int btrfs_permission(struct inode *inode, int mask, |
3140 | struct nameidata *nd) | 3205 | struct nameidata *nd) |
3141 | { | 3206 | { |
@@ -3193,6 +3258,7 @@ static struct address_space_operations btrfs_aops = { | |||
3193 | .readpages = btrfs_readpages, | 3258 | .readpages = btrfs_readpages, |
3194 | .sync_page = block_sync_page, | 3259 | .sync_page = block_sync_page, |
3195 | .bmap = btrfs_bmap, | 3260 | .bmap = btrfs_bmap, |
3261 | .direct_IO = btrfs_direct_IO, | ||
3196 | .invalidatepage = btrfs_invalidatepage, | 3262 | .invalidatepage = btrfs_invalidatepage, |
3197 | .releasepage = btrfs_releasepage, | 3263 | .releasepage = btrfs_releasepage, |
3198 | .set_page_dirty = __set_page_dirty_nobuffers, | 3264 | .set_page_dirty = __set_page_dirty_nobuffers, |