A bio that carries an encryption context must cover a whole number of data units: its data unit number is generated from its first data unit (see bio_crypt_set_ctx()), and it is only split at data unit boundaries. iomap attaches the encryption context of an encrypted file to the bios that it builds for direct I/O, but it only cuts them at the logical block size of the device. A bio ends where the bio_vecs that fit into it end, not at a fixed number of bytes: the extraction stops when the bio is out of bio_vecs or when the iterator is exhausted, and a bio_vec can cover a whole folio. A bio can end in the middle of a data unit, and a data unit that is larger than a page makes this reachable without any misaligned input at all: the extraction then stops in the middle of a folio, and a cut at a page granularity inside such a data unit is not data unit aligned even when the position, the length and the buffer are. The fallback rejects such a bio once it reaches the block layer: one of its bio_vecs is not data unit aligned, so a direct I/O that should work fails. By contrast, the equivalent check on the hardware path lives in the splitting code, which a small single segment bio skips, so a bio that starts in the middle of a data unit can reach the hardware without being rejected. Fix this by cutting the bios of an encrypted file at the block size, like the IOMAP_DIO_FSBLOCK_ALIGNED flag does for the filesystems that pass it. fscrypt rejects a data unit size larger than the block size, so a bio that is cut at block size boundaries covers a whole number of data units. This also makes the file systems that don't pass the flag follow the contract, e.g. f2fs, which passes direct I/O that is not aligned to the logical block size of the device on to iomap expecting it to fail with -EINVAL, while iomap checks only the file position and the length, so such a request can reach bio submission. Signed-off-by: Baokun Li --- fs/iomap/direct-io.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 8b4039d16ce8..9b6cdf04dced 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -410,6 +410,16 @@ static inline unsigned int iomap_dio_alignment(struct inode *inode, { if (dio_flags & IOMAP_DIO_FSBLOCK_ALIGNED) return i_blocksize(inode); + + /* + * A bio of an encrypted file must cover a whole number of data + * units. The data unit size of an encryption policy never exceeds + * the block size, so cutting the bios at block size boundaries is + * enough. + */ + if (IS_ENCRYPTED(inode)) + return i_blocksize(inode); + return bdev_logical_block_size(bdev); } -- 2.43.7