summary refs log tree commit diff
path: root/fs/btrfs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2010-05-26 21:33:37 -0400
committerChris Mason <chris.mason@oracle.com>2010-05-26 21:35:35 -0400
commit5a5f79b57069c5691f5b6fd8381fdf487f548ae5 (patch)
treed101b4025a2b23c5e258b04714907f4266c3b2df /fs/btrfs
parent933b585f70d524f1b6f0f6867bedb11d3dd82d3b (diff)
downloadlinux-5a5f79b57069c5691f5b6fd8381fdf487f548ae5.tar.gz
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.c38
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:
 	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,
 			const struct iovec *iov, loff_t offset,
 			unsigned long nr_segs)
@@ -5736,6 +5762,11 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
 	int write_bits = 0;
 	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;
 	lockend = offset + count - 1;
 
@@ -5784,9 +5815,10 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
 	free_extent_state(cached_state);
 	cached_state = NULL;
 
-	ret = __blockdev_direct_IO(rw, iocb, inode, NULL, iov, offset, nr_segs,
-				   btrfs_get_blocks_direct, NULL,
-				   btrfs_submit_direct, 0);
+	ret = __blockdev_direct_IO(rw, iocb, inode,
+		   BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev,
+		   iov, offset, nr_segs, btrfs_get_blocks_direct, NULL,
+		   btrfs_submit_direct, 0);
 
 	if (ret < 0 && ret != -EIOCBQUEUED) {
 		clear_extent_bit(&BTRFS_I(inode)->io_tree, offset,