diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 3e227292b0ac..b80dc45c3ff9 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -817,13 +817,41 @@ static ssize_t btrfs_dio_read(struct kiocb *iocb, struct iov_iter *iter, IOMAP_DIO_PARTIAL | IOMAP_DIO_FSBLOCK_ALIGNED, &data, done_before); } +static bool need_stable_write(struct btrfs_inode *inode) +{ + const u64 data_profile = btrfs_data_alloc_profile(inode->root->fs_info) & + BTRFS_BLOCK_GROUP_PROFILE_MASK; + + /* Data checksum requires stable buffer. */ + if (!(inode->flags & BTRFS_INODE_NODATASUM)) + return true; + /* + * Any profile with mirror/parity will require stable buffer. + * Otherwise the mirror may differ from each other. + * + * Thus only SINGLE and RAID0 doesn't require stable buffer. + */ + if (data_profile != 0 && data_profile != BTRFS_BLOCK_GROUP_RAID0) + return true; + return false; +} + static struct iomap_dio *btrfs_dio_write(struct kiocb *iocb, struct iov_iter *iter, size_t done_before) { struct btrfs_dio_data data = { 0 }; + unsigned int dio_flags = IOMAP_DIO_PARTIAL | IOMAP_DIO_FSBLOCK_ALIGNED; + + if (need_stable_write(BTRFS_I(file_inode(iocb->ki_filp)))) { + /* For now no support for BOUNCE and NOWAIT direct write. */ + if (iocb->ki_flags & IOCB_NOWAIT) + return ERR_PTR(-EAGAIN); + + dio_flags |= IOMAP_DIO_BOUNCE; + } return __iomap_dio_rw(iocb, iter, &btrfs_dio_iomap_ops, &btrfs_dio_ops, - IOMAP_DIO_PARTIAL | IOMAP_DIO_FSBLOCK_ALIGNED, &data, done_before); + dio_flags, &data, done_before); } static ssize_t check_direct_IO(struct btrfs_fs_info *fs_info, @@ -852,8 +880,6 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) ssize_t ret; unsigned int ilock_flags = 0; struct iomap_dio *dio; - const u64 data_profile = btrfs_data_alloc_profile(fs_info) & - BTRFS_BLOCK_GROUP_PROFILE_MASK; if (iocb->ki_flags & IOCB_NOWAIT) ilock_flags |= BTRFS_ILOCK_TRY; @@ -867,16 +893,6 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode) && IS_NOSEC(inode)) ilock_flags |= BTRFS_ILOCK_SHARED; - /* - * If our data profile has duplication (either extra mirrors or RAID56), - * we can not trust the direct IO buffer, the content may change during - * writeback and cause different contents written to different mirrors. - * - * Thus only RAID0 and SINGLE can go true zero-copy direct IO. - */ - if (data_profile != BTRFS_BLOCK_GROUP_RAID0 && data_profile != 0) - goto buffered; - relock: ret = btrfs_inode_lock(BTRFS_I(inode), ilock_flags); if (ret < 0) @@ -917,22 +933,6 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) btrfs_inode_unlock(BTRFS_I(inode), ilock_flags); goto buffered; } - /* - * We can't control the folios being passed in, applications can write - * to them while a direct IO write is in progress. This means the - * content might change after we calculated the data checksum. - * Therefore we can end up storing a checksum that doesn't match the - * persisted data. - * - * To be extra safe and avoid false data checksum mismatch, if the - * inode requires data checksum, just fallback to buffered IO. - * For buffered IO we have full control of page cache and can ensure - * no one is modifying the content during writeback. - */ - if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) { - btrfs_inode_unlock(BTRFS_I(inode), ilock_flags); - goto buffered; - } /* * The iov_iter can be mapped to the same file range we are writing to.