diff options
author | Chris Mason <chris.mason@oracle.com> | 2010-05-26 21:33:37 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2010-05-26 21:35:35 -0400 |
commit | 5a5f79b57069c5691f5b6fd8381fdf487f548ae5 (patch) | |
tree | d101b4025a2b23c5e258b04714907f4266c3b2df /fs/btrfs | |
parent | 933b585f70d524f1b6f0f6867bedb11d3dd82d3b (diff) |
Btrfs: allow unaligned DIO
In order to support DIO that isn't aligned to the filesystem blocksize,
we fall back to buffered for any unaligned DIOs.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/inode.c | 38 |
1 files changed, 35 insertions, 3 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5ab120d544bc..6866c36c26fb 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -5722,6 +5722,32 @@ free_ordered: | |||
5722 | bio_endio(bio, ret); | 5722 | bio_endio(bio, ret); |
5723 | } | 5723 | } |
5724 | 5724 | ||
5725 | static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *iocb, | ||
5726 | const struct iovec *iov, loff_t offset, | ||
5727 | unsigned long nr_segs) | ||
5728 | { | ||
5729 | int seg; | ||
5730 | size_t size; | ||
5731 | unsigned long addr; | ||
5732 | unsigned blocksize_mask = root->sectorsize - 1; | ||
5733 | ssize_t retval = -EINVAL; | ||
5734 | loff_t end = offset; | ||
5735 | |||
5736 | if (offset & blocksize_mask) | ||
5737 | goto out; | ||
5738 | |||
5739 | /* Check the memory alignment. Blocks cannot straddle pages */ | ||
5740 | for (seg = 0; seg < nr_segs; seg++) { | ||
5741 | addr = (unsigned long)iov[seg].iov_base; | ||
5742 | size = iov[seg].iov_len; | ||
5743 | end += size; | ||
5744 | if ((addr & blocksize_mask) || (size & blocksize_mask)) | ||
5745 | goto out; | ||
5746 | } | ||
5747 | retval = 0; | ||
5748 | out: | ||
5749 | return retval; | ||
5750 | } | ||
5725 | static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, | 5751 | static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, |
5726 | const struct iovec *iov, loff_t offset, | 5752 | const struct iovec *iov, loff_t offset, |
5727 | unsigned long nr_segs) | 5753 | unsigned long nr_segs) |
@@ -5736,6 +5762,11 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, | |||
5736 | int write_bits = 0; | 5762 | int write_bits = 0; |
5737 | size_t count = iov_length(iov, nr_segs); | 5763 | size_t count = iov_length(iov, nr_segs); |
5738 | 5764 | ||
5765 | if (check_direct_IO(BTRFS_I(inode)->root, rw, iocb, iov, | ||
5766 | offset, nr_segs)) { | ||
5767 | return 0; | ||
5768 | } | ||
5769 | |||
5739 | lockstart = offset; | 5770 | lockstart = offset; |
5740 | lockend = offset + count - 1; | 5771 | lockend = offset + count - 1; |
5741 | 5772 | ||
@@ -5784,9 +5815,10 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, | |||
5784 | free_extent_state(cached_state); | 5815 | free_extent_state(cached_state); |
5785 | cached_state = NULL; | 5816 | cached_state = NULL; |
5786 | 5817 | ||
5787 | ret = __blockdev_direct_IO(rw, iocb, inode, NULL, iov, offset, nr_segs, | 5818 | ret = __blockdev_direct_IO(rw, iocb, inode, |
5788 | btrfs_get_blocks_direct, NULL, | 5819 | BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev, |
5789 | btrfs_submit_direct, 0); | 5820 | iov, offset, nr_segs, btrfs_get_blocks_direct, NULL, |
5821 | btrfs_submit_direct, 0); | ||
5790 | 5822 | ||
5791 | if (ret < 0 && ret != -EIOCBQUEUED) { | 5823 | if (ret < 0 && ret != -EIOCBQUEUED) { |
5792 | clear_extent_bit(&BTRFS_I(inode)->io_tree, offset, | 5824 | clear_extent_bit(&BTRFS_I(inode)->io_tree, offset, |