aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <josef@redhat.com>2011-04-08 11:51:18 -0400
committerChris Mason <chris.mason@oracle.com>2011-04-11 20:25:06 -0400
commita1b75f7d961955e697ec377f90115e3517df98f9 (patch)
treeae7fe104dc20222f05be528d85f3c4fa40aa9331 /fs
parent3387206f26e1b48703e810175b98611a4fd8e8ea (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> Signed-off-by: Chris Mason <chris.mason@oracle.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 2d1208f964eb..edafc28883af 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6153,6 +6153,7 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io
6153 unsigned long nr_segs) 6153 unsigned long nr_segs)
6154{ 6154{
6155 int seg; 6155 int seg;
6156 int i;
6156 size_t size; 6157 size_t size;
6157 unsigned long addr; 6158 unsigned long addr;
6158 unsigned blocksize_mask = root->sectorsize - 1; 6159 unsigned blocksize_mask = root->sectorsize - 1;
@@ -6167,8 +6168,22 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io
6167 addr = (unsigned long)iov[seg].iov_base; 6168 addr = (unsigned long)iov[seg].iov_base;
6168 size = iov[seg].iov_len; 6169 size = iov[seg].iov_len;
6169 end += size; 6170 end += size;
6170 if ((addr & blocksize_mask) || (size & blocksize_mask)) 6171 if ((addr & blocksize_mask) || (size & blocksize_mask))
6171 goto out; 6172 goto out;
6173
6174 /* If this is a write we don't need to check anymore */
6175 if (rw & WRITE)
6176 continue;
6177
6178 /*
6179 * Check to make sure we don't have duplicate iov_base's in this
6180 * iovec, if so return EINVAL, otherwise we'll get csum errors
6181 * when reading back.
6182 */
6183 for (i = seg + 1; i < nr_segs; i++) {
6184 if (iov[seg].iov_base == iov[i].iov_base)
6185 goto out;
6186 }
6172 } 6187 }
6173 retval = 0; 6188 retval = 0;
6174out: 6189out: