Commit 5a5f79b5 authored by Chris Mason's avatar Chris Mason

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: default avatarChris Mason <chris.mason@oracle.com>
parent 933b585f
...@@ -5722,6 +5722,32 @@ static void btrfs_submit_direct(int rw, struct bio *bio, struct inode *inode, ...@@ -5722,6 +5722,32 @@ static void btrfs_submit_direct(int rw, struct bio *bio, struct inode *inode,
bio_endio(bio, ret); bio_endio(bio, ret);
} }
static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset,
unsigned long nr_segs)
{
int seg;
size_t size;
unsigned long addr;
unsigned blocksize_mask = root->sectorsize - 1;
ssize_t retval = -EINVAL;
loff_t end = offset;
if (offset & blocksize_mask)
goto out;
/* Check the memory alignment. Blocks cannot straddle pages */
for (seg = 0; seg < nr_segs; seg++) {
addr = (unsigned long)iov[seg].iov_base;
size = iov[seg].iov_len;
end += size;
if ((addr & blocksize_mask) || (size & blocksize_mask))
goto out;
}
retval = 0;
out:
return retval;
}
static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset, const struct iovec *iov, loff_t offset,
unsigned long nr_segs) unsigned long nr_segs)
...@@ -5736,6 +5762,11 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, ...@@ -5736,6 +5762,11 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
int write_bits = 0; int write_bits = 0;
size_t count = iov_length(iov, nr_segs); size_t count = iov_length(iov, nr_segs);
if (check_direct_IO(BTRFS_I(inode)->root, rw, iocb, iov,
offset, nr_segs)) {
return 0;
}
lockstart = offset; lockstart = offset;
lockend = offset + count - 1; lockend = offset + count - 1;
...@@ -5784,9 +5815,10 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, ...@@ -5784,9 +5815,10 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
free_extent_state(cached_state); free_extent_state(cached_state);
cached_state = NULL; cached_state = NULL;
ret = __blockdev_direct_IO(rw, iocb, inode, NULL, iov, offset, nr_segs, ret = __blockdev_direct_IO(rw, iocb, inode,
btrfs_get_blocks_direct, NULL, BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev,
btrfs_submit_direct, 0); iov, offset, nr_segs, btrfs_get_blocks_direct, NULL,
btrfs_submit_direct, 0);
if (ret < 0 && ret != -EIOCBQUEUED) { if (ret < 0 && ret != -EIOCBQUEUED) {
clear_extent_bit(&BTRFS_I(inode)->io_tree, offset, clear_extent_bit(&BTRFS_I(inode)->io_tree, offset,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment