Skip to contentSkip to conversation

NFSD: Implement NFSD_IO_DIRECT for NFS WRITE

PATCH Superseded Chuck Lever 4 participants 21 messages Oct 18, 2025 – Oct 23, 2025

Subsystems (3)

v4 of 12 Superseded Latest is v12 →

lore
Share & tools
lore

Chuck Lever

author maintainer
#0 lore raw

v4 cover NFSD: Implement NFSD_IO_DIRECT for NFS WRITE

From: Chuck Lever <[email protected]>

Following on https://lore.kernel.org/linux-nfs/[email protected]/
this series applies on the nfsd-testing testing branch and includes both patches needed to make NFSD Direct WRITE work.

Changes since v3:
* Address checkpatch.pl nits in 2/3
* Add an untested patch to mark ingress RDMA Read chunks

Chuck Lever (2):
NFSD: Enable return of an updated stable_how to NFS clients
svcrdma: Mark Read chunks

Mike Snitzer (1):
NFSD: Implement NFSD_IO_DIRECT for NFS WRITE

 fs/nfsd/debugfs.c                       |   1 +
 fs/nfsd/nfs3proc.c                      |   2 +-
 fs/nfsd/nfs4proc.c                      |   2 +-
 fs/nfsd/nfsproc.c                       |   3 +-
 fs/nfsd/trace.h                         |   1 +
 fs/nfsd/vfs.c                           | 233 +++++++++++++++++++++++-
 fs/nfsd/vfs.h                           |   6 +-
 fs/nfsd/xdr3.h                          |   2 +-
 net/sunrpc/xprtrdma/svc_rdma_recvfrom.c |   5 +
 9 files changed, 239 insertions(+), 16 deletions(-)
Signature · 1 line
2.51.0
author maintainer
#1 lore raw

PATCH v4 1/3 NFSD: Enable return of an updated stable_how to NFS clients

From: Chuck Lever <[email protected]>

In a subsequent patch, nfsd_vfs_write() will promote an UNSTABLE WRITE to be a FILE_SYNC WRITE. This indicates that the client does not need a subsequent COMMIT operation, saving a round trip and allowing the client to dispense with cached dirty data as soon as it receives the server's WRITE response.

This patch refactors nfsd_vfs_write() to return a possibly modified stable_how value to its callers. No behavior change is expected.

---
 fs/nfsd/nfs3proc.c |  2 +-
 fs/nfsd/nfs4proc.c |  2 +-
 fs/nfsd/nfsproc.c  |  3 ++-
 fs/nfsd/vfs.c      | 11 ++++++-----
 fs/nfsd/vfs.h      |  6 ++++--
 fs/nfsd/xdr3.h     |  2 +-
 6 files changed, 15 insertions(+), 11 deletions(-)
fs/nfsd/nfs3proc.c +1 -1
index b6d03e1ef5f7..ad14b34583bb 100644
@@ -236,7 +236,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
 	resp->committed = argp->stable;
 	resp->status = nfsd_write(rqstp, &resp->fh, argp->offset,
 				  &argp->payload, &cnt,
-				  resp->committed, resp->verf);
+				  &resp->committed, resp->verf);
 	resp->count = cnt;
 	resp->status = nfsd3_map_status(resp->status);
 	return rpc_success;
fs/nfsd/nfs4proc.c +1 -1
index 7f7e6bb23a90..2222bb283baf 100644
@@ -1285,7 +1285,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	write->wr_how_written = write->wr_stable_how;
 	status = nfsd_vfs_write(rqstp, &cstate->current_fh, nf,
 				write->wr_offset, &write->wr_payload,
-				&cnt, write->wr_how_written,
+				&cnt, &write->wr_how_written,
 				(__be32 *)write->wr_verifier.data);
 	nfsd_file_put(nf);
 
fs/nfsd/nfsproc.c +2 -1
index 8f71f5748c75..706401ed6f8d 100644
@@ -251,6 +251,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
 	struct nfsd_writeargs *argp = rqstp->rq_argp;
 	struct nfsd_attrstat *resp = rqstp->rq_resp;
 	unsigned long cnt = argp->len;
+	u32 committed = NFS_DATA_SYNC;
 
 	dprintk("nfsd: WRITE    %s %u bytes at %d\n",
 		SVCFH_fmt(&argp->fh),
@@ -258,7 +259,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
 
 	fh_copy(&resp->fh, &argp->fh);
 	resp->status = nfsd_write(rqstp, &resp->fh, argp->offset,
-				  &argp->payload, &cnt, NFS_DATA_SYNC, NULL);
+				  &argp->payload, &cnt, &committed, NULL);
 	if (resp->status == nfs_ok)
 		resp->status = fh_getattr(&resp->fh, &resp->stat);
 	else if (resp->status == nfserr_jukebox)
fs/nfsd/vfs.c +6 -5
index f537a7b4ee01..8b2dc7a88aab 100644
@@ -1262,7 +1262,7 @@ static int wait_for_concurrent_writes(struct file *file)
  * @offset: Byte offset of start
  * @payload: xdr_buf containing the write payload
  * @cnt: IN: number of bytes to write, OUT: number of bytes actually written
- * @stable: An NFS stable_how value
+ * @stable_how: IN: Client's requested stable_how, OUT: Actual stable_how
  * @verf: NFS WRITE verifier
  *
  * Upon return, caller must invoke fh_put on @fhp.
@@ -1274,11 +1274,12 @@ __be32
 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	       struct nfsd_file *nf, loff_t offset,
 	       const struct xdr_buf *payload, unsigned long *cnt,
-	       int stable, __be32 *verf)
+	       u32 *stable_how, __be32 *verf)
 {
 	struct nfsd_net		*nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
 	struct file		*file = nf->nf_file;
 	struct super_block	*sb = file_inode(file)->i_sb;
+	u32			stable = *stable_how;
 	struct kiocb		kiocb;
 	struct svc_export	*exp;
 	struct iov_iter		iter;
@@ -1434,7 +1435,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
  * @offset: Byte offset of start
  * @payload: xdr_buf containing the write payload
  * @cnt: IN: number of bytes to write, OUT: number of bytes actually written
- * @stable: An NFS stable_how value
+ * @stable_how: IN: Client's requested stable_how, OUT: Actual stable_how
  * @verf: NFS WRITE verifier
  *
  * Upon return, caller must invoke fh_put on @fhp.
@@ -1444,7 +1445,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
  */
 __be32
 nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
-	   const struct xdr_buf *payload, unsigned long *cnt, int stable,
+	   const struct xdr_buf *payload, unsigned long *cnt, u32 *stable_how,
 	   __be32 *verf)
 {
 	struct nfsd_file *nf;
@@ -1457,7 +1458,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 		goto out;
 
 	err = nfsd_vfs_write(rqstp, fhp, nf, offset, payload, cnt,
-			     stable, verf);
+			     stable_how, verf);
 	nfsd_file_put(nf);
 out:
 	trace_nfsd_write_done(rqstp, fhp, offset, *cnt);
fs/nfsd/vfs.h +4 -2
index fa46f8b5f132..c713ed0b04e0 100644
@@ -130,11 +130,13 @@ __be32		nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
 				u32 *eof);
 __be32		nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
 				loff_t offset, const struct xdr_buf *payload,
-				unsigned long *cnt, int stable, __be32 *verf);
+				unsigned long *cnt, u32 *stable_how,
+				__be32 *verf);
 __be32		nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
 				struct nfsd_file *nf, loff_t offset,
 				const struct xdr_buf *payload,
-				unsigned long *cnt, int stable, __be32 *verf);
+				unsigned long *cnt, u32 *stable_how,
+				__be32 *verf);
 __be32		nfsd_readlink(struct svc_rqst *, struct svc_fh *,
 				char *, int *);
 __be32		nfsd_symlink(struct svc_rqst *, struct svc_fh *,
fs/nfsd/xdr3.h +1 -1
index 522067b7fd75..c0e443ef3a6b 100644
@@ -152,7 +152,7 @@ struct nfsd3_writeres {
 	__be32			status;
 	struct svc_fh		fh;
 	unsigned long		count;
-	int			committed;
+	u32			committed;
 	__be32			verf[2];
 };
 
Signature · 1 line
2.51.0
#2 lore raw

Re: [PATCH v4 1/3] NFSD: Enable return of an updated stable_how to NFS clients

Quoted 4 lines · Chuck Lever

From: Chuck Lever <[email protected]>

In a subsequent patch, nfsd_vfs_write() will promote an UNSTABLE WRITE to be a FILE_SYNC WRITE. This indicates that the client does

I'd much prefer you'd mention why that subsequen patch needs to update it instead of being so vague. That really helps when trying to understand the change when finding it in a bisect or blame.

author maintainer
#3 lore raw

PATCH v4 2/3 NFSD: Implement NFSD_IO_DIRECT for NFS WRITE

In reply to Chuck Lever

From: Mike Snitzer <[email protected]>

If NFSD_IO_DIRECT is used, split any misaligned WRITE into a start, middle and end as needed. The large middle extent is DIO-aligned and the start and/or end are misaligned. Synchronous buffered IO (with preference towards using DONTCACHE) is used for the misaligned extents and O_DIRECT is used for the middle DIO-aligned extent.

nfsd_issue_write_dio() promotes @stable_how to NFS_FILE_SYNC, which allows the client to drop its dirty data and avoid needing an extra COMMIT operation.

If vfs_iocb_iter_write() returns -ENOTBLK, due to its inability to invalidate the page cache on behalf of the DIO WRITE, then nfsd_issue_write_dio() will fall back to using buffered IO.

These changes served as the original starting point for the NFS client's misaligned O_DIRECT support that landed with commit c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE"). But NFSD's support is simpler because it currently doesn't use AIO completion.

---
 fs/nfsd/debugfs.c |   1 +
 fs/nfsd/trace.h   |   1 +
 fs/nfsd/vfs.c     | 222 ++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 219 insertions(+), 5 deletions(-)
fs/nfsd/debugfs.c +1 -0
index 00eb1ecef6ac..7f44689e0a53 100644
@@ -108,6 +108,7 @@ static int nfsd_io_cache_write_set(void *data, u64 val)
 	switch (val) {
 	case NFSD_IO_BUFFERED:
 	case NFSD_IO_DONTCACHE:
+	case NFSD_IO_DIRECT:
 		nfsd_io_cache_write = val;
 		break;
 	default:
fs/nfsd/trace.h +1 -0
index bfd41236aff2..ad74439d0105 100644
@@ -469,6 +469,7 @@ DEFINE_NFSD_IO_EVENT(read_io_done);
 DEFINE_NFSD_IO_EVENT(read_done);
 DEFINE_NFSD_IO_EVENT(write_start);
 DEFINE_NFSD_IO_EVENT(write_opened);
+DEFINE_NFSD_IO_EVENT(write_direct);
 DEFINE_NFSD_IO_EVENT(write_io_done);
 DEFINE_NFSD_IO_EVENT(write_done);
 DEFINE_NFSD_IO_EVENT(commit_start);
fs/nfsd/vfs.c +217 -5

255 lines not on this page · raw message

Signature · 1 line
2.51.0
#4 lore raw
Quoted 7 lines · Chuck Lever

From: Mike Snitzer <[email protected]>

If NFSD_IO_DIRECT is used, split any misaligned WRITE into a start, middle and end as needed. The large middle extent is DIO-aligned and the start and/or end are misaligned. Synchronous buffered IO (with preference towards using DONTCACHE) is used for the misaligned extents and O_DIRECT is used for the middle DIO-aligned extent.

Can you define synchronous better here? The term is unfortunately overloaded between synchronous syscalls vs aio/io_uring and O_(D)SYNC style I/O. As of now I don't understand which one you mean, especially with the DONTCACHE reference thrown in, but I guess I'll figure it out reading the patch.

Quoted 3 lines

If vfs_iocb_iter_write() returns -ENOTBLK, due to its inability to invalidate the page cache on behalf of the DIO WRITE, then nfsd_issue_write_dio() will fall back to using buffered IO.

Did you see -ENOTBLK leaking out of the file systems? Because at least for iomap it is supposed to be an indication that the file system ->write_iter handler needs to retry using buffered I/O and never leak to the caller.

Quoted 5 lines

These changes served as the original starting point for the NFS client's misaligned O_DIRECT support that landed with commit c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE"). But NFSD's support is simpler because it currently doesn't use AIO completion.

I don't understand this paragraph. What does starting point mean here? How does it matter for the patch description?

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1261

Quoted 5 lines
fs/nfsd/vfs.c +5 -0
+struct nfsd_write_dio {
+     ssize_t start_len;      /* Length for misaligned first extent */
+     ssize_t middle_len;     /* Length for DIO-aligned middle extent */
+     ssize_t end_len;        /* Length for misaligned last extent */
+};

Looking at how the code is structured later on, it seems like it would work much better if each of these sections had it's own object with the len, iov_iter, flag if it's aligned, etc. Otherwise we have this structure and lots of arrays of three items passed around.

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1291

Quoted 3 lines
fs/nfsd/vfs.c +3 -0
+static bool
+nfsd_iov_iter_aligned_bvec(const struct iov_iter *i, unsigned int addr_mask,
+                        unsigned int len_mask)

Wouldn't it make sense to track the alignment when building the bio_vec array instead of doing another walk here touching all cache lines?

Review

Quoted 2 lines
(quoted diff) +2 -0
+	if (unlikely(dio_blocksize > PAGE_SIZE))
+		return false;

Why does this matter? Can you add a comment explaining it?

Quoted 5 lines
fs/nfsd/vfs.c +5 -0
+static int
+nfsd_buffered_write(struct svc_rqst *rqstp, struct file *file,
+		    unsigned int nvecs, unsigned long *cnt,
+		    struct kiocb *kiocb)
+{

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1374

Quoted 10 lines
fs/nfsd/vfs.c +10 -0
+	struct iov_iter iter;
+	int host_err;
+
+	iov_iter_bvec(&iter, ITER_SOURCE, rqstp->rq_bvec, nvecs, *cnt);
+	host_err = vfs_iocb_iter_write(file, kiocb, &iter);
+	if (host_err < 0)
+		return host_err;
+	*cnt = host_err;
+
+	return 0;

Nothing really buffered here per se, it's just a small wrapper around vfs_iocb_iter_write.

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1404

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+	/*
+	 * Any buffered IO issued here will be misaligned, use
+	 * sync IO to ensure it has completed before returning.
+	 * Also update @stable_how to avoid need for COMMIT.
+	 */
+	kiocb->ki_flags |= (IOCB_DSYNC | IOCB_SYNC);

What do you mean with completed before returning? I guess you mean writeback actually happening, right? Why do you need that, why do you also force it for the direct I/O?

Also IOCB_SYNC is wrong here, as the only thing it does over IOCB_DSYNC is also forcing back of metadata not needed to find data (aka timestamps), which I can't see any need for here.

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+	*stable_how = NFS_FILE_SYNC;
+
+	*cnt = 0;
+	for (int i = 0; i < n_iters; i++) {
+		if (iter_is_dio_aligned[i])
+			kiocb->ki_flags |= IOCB_DIRECT;

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1420

Quoted 10 lines
fs/nfsd/vfs.c +10 -0
+		else
+			kiocb->ki_flags &= ~IOCB_DIRECT;
+
+		host_err = vfs_iocb_iter_write(file, kiocb, &iter[i]);
+		if (host_err < 0) {
+			/*
+			 * VFS will return -ENOTBLK if DIO WRITE fails to
+			 * invalidate the page cache. Retry using buffered IO.
+			 */
+			if (unlikely(host_err == -ENOTBLK)) {

The VFS certainly does not, and if it leaks out of a specific file system we need to fix that.

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1431

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+			} else if (unlikely(host_err == -EINVAL)) {
+				struct inode *inode = d_inode(fhp->fh_dentry);
+
+				pr_info_ratelimited("nfsd: Direct I/O alignment failure on %s/%ld\n",
+						    inode->i_sb->s_id, inode->i_ino);
+				host_err = -ESERVERFAULT;

-EINVAL can be lot more things than alignment failure. And more
importantly alignment failures should not happen with the proper checks in place.

author maintainer
#5 lore raw
Quoted 14 lines · Christoph Hellwig
Quoted 13 lines · Chuck Lever

From: Mike Snitzer <[email protected]>

fs/nfsd/vfs.c +11 -0
+static int
+nfsd_buffered_write(struct svc_rqst *rqstp, struct file *file,
+		    unsigned int nvecs, unsigned long *cnt,
+		    struct kiocb *kiocb)
+{
+	struct iov_iter iter;
+	int host_err;
+
+	iov_iter_bvec(&iter, ITER_SOURCE, rqstp->rq_bvec, nvecs, *cnt);
+	host_err = vfs_iocb_iter_write(file, kiocb, &iter);
+	if (host_err < 0)
Quoted 8 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1374

Quoted 4 lines
fs/nfsd/vfs.c +4 -0
+		return host_err;
+	*cnt = host_err;
+
+	return 0;

Nothing really buffered here per se, it's just a small wrapper around vfs_iocb_iter_write.

This is the original NFSD_IO_BUFFERED code, refactored out of nfsd_vfs_write(). IMO it might be more clear what's going on if the refactoring was done in a separate prerequisite patch.

Quoted 10 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1431

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+			} else if (unlikely(host_err == -EINVAL)) {
+				struct inode *inode = d_inode(fhp->fh_dentry);
+
+				pr_info_ratelimited("nfsd: Direct I/O alignment failure on %s/%ld\n",
+						    inode->i_sb->s_id, inode->i_ino);
+				host_err = -ESERVERFAULT;

-EINVAL can be lot more things than alignment failure. And more
importantly alignment failures should not happen with the proper checks in place.

I tend to agree that I expect an alignment failure will rarely if ever happen if we've done our job well.

Signature · 1 line
Chuck Lever
#6 lore raw
Quoted 2 lines · Chuck Lever

I tend to agree that I expect an alignment failure will rarely if ever happen if we've done our job well.

It should never happen if the job was done properly. But when -EINVAL happens it can be just as likely any other mismatched condition.

#7 lore raw
In reply to Christoph Hellwig
Quoted 9 lines · Christoph Hellwig
Quoted 7 lines · Chuck Lever

From: Mike Snitzer <[email protected]>

If NFSD_IO_DIRECT is used, split any misaligned WRITE into a start, middle and end as needed. The large middle extent is DIO-aligned and the start and/or end are misaligned. Synchronous buffered IO (with preference towards using DONTCACHE) is used for the misaligned extents and O_DIRECT is used for the middle DIO-aligned extent.

Quoted 5 lines

Can you define synchronous better here? The term is unfortunately overloaded between synchronous syscalls vs aio/io_uring and O_(D)SYNC style I/O. As of now I don't understand which one you mean, especially with the DONTCACHE reference thrown in, but I guess I'll figure it out reading the patch.

It clearly talks about synchronous IO. DONTCACHE just happens to be the buffered IO that'll be used if supported.

I don't see anything that needs changing here.

Quoted 8 lines
Quoted 3 lines

If vfs_iocb_iter_write() returns -ENOTBLK, due to its inability to invalidate the page cache on behalf of the DIO WRITE, then nfsd_issue_write_dio() will fall back to using buffered IO.

Did you see -ENOTBLK leaking out of the file systems? Because at least for iomap it is supposed to be an indication that the file system ->write_iter handler needs to retry using buffered I/O and never leak to the caller.

fs/iomap/direct-io.c:__iomap_dio_rw() will return -ENOTBLK on its own.

Quoted 8 lines
Quoted 5 lines

These changes served as the original starting point for the NFS client's misaligned O_DIRECT support that landed with commit c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE"). But NFSD's support is simpler because it currently doesn't use AIO completion.

I don't understand this paragraph. What does starting point mean here? How does it matter for the patch description?

This patch's NFSD changes were starting point for NFS commit c817248fc831

How does it matter that this NFSD code served as the basis for NFS code that has been merged? I thought it useful context.

Quoted 10 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1261

Quoted 5 lines
fs/nfsd/vfs.c +5 -0
+struct nfsd_write_dio {
+     ssize_t start_len;      /* Length for misaligned first extent */
+     ssize_t middle_len;     /* Length for DIO-aligned middle extent */
+     ssize_t end_len;        /* Length for misaligned last extent */
+};

Looking at how the code is structured later on, it seems like it would work much better if each of these sections had it's own object with the len, iov_iter, flag if it's aligned, etc. Otherwise we have this structure and lots of arrays of three items passed around.

I did look at doing that, really isn't much better. And 2 isn't lots.

But not opposed to revisiting it.

Would prefer that cleanup, if done, to happen with an incremental follow-up patch.

Quoted 6 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1291

Quoted 3 lines
fs/nfsd/vfs.c +3 -0
+static bool
+nfsd_iov_iter_aligned_bvec(const struct iov_iter *i, unsigned int addr_mask,
+                        unsigned int len_mask)

Wouldn't it make sense to track the alignment when building the bio_vec array instead of doing another walk here touching all cache lines?

Yes, that is the conventional wisdom that justified Keith removing iov_iter_aligned. But for NFSD's WRITE payload the bio_vec is built outside of the fs/nfsd/vfs.c code. Could be there is a natural way to clean this up (to make the sunrpc layer to conditionally care about alignment) but I didn't confront that yet.

Room for follow-on improvement to be sure.

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	if (unlikely(dio_blocksize > PAGE_SIZE))
+		return false;

Why does this matter? Can you add a comment explaining it?

I did/do have concerns that a bug in the storage driver could expose dio_offset_align that is far too large and so bounded dio_blocksize to a conservative size. What is a better constant to check?

But... for trusted storage and drivers, probably doesn't matter and could be removed.

I split the check out:

if (unlikely(dio_blocksize > PAGE_SIZE))
return false;
if (unlikely(len < dio_blocksize))
return false;

could've been:

        if (unlikely(dio_blocksize > PAGE_SIZE || len < dio_blocksize))
                return false;

physical blocksizes larger than PAGE_SIZE are coming (or here?) but overall this initial NFSD O_DIRECT support isn't meant to concern itself with such exotics at this point.

Quoted 11 lines
Quoted 11 lines
fs/nfsd/vfs.c +11 -0
+static int
+nfsd_buffered_write(struct svc_rqst *rqstp, struct file *file,
+		    unsigned int nvecs, unsigned long *cnt,
+		    struct kiocb *kiocb)
+{
+	struct iov_iter iter;
+	int host_err;
+
+	iov_iter_bvec(&iter, ITER_SOURCE, rqstp->rq_bvec, nvecs, *cnt);
+	host_err = vfs_iocb_iter_write(file, kiocb, &iter);
+	if (host_err < 0)
Quoted 8 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1374

Quoted 4 lines
fs/nfsd/vfs.c +4 -0
+		return host_err;
+	*cnt = host_err;
+
+	return 0;

Nothing really buffered here per se, it's just a small wrapper around vfs_iocb_iter_write.

Its factored out code with in a named function. Name fit its purpose.

Quoted 10 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1404

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+	/*
+	 * Any buffered IO issued here will be misaligned, use
+	 * sync IO to ensure it has completed before returning.
+	 * Also update @stable_how to avoid need for COMMIT.
+	 */
+	kiocb->ki_flags |= (IOCB_DSYNC | IOCB_SYNC);

What do you mean with completed before returning? I guess you mean writeback actually happening, right? Why do you need that, why do you also force it for the direct I/O?

The IO needs to have reached stable storage.

Changing these flags when IOCB_DIRECT used doesn't seem needed.

Open to suggestions, could be I'm being too heavy but the goal is to ensure the misaligned ends (head and tail) have hit the storage.

Quoted 3 lines

Also IOCB_SYNC is wrong here, as the only thing it does over IOCB_DSYNC is also forcing back of metadata not needed to find data (aka timestamps), which I can't see any need for here.

Well, we're eliding any followup SYNC from the NFS client by setting stable_how to NFS_FILE_SYNC, so I made sure to use SYNC:

Quoted 11 lines
Quoted 11 lines
fs/nfsd/vfs.c +11 -0
+	*stable_how = NFS_FILE_SYNC;
+
+	*cnt = 0;
+	for (int i = 0; i < n_iters; i++) {
+		if (iter_is_dio_aligned[i])
+			kiocb->ki_flags |= IOCB_DIRECT;
+		else
+			kiocb->ki_flags &= ~IOCB_DIRECT;
+
+		host_err = vfs_iocb_iter_write(file, kiocb, &iter[i]);
+		if (host_err < 0) {
Quoted 8 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1420

Quoted 5 lines
fs/nfsd/vfs.c +5 -0
+			/*
+			 * VFS will return -ENOTBLK if DIO WRITE fails to
+			 * invalidate the page cache. Retry using buffered IO.
+			 */
+			if (unlikely(host_err == -ENOTBLK)) {

The VFS certainly does not, and if it leaks out of a specific file system we need to fix that.

As I said above, fs/iomap/direct-io.c:__iomap_dio_rw() does.

Quoted 10 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1431

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+			} else if (unlikely(host_err == -EINVAL)) {
+				struct inode *inode = d_inode(fhp->fh_dentry);
+
+				pr_info_ratelimited("nfsd: Direct I/O alignment failure on %s/%ld\n",
+						    inode->i_sb->s_id, inode->i_ino);
+				host_err = -ESERVERFAULT;

-EINVAL can be lot more things than alignment failure. And more
importantly alignment failures should not happen with the proper checks in place.

Regardless, the most likely outcome from issing what should be aligned DIO only to get -EINVAL is exactly what is addressed with this _unlikely_ error handling: misaligned IO.. should happen but bugs happen (or optimization happens, e.g. in the future to fold alignment checking into sunrpc's WRITE bio_vec creation, and in turn bugs could happen).

Not submitting a new version of this patch until/unless there is clear feedback there is something in this v4 that really cannot stand.

Thanks for your review!

#8 lore raw
Quoted 10 lines · Mike Snitzer
Quoted 5 lines

Can you define synchronous better here? The term is unfortunately overloaded between synchronous syscalls vs aio/io_uring and O_(D)SYNC style I/O. As of now I don't understand which one you mean, especially with the DONTCACHE reference thrown in, but I guess I'll figure it out reading the patch.

It clearly talks about synchronous IO. DONTCACHE just happens to be the buffered IO that'll be used if supported.

I don't see anything that needs changing here.

Again, what synchronous? O_(D)SYNC, or !async?

Quoted 11 lines
Quoted 9 lines
Quoted 4 lines

If vfs_iocb_iter_write() returns -ENOTBLK, due to its inability to invalidate the page cache on behalf of the DIO WRITE, then nfsd_issue_write_dio() will fall back to using buffered IO.

Did you see -ENOTBLK leaking out of the file systems? Because at least for iomap it is supposed to be an indication that the file system ->write_iter handler needs to retry using buffered I/O and never leak to the caller.

fs/iomap/direct-io.c:__iomap_dio_rw() will return -ENOTBLK on its own.

Yes, and the callers in the file system methods are supposed to handle that and fall back to buffered I/O. Take a look at xfs_file_write_iter.

If this leaks out of ->write_iter we have a problem that needs fixing in the affected file system.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines

These changes served as the original starting point for the NFS client's misaligned O_DIRECT support that landed with commit c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE"). But NFSD's support is simpler because it currently doesn't use AIO completion.

Quoted 5 lines
Quoted 2 lines

I don't understand this paragraph. What does starting point mean here? How does it matter for the patch description?

This patch's NFSD changes were starting point for NFS commit c817248fc831

But that commit is already in? This sentence really confuses me.

Quoted 2 lines

Would prefer that cleanup, if done, to happen with an incremental follow-up patch.

Starting out with well structured code is generally a good idea :(

Quoted 8 lines
Quoted 7 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1291

Quoted 4 lines
fs/nfsd/vfs.c +3 -0
+static bool
+nfsd_iov_iter_aligned_bvec(const struct iov_iter *i, unsigned int addr_mask,
+                        unsigned int len_mask)

Wouldn't it make sense to track the alignment when building the bio_vec array instead of doing another walk here touching all cache lines?

Quoted 5 lines

Yes, that is the conventional wisdom that justified Keith removing iov_iter_aligned. But for NFSD's WRITE payload the bio_vec is built outside of the fs/nfsd/vfs.c code. Could be there is a natural way to clean this up (to make the sunrpc layer to conditionally care about alignment) but I didn't confront that yet.

Well, for the block code it's also build outside the layer consuming it. But Keith showed that you can easily communicate that information and avoid extra loops touching the cache lines.

Quoted 11 lines

Room for follow-on improvement to be sure.

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	if (unlikely(dio_blocksize > PAGE_SIZE))
+		return false;

Why does this matter? Can you add a comment explaining it?

I did/do have concerns that a bug in the storage driver could expose dio_offset_align that is far too large and so bounded dio_blocksize to a conservative size. What is a better constant to check?

I don't think there is a good upper limit, we not do supper LBA sizes larger than PAGE_SIZE, although I don't think there's any shipping devices that do that. But the most important thing is to always add a comment with the rationale for such non-obvious checks so that someone who has to lift it later can understand why it is done.

Quoted 9 lines
Quoted 7 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1404

Quoted 3 lines
fs/nfsd/vfs.c +3 -0
+	 * Also update @stable_how to avoid need for COMMIT.
+	 */
+	kiocb->ki_flags |= (IOCB_DSYNC | IOCB_SYNC);

What do you mean with completed before returning? I guess you mean writeback actually happening, right? Why do you need that, why do you also force it for the direct I/O?

The IO needs to have reached stable storage.

Please spell that out. Because that's different from how completed is generally used in file systems and storage protocols.

Quoted 6 lines
Quoted 3 lines

Also IOCB_SYNC is wrong here, as the only thing it does over IOCB_DSYNC is also forcing back of metadata not needed to find data (aka timestamps), which I can't see any need for here.

Well, we're eliding any followup SYNC from the NFS client by setting stable_how to NFS_FILE_SYNC, so I made sure to use SYNC:

Why do you care about timestamps reaching stable storage? Which is the only practical extra thing IOCB_SYNC will give you. If there is a good reason for that, add a comment because that's a bit unexpected (and in general IOCB_SYNC / O_SYNC is just a sign that someone does not know about SYNC vs DSYNC semantics).

Quoted 4 lines
Quoted 2 lines

The VFS certainly does not, and if it leaks out of a specific file system we need to fix that.

As I said above, fs/iomap/direct-io.c:__iomap_dio_rw() does.

As stated above that's an internal helper and the code is supposed to be handled by the file system and never leak out.

Quoted 3 lines

Regardless, the most likely outcome from issing what should be aligned DIO only to get -EINVAL is exactly what is addressed with this _unlikely_ error handling: misaligned IO..

No. There is no reason why this is any more likely than any of the other -EINVAL cases.

author maintainer
#9 lore raw
Quoted 24 lines · Christoph Hellwig
Quoted 10 lines · Mike Snitzer
Quoted 5 lines

Can you define synchronous better here? The term is unfortunately overloaded between synchronous syscalls vs aio/io_uring and O_(D)SYNC style I/O. As of now I don't understand which one you mean, especially with the DONTCACHE reference thrown in, but I guess I'll figure it out reading the patch.

It clearly talks about synchronous IO. DONTCACHE just happens to be the buffered IO that'll be used if supported.

I don't see anything that needs changing here.

Again, what synchronous? O_(D)SYNC, or !async?

Quoted 10 lines
Quoted 9 lines
Quoted 4 lines

If vfs_iocb_iter_write() returns -ENOTBLK, due to its inability to invalidate the page cache on behalf of the DIO WRITE, then nfsd_issue_write_dio() will fall back to using buffered IO.

Did you see -ENOTBLK leaking out of the file systems? Because at least for iomap it is supposed to be an indication that the file system ->write_iter handler needs to retry using buffered I/O and never leak to the caller.

Quoted 7 lines
Quoted 1 line

fs/iomap/direct-io.c:__iomap_dio_rw() will return -ENOTBLK on its own.

Yes, and the callers in the file system methods are supposed to handle that and fall back to buffered I/O. Take a look at xfs_file_write_iter.

If this leaks out of ->write_iter we have a problem that needs fixing in the affected file system.

What I'm hearing is that nfsd_vfs_write() should never see -ENOTBLK, so this check is essentially dead code.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines

These changes served as the original starting point for the NFS client's misaligned O_DIRECT support that landed with commit c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE"). But NFSD's support is simpler because it currently doesn't use AIO completion.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

I don't understand this paragraph. What does starting point mean here? How does it matter for the patch description?

This patch's NFSD changes were starting point for NFS commit c817248fc831

But that commit is already in? This sentence really confuses me.

I'm not convinced this paragraph in the patch description is needed. Can it be removed?

Quoted 2 lines
Quoted 2 lines

Would prefer that cleanup, if done, to happen with an incremental follow-up patch.

I'm not following. What clean-up? And why push it out to a later merge?

Quoted 1 line

Starting out with well structured code is generally a good idea :(

That is my preference as well, though I recognize that some subsystems do not mind merging incomplete features and moving forward from there.

Let's remember, upstream does not target specific kernel releases when developing new features. You might want your feature to hit the next LTS kernel, and even that is frowned upon. Let's focus on getting this as right as we know how to, rather than hitting a delivery date.

Quoted 13 lines
Quoted 7 lines
Quoted 6 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1291

Quoted 3 lines
fs/nfsd/vfs.c +3 -0
+static bool
+nfsd_iov_iter_aligned_bvec(const struct iov_iter *i, unsigned int addr_mask,
+                        unsigned int len_mask)

Wouldn't it make sense to track the alignment when building the bio_vec array instead of doing another walk here touching all cache lines?

Quoted 5 lines

Yes, that is the conventional wisdom that justified Keith removing iov_iter_aligned. But for NFSD's WRITE payload the bio_vec is built outside of the fs/nfsd/vfs.c code. Could be there is a natural way to clean this up (to make the sunrpc layer to conditionally care about alignment) but I didn't confront that yet.

Quoted 3 lines

Well, for the block code it's also build outside the layer consuming it. But Keith showed that you can easily communicate that information and avoid extra loops touching the cache lines.

Again, is there a reason to push this suggestion out to a later merge window? It seems reasonable to tackle it now.

(Perhaps to get us started, Christoph, do you know of specific code that shows how this code could be reorganized?)

Quoted 25 lines
Quoted 10 lines

Room for follow-on improvement to be sure.

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	if (unlikely(dio_blocksize > PAGE_SIZE))
+		return false;

Why does this matter? Can you add a comment explaining it?

I did/do have concerns that a bug in the storage driver could expose dio_offset_align that is far too large and so bounded dio_blocksize to a conservative size. What is a better constant to check?

I don't think there is a good upper limit, we not do supper LBA sizes larger than PAGE_SIZE, although I don't think there's any shipping devices that do that. But the most important thing is to always add a comment with the rationale for such non-obvious checks so that someone who has to lift it later can understand why it is done.

Quoted 8 lines
Quoted 7 lines

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1404

Quoted 3 lines
fs/nfsd/vfs.c +3 -0
+	 * Also update @stable_how to avoid need for COMMIT.
+	 */
+	kiocb->ki_flags |= (IOCB_DSYNC | IOCB_SYNC);

What do you mean with completed before returning? I guess you mean writeback actually happening, right? Why do you need that, why do you also force it for the direct I/O?

Quoted 4 lines
Quoted 1 line

The IO needs to have reached stable storage.

Please spell that out. Because that's different from how completed is generally used in file systems and storage protocols.

It's sensible to add a comment that cites the NFS protocol compliance mandate that states a data durability requirement.

Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

Also IOCB_SYNC is wrong here, as the only thing it does over IOCB_DSYNC is also forcing back of metadata not needed to find data (aka timestamps), which I can't see any need for here.

Quoted 8 lines
Quoted 2 lines

Well, we're eliding any followup SYNC from the NFS client by setting stable_how to NFS_FILE_SYNC, so I made sure to use SYNC:

Why do you care about timestamps reaching stable storage? Which is the only practical extra thing IOCB_SYNC will give you. If there is a good reason for that, add a comment because that's a bit unexpected (and in general IOCB_SYNC / O_SYNC is just a sign that someone does not know about SYNC vs DSYNC semantics).

If NFSD responds with NFS_FILE_SYNC here, then timestamps need to be persisted as well as data. As discussed in other threads, currently nfsd_vfs_write() seems to miss that mark.

So either: return NFS_DATA_SYNC (which means we've persisted only the data) or make this path persist the timestamps too.

I haven't seen any existing code that convinces me that setting both IOCB_SYNC and IOCB_DSYNC is necessary.

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

The VFS certainly does not, and if it leaks out of a specific file system we need to fix that.

As I said above, fs/iomap/direct-io.c:__iomap_dio_rw() does.

As stated above that's an internal helper and the code is supposed to be handled by the file system and never leak out.

Quoted 6 lines
Quoted 3 lines

Regardless, the most likely outcome from issing what should be aligned DIO only to get -EINVAL is exactly what is addressed with this _unlikely_ error handling: misaligned IO..

No. There is no reason why this is any more likely than any of the other -EINVAL cases.

Agreed, the likelihood of getting an -EINVAL here due to a block alignment issue is much lower than getting -EINVAL due to other problems.

Signature · 1 line
Chuck Lever
#10 lore raw
Quoted 2 lines · Chuck Lever

What I'm hearing is that nfsd_vfs_write() should never see -ENOTBLK, so this check is essentially dead code.

That's how iomap_dio_rw is designed to work. If it ever leaks out, that is a file system bug because no other caller of ->write_iter handles -ENOTBLK.

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
Quoted 5 lines

These changes served as the original starting point for the NFS client's misaligned O_DIRECT support that landed with commit c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE"). But NFSD's support is simpler because it currently doesn't use AIO completion.

I don't understand this paragraph. What does starting point mean here? How does it matter for the patch description?

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

This patch's NFSD changes were starting point for NFS commit c817248fc831

But that commit is already in? This sentence really confuses me.

I'm not convinced this paragraph in the patch description is needed. Can it be removed?

I guess Mike wants to say the two are based on the concept. But given that c817248fc831 is already in I'd expect to word it as "these changes are similar to the direct I/O writes implemented for the NFS client local I/O feature in commit c817248fc831 <insert subject line here>. But I'm not really sure there is much of a point in that.

Quoted 3 lines

(Perhaps to get us started, Christoph, do you know of specific code that shows how this code could be reorganized?)

This is what I had in mind:

https://lore.kernel.org/linux-block/[email protected]/T/#m3588b5824b7fa76f001d57e54664889b73471422

It records the alignment while building (or iterating for another reason), and then just checking a mask later. For nfsd this would be a bit different as you'd also record the boundaries of the aligned segments, but the idea is the same.

Quoted 9 lines

If NFSD responds with NFS_FILE_SYNC here, then timestamps need to be persisted as well as data. As discussed in other threads, currently nfsd_vfs_write() seems to miss that mark.

So either: return NFS_DATA_SYNC (which means we've persisted only the data) or make this path persist the timestamps too.

I haven't seen any existing code that convinces me that setting both IOCB_SYNC and IOCB_DSYNC is necessary.

iocb_flags is the canonical place. IOCB_DSYNC is the main path driving the syncing, and IOCB_SYNC drivers the additional syncing of the timestamps.

This is all a bit odd, because Linux historically only supposed the O_SYNC flag, but used it for O_DSYNC semantics. When I fixed that, I had to do a few non-standard things so that we could keep existing binaries work as expected despite renaming the flags. See commit 6b2f3d1f769b ("vfs: Implement proper O_SYNC semantics") for details.

maintainer
#11 lore raw
In reply to Christoph Hellwig
Quoted 87 lines · Christoph Hellwig
Quoted 7 lines · Chuck Lever

From: Mike Snitzer <[email protected]>

If NFSD_IO_DIRECT is used, split any misaligned WRITE into a start, middle and end as needed. The large middle extent is DIO-aligned and the start and/or end are misaligned. Synchronous buffered IO (with preference towards using DONTCACHE) is used for the misaligned extents and O_DIRECT is used for the middle DIO-aligned extent.

Can you define synchronous better here? The term is unfortunately overloaded between synchronous syscalls vs aio/io_uring and O_(D)SYNC style I/O. As of now I don't understand which one you mean, especially with the DONTCACHE reference thrown in, but I guess I'll figure it out reading the patch.

Quoted 3 lines

If vfs_iocb_iter_write() returns -ENOTBLK, due to its inability to invalidate the page cache on behalf of the DIO WRITE, then nfsd_issue_write_dio() will fall back to using buffered IO.

Did you see -ENOTBLK leaking out of the file systems? Because at least for iomap it is supposed to be an indication that the file system ->write_iter handler needs to retry using buffered I/O and never leak to the caller.

Quoted 5 lines

These changes served as the original starting point for the NFS client's misaligned O_DIRECT support that landed with commit c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE"). But NFSD's support is simpler because it currently doesn't use AIO completion.

I don't understand this paragraph. What does starting point mean here? How does it matter for the patch description?

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1261

Quoted 5 lines
fs/nfsd/vfs.c +5 -0
+struct nfsd_write_dio {
+     ssize_t start_len;      /* Length for misaligned first extent */
+     ssize_t middle_len;     /* Length for DIO-aligned middle extent */
+     ssize_t end_len;        /* Length for misaligned last extent */
+};

Looking at how the code is structured later on, it seems like it would work much better if each of these sections had it's own object with the len, iov_iter, flag if it's aligned, etc. Otherwise we have this structure and lots of arrays of three items passed around.

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1291

Quoted 3 lines
fs/nfsd/vfs.c +3 -0
+static bool
+nfsd_iov_iter_aligned_bvec(const struct iov_iter *i, unsigned int addr_mask,
+                        unsigned int len_mask)

Wouldn't it make sense to track the alignment when building the bio_vec array instead of doing another walk here touching all cache lines?

Review

Quoted 2 lines
(quoted diff) +2 -0
+	if (unlikely(dio_blocksize > PAGE_SIZE))
+		return false;

Why does this matter? Can you add a comment explaining it?

Quoted 5 lines
fs/nfsd/vfs.c +5 -0
+static int
+nfsd_buffered_write(struct svc_rqst *rqstp, struct file *file,
+		    unsigned int nvecs, unsigned long *cnt,
+		    struct kiocb *kiocb)
+{

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1374

Quoted 10 lines
fs/nfsd/vfs.c +10 -0
+	struct iov_iter iter;
+	int host_err;
+
+	iov_iter_bvec(&iter, ITER_SOURCE, rqstp->rq_bvec, nvecs, *cnt);
+	host_err = vfs_iocb_iter_write(file, kiocb, &iter);
+	if (host_err < 0)
+		return host_err;
+	*cnt = host_err;
+
+	return 0;

Nothing really buffered here per se, it's just a small wrapper around vfs_iocb_iter_write.

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1404

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+	/*
+	 * Any buffered IO issued here will be misaligned, use
+	 * sync IO to ensure it has completed before returning.
+	 * Also update @stable_how to avoid need for COMMIT.
+	 */
+	kiocb->ki_flags |= (IOCB_DSYNC | IOCB_SYNC);

What do you mean with completed before returning? I guess you mean writeback actually happening, right? Why do you need that, why do you also force it for the direct I/O?

Quoted 4 lines

Also IOCB_SYNC is wrong here, as the only thing it does over IOCB_DSYNC is also forcing back of metadata not needed to find data (aka timestamps), which I can't see any need for here.

Responding to a WRITE with NFS_FILE_SYNC flag set means that the data the client wrote is now on stable storage (and hence the client doesn't need to follow up with a COMMIT). This patch is using DIO for the aligned middle section but any unaligned ends use buffered I/O. If we want to return NFS_FILE_SYNC here then all of the data and metadata need to be on disk when the reply goes out.

Don't we need IOCB_SYNC here in this case? Otherwise, the server could crash while the metadata is still in memory. When it comes back up, the client could see stale timestamps. Maybe that's not fatal, but it seems pretty sketchy.

Quoted 30 lines
Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+	*stable_how = NFS_FILE_SYNC;
+
+	*cnt = 0;
+	for (int i = 0; i < n_iters; i++) {
+		if (iter_is_dio_aligned[i])
+			kiocb->ki_flags |= IOCB_DIRECT;

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1420

Quoted 10 lines
fs/nfsd/vfs.c +10 -0
+		else
+			kiocb->ki_flags &= ~IOCB_DIRECT;
+
+		host_err = vfs_iocb_iter_write(file, kiocb, &iter[i]);
+		if (host_err < 0) {
+			/*
+			 * VFS will return -ENOTBLK if DIO WRITE fails to
+			 * invalidate the page cache. Retry using buffered IO.
+			 */
+			if (unlikely(host_err == -ENOTBLK)) {

The VFS certainly does not, and if it leaks out of a specific file system we need to fix that.

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1431

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+			} else if (unlikely(host_err == -EINVAL)) {
+				struct inode *inode = d_inode(fhp->fh_dentry);
+
+				pr_info_ratelimited("nfsd: Direct I/O alignment failure on %s/%ld\n",
+						    inode->i_sb->s_id, inode->i_ino);
+				host_err = -ESERVERFAULT;

-EINVAL can be lot more things than alignment failure. And more
importantly alignment failures should not happen with the proper checks in place.

Signature · 1 line
Jeff Layton <[email protected]>
#12 lore raw
Quoted 11 lines · Jeff Layton

Responding to a WRITE with NFS_FILE_SYNC flag set means that the data the client wrote is now on stable storage (and hence the client doesn't need to follow up with a COMMIT). This patch is using DIO for the aligned middle section but any unaligned ends use buffered I/O. If we want to return NFS_FILE_SYNC here then all of the data and metadata need to be on disk when the reply goes out.

Don't we need IOCB_SYNC here in this case? Otherwise, the server could crash while the metadata is still in memory. When it comes back up, the client could see stale timestamps. Maybe that's not fatal, but it seems pretty sketchy.

Why do you care about the timestamps which in NFS AFAIK aren't tied to the data writeback in any way?

maintainer
#13 lore raw
Quoted 8 lines · Christoph Hellwig
Quoted 7 lines · Jeff Layton

Responding to a WRITE with NFS_FILE_SYNC flag set means that the data the client wrote is now on stable storage (and hence the client doesn't need to follow up with a COMMIT). This patch is using DIO for the aligned middle section but any unaligned ends use buffered I/O. If we want to return NFS_FILE_SYNC here then all of the data and metadata need to be on disk when the reply goes out.

Quoted 7 lines
Quoted 4 lines

Don't we need IOCB_SYNC here in this case? Otherwise, the server could crash while the metadata is still in memory. When it comes back up, the client could see stale timestamps. Maybe that's not fatal, but it seems pretty sketchy.

Why do you care about the timestamps which in NFS AFAIK aren't tied to the data writeback in any way?

Cache coherency. If the timestamps roll back, some clients may not notice that data has changed after a write.

It's not the end of the world -- non-evident timestamp changes were typical prior to the multigrain timestamp patches going in, but it's not ideal.

Signature · 1 line
Jeff Layton <[email protected]>
#14 lore raw
Quoted 6 lines · Jeff Layton

Cache coherency. If the timestamps roll back, some clients may not notice that data has changed after a write.

It's not the end of the world -- non-evident timestamp changes were typical prior to the multigrain timestamp patches going in, but it's not ideal.

Well, in that case nfsd_vfs_write needs to use IOCB_SYNC as well. And both that and this code need a big fat comment about it.

maintainer
#15 lore raw
Quoted 10 lines · Christoph Hellwig
Quoted 6 lines · Jeff Layton

Cache coherency. If the timestamps roll back, some clients may not notice that data has changed after a write.

It's not the end of the world -- non-evident timestamp changes were typical prior to the multigrain timestamp patches going in, but it's not ideal.

Well, in that case nfsd_vfs_write needs to use IOCB_SYNC as well. And both that and this code need a big fat comment about it.

That was my thinking too, but Chuck pointed out that nfsd has been violating this basically since forever. Given that, I think using IOCB_DSYNC is probably fine, and it would certainly be less costly.

Signature · 1 line
Jeff Layton <[email protected]>
author maintainer
#16 lore raw
In reply to Christoph Hellwig
Quoted 10 lines · Christoph Hellwig
Quoted 6 lines · Jeff Layton

Cache coherency. If the timestamps roll back, some clients may not notice that data has changed after a write.

It's not the end of the world -- non-evident timestamp changes were typical prior to the multigrain timestamp patches going in, but it's not ideal.

Well, in that case nfsd_vfs_write needs to use IOCB_SYNC as well. And both that and this code need a big fat comment about it.

NFSD's historical behavior doesn't match the spec, so yes, I'd like a comment or two that calls this out if we agree that's something we do not want to change.

Otherwise, I'm not opposed to bringing NFSD into compliance. But that might need patches that can be backported. Not technically difficult, but some planning is necessary.

Signature · 1 line
Chuck Lever
#17 lore raw
Quoted 11 lines · Chuck Lever
Quoted 10 lines · Christoph Hellwig
Quoted 6 lines · Jeff Layton

Cache coherency. If the timestamps roll back, some clients may not notice that data has changed after a write.

It's not the end of the world -- non-evident timestamp changes were typical prior to the multigrain timestamp patches going in, but it's not ideal.

Well, in that case nfsd_vfs_write needs to use IOCB_SYNC as well. And both that and this code need a big fat comment about it.

Quoted 7 lines

NFSD's historical behavior doesn't match the spec, so yes, I'd like a comment or two that calls this out if we agree that's something we do not want to change.

Otherwise, I'm not opposed to bringing NFSD into compliance. But that might need patches that can be backported. Not technically difficult, but some planning is necessary.

It's basically just adding IOCB_SYNC in nfsd_vfs_write. If you do that before this series it should be fairly easily backportable.

author maintainer
#18 lore raw
In reply to Christoph Hellwig
Quoted 9 lines · Christoph Hellwig

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1404

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+	/*
+	 * Any buffered IO issued here will be misaligned, use
+	 * sync IO to ensure it has completed before returning.
+	 * Also update @stable_how to avoid need for COMMIT.
+	 */
+	kiocb->ki_flags |= (IOCB_DSYNC | IOCB_SYNC);

What do you mean with completed before returning? I guess you mean writeback actually happening, right? Why do you need that, why do you also force it for the direct I/O?

This is the only comment where I'm not clear on what corrective action to take.

I think IOCB_SYNC would be needed with O_DIRECT to force timestamp updates. Otherwise, IOCB_SYNC is relevant only when the function is forced to fall back to some form of write through the page cache.

Is that correct?

Signature · 1 line
Chuck Lever
#19 lore raw
Quoted 11 lines · Chuck Lever
Quoted 9 lines · Christoph Hellwig

fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1404

Quoted 6 lines
fs/nfsd/vfs.c +6 -0
+	/*
+	 * Any buffered IO issued here will be misaligned, use
+	 * sync IO to ensure it has completed before returning.
+	 * Also update @stable_how to avoid need for COMMIT.
+	 */
+	kiocb->ki_flags |= (IOCB_DSYNC | IOCB_SYNC);

What do you mean with completed before returning? I guess you mean writeback actually happening, right? Why do you need that, why do you also force it for the direct I/O?

Quoted 2 lines

This is the only comment where I'm not clear on what corrective action to take.

I have multiple issues with the comment. One is that it really should talk about commiting data to stable storage instead of completing, as that seems to be the main point (and also what IOCB_DSYNC / IOCB_SYNC) does. If we stick to using IOCB_SYNC it should also mention metadata or more specifically timestamps.

But I also really don't get the "Any buffered IO issued here will be misaligned, ..." part. What does buffered I/O and/or misalignment have to do with not wanting to do a COMMIT?

Quoted 3 lines

I think IOCB_SYNC would be needed with O_DIRECT to force timestamp updates. Otherwise, IOCB_SYNC is relevant only when the function is forced to fall back to some form of write through the page cache.

Well, IOCB_SYNC is only needed to commit timestamps. O_DSYNC is always required if you want to commit to stable storage. As said above I don't really understand from the patch why we want to do that, but IFF we want to do that, we need IOCB_DSYNC bother for direct and buffered I/O.

author maintainer
#20 lore raw

PATCH v4 3/3 svcrdma: Mark Read chunks

In reply to Chuck Lever

From: Chuck Lever <[email protected]>

The upper layer may want to know when the receive buffer's .pages array is guaranteed to contain only an opaque payload. This permits the upper layer to optimize its buffer handling.

NB: Since svc_rdma_recvfrom.c is under net/, we use the comment style that is preferred in the networking layer.

---
 net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 5 +++++
 1 file changed, 5 insertions(+)
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +5 -0
index e7e4a39ca6c6..b1a0c72f73de 100644
@@ -815,6 +815,11 @@ static void svc_rdma_read_complete_one(struct svc_rqst *rqstp,
 	buf->page_len = length;
 	buf->len += length;
 	buf->buflen += length;
+
+	/* Transport guarantees that only the chunk payload
+	 * appears in buf->pages.
+	 */
+	buf->flags |= XDRBUF_READ;
 }
 
 /* Finish constructing the RPC Call message in rqstp::rq_arg.
Signature · 1 line
2.51.0