Message ID | b88c888c800d66ad39b4a561ec6601d2db59529e.1583186403.git.osandov@fb.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | btrfs: fix RAID direct I/O reads with alternate csums | expand |
Good catch,
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
On Mon, Mar 02, 2020 at 02:02:49PM -0800, Omar Sandoval wrote: > From: Omar Sandoval <osandov@fb.com> > > btrfs_lookup_and_bind_dio_csum() does pointer arithmetic which assumes > 32-bit checksums. If using a larger checksum, this leads to spurious > failures when a direct I/O read crosses a stripe. This is easy > to reproduce: > > # mkfs.btrfs -f --checksum BLAKE2b -d raid0 /dev/vdc /dev/vdd > ... > # mount /dev/vdc /mnt > # cd /mnt > # dd if=/dev/urandom of=foo bs=1M count=1 status=none > # dd if=foo of=/dev/null bs=1M iflag=direct status=none > dd: error reading 'foo': Input/output error > # dmesg | tail -1 > [ 135.821568] BTRFS warning (device vdc): csum failed root 5 ino 257 off 421888 ... > > Fix it by using the actual checksum size. > > Fixes: 1e25a2e3ca0d ("btrfs: don't assume ordered sums to be 4 bytes") > Signed-off-by: Omar Sandoval <osandov@fb.com> > --- > I wasn't sure what commit to point at for the fixes tag (or whether to > just add a stable tag). That one is fine, added to the rc queue so we can get it to stable soon. Thanks.
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index dacfd17a3121..8a3bc19d83ff 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7840,6 +7840,7 @@ static inline blk_status_t btrfs_lookup_and_bind_dio_csum(struct inode *inode, { struct btrfs_io_bio *io_bio = btrfs_io_bio(bio); struct btrfs_io_bio *orig_io_bio = btrfs_io_bio(dip->orig_bio); + u16 csum_size; blk_status_t ret; /* @@ -7859,7 +7860,8 @@ static inline blk_status_t btrfs_lookup_and_bind_dio_csum(struct inode *inode, file_offset -= dip->logical_offset; file_offset >>= inode->i_sb->s_blocksize_bits; - io_bio->csum = (u8 *)(((u32 *)orig_io_bio->csum) + file_offset); + csum_size = btrfs_super_csum_size(btrfs_sb(inode->i_sb)->super_copy); + io_bio->csum = orig_io_bio->csum + csum_size * file_offset; return 0; }