From acf9ed3a6c0025f44434768b0dd76b0f61ce1171 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 4 Jun 2026 09:59:48 +0930 Subject: [PATCH] btrfs: retry faulting in the pages after a zero sized short direct write Currently btrfs_direct_write() will not try to fault in the pages, but directly fall back to buffered writes, if the first page of the buffer can not be faulted in. For example, during generic/362 with nodatasum mount option, there is a write at file offset 0, length PAGE_SIZE, and the page is not faulted in. Then we go the following callchain and directly fall back to buffered IO: btrfs_direct_write() |- btrfs_dio_write() |- __iomap_dio_rw() | |- iomap_iter() | | |- btrfs_dio_iomap_begin() | | Now an ordered extent is allocated for the 4K write. | | | |- iomi.status = iomap_dio_iter() | | Where iomap_dio_iter() returned -EFAULT. | | | |- ret = iomap_iter() | | |- btrfs_dio_iomap_end() | | | | return -ENOTBLK | | |- return -ENOTBLK | |- if (ret == -ENOTBLK) { ret = 0; } | Now the return value is reset to 0. | |- ret = iomap_dio_complete() | Since no byte is submitted, @ret is now zero. | |- if (iov_iter_count() > 0 && (ret == -EFAULT || ret > 0)) | @ret is zero, thus not meeting the above retry condition | |- Fallback to buffered Just slightly loosen the condition to allow retry faulting in pages after a zero sized short write. Unlike the previous two bug fixes, this one is not really cause any real bug, but only reducing the chance to do zero-copy direct IO. Thus it doesn't really require stable-CC nor fixes-tag. Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/direct-io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 412309825d6f..460326d34143 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -978,7 +978,7 @@ ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) if (ret > 0) written = ret; - if (iov_iter_count(from) > 0 && (ret == -EFAULT || ret > 0)) { + if (iov_iter_count(from) > 0 && (ret == -EFAULT || ret >= 0)) { const size_t left = iov_iter_count(from); /* * We have more data left to write. Try to fault in as many as