| From e7a04894c766daa4248cb736efee93550f2d5872 Mon Sep 17 00:00:00 2001 |
| From: Omar Sandoval <osandov@fb.com> |
| Date: Mon, 2 Mar 2020 14:02:49 -0800 |
| Subject: btrfs: fix RAID direct I/O reads with alternate csums |
| |
| From: Omar Sandoval <osandov@fb.com> |
| |
| commit e7a04894c766daa4248cb736efee93550f2d5872 upstream. |
| |
| 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 blake2 -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") |
| CC: stable@vger.kernel.org # 5.4+ |
| Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> |
| Signed-off-by: Omar Sandoval <osandov@fb.com> |
| Reviewed-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/inode.c | 4 +++- |
| 1 file changed, 3 insertions(+), 1 deletion(-) |
| |
| --- a/fs/btrfs/inode.c |
| +++ b/fs/btrfs/inode.c |
| @@ -8444,6 +8444,7 @@ static inline blk_status_t btrfs_lookup_ |
| { |
| 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; |
| |
| /* |
| @@ -8463,7 +8464,8 @@ static inline blk_status_t btrfs_lookup_ |
| |
| 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; |
| } |