Re: Direct IO reads being split unexpected at page boundary, but in the middle of a fs block (bs > ps cases)

Qu Wenruo <[email protected]> Tue, 7 Oct 2025 13:00:58 +1030
Newsgroups org.kernel.vger.linux-bcachefs,org.kernel.vger.linux-btrfs,org.kernel.vger.linux-fsdevel
Message-ID <[email protected]>

=E5=9C=A8 2025/10/7 01:37, Matthew Wilcox =E5=86=99=E9=81=93:
> On Wed, Oct 01, 2025 at 10:59:18AM +0930, Qu Wenruo wrote:
>> Recently during the btrfs bs > ps direct IO enablement, I'm hitting a c=
ase
>> where:
>>
>> - The direct IO iov is properly aligned to fs block size (8K, 2 pages)
>>    They do not need to be large folio backed, regular incontiguous page=
s
>>    are supported.
>>
>> - The btrfs now can handle sub-block pages
>>    But still require the bi_size and (bi_sector << 9) to be block size
>>    aligned.
>>
>> - The bio passed into iomap_dio_ops::submit_io is not block size
>>    aligned
>>    The bio only contains one page, not 2.
>=20
> That seems like a bug in the VFS/iomap somewhere.  Maybe try cc'ing the
> people who know this code?
>=20

Add xfs and bcachefs subsystem into CC.

The root cause is that, function __bio_iov_iter_get_pages() can split=20
the iov.

In my case, I hit the following dio during iomap_dio_bio_iter();

  fsstress-1153      6..... 68530us : iomap_dio_bio_iter: length=3D81920=
=20
nr_pages=3D20 enter
  fsstress-1153      6..... 68539us : iomap_dio_bio_iter: length=3D81920=
=20
realsize=3D69632(17 pages)
  fsstress-1153      6..... 68540us : iomap_dio_bio_iter: nr_pages=3D3 for=
=20
next

Which bio_iov_iter_get_pages() split the 20 pages into two segments (17=20
+ 3 pages).
That 17/3 split is not meeting the btrfs' block size requirement (in my=20
case it's 8K block size).


I'm seeing XFS having a comment related to bio_iov_iter_get_pages()=20
inside xfs_file_dio_write(), but there is no special checks other than=20
iov_iter_alignment() check, which btrfs is also doing.

I guess since XFS do not need to bother data checksum thus such split is=
=20
not a big deal?


On the other hand, bcachefs is doing reverting to the block boundary=20
instead thus solved the problem.
However btrfs is using iomap for direct IOs, thus we can not manually=20
revert the iov/bio just inside btrfs.

So I guess in this case we need to add a callback for iomap, to get the=20
fs block size so that at least iomap_dio_bio_iter() can revert to the fs=
=20
block boundary?

Thanks,
Qu