aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <josef@redhat.com>2011-04-06 15:11:44 -0400
committerJosef Bacik <josef@redhat.com>2011-04-08 13:00:43 -0400
commit93a54bc4c28a125978cddbe2db9e347391e3522d (patch)
tree910dedd07332c4f1e25893d749fc145263b58c45 /fs
parent16d299ac7446b5a75c5683a9ae11d7907d444c86 (diff)
Btrfs: check for duplicate iov_base's when doing dio reads
Apparently it is ok to submit a read to an IDE device with the same target page for different offsets. This is what Windows does under qemu. The problem is under DIO we expect them to be different buffers for checksumming reasons, and so this sort of thing will result in checksum errors, when in reality the file is fine. So when reading, check to make sure that all iov bases are different, and if they aren't fall back to buffered mode, since that will work out right. Thanks, Signed-off-by: Josef Bacik <josef@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/inode.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 24310c9cb14f..00d59c6a9769 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6207,6 +6207,7 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io
6207 unsigned long nr_segs) 6207 unsigned long nr_segs)
6208{ 6208{
6209 int seg; 6209 int seg;
6210 int i;
6210 size_t size; 6211 size_t size;
6211 unsigned long addr; 6212 unsigned long addr;
6212 unsigned blocksize_mask = root->sectorsize - 1; 6213 unsigned blocksize_mask = root->sectorsize - 1;
@@ -6221,8 +6222,22 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io
6221 addr = (unsigned long)iov[seg].iov_base; 6222 addr = (unsigned long)iov[seg].iov_base;
6222 size = iov[seg].iov_len; 6223 size = iov[seg].iov_len;
6223 end += size; 6224 end += size;
6224 if ((addr & blocksize_mask) || (size & blocksize_mask)) 6225 if ((addr & blocksize_mask) || (size & blocksize_mask))
6225 goto out; 6226 goto out;
6227
6228 /* If this is a write we don't need to check anymore */
6229 if (rw & WRITE)
6230 continue;
6231
6232 /*
6233 * Check to make sure we don't have duplicate iov_base's in this
6234 * iovec, if so return EINVAL, otherwise we'll get csum errors
6235 * when reading back.
6236 */
6237 for (i = seg + 1; i < nr_segs; i++) {
6238 if (iov[seg].iov_base == iov[i].iov_base)
6239 goto out;
6240 }
6226 } 6241 }
6227 retval = 0; 6242 retval = 0;
6228out: 6243out: