[PATCH v2 1/3] nfsd: avoid using DONTCACHE for misaligned DIO's buffered IO fallback

Mike Snitzer
2025-11-04T18:11:55Z

Also, use buffered IO (without DONTCACHE) if READ is less than 32K.
But do use DONTCACHE if an entire WRITE is misaligned, this preserves
intent of NFSD_IO_DIRECT.

The misaligned ends of a misaligned DIO WRITE will use buffered IO
(without DONTCACHE) but the middle DIO-aligned segment with use direct
IO. This provides ideal performance for streaming misaligned DIO
(e.g. IO500's IOR_HARD) because buffered IO is used to benefit RMW.

On one capable testbed, this commit improved IOR_HARD WRITE
performance from 0.3433GB/s to 1.26GB/s.

Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfsd/vfs.c | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)

v2: don't use nfserrno(), just return nfserr_eagain directly from nfsd_direct_read

diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 701dd261c252..a87ee9736d66 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1099,13 +1099,18 @@ nfsd_direct_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
size_t len;

init_sync_kiocb(&kiocb, nf->nf_file);
- kiocb.ki_flags |= IOCB_DIRECT;

/* Read a properly-aligned region of bytes into rq_bvec */
dio_start = round_down(offset, nf->nf_dio_read_offset_align);
dio_end = round_up((u64)offset + *count, nf->nf_dio_read_offset_align);

+ /* Don't use expanded DIO READ for IO less than 32K */
+ if ((*count < (32 << 10)) &&
+ (((offset - dio_start) > 0) || ((dio_end - (offset + *count)) > 0)))
+ return nfserr_eagain; /* fallback to buffered */
+
kiocb.ki_pos = dio_start;
+ kiocb.ki_flags |= IOCB_DIRECT;

v = 0;
total = dio_end - dio_start;
@@ -1184,10 +1189,13 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
break;
case NFSD_IO_DIRECT:
/* When dio_read_offset_align is zero, dio is not supported */
- if (nf->nf_dio_read_offset_align && !rqstp->rq_res.page_len)
- return nfsd_direct_read(rqstp, fhp, nf, offset,
- count, eof);
- fallthrough;
+ if (nf->nf_dio_read_offset_align && !rqstp->rq_res.page_len) {
+ __be32 nfserr = nfsd_direct_read(rqstp, fhp, nf, offset,
+ count, eof);
+ if (nfserr != nfserr_eagain)
+ return nfserr;
+ }
+ break; /* fallback to buffered */
case NFSD_IO_DONTCACHE:
if (file->f_op->fop_flags & FOP_DONTCACHE)
kiocb.ki_flags = IOCB_DONTCACHE;
@@ -1347,6 +1355,15 @@ nfsd_write_dio_iters_init(struct bio_vec *bvec, unsigned int nvecs,
++args->nsegs;
}

+ /*
+ * Don't use IOCB_DONTCACHE if misaligned DIO WRITE (args->nsegs > 1),
+ * because it compromises unaligned segments' RMW IO being able to
+ * benefit from buffered IO (especially important for streaming
+ * misaligned DIO WRITE performance).
+ */
+ if (args->nsegs > 1 && (args->flags_buffered & IOCB_DONTCACHE))
+ args->flags_buffered &= ~IOCB_DONTCACHE;
+
return;

no_dio:
@@ -1400,7 +1417,7 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp,

/*
* IOCB_DONTCACHE preserves the intent of NFSD_IO_DIRECT when
- * writing unaligned segments or handling fallback I/O.
+ * falling back to buffered IO if entire WRITE is unaligned.
*/
args.flags_buffered = kiocb->ki_flags;
if (args.nf->nf_file->f_op->fop_flags & FOP_DONTCACHE)
--
2.44.0