diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-04-10 10:23:21 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:01 -0400 |
commit | 16432985920f3c45af82da214e2498f3e2f9066b (patch) | |
tree | ce05f57eefb1a6f5952c53205ec8a07e93d03b3c | |
parent | 85d824c4a407ba8dbd897f4e05bbe21bf24cc071 (diff) |
Btrfs: Add O_DIRECT read and write (writes == buffered + cache flush)
This adds basic O_DIRECT read and write support. In the write case, we
just do a normal buffered write followed by a cache flush. O_DIRECT +
O_SYNC are required to trigger metadata syncs.
In the read case, there is a basic btrfs_get_block call for use by
the generic O_DIRECT code. This does honor multi-volume mapping rules
but it skips all checksumming.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | fs/btrfs/file.c | 11 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 66 |
2 files changed, 75 insertions, 2 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 5fa450452f9b..5b9386452218 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -796,8 +796,6 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, | |||
796 | PAGE_CACHE_SIZE / (sizeof(struct page *))); | 796 | PAGE_CACHE_SIZE / (sizeof(struct page *))); |
797 | pinned[0] = NULL; | 797 | pinned[0] = NULL; |
798 | pinned[1] = NULL; | 798 | pinned[1] = NULL; |
799 | if (file->f_flags & O_DIRECT) | ||
800 | return -EINVAL; | ||
801 | 799 | ||
802 | pos = *ppos; | 800 | pos = *ppos; |
803 | start_pos = pos; | 801 | start_pos = pos; |
@@ -909,6 +907,15 @@ out_nolock: | |||
909 | start_pos, num_written); | 907 | start_pos, num_written); |
910 | if (err < 0) | 908 | if (err < 0) |
911 | num_written = err; | 909 | num_written = err; |
910 | } else if (num_written > 0 && (file->f_flags & O_DIRECT)) { | ||
911 | do_sync_mapping_range(inode->i_mapping, start_pos, | ||
912 | start_pos + num_written - 1, | ||
913 | SYNC_FILE_RANGE_WRITE | | ||
914 | SYNC_FILE_RANGE_WAIT_AFTER); | ||
915 | |||
916 | invalidate_mapping_pages(inode->i_mapping, | ||
917 | start_pos >> PAGE_CACHE_SHIFT, | ||
918 | (start_pos + num_written - 1) >> PAGE_CACHE_SHIFT); | ||
912 | } | 919 | } |
913 | current->backing_dev_info = NULL; | 920 | current->backing_dev_info = NULL; |
914 | return num_written ? num_written : err; | 921 | return num_written ? num_written : err; |
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, |