nfsd_direct_read() expands a misaligned READ out to DIO-aligned boundaries: it reads from round_down(offset, dio_read_offset_align) to round_up(offset + count, dio_read_offset_align) and returns only the requested bytes from within that window. When the READ is smaller than the alignment, that window is always at least one full alignment unit, and two when the READ straddles a boundary, so a few hundred bytes of payload can cost a 4K or 64K device read. Decline direct I/O for those. A READ smaller than dio_read_offset_align now falls through to the DONTCACHE path, which issues DONTCACHE buffered I/O when the file system supports FOP_DONTCACHE and normal buffered I/O otherwise. This mirrors the WRITE side, which already declines direct I/O for a WRITE smaller than the larger of its offset and memory alignments. Only dio_read_offset_align is consulted: nfsd_direct_read() lands the data in whole rq_pages pages at offset zero, so dio_mem_align is always satisfied and only the file offset and length can be misaligned. The threshold only bites when the file system advertises a large alignment; where it reports 512 almost no READ is excluded. Document it in the "Misaligned READ" section of nfsd-io-modes.rst. Assisted-by: LLM Signed-off-by: Mike Snitzer --- Documentation/filesystems/nfs/nfsd-io-modes.rst | 5 +++++ fs/nfsd/vfs.c | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Documentation/filesystems/nfs/nfsd-io-modes.rst b/Documentation/filesystems/nfs/nfsd-io-modes.rst index 2679d85152dfc..90bf1038453cc 100644 --- a/Documentation/filesystems/nfs/nfsd-io-modes.rst +++ b/Documentation/filesystems/nfs/nfsd-io-modes.rst @@ -121,6 +121,11 @@ Misaligned READ: verified to have proper offset/len (logical_block_size) and dma_alignment checking. + A READ smaller than dio_read_offset_align is not expanded: it would + read one or two whole alignment units to return less than one unit + of data. It is issued as DONTCACHE buffered IO instead (normal + buffered IO if the filesystem lacks FOP_DONTCACHE). + Misaligned WRITE: If NFSD_IO_DIRECT is used, split any misaligned WRITE into a start, middle and end as needed. The large middle segment is DIO-aligned diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index b596bd61f8cb7..7e65328de7c86 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1166,11 +1166,18 @@ nfsd_direct_read(struct svc_rqst *rqstp, struct svc_fh *fhp, eof, host_err); } -static bool nfsd_direct_read_ok(struct svc_rqst *rqstp, struct nfsd_file *nf) +static bool nfsd_direct_read_ok(struct svc_rqst *rqstp, struct nfsd_file *nf, + unsigned long count) { /* When dio_read_offset_align is zero, dio is not supported */ if (!nf->nf_dio_read_offset_align) return false; + /* + * A READ smaller than that alignment is not worth expanding to one + * or two whole alignment units. + */ + if (count < nf->nf_dio_read_offset_align) + return false; if (rqstp->rq_res.page_len) return false; #ifdef CONFIG_NFSD_V4 @@ -1222,7 +1229,7 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp, case NFSD_IO_BUFFERED: break; case NFSD_IO_DIRECT: - if (nfsd_direct_read_ok(rqstp, nf)) + if (nfsd_direct_read_ok(rqstp, nf, *count)) return nfsd_direct_read(rqstp, fhp, nf, offset, count, page, eof); fallthrough; -- 2.52.0