NFSD direct I/O read
Subsystems (3)
Chuck Lever
maintainercover NFSD direct I/O read
From: Chuck Lever <[email protected]>
This series replaces patches 1, 2, and 5 in Mike's v9 series:
https://lore.kernel.org/linux-nfs/[email protected]/T/#t
Changes from Mike's v9:
* The LOC introduced by the feature has been reduced considerably.
* A new trace point in nfsd_file_getattr reports each file's dio alignment parameters when it is opened.
* The direct I/O path has been taken out-of-line so that it may continue to be modified and optimized without perturbing the more commonly used I/O paths.
* When an exported file system does not implement direct I/O, more commonly used modes are employed instead to avoid returning EOPNOTSUPP unexpectedly.
* When NFSD_IO_DIRECT is selected, NFS READs of all sizes use direct I/O to provide better experimental data about small I/O workloads.
I haven't found any issues with NFSv3, NFSv4.0, and NFSv4.1 tested on TCP and RDMA while /sys/kernel/debug/nfsd/io_cache_read is set to 2. Trace points confirm that NFSD is using direct I/O.
The goal is to get the experimental read-side direct I/O implementation merged sooner, as the write side has a few gray areas that still need discussion and resolution.
Chuck Lever (1):
NFSD: Implement NFSD_IO_DIRECT for NFS READ
Mike Snitzer (2):
NFSD: filecache: add STATX_DIOALIGN and STATX_DIO_READ_ALIGN support
NFSD: pass nfsd_file to nfsd_iter_read()
fs/nfsd/debugfs.c | 2 + fs/nfsd/filecache.c | 34 +++++++++++++++++ fs/nfsd/filecache.h | 4 ++ fs/nfsd/nfs4xdr.c | 8 ++-- fs/nfsd/nfsd.h | 1 + fs/nfsd/nfsfh.c | 4 ++ fs/nfsd/trace.h | 28 ++++++++++++++ fs/nfsd/vfs.c | 85 +++++++++++++++++++++++++++++++++++++++-- fs/nfsd/vfs.h | 2 +- include/trace/misc/fs.h | 22 +++++++++++ 10 files changed, 182 insertions(+), 8 deletions(-)
Signature · 1 line
2.50.0
Chuck Lever
maintainerPATCH 1/3 NFSD: filecache: add STATX_DIOALIGN and STATX_DIO_READ_ALIGN support
From: Mike Snitzer <[email protected]>
Use STATX_DIOALIGN and STATX_DIO_READ_ALIGN to get DIO alignment attributes from the underlying filesystem and store them in the associated nfsd_file. This is done when the nfsd_file is first opened for each regular file.
--- fs/nfsd/filecache.c | 34 ++++++++++++++++++++++++++++++++++ fs/nfsd/filecache.h | 4 ++++ fs/nfsd/nfsfh.c | 4 ++++ fs/nfsd/trace.h | 27 +++++++++++++++++++++++++++ include/trace/misc/fs.h | 22 ++++++++++++++++++++++ 5 files changed, 91 insertions(+)
fs/nfsd/filecache.c +34 -0
@@ -231,6 +231,9 @@ nfsd_file_alloc(struct net *net, struct inode *inode, unsigned char need,refcount_set (&nf->nf_ref,1 ); nf->nf_may = need; nf->nf_mark =NULL ; + nf->nf_dio_mem_align =0 ; + nf->nf_dio_offset_align =0 ; + nf->nf_dio_read_offset_align =0 ;return nf; }
@@ -1048,6 +1051,35 @@ nfsd_file_is_cached(struct inode *inode)return ret; } +static __be32 +nfsd_file_getattr (const struct svc_fh *fhp,struct nfsd_file *nf) +{ +struct inode *inode =file_inode (nf->nf_file); +struct kstat stat; +__be32 status; + +/* Currently only need to get DIO alignment info for regular files */ +if (!S_ISREG (inode->i_mode)) +return nfs_ok; + + status =fh_getattr (fhp, &stat); +if (status != nfs_ok) +return status; + +trace_nfsd_file_getattr (inode, &stat); + +if (stat.result_mask &STATX_DIOALIGN ) { + nf->nf_dio_mem_align = stat.dio_mem_align; + nf->nf_dio_offset_align = stat.dio_offset_align; + } +if (stat.result_mask &STATX_DIO_READ_ALIGN ) + nf->nf_dio_read_offset_align = stat.dio_read_offset_align; +else + nf->nf_dio_read_offset_align = nf->nf_dio_offset_align; + +return nfs_ok; +} + static __be32 nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net, struct svc_cred *cred,
@@ -1166,6 +1198,8 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net, } status =nfserrno (ret);trace_nfsd_file_open (nf, status); +if (status == nfs_ok) + status =nfsd_file_getattr (fhp, nf); } }else status = nfserr_jukebox;
fs/nfsd/filecache.h +4 -0
@@ -54,6 +54,10 @@ struct nfsd_file {struct list_head nf_gc;struct rcu_head nf_rcu;ktime_t nf_birthtime; + +u32 nf_dio_mem_align; +u32 nf_dio_offset_align; +u32 nf_dio_read_offset_align; };int nfsd_file_cache_init (void );
fs/nfsd/nfsfh.c +4 -0
@@ -696,8 +696,12 @@ __be32 fh_getattr(const struct svc_fh *fhp, struct kstat *stat) .mnt = fhp->fh_export->ex_path.mnt, .dentry = fhp->fh_dentry, }; +struct inode *inode =d_inode (p.dentry);u32 request_mask =STATX_BASIC_STATS ; +if (S_ISREG (inode->i_mode)) + request_mask |= (STATX_DIOALIGN |STATX_DIO_READ_ALIGN ); +if (fhp->fh_maxsize ==NFS4_FHSIZE ) request_mask |= (STATX_BTIME |STATX_CHANGE_COOKIE );
fs/nfsd/trace.h +27 -0
@@ -1133,6 +1133,33 @@ TRACE_EVENT(nfsd_file_alloc, ) ); +TRACE_EVENT (nfsd_file_getattr, +TP_PROTO ( + const struct inode *inode, + const struct kstat *stat + ), +TP_ARGS (inode, stat), +TP_STRUCT__entry ( + __field(const void *, inode) + __field(unsigned long, mask) + __field(u32, mem_align) + __field(u32, offset_align) +__field (u32, read_offset_align) + ), +TP_fast_assign ( + __entry->inode = inode; + __entry->mask = stat->result_mask; + __entry->mem_align = stat->dio_mem_align; + __entry->offset_align = stat->dio_offset_align; + __entry->read_offset_align = stat->dio_read_offset_align; + ), +TP_printk ("inode=%p flags=%s mem_align=%u offset_align=%u read_offset_align=%u" , + __entry->inode,show_statx_mask (__entry->mask), + __entry->mem_align, __entry->offset_align, + __entry->read_offset_align + ) +); + TRACE_EVENT(nfsd_file_acquire, TP_PROTO( const struct svc_rqst *rqstp,
include/trace/misc/fs.h +22 -0
@@ -141,3 +141,25 @@ { ATTR_TIMES_SET, "TIMES_SET" }, \ { ATTR_TOUCH, "TOUCH"}, \ { ATTR_DELEG, "DELEG"}) + +#define show_statx_mask (flags) \ + __print_flags(flags, "|", \ + { STATX_TYPE, "TYPE" }, \ + { STATX_MODE, "MODE" }, \ + { STATX_NLINK, "NLINK" }, \ + { STATX_UID, "UID" }, \ + { STATX_GID, "GID" }, \ + { STATX_ATIME, "ATIME" }, \ + { STATX_MTIME, "MTIME" }, \ + { STATX_CTIME, "CTIME" }, \ + { STATX_INO, "INO" }, \ + { STATX_SIZE, "SIZE" }, \ + { STATX_BLOCKS, "BLOCKS" }, \ + { STATX_BASIC_STATS, "BASIC_STATS" }, \ + { STATX_BTIME, "BTIME" }, \ + { STATX_MNT_ID, "MNT_ID" }, \ + { STATX_DIOALIGN, "DIOALIGN" }, \ + { STATX_MNT_ID_UNIQUE, "MNT_ID_UNIQUE" }, \ + { STATX_SUBVOL, "SUBVOL" }, \ + { STATX_WRITE_ATOMIC, "WRITE_ATOMIC" }, \ + { STATX_DIO_READ_ALIGN, "DIO_READ_ALIGN" })
Signature · 1 line
2.50.0
NeilBrown
reviewerRe: [PATCH v1 1/3] NFSD: filecache: add STATX_DIOALIGN and STATX_DIO_READ_ALIGN support
fs/nfsd/filecache.c nfsd_file_is_cached(struct inode *inode) · line 1055
Quoted 2 lines · Chuck Lever
fs/nfsd/filecache.c +2
-0+static __be32 +nfsd_file_getattr (const struct svc_fh *fhp,struct nfsd_file *nf)
<bikeshed>
should this function have a name which reflects the fact that it is getting the dio attrs, not all attrs?
e.h. nfsd_file_get_dio_attr()
....
fs/nfsd/filecache.c nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net, · line 1203
Quoted 7 lines
fs/nfsd/filecache.c +2
-0@@ -1166,6 +1198,8 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net, } status =nfserrno (ret);trace_nfsd_file_open (nf, status); +if (status == nfs_ok) + status =nfsd_file_getattr (fhp, nf); }
I ask because this stanza looks strange. "Why are the file attrs being got here?" I ask. Oh, it is really getting the dio attrs - that makes sense.
</bikeshed>
Thanks,
NeilBrown
Quoted 106 lines
fs/nfsd/filecache.c +0
-0}else status = nfserr_jukebox;fs/nfsd/filecache.h +4
-0@@ -54,6 +54,10 @@ struct nfsd_file {struct list_head nf_gc;struct rcu_head nf_rcu;ktime_t nf_birthtime; + +u32 nf_dio_mem_align; +u32 nf_dio_offset_align; +u32 nf_dio_read_offset_align; };int nfsd_file_cache_init (void );fs/nfsd/nfsfh.c +4
-0@@ -696,8 +696,12 @@ __be32 fh_getattr(const struct svc_fh *fhp, struct kstat *stat) .mnt = fhp->fh_export->ex_path.mnt, .dentry = fhp->fh_dentry, }; +struct inode *inode =d_inode (p.dentry);u32 request_mask =STATX_BASIC_STATS ; +if (S_ISREG (inode->i_mode)) + request_mask |= (STATX_DIOALIGN |STATX_DIO_READ_ALIGN ); +if (fhp->fh_maxsize ==NFS4_FHSIZE ) request_mask |= (STATX_BTIME |STATX_CHANGE_COOKIE );fs/nfsd/trace.h +27
-0@@ -1133,6 +1133,33 @@ TRACE_EVENT(nfsd_file_alloc, ) ); +TRACE_EVENT (nfsd_file_getattr, +TP_PROTO ( + const struct inode *inode, + const struct kstat *stat + ), +TP_ARGS (inode, stat), +TP_STRUCT__entry ( + __field(const void *, inode) + __field(unsigned long, mask) + __field(u32, mem_align) + __field(u32, offset_align) +__field (u32, read_offset_align) + ), +TP_fast_assign ( + __entry->inode = inode; + __entry->mask = stat->result_mask; + __entry->mem_align = stat->dio_mem_align; + __entry->offset_align = stat->dio_offset_align; + __entry->read_offset_align = stat->dio_read_offset_align; + ), +TP_printk ("inode=%p flags=%s mem_align=%u offset_align=%u read_offset_align=%u" , + __entry->inode,show_statx_mask (__entry->mask), + __entry->mem_align, __entry->offset_align, + __entry->read_offset_align + ) +); + TRACE_EVENT(nfsd_file_acquire, TP_PROTO( const struct svc_rqst *rqstp,include/trace/misc/fs.h +22
-0@@ -141,3 +141,25 @@ { ATTR_TIMES_SET, "TIMES_SET" }, \ { ATTR_TOUCH, "TOUCH"}, \ { ATTR_DELEG, "DELEG"}) + +#define show_statx_mask (flags) \ + __print_flags(flags, "|", \ + { STATX_TYPE, "TYPE" }, \ + { STATX_MODE, "MODE" }, \ + { STATX_NLINK, "NLINK" }, \ + { STATX_UID, "UID" }, \ + { STATX_GID, "GID" }, \ + { STATX_ATIME, "ATIME" }, \ + { STATX_MTIME, "MTIME" }, \ + { STATX_CTIME, "CTIME" }, \ + { STATX_INO, "INO" }, \ + { STATX_SIZE, "SIZE" }, \ + { STATX_BLOCKS, "BLOCKS" }, \ + { STATX_BASIC_STATS, "BASIC_STATS" }, \ + { STATX_BTIME, "BTIME" }, \ + { STATX_MNT_ID, "MNT_ID" }, \ + { STATX_DIOALIGN, "DIOALIGN" }, \ + { STATX_MNT_ID_UNIQUE, "MNT_ID_UNIQUE" }, \ + { STATX_SUBVOL, "SUBVOL" }, \ + { STATX_WRITE_ATOMIC, "WRITE_ATOMIC" }, \ + { STATX_DIO_READ_ALIGN, "DIO_READ_ALIGN" })Signature · 1 line
2.50.0
Chuck Lever
maintainerPATCH 2/3 NFSD: pass nfsd_file to nfsd_iter_read()
In reply to Chuck LeverFrom: Mike Snitzer <[email protected]>
Prepares for nfsd_iter_read() to use DIO alignment stored in nfsd_file.
--- fs/nfsd/nfs4xdr.c | 8 ++++---- fs/nfsd/vfs.c | 7 ++++--- fs/nfsd/vfs.h | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-)
fs/nfsd/nfs4xdr.c +4 -4
@@ -4465,7 +4465,7 @@ static __be32 nfsd4_encode_splice_read(static __be32 nfsd4_encode_readv (struct nfsd4_compoundres *resp,struct nfsd4_read *read, -struct file *file,unsigned long maxcount) +unsigned long maxcount) {struct xdr_stream *xdr = resp->xdr;unsigned int base = xdr->buf->page_len & ~PAGE_MASK ;
@@ -4476,7 +4476,7 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,if (xdr_reserve_space_vec (xdr, maxcount) <0 )return nfserr_resource; - nfserr =nfsd_iter_read (resp->rqstp, read->rd_fhp, file, + nfserr =nfsd_iter_read (resp->rqstp, read->rd_fhp, read->rd_nf, read->rd_offset, &maxcount, base, &read->rd_eof); read->rd_length = maxcount;
@@ -4523,7 +4523,7 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,if (file->f_op->splice_read && splice_ok) nfserr =nfsd4_encode_splice_read (resp, read, file, maxcount);else - nfserr =nfsd4_encode_readv (resp, read, file, maxcount); + nfserr =nfsd4_encode_readv (resp, read, maxcount);if (nfserr) {xdr_truncate_encode (xdr, eof_offset);return nfserr;
@@ -5419,7 +5419,7 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,if (file->f_op->splice_read && splice_ok) nfserr =nfsd4_encode_splice_read (resp, read, file, maxcount);else - nfserr =nfsd4_encode_readv (resp, read, file, maxcount); + nfserr =nfsd4_encode_readv (resp, read, maxcount);if (nfserr)return nfserr;
fs/nfsd/vfs.c +4 -3
@@ -1078,7 +1078,7 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,* nfsd_iter_read - Perform a VFS read using an iterator * @rqstp: RPC transaction context * @fhp: file handle of file to be read -* @file: opened struct file of file to be read +* @nf: opened struct nfsd_file of file to be read * @offset: starting byte offset * @count: IN: requested number of bytes; OUT: number of bytes read * @base: offset in first page of read buffer
@@ -1091,9 +1091,10 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,* returned. */ __be32 nfsd_iter_read (struct svc_rqst *rqstp,struct svc_fh *fhp, -struct file *file,loff_t offset,unsigned long *count, +struct nfsd_file *nf,loff_t offset,unsigned long *count,unsigned int base,u32 *eof) { +struct file *file = nf->nf_file;unsigned long v, total;struct iov_iter iter;struct kiocb kiocb;
@@ -1334,7 +1335,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,if (file->f_op->splice_read &&nfsd_read_splice_ok (rqstp)) err =nfsd_splice_read (rqstp, fhp, file, offset, count, eof);else - err =nfsd_iter_read (rqstp, fhp, file, offset, count,0 , eof); + err =nfsd_iter_read (rqstp, fhp, nf, offset, count,0 , eof);nfsd_file_put (nf);trace_nfsd_read_done (rqstp, fhp, offset, *count);
fs/nfsd/vfs.h +1 -1
@@ -121,7 +121,7 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,unsigned long *count, u32 *eof);__be32 nfsd_iter_read (struct svc_rqst *rqstp,struct svc_fh *fhp, -struct file *file,loff_t offset, +struct nfsd_file *nf,loff_t offset,unsigned long *count,unsigned int base,u32 *eof);bool nfsd_read_splice_ok (struct svc_rqst *rqstp);
Signature · 1 line
2.50.0
Re: [PATCH v1 2/3] NFSD: pass nfsd_file to nfsd_iter_read()
Quoted 3 lines · Chuck Lever
From: Mike Snitzer <[email protected]>
Prepares for
nfsd_iter_read()to use DIO alignment stored innfsd_file.
This is particularly unhelpful commit description. It would be perfectly good as an introduce to a description which actually gives some details, which could then be compared to the code.
Prepare for nfsd_iter_read() to use the DIO alignment stored in nfsd_file by passing the nfsd_file to nfsd_iter_read() rather than just the file which is associaed with the nfsd_file.
This means nfsd4_encode_readv() now also needs the nfsd_file rather than the file. Instead of changing the file arg to be the nfsd_file, we discard the file arg as the nfsd_file (and indeed the file) is already available via the "read" argument.
Now when I read the patch it makes perfect sense.
Thanks,
NeilBrown
Quoted 102 lines
Signed-off-by Mike Snitzer <[email protected]>Reviewed-by Jeff Layton <[email protected]>Signed-off-by Chuck Lever <[email protected]>--- fs/nfsd/nfs4xdr.c | 8 ++++---- fs/nfsd/vfs.c | 7 ++++--- fs/nfsd/vfs.h | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-)fs/nfsd/nfs4xdr.c +4
-4@@ -4465,7 +4465,7 @@ static __be32 nfsd4_encode_splice_read(static __be32 nfsd4_encode_readv (struct nfsd4_compoundres *resp,struct nfsd4_read *read, -struct file *file,unsigned long maxcount) +unsigned long maxcount) {struct xdr_stream *xdr = resp->xdr;unsigned int base = xdr->buf->page_len & ~PAGE_MASK ;@@ -4476,7 +4476,7 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,if (xdr_reserve_space_vec (xdr, maxcount) <0 )return nfserr_resource; - nfserr =nfsd_iter_read (resp->rqstp, read->rd_fhp, file, + nfserr =nfsd_iter_read (resp->rqstp, read->rd_fhp, read->rd_nf, read->rd_offset, &maxcount, base, &read->rd_eof); read->rd_length = maxcount;@@ -4523,7 +4523,7 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,if (file->f_op->splice_read && splice_ok) nfserr =nfsd4_encode_splice_read (resp, read, file, maxcount);else - nfserr =nfsd4_encode_readv (resp, read, file, maxcount); + nfserr =nfsd4_encode_readv (resp, read, maxcount);if (nfserr) {xdr_truncate_encode (xdr, eof_offset);return nfserr;@@ -5419,7 +5419,7 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,if (file->f_op->splice_read && splice_ok) nfserr =nfsd4_encode_splice_read (resp, read, file, maxcount);else - nfserr =nfsd4_encode_readv (resp, read, file, maxcount); + nfserr =nfsd4_encode_readv (resp, read, maxcount);if (nfserr)return nfserr;fs/nfsd/vfs.c +4
-3@@ -1078,7 +1078,7 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,* nfsd_iter_read - Perform a VFS read using an iterator * @rqstp: RPC transaction context * @fhp: file handle of file to be read -* @file: opened struct file of file to be read +* @nf: opened struct nfsd_file of file to be read * @offset: starting byte offset * @count: IN: requested number of bytes; OUT: number of bytes read * @base: offset in first page of read buffer @@ -1091,9 +1091,10 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,* returned. */ __be32 nfsd_iter_read (struct svc_rqst *rqstp,struct svc_fh *fhp, -struct file *file,loff_t offset,unsigned long *count, +struct nfsd_file *nf,loff_t offset,unsigned long *count,unsigned int base,u32 *eof) { +struct file *file = nf->nf_file;unsigned long v, total;struct iov_iter iter;struct kiocb kiocb;@@ -1334,7 +1335,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,if (file->f_op->splice_read &&nfsd_read_splice_ok (rqstp)) err =nfsd_splice_read (rqstp, fhp, file, offset, count, eof);else - err =nfsd_iter_read (rqstp, fhp, file, offset, count,0 , eof); + err =nfsd_iter_read (rqstp, fhp, nf, offset, count,0 , eof);nfsd_file_put (nf);trace_nfsd_read_done (rqstp, fhp, offset, *count);fs/nfsd/vfs.h +1
-1@@ -121,7 +121,7 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,unsigned long *count, u32 *eof);__be32 nfsd_iter_read (struct svc_rqst *rqstp,struct svc_fh *fhp, -struct file *file,loff_t offset, +struct nfsd_file *nf,loff_t offset,unsigned long *count,unsigned int base,u32 *eof);bool nfsd_read_splice_ok (struct svc_rqst *rqstp);Signature · 1 line
2.50.0
Chuck Lever
maintainerPATCH 3/3 NFSD: Implement NFSD_IO_DIRECT for NFS READ
In reply to Chuck LeverFrom: Chuck Lever <[email protected]>
Add an experimental option that forces NFS READ operations to use direct I/O instead of reading through the NFS server's page cache.
There are already other layers of caching:
- The page cache on NFS clients
- The block device underlying the exported file system
The server's page cache, in many cases, is unlikely to provide additional benefit. Some benchmarks have demonstrated that the server's page cache is actively detrimental for workloads whose working set is larger than the server's available physical memory.
For instance, on small NFS servers, cached NFS file content can squeeze out local memory consumers. For large sequential workloads, an enormous amount of data flows into and out of the page cache and is consumed by NFS clients exactly once -- caching that data is expensive to do and totally valueless.
For now this is a hidden option that can be enabled on test systems for benchmarking. In the longer term, this option might be enabled persistently or per-export. When the exported file system does not support direct I/O, NFSD falls back to using either DONTCACHE or buffered I/O to fulfill NFS READ requests.
--- fs/nfsd/debugfs.c | 2 ++ fs/nfsd/nfsd.h | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+)
fs/nfsd/debugfs.c +2 -0
@@ -44,6 +44,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(nfsd_dsr_fops, nfsd_dsr_get, nfsd_dsr_set, "%llu\n");* Contents: * %0: NFS READ will use buffered IO * %1: NFS READ will use dontcache (buffered IO w/ dropbehind) +* %2: NFS READ will use direct IO * * This setting takes immediate effect for all NFS versions, * all exports, and in all NFSD net namespaces.
@@ -64,6 +65,7 @@ static int nfsd_io_cache_read_set(void *data, u64 val) nfsd_io_cache_read =NFSD_IO_BUFFERED ;break ;case NFSD_IO_DONTCACHE : +case NFSD_IO_DIRECT :/* * Must disable splice_read when enabling * NFSD_IO_DONTCACHE.
fs/nfsd/nfsd.h +1 -0
@@ -157,6 +157,7 @@ enum {/* Any new NFSD_IO enum value must be added at the end */ NFSD_IO_BUFFERED ,NFSD_IO_DONTCACHE , + NFSD_IO_DIRECT, };extern u64 nfsd_io_cache_read__read_mostly ;
fs/nfsd/trace.h +1 -0
@@ -464,6 +464,7 @@ DEFINE_EVENT(nfsd_io_class, nfsd_##name, \DEFINE_NFSD_IO_EVENT (read_start);DEFINE_NFSD_IO_EVENT (read_splice);DEFINE_NFSD_IO_EVENT (read_vector); +DEFINE_NFSD_IO_EVENT (read_direct);DEFINE_NFSD_IO_EVENT (read_io_done);DEFINE_NFSD_IO_EVENT (read_done);DEFINE_NFSD_IO_EVENT (write_start);
fs/nfsd/vfs.c +78 -0
90 lines not on this page · raw message
Signature · 1 line
2.50.0
Re: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
Quoted 20 lines · Chuck Lever
From: Chuck Lever <[email protected]>
Add an experimental option that forces NFS READ operations to use direct I/O instead of reading through the NFS server's page cache.
There are already other layers of caching:
- The page cache on NFS clients
- The block device underlying the exported file systemThe server's page cache, in many cases, is unlikely to provide additional benefit. Some benchmarks have demonstrated that the server's page cache is actively detrimental for workloads whose working set is larger than the server's available physical memory.
For instance, on small NFS servers, cached NFS file content can squeeze out local memory consumers. For large sequential workloads, an enormous amount of data flows into and out of the page cache and is consumed by NFS clients exactly once -- caching that data is expensive to do and totally valueless.
Quoted 8 lines
For now this is a hidden option that can be enabled on test systems for benchmarking. In the longer term, this option might be enabled persistently or per-export. When the exported file system does not support direct I/O, NFSD falls back to using either DONTCACHE or buffered I/O to fulfill NFS READ requests.
Suggested-by Mike Snitzer <[email protected]>Signed-off-by Chuck Lever <[email protected]>
Re: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
In reply to Chuck LeverQuoted 90 lines · Chuck Lever
From: Chuck Lever <[email protected]>
Add an experimental option that forces NFS READ operations to use direct I/O instead of reading through the NFS server's page cache.
There are already other layers of caching:
- The page cache on NFS clients
- The block device underlying the exported file systemThe server's page cache, in many cases, is unlikely to provide additional benefit. Some benchmarks have demonstrated that the server's page cache is actively detrimental for workloads whose working set is larger than the server's available physical memory.
For instance, on small NFS servers, cached NFS file content can squeeze out local memory consumers. For large sequential workloads, an enormous amount of data flows into and out of the page cache and is consumed by NFS clients exactly once -- caching that data is expensive to do and totally valueless.
For now this is a hidden option that can be enabled on test systems for benchmarking. In the longer term, this option might be enabled persistently or per-export. When the exported file system does not support direct I/O, NFSD falls back to using either DONTCACHE or buffered I/O to fulfill NFS READ requests.
Suggested-by Mike Snitzer <[email protected]>Signed-off-by Chuck Lever <[email protected]>--- fs/nfsd/debugfs.c | 2 ++ fs/nfsd/nfsd.h | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+)fs/nfsd/debugfs.c +2
-0@@ -44,6 +44,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(nfsd_dsr_fops, nfsd_dsr_get, nfsd_dsr_set, "%llu\n");* Contents: * %0: NFS READ will use buffered IO * %1: NFS READ will use dontcache (buffered IO w/ dropbehind) +* %2: NFS READ will use direct IO * * This setting takes immediate effect for all NFS versions, * all exports, and in all NFSD net namespaces. @@ -64,6 +65,7 @@ static int nfsd_io_cache_read_set(void *data, u64 val) nfsd_io_cache_read =NFSD_IO_BUFFERED ;break ;case NFSD_IO_DONTCACHE : +case NFSD_IO_DIRECT :/* * Must disable splice_read when enabling * NFSD_IO_DONTCACHE. fs/nfsd/nfsd.h +1
-0@@ -157,6 +157,7 @@ enum {/* Any new NFSD_IO enum value must be added at the end */ NFSD_IO_BUFFERED ,NFSD_IO_DONTCACHE , + NFSD_IO_DIRECT, };extern u64 nfsd_io_cache_read__read_mostly ;fs/nfsd/trace.h +1
-0@@ -464,6 +464,7 @@ DEFINE_EVENT(nfsd_io_class, nfsd_##name, \DEFINE_NFSD_IO_EVENT (read_start);DEFINE_NFSD_IO_EVENT (read_splice);DEFINE_NFSD_IO_EVENT (read_vector); +DEFINE_NFSD_IO_EVENT (read_direct);DEFINE_NFSD_IO_EVENT (read_io_done);DEFINE_NFSD_IO_EVENT (read_done);DEFINE_NFSD_IO_EVENT (write_start);fs/nfsd/vfs.c +3
-0@@ -1074,6 +1074,79 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,return nfsd_finish_read (rqstp, fhp, file, offset, count, eof, host_err); } +/* +* The byte range of the client's READ request is expanded on both +* ends until it meets the underlying file system's direct I/O
fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1089
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+* alignment requirements. After the internal read is complete, the +* byte range of the NFS READ payload is reduced to the byte range +* that was originally requested. +* +* Note that a direct read can be done only when the xdr_buf +* containing the NFS READ reply does not already have contents in +* its .pages array. This is due to potentially restrictive +* alignment requirements on the read buffer. When .page_len and +* @base are zero, the .pages array is guaranteed to be page- +* aligned.
Where do we test that this condition is met?
I can see that nfsd_direct_read() is only called if "base" is zero, but that means the current contents of the .pages array are page-aligned, not that there are now.
Quoted 22 lines
fs/nfsd/vfs.c +22
-0+*/ +static noinline_for_stack __be32 +nfsd_direct_read (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct nfsd_file *nf,loff_t offset,unsigned long *count, +u32 *eof) +{ +loff_t dio_start, dio_end; +unsigned long v, total; +struct iov_iter iter; +struct kiocb kiocb; +ssize_t host_err; +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 (offset + *count, nf->nf_dio_read_offset_align); + + kiocb.ki_pos = dio_start; +
fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1121
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+ v =0 ; + total = dio_end - dio_start; +while (total) { + len =min_t (size_t, total,PAGE_SIZE ); +bvec_set_page (&rqstp->rq_bvec[v], *(rqstp->rq_next_page++), + len,0 ); + total -= len; + ++v; + } +WARN_ON_ONCE (v > rqstp->rq_maxpages);
I would rather we had an early test rather than a late warn-on. e.g.
if (total > (rqstp->rq_maxpages >> PAGE_SHIFT))
return -EINVAL /* or whatever */;
Otherwise it seems to be making unstated assumptions about how big the alignment requirements could be.
Thanks,
NeilBrown
Quoted 48 lines
fs/nfsd/vfs.c +33
-0+ +trace_nfsd_read_direct (rqstp, fhp, offset, *count); +iov_iter_bvec (&iter,ITER_DEST , rqstp->rq_bvec, v, dio_end - dio_start); + + host_err =vfs_iocb_iter_read (nf->nf_file, &kiocb, &iter); +if (host_err >=0 ) { +unsigned int pad = offset - dio_start; + +/* The returned payload starts after the pad */ + rqstp->rq_res.page_base = pad; + +/* Compute the count of bytes to be returned */ +if (host_err > pad + *count) { + host_err = *count; + }else if (host_err > pad) { + host_err -= pad; + }else { + host_err =0 ; + } + }else if (unlikely (host_err == -EINVAL )) { +pr_info_ratelimited ("nfsd: Unexpected direct I/O alignment failure\n" ); + host_err = -ESERVERFAULT ; + } + +return nfsd_finish_read (rqstp, fhp, nf->nf_file, offset, count, + eof, host_err); +} +/** * nfsd_iter_read - Perform a VFS read using an iterator * @rqstp: RPC transaction context @@ -1106,6 +1179,11 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,switch (nfsd_io_cache_read) {case NFSD_IO_BUFFERED :break ; +case NFSD_IO_DIRECT : +if (nf->nf_dio_read_offset_align && !base) +return nfsd_direct_read (rqstp, fhp, nf, offset, + count, eof); + fallthrough;case NFSD_IO_DONTCACHE :if (file->f_op->fop_flags &FOP_DONTCACHE ) kiocb.ki_flags =IOCB_DONTCACHE ;Signature · 1 line
2.50.0
Chuck Lever
maintainerRe: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
Quoted 144 lines · NeilBrown
Quoted 90 lines · Chuck Lever
From: Chuck Lever <[email protected]>
Add an experimental option that forces NFS READ operations to use direct I/O instead of reading through the NFS server's page cache.
There are already other layers of caching:
- The page cache on NFS clients
- The block device underlying the exported file systemThe server's page cache, in many cases, is unlikely to provide additional benefit. Some benchmarks have demonstrated that the server's page cache is actively detrimental for workloads whose working set is larger than the server's available physical memory.
For instance, on small NFS servers, cached NFS file content can squeeze out local memory consumers. For large sequential workloads, an enormous amount of data flows into and out of the page cache and is consumed by NFS clients exactly once -- caching that data is expensive to do and totally valueless.
For now this is a hidden option that can be enabled on test systems for benchmarking. In the longer term, this option might be enabled persistently or per-export. When the exported file system does not support direct I/O, NFSD falls back to using either DONTCACHE or buffered I/O to fulfill NFS READ requests.
Suggested-by Mike Snitzer <[email protected]>Signed-off-by Chuck Lever <[email protected]>--- fs/nfsd/debugfs.c | 2 ++ fs/nfsd/nfsd.h | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+)fs/nfsd/debugfs.c +2
-0@@ -44,6 +44,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(nfsd_dsr_fops, nfsd_dsr_get, nfsd_dsr_set, "%llu\n");* Contents: * %0: NFS READ will use buffered IO * %1: NFS READ will use dontcache (buffered IO w/ dropbehind) +* %2: NFS READ will use direct IO * * This setting takes immediate effect for all NFS versions, * all exports, and in all NFSD net namespaces. @@ -64,6 +65,7 @@ static int nfsd_io_cache_read_set(void *data, u64 val) nfsd_io_cache_read =NFSD_IO_BUFFERED ;break ;case NFSD_IO_DONTCACHE : +case NFSD_IO_DIRECT :/* * Must disable splice_read when enabling * NFSD_IO_DONTCACHE. fs/nfsd/nfsd.h +1
-0@@ -157,6 +157,7 @@ enum {/* Any new NFSD_IO enum value must be added at the end */ NFSD_IO_BUFFERED ,NFSD_IO_DONTCACHE , + NFSD_IO_DIRECT, };extern u64 nfsd_io_cache_read__read_mostly ;fs/nfsd/trace.h +1
-0@@ -464,6 +464,7 @@ DEFINE_EVENT(nfsd_io_class, nfsd_##name, \DEFINE_NFSD_IO_EVENT (read_start);DEFINE_NFSD_IO_EVENT (read_splice);DEFINE_NFSD_IO_EVENT (read_vector); +DEFINE_NFSD_IO_EVENT (read_direct);DEFINE_NFSD_IO_EVENT (read_io_done);DEFINE_NFSD_IO_EVENT (read_done);DEFINE_NFSD_IO_EVENT (write_start);fs/nfsd/vfs.c +3
-0@@ -1074,6 +1074,79 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,return nfsd_finish_read (rqstp, fhp, file, offset, count, eof, host_err); } +/* +* The byte range of the client's READ request is expanded on both +* ends until it meets the underlying file system's direct I/O fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1089
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+* alignment requirements. After the internal read is complete, the +* byte range of the NFS READ payload is reduced to the byte range +* that was originally requested. +* +* Note that a direct read can be done only when the xdr_buf +* containing the NFS READ reply does not already have contents in +* its .pages array. This is due to potentially restrictive +* alignment requirements on the read buffer. When .page_len and +* @base are zero, the .pages array is guaranteed to be page- +* aligned. Where do we test that this condition is met?
I can see that
nfsd_direct_read()is only called if "base" is zero, but that means the current contents of the .pages array are page-aligned, not that there are now.Quoted 22 lines
fs/nfsd/vfs.c +22
-0+*/ +static noinline_for_stack __be32 +nfsd_direct_read (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct nfsd_file *nf,loff_t offset,unsigned long *count, +u32 *eof) +{ +loff_t dio_start, dio_end; +unsigned long v, total; +struct iov_iter iter; +struct kiocb kiocb; +ssize_t host_err; +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 (offset + *count, nf->nf_dio_read_offset_align); + + kiocb.ki_pos = dio_start; +fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1121
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+ v =0 ; + total = dio_end - dio_start; +while (total) { + len =min_t (size_t, total,PAGE_SIZE ); +bvec_set_page (&rqstp->rq_bvec[v], *(rqstp->rq_next_page++), + len,0 ); + total -= len; + ++v; + } +WARN_ON_ONCE (v > rqstp->rq_maxpages);I would rather we had an early test rather than a late warn-on. e.g.
if (total > (rqstp->rq_maxpages>>PAGE_SHIFT))
Quoted 4 lines
return -EINVAL /* or whatever */;Otherwise it seems to be making unstated assumptions about how big the alignment requirements could be.
This is the same warn-on test that nfsd_iter_read does for buffered and dontcache reads. It's done late because the final value of v is computed here, not known before the loop.
I think we might be able to turn this into a short read, for all I/O modes?
Quoted 52 lines
Thanks,
NeilBrownQuoted 48 lines
fs/nfsd/vfs.c +33
-0+ +trace_nfsd_read_direct (rqstp, fhp, offset, *count); +iov_iter_bvec (&iter,ITER_DEST , rqstp->rq_bvec, v, dio_end - dio_start); + + host_err =vfs_iocb_iter_read (nf->nf_file, &kiocb, &iter); +if (host_err >=0 ) { +unsigned int pad = offset - dio_start; + +/* The returned payload starts after the pad */ + rqstp->rq_res.page_base = pad; + +/* Compute the count of bytes to be returned */ +if (host_err > pad + *count) { + host_err = *count; + }else if (host_err > pad) { + host_err -= pad; + }else { + host_err =0 ; + } + }else if (unlikely (host_err == -EINVAL )) { +pr_info_ratelimited ("nfsd: Unexpected direct I/O alignment failure\n" ); + host_err = -ESERVERFAULT ; + } + +return nfsd_finish_read (rqstp, fhp, nf->nf_file, offset, count, + eof, host_err); +} +/** * nfsd_iter_read - Perform a VFS read using an iterator * @rqstp: RPC transaction context @@ -1106,6 +1179,11 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,switch (nfsd_io_cache_read) {case NFSD_IO_BUFFERED :break ; +case NFSD_IO_DIRECT : +if (nf->nf_dio_read_offset_align && !base) +return nfsd_direct_read (rqstp, fhp, nf, offset, + count, eof); + fallthrough;case NFSD_IO_DONTCACHE :if (file->f_op->fop_flags &FOP_DONTCACHE ) kiocb.ki_flags =IOCB_DONTCACHE ;Signature · 1 line
2.50.0
Signature · 1 line
Chuck Lever
Re: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
Quoted 23 lines · Chuck Lever
Quoted 11 lines
fs/nfsd/vfs.c +3
-0@@ -1074,6 +1074,79 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,return nfsd_finish_read (rqstp, fhp, file, offset, count, eof, host_err); } +/* +* The byte range of the client's READ request is expanded on both +* ends until it meets the underlying file system's direct I/O fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1089
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+* alignment requirements. After the internal read is complete, the +* byte range of the NFS READ payload is reduced to the byte range +* that was originally requested. +* +* Note that a direct read can be done only when the xdr_buf +* containing the NFS READ reply does not already have contents in +* its .pages array. This is due to potentially restrictive +* alignment requirements on the read buffer. When .page_len and +* @base are zero, the .pages array is guaranteed to be page- +* aligned. Where do we test that this condition is met?
Quoted 3 lines
I can see that
nfsd_direct_read()is only called if "base" is zero, but that means the current contents of the .pages array are page-aligned, not that there are now.
The above comment might be stale; I'm not sure the .page_len test is necessary. As long as the payload starts on a page boundary, it should meet most any plausible buffer alignment restriction.
However, if there are additional restrictions needed, checks can be added in nfsd_iter_read() just before nfsd_direct_read() is called.
Signature · 1 line
Chuck Lever
NeilBrown
reviewerRe: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
Quoted 28 lines · Chuck Lever
Quoted 23 lines · Chuck Lever
Quoted 11 lines
fs/nfsd/vfs.c +3
-0@@ -1074,6 +1074,79 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,return nfsd_finish_read (rqstp, fhp, file, offset, count, eof, host_err); } +/* +* The byte range of the client's READ request is expanded on both +* ends until it meets the underlying file system's direct I/O fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1089
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+* alignment requirements. After the internal read is complete, the +* byte range of the NFS READ payload is reduced to the byte range +* that was originally requested. +* +* Note that a direct read can be done only when the xdr_buf +* containing the NFS READ reply does not already have contents in +* its .pages array. This is due to potentially restrictive +* alignment requirements on the read buffer. When .page_len and +* @base are zero, the .pages array is guaranteed to be page- +* aligned. Where do we test that this condition is met?
Quoted 3 lines
I can see that
nfsd_direct_read()is only called if "base" is zero, but that means the current contents of the .pages array are page-aligned, not that there are now.
Quoted 6 lines
The above comment might be stale; I'm not sure the .page_len test is necessary. As long as the payload starts on a page boundary, it should meet most any plausible buffer alignment restriction.
However, if there are additional restrictions needed, checks can be added in
nfsd_iter_read()just beforenfsd_direct_read()is called.
I think that current test against rq_maxpages (in the WARN_ON) only makes sense if .page_len was zero.
NeilBrown
Quoted 5 lines
Signature · 2 lines
-- Chuck Lever
NeilBrown
reviewerRe: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
In reply to Chuck LeverQuoted 147 lines · Chuck Lever
Quoted 146 lines · NeilBrown
Quoted 90 lines · Chuck Lever
From: Chuck Lever <[email protected]>
Add an experimental option that forces NFS READ operations to use direct I/O instead of reading through the NFS server's page cache.
There are already other layers of caching:
- The page cache on NFS clients
- The block device underlying the exported file systemThe server's page cache, in many cases, is unlikely to provide additional benefit. Some benchmarks have demonstrated that the server's page cache is actively detrimental for workloads whose working set is larger than the server's available physical memory.
For instance, on small NFS servers, cached NFS file content can squeeze out local memory consumers. For large sequential workloads, an enormous amount of data flows into and out of the page cache and is consumed by NFS clients exactly once -- caching that data is expensive to do and totally valueless.
For now this is a hidden option that can be enabled on test systems for benchmarking. In the longer term, this option might be enabled persistently or per-export. When the exported file system does not support direct I/O, NFSD falls back to using either DONTCACHE or buffered I/O to fulfill NFS READ requests.
Suggested-by Mike Snitzer <[email protected]>Signed-off-by Chuck Lever <[email protected]>--- fs/nfsd/debugfs.c | 2 ++ fs/nfsd/nfsd.h | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+)fs/nfsd/debugfs.c +2
-0@@ -44,6 +44,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(nfsd_dsr_fops, nfsd_dsr_get, nfsd_dsr_set, "%llu\n");* Contents: * %0: NFS READ will use buffered IO * %1: NFS READ will use dontcache (buffered IO w/ dropbehind) +* %2: NFS READ will use direct IO * * This setting takes immediate effect for all NFS versions, * all exports, and in all NFSD net namespaces. @@ -64,6 +65,7 @@ static int nfsd_io_cache_read_set(void *data, u64 val) nfsd_io_cache_read =NFSD_IO_BUFFERED ;break ;case NFSD_IO_DONTCACHE : +case NFSD_IO_DIRECT :/* * Must disable splice_read when enabling * NFSD_IO_DONTCACHE. fs/nfsd/nfsd.h +1
-0@@ -157,6 +157,7 @@ enum {/* Any new NFSD_IO enum value must be added at the end */ NFSD_IO_BUFFERED ,NFSD_IO_DONTCACHE , + NFSD_IO_DIRECT, };extern u64 nfsd_io_cache_read__read_mostly ;fs/nfsd/trace.h +1
-0@@ -464,6 +464,7 @@ DEFINE_EVENT(nfsd_io_class, nfsd_##name, \DEFINE_NFSD_IO_EVENT (read_start);DEFINE_NFSD_IO_EVENT (read_splice);DEFINE_NFSD_IO_EVENT (read_vector); +DEFINE_NFSD_IO_EVENT (read_direct);DEFINE_NFSD_IO_EVENT (read_io_done);DEFINE_NFSD_IO_EVENT (read_done);DEFINE_NFSD_IO_EVENT (write_start);fs/nfsd/vfs.c +3
-0@@ -1074,6 +1074,79 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,return nfsd_finish_read (rqstp, fhp, file, offset, count, eof, host_err); } +/* +* The byte range of the client's READ request is expanded on both +* ends until it meets the underlying file system's direct I/O fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1089
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+* alignment requirements. After the internal read is complete, the +* byte range of the NFS READ payload is reduced to the byte range +* that was originally requested. +* +* Note that a direct read can be done only when the xdr_buf +* containing the NFS READ reply does not already have contents in +* its .pages array. This is due to potentially restrictive +* alignment requirements on the read buffer. When .page_len and +* @base are zero, the .pages array is guaranteed to be page- +* aligned. Where do we test that this condition is met?
I can see that
nfsd_direct_read()is only called if "base" is zero, but that means the current contents of the .pages array are page-aligned, not that there are now.Quoted 22 lines
fs/nfsd/vfs.c +22
-0+*/ +static noinline_for_stack __be32 +nfsd_direct_read (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct nfsd_file *nf,loff_t offset,unsigned long *count, +u32 *eof) +{ +loff_t dio_start, dio_end; +unsigned long v, total; +struct iov_iter iter; +struct kiocb kiocb; +ssize_t host_err; +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 (offset + *count, nf->nf_dio_read_offset_align); + + kiocb.ki_pos = dio_start; +fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1121
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+ v =0 ; + total = dio_end - dio_start; +while (total) { + len =min_t (size_t, total,PAGE_SIZE ); +bvec_set_page (&rqstp->rq_bvec[v], *(rqstp->rq_next_page++), + len,0 ); + total -= len; + ++v; + } +WARN_ON_ONCE (v > rqstp->rq_maxpages);I would rather we had an early test rather than a late warn-on. e.g.
if (total > (rqstp->rq_maxpages>>PAGE_SHIFT))return -EINVAL /* or whatever */;
Quoted 6 lines
Quoted 2 lines
Otherwise it seems to be making unstated assumptions about how big the alignment requirements could be.
This is the same warn-on test that
nfsd_iter_readdoes for buffered and dontcache reads. It's done late because the final value of v is computed here, not known before the loop.
True, but in this case "total" could be larger than "*count" which was size-checked in e.g. nfsd4_encode_read. So it could now be larger than the available space.
Quoted 3 lines
I think we might be able to turn this into a short read, for all I/O modes?
Yes, that could be a clean way to handle the unlikely case that the reads doesn't fit any more.
Thanks,
NeilBrown
Quoted 59 lines
Quoted 54 lines
Thanks,
NeilBrownQuoted 48 lines
fs/nfsd/vfs.c +33
-0+ +trace_nfsd_read_direct (rqstp, fhp, offset, *count); +iov_iter_bvec (&iter,ITER_DEST , rqstp->rq_bvec, v, dio_end - dio_start); + + host_err =vfs_iocb_iter_read (nf->nf_file, &kiocb, &iter); +if (host_err >=0 ) { +unsigned int pad = offset - dio_start; + +/* The returned payload starts after the pad */ + rqstp->rq_res.page_base = pad; + +/* Compute the count of bytes to be returned */ +if (host_err > pad + *count) { + host_err = *count; + }else if (host_err > pad) { + host_err -= pad; + }else { + host_err =0 ; + } + }else if (unlikely (host_err == -EINVAL )) { +pr_info_ratelimited ("nfsd: Unexpected direct I/O alignment failure\n" ); + host_err = -ESERVERFAULT ; + } + +return nfsd_finish_read (rqstp, fhp, nf->nf_file, offset, count, + eof, host_err); +} +/** * nfsd_iter_read - Perform a VFS read using an iterator * @rqstp: RPC transaction context @@ -1106,6 +1179,11 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,switch (nfsd_io_cache_read) {case NFSD_IO_BUFFERED :break ; +case NFSD_IO_DIRECT : +if (nf->nf_dio_read_offset_align && !base) +return nfsd_direct_read (rqstp, fhp, nf, offset, + count, eof); + fallthrough;case NFSD_IO_DONTCACHE :if (file->f_op->fop_flags &FOP_DONTCACHE ) kiocb.ki_flags =IOCB_DONTCACHE ;Signature · 1 line
2.50.0Signature · 1 line
Chuck Lever
Chuck Lever
maintainerRe: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
Quoted 15 lines · NeilBrown
Quoted 15 lines
Quoted 15 lines
fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1121
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+ v =0 ; + total = dio_end - dio_start; +while (total) { + len =min_t (size_t, total,PAGE_SIZE ); +bvec_set_page (&rqstp->rq_bvec[v], *(rqstp->rq_next_page++), + len,0 ); + total -= len; + ++v; + } +WARN_ON_ONCE (v > rqstp->rq_maxpages);I would rather we had an early test rather than a late warn-on. e.g.
if (total > (rqstp->rq_maxpages>>PAGE_SHIFT))return -EINVAL /* or whatever */;
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Otherwise it seems to be making unstated assumptions about how big the alignment requirements could be.
This is the same warn-on test that
nfsd_iter_readdoes for buffered and dontcache reads. It's done late because the final value of v is computed here, not known before the loop.True, but in this case "total" could be larger than "*count" which was size-checked in e.g.
nfsd4_encode_read. So it could now be larger than the available space.
Expanding the byte range is constrained to the alignment parameters, meaning the most the range can increase is by a single page (assuming the needed alignment is always less than or equal to a page size, or that we stipulate larger alignments are not yet supported).
Both rq_bvec and rq_pages have that extra page already.
Quoted 4 lines
Quoted 2 lines
I think we might be able to turn this into a short read, for all I/O modes?
Yes, that could be a clean way to handle the unlikely case that the reads doesn't fit any more.
It's probably best to not have the WARN_ON at all. Either convert the failure to a short read, or prove formally that the condition cannot happen and simply remove the WARN_ON. I have never seen it fire.
That should be done to nfsd_iter_read() /before/ this series. Then 3/3 can "copy" and use the improved loop logic.
Signature · 1 line
Chuck Lever
Re: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
In reply to NeilBrownQuoted 144 lines · NeilBrown
Quoted 90 lines · Chuck Lever
From: Chuck Lever <[email protected]>
Add an experimental option that forces NFS READ operations to use direct I/O instead of reading through the NFS server's page cache.
There are already other layers of caching:
- The page cache on NFS clients
- The block device underlying the exported file systemThe server's page cache, in many cases, is unlikely to provide additional benefit. Some benchmarks have demonstrated that the server's page cache is actively detrimental for workloads whose working set is larger than the server's available physical memory.
For instance, on small NFS servers, cached NFS file content can squeeze out local memory consumers. For large sequential workloads, an enormous amount of data flows into and out of the page cache and is consumed by NFS clients exactly once -- caching that data is expensive to do and totally valueless.
For now this is a hidden option that can be enabled on test systems for benchmarking. In the longer term, this option might be enabled persistently or per-export. When the exported file system does not support direct I/O, NFSD falls back to using either DONTCACHE or buffered I/O to fulfill NFS READ requests.
Suggested-by Mike Snitzer <[email protected]>Signed-off-by Chuck Lever <[email protected]>--- fs/nfsd/debugfs.c | 2 ++ fs/nfsd/nfsd.h | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+)fs/nfsd/debugfs.c +2
-0@@ -44,6 +44,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(nfsd_dsr_fops, nfsd_dsr_get, nfsd_dsr_set, "%llu\n");* Contents: * %0: NFS READ will use buffered IO * %1: NFS READ will use dontcache (buffered IO w/ dropbehind) +* %2: NFS READ will use direct IO * * This setting takes immediate effect for all NFS versions, * all exports, and in all NFSD net namespaces. @@ -64,6 +65,7 @@ static int nfsd_io_cache_read_set(void *data, u64 val) nfsd_io_cache_read =NFSD_IO_BUFFERED ;break ;case NFSD_IO_DONTCACHE : +case NFSD_IO_DIRECT :/* * Must disable splice_read when enabling * NFSD_IO_DONTCACHE. fs/nfsd/nfsd.h +1
-0@@ -157,6 +157,7 @@ enum {/* Any new NFSD_IO enum value must be added at the end */ NFSD_IO_BUFFERED ,NFSD_IO_DONTCACHE , + NFSD_IO_DIRECT, };extern u64 nfsd_io_cache_read__read_mostly ;fs/nfsd/trace.h +1
-0@@ -464,6 +464,7 @@ DEFINE_EVENT(nfsd_io_class, nfsd_##name, \DEFINE_NFSD_IO_EVENT (read_start);DEFINE_NFSD_IO_EVENT (read_splice);DEFINE_NFSD_IO_EVENT (read_vector); +DEFINE_NFSD_IO_EVENT (read_direct);DEFINE_NFSD_IO_EVENT (read_io_done);DEFINE_NFSD_IO_EVENT (read_done);DEFINE_NFSD_IO_EVENT (write_start);fs/nfsd/vfs.c +3
-0@@ -1074,6 +1074,79 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,return nfsd_finish_read (rqstp, fhp, file, offset, count, eof, host_err); } +/* +* The byte range of the client's READ request is expanded on both +* ends until it meets the underlying file system's direct I/O fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1089
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+* alignment requirements. After the internal read is complete, the +* byte range of the NFS READ payload is reduced to the byte range +* that was originally requested. +* +* Note that a direct read can be done only when the xdr_buf +* containing the NFS READ reply does not already have contents in +* its .pages array. This is due to potentially restrictive +* alignment requirements on the read buffer. When .page_len and +* @base are zero, the .pages array is guaranteed to be page- +* aligned. Where do we test that this condition is met?
I can see that
nfsd_direct_read()is only called if "base" is zero, but that means the current contents of the .pages array are page-aligned, not that there are now.Quoted 22 lines
fs/nfsd/vfs.c +22
-0+*/ +static noinline_for_stack __be32 +nfsd_direct_read (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct nfsd_file *nf,loff_t offset,unsigned long *count, +u32 *eof) +{ +loff_t dio_start, dio_end; +unsigned long v, total; +struct iov_iter iter; +struct kiocb kiocb; +ssize_t host_err; +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 (offset + *count, nf->nf_dio_read_offset_align); + + kiocb.ki_pos = dio_start; +fs/nfsd/vfs.c __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1121
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+ v =0 ; + total = dio_end - dio_start; +while (total) { + len =min_t (size_t, total,PAGE_SIZE ); +bvec_set_page (&rqstp->rq_bvec[v], *(rqstp->rq_next_page++), + len,0 ); + total -= len; + ++v; + } +WARN_ON_ONCE (v > rqstp->rq_maxpages);I would rather we had an early test rather than a late warn-on. e.g.
if (total > (rqstp->rq_maxpages>>PAGE_SHIFT))
Quoted 1 line
return -EINVAL /* or whatever */;
-EINVAL is the devil. ;)
My follow-up patch should avoid any possibility of this WARN_ON_ONCE ever triggering:
https://lore.kernel.org/linux-nfs/[email protected]/
(and should/could be folded into this 3/3 patch?)
Quoted 2 lines
Otherwise it seems to be making unstated assumptions about how big the alignment requirements could be.
Just FYI, nfsd_direct_read's while loop code is nearly identical to that which is in nfsd_iter_read().
So if an early return warranted in nfsd_direct_read, it is also warranted in nfsd_iter_read.
(I had an early return in nfsd_iter_read N iterations ago, decided it not needed given its not something that we'll ever hit.. really just a canary in the coal mine that offers companionship until one of us dies of black lung)
Mike
Re: [PATCH v1 3/3] NFSD: Implement NFSD_IO_DIRECT for NFS READ
In reply to Chuck LeverQuoted 165 lines · Chuck Lever
From: Chuck Lever <[email protected]>
Add an experimental option that forces NFS READ operations to use direct I/O instead of reading through the NFS server's page cache.
There are already other layers of caching:
- The page cache on NFS clients
- The block device underlying the exported file systemThe server's page cache, in many cases, is unlikely to provide additional benefit. Some benchmarks have demonstrated that the server's page cache is actively detrimental for workloads whose working set is larger than the server's available physical memory.
For instance, on small NFS servers, cached NFS file content can squeeze out local memory consumers. For large sequential workloads, an enormous amount of data flows into and out of the page cache and is consumed by NFS clients exactly once -- caching that data is expensive to do and totally valueless.
For now this is a hidden option that can be enabled on test systems for benchmarking. In the longer term, this option might be enabled persistently or per-export. When the exported file system does not support direct I/O, NFSD falls back to using either DONTCACHE or buffered I/O to fulfill NFS READ requests.
Suggested-by Mike Snitzer <[email protected]>Signed-off-by Chuck Lever <[email protected]>--- fs/nfsd/debugfs.c | 2 ++ fs/nfsd/nfsd.h | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+)fs/nfsd/debugfs.c +2
-0@@ -44,6 +44,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(nfsd_dsr_fops, nfsd_dsr_get, nfsd_dsr_set, "%llu\n");* Contents: * %0: NFS READ will use buffered IO * %1: NFS READ will use dontcache (buffered IO w/ dropbehind) +* %2: NFS READ will use direct IO * * This setting takes immediate effect for all NFS versions, * all exports, and in all NFSD net namespaces. @@ -64,6 +65,7 @@ static int nfsd_io_cache_read_set(void *data, u64 val) nfsd_io_cache_read =NFSD_IO_BUFFERED ;break ;case NFSD_IO_DONTCACHE : +case NFSD_IO_DIRECT :/* * Must disable splice_read when enabling * NFSD_IO_DONTCACHE. fs/nfsd/nfsd.h +1
-0@@ -157,6 +157,7 @@ enum {/* Any new NFSD_IO enum value must be added at the end */ NFSD_IO_BUFFERED ,NFSD_IO_DONTCACHE , + NFSD_IO_DIRECT, };extern u64 nfsd_io_cache_read__read_mostly ;fs/nfsd/trace.h +1
-0@@ -464,6 +464,7 @@ DEFINE_EVENT(nfsd_io_class, nfsd_##name, \DEFINE_NFSD_IO_EVENT (read_start);DEFINE_NFSD_IO_EVENT (read_splice);DEFINE_NFSD_IO_EVENT (read_vector); +DEFINE_NFSD_IO_EVENT (read_direct);DEFINE_NFSD_IO_EVENT (read_io_done);DEFINE_NFSD_IO_EVENT (read_done);DEFINE_NFSD_IO_EVENT (write_start);fs/nfsd/vfs.c +73
-0@@ -1074,6 +1074,79 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,return nfsd_finish_read (rqstp, fhp, file, offset, count, eof, host_err); } +/* +* The byte range of the client's READ request is expanded on both +* ends until it meets the underlying file system's direct I/O +* alignment requirements. After the internal read is complete, the +* byte range of the NFS READ payload is reduced to the byte range +* that was originally requested. +* +* Note that a direct read can be done only when the xdr_buf +* containing the NFS READ reply does not already have contents in +* its .pages array. This is due to potentially restrictive +* alignment requirements on the read buffer. When .page_len and +* @base are zero, the .pages array is guaranteed to be page- +* aligned. +*/ +static noinline_for_stack __be32 +nfsd_direct_read (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct nfsd_file *nf,loff_t offset,unsigned long *count, +u32 *eof) +{ +loff_t dio_start, dio_end; +unsigned long v, total; +struct iov_iter iter; +struct kiocb kiocb; +ssize_t host_err; +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 (offset + *count, nf->nf_dio_read_offset_align); + + kiocb.ki_pos = dio_start; + + v =0 ; + total = dio_end - dio_start; +while (total) { + len =min_t (size_t, total,PAGE_SIZE ); +bvec_set_page (&rqstp->rq_bvec[v], *(rqstp->rq_next_page++), + len,0 ); + total -= len; + ++v; + } +WARN_ON_ONCE (v > rqstp->rq_maxpages); + +trace_nfsd_read_direct (rqstp, fhp, offset, *count); +iov_iter_bvec (&iter,ITER_DEST , rqstp->rq_bvec, v, dio_end - dio_start); + + host_err =vfs_iocb_iter_read (nf->nf_file, &kiocb, &iter); +if (host_err >=0 ) { +unsigned int pad = offset - dio_start; + +/* The returned payload starts after the pad */ + rqstp->rq_res.page_base = pad; + +/* Compute the count of bytes to be returned */ +if (host_err > pad + *count) { + host_err = *count; + }else if (host_err > pad) { + host_err -= pad; + }else { + host_err =0 ; + } + }else if (unlikely (host_err == -EINVAL )) { +pr_info_ratelimited ("nfsd: Unexpected direct I/O alignment failure\n" ); + host_err = -ESERVERFAULT ; + } + +return nfsd_finish_read (rqstp, fhp, nf->nf_file, offset, count, + eof, host_err); +} +/** * nfsd_iter_read - Perform a VFS read using an iterator * @rqstp: RPC transaction context @@ -1106,6 +1179,11 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,switch (nfsd_io_cache_read) {
fs/nfsd/vfs.c __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1189
Quoted 10 lines
fs/nfsd/vfs.c +5
-0case NFSD_IO_BUFFERED :break ; +case NFSD_IO_DIRECT : +if (nf->nf_dio_read_offset_align && !base) +return nfsd_direct_read (rqstp, fhp, nf, offset, + count, eof); + fallthrough;case NFSD_IO_DONTCACHE :if (file->f_op->fop_flags &FOP_DONTCACHE ) kiocb.ki_flags =IOCB_DONTCACHE ;
This looks fine:
Since this is a "hidden" option, it may also be a good idea to add a pr_info that pops when someone enables it. Then if we get a bug report involving this later, that would at least show up in the log.
cover NFSD: continuation of NFSD DIRECT
In reply to Chuck LeverHi Chuck,
These patches build ontop of your recent series, here:
https://lore.kernel.org/linux-nfs/[email protected]/
The first sunrpc patch is carry-over from earlier iterations of my NFSD DIRECT patchsets. I think it needed to be sure we have adequate pages to handle expanding misaligned READ to fit in max payload. But maybe not?
The 2nd "NFSD: Implement NFSD_IO_DIRECT for NFS WRITE" patch is a refactored version of my earlier NFS WRITE support. I tried to honor the types of changes I saw in your rewrite of the NFSD_IO_DIRECT NFS READ support. So I avoided needless maths and variables that were only for the benefit of excessive tracepoint complexity (which I also removed), while also establishing cleaner code that you can iterate on further if you think something else needed. LOC isn't much smaller but maybe you or others will see something that can be elided -- the good news is this NFSD DIRECT WRITE code stands on its own like the NFSD DIRECT READ does.
Not sure what NFSD DIRECT WRITE gray areas you think we need to work through further in NFSD (but I could imagine there being room for common Linux vfs code or other FS-specific code improvements to make buffered vs direct safer). I know others have repeated concern but in practice I haven't had any issues with this NFSD DIRECT WRITE suppoort. So I welcome further discussion on what else needed.
Thanks,
Mike
Mike Snitzer (2):
sunrpc: add an extra reserve page to svc_serv_maxpages()
NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
fs/nfsd/debugfs.c | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 215 ++++++++++++++++++++++++++++++++++++- include/linux/sunrpc/svc.h | 5 +- 4 files changed, 216 insertions(+), 6 deletions(-)
Signature · 1 line
2.44.0
PATCH 1/2 sunrpc: add an extra reserve page to svc_serv_maxpages()
nfsd_iter_read() might need two extra pages when a READ payload is not DIO-aligned -- but nfsd_iter_read() and nfsd_splice_actor() are mutually exclusive (so reuse page reserved for nfsd_splice_actor).
--- include/linux/sunrpc/svc.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
include/linux/sunrpc/svc.h +4 -1
@@ -163,10 +163,13 @@ extern u32 svc_max_payload(const struct svc_rqst *rqstp);* pages, one for the request, and one for the reply. * nfsd_splice_actor() might need an extra page when a READ payload * is not page-aligned. +* nfsd_iter_read() might need two extra pages when a READ payload +* is not DIO-aligned -- but nfsd_iter_read() and nfsd_splice_actor() +* are mutually exclusive (so reuse page reserved for nfsd_splice_actor). */ static inline unsigned long svc_serv_maxpages (const struct svc_serv *serv) { -return DIV_ROUND_UP (serv->sv_max_mesg,PAGE_SIZE ) +2 +1 ; +return DIV_ROUND_UP (serv->sv_max_mesg,PAGE_SIZE ) +2 +1 +1 ; }/*
Signature · 1 line
2.44.0
Re: [PATCH 1/2] sunrpc: add an extra reserve page to svc_serv_maxpages()
Quoted 18 lines · Mike Snitzer
nfsd_iter_read()might need two extra pages when a READ payload is not DIO-aligned -- butnfsd_iter_read()andnfsd_splice_actor()are mutually exclusive (so reuse page reserved fornfsd_splice_actor).Signed-off-by Mike Snitzer <[email protected]>--- include/linux/sunrpc/svc.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)include/linux/sunrpc/svc.h +1
-0@@ -163,10 +163,13 @@ extern u32 svc_max_payload(const struct svc_rqst *rqstp);* pages, one for the request, and one for the reply. * nfsd_splice_actor() might need an extra page when a READ payload * is not page-aligned. +* nfsd_iter_read() might need two extra pages when a READ payload
include/linux/sunrpc/svc.h extern u32 svc_max_payload(const struct svc_rqst *rqstp); · line 175
Quoted 10 lines
include/linux/sunrpc/svc.h +3
-1+* is not DIO-aligned -- but nfsd_iter_read() and nfsd_splice_actor() +* are mutually exclusive (so reuse page reserved for nfsd_splice_actor). */ static inline unsigned long svc_serv_maxpages (const struct svc_serv *serv) { -return DIV_ROUND_UP (serv->sv_max_mesg,PAGE_SIZE ) +2 +1 ; +return DIV_ROUND_UP (serv->sv_max_mesg,PAGE_SIZE ) +2 +1 +1 ; }/*
I'm not convinced an additional page is necessary. The most the direct I/O range can be is rsize + 1 page, AFAICT, and that seems to be covered already.
If we want NFSD to support file systems / block devices with alignment requirements large than a page, then perhaps additional buffer space will be needed... or NFSD can return a short read.
Signature · 1 line
Chuck Lever
PATCH 2/2 NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
In reply to Mike SnitzerIf 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. Buffered IO (with preference towards using DONTCACHE) is used for the misaligned extents and O_DIRECT is used for the middle DIO-aligned extent.
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.
--- fs/nfsd/debugfs.c | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 215 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 212 insertions(+), 5 deletions(-)
fs/nfsd/debugfs.c +1 -0
@@ -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
@@ -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 +210 -5
248 lines not on this page · raw message
Signature · 1 line
2.44.0
PATCH v2 1/1 NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
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. Buffered IO (with preference towards using DONTCACHE) is used for the misaligned extents and O_DIRECT is used for the middle DIO-aligned extent.
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 | 210 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 207 insertions(+), 5 deletions(-)
changes in v2:
- add improved -EINVAL logging that matches latest DIO READ support
- update patch header
fs/nfsd/debugfs.c +1 -0
@@ -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
@@ -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 +205 -5
243 lines not on this page · raw message
Signature · 1 line
2.44.0
Re: [PATCH v2] NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
Quoted 291 lines · Mike Snitzer
If
NFSD_IO_DIRECTis 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. Buffered IO (with preference towards using DONTCACHE) is used for the misaligned extents andO_DIRECTis used for the middle DIO-aligned extent.If
vfs_iocb_iter_write()returns-ENOTBLK, due to its inability to invalidate the page cache on behalf of the DIO WRITE, thennfsd_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_DIRECTsupport that landed with commitc817248fc831("nfs/localio: add properO_DIRECTsupport for READ and WRITE"). But NFSD's support is simpler because it currently doesn't use AIO completion.Signed-off-by Mike Snitzer <[email protected]>--- fs/nfsd/debugfs.c | 1 + fs/nfsd/trace.h | 1 + fs/nfsd/vfs.c | 210 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 207 insertions(+), 5 deletions(-)changes in v2:
- add improved-EINVALlogging that matches latest DIO READ support
- update patch headerfs/nfsd/debugfs.c +1
-0@@ -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@@ -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 +205
-3@@ -1254,6 +1254,203 @@ static int wait_for_concurrent_writes(struct file *file)return err; } +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 */ +}; + +static bool +nfsd_is_write_dio_possible (loff_t offset,unsigned long len, +struct nfsd_file *nf,struct nfsd_write_dio *write_dio) +{ +const u32 dio_blocksize = nf->nf_dio_offset_align; +loff_t start_end, orig_end, middle_end; + +if (unlikely (!nf->nf_dio_mem_align || !dio_blocksize)) +return false; +if (unlikely (dio_blocksize >PAGE_SIZE )) +return false; +if (unlikely (len < dio_blocksize)) +return false; + + start_end =round_up (offset, dio_blocksize); + orig_end = offset + len; + middle_end =round_down (orig_end, dio_blocksize); + + write_dio->start_len = start_end - offset; + write_dio->middle_len = middle_end - start_end; + write_dio->end_len = orig_end - middle_end; + +return true; +} + +static bool nfsd_iov_iter_aligned_bvec (const struct iov_iter *i, +unsigned int addr_mask,unsigned int len_mask) +{ +const struct bio_vec *bvec = i->bvec; +size_t skip = i->iov_offset; +size_t size = i->count; + +if (size & len_mask) +return false; +do { +size_t len = bvec->bv_len; + +if (len > size) + len = size; +if ((unsigned long )(bvec->bv_offset + skip) & addr_mask) +return false; + bvec++; + size -= len; + skip =0 ; + }while (size); + +return true; +} + +/* +* Setup as many as 3 iov_iter based on extents described by @write_dio. +* Returns the number of iov_iter that were setup. +*/ +static int +nfsd_setup_write_dio_iters (struct iov_iter **iterp,bool *iter_is_dio_aligned, +struct bio_vec *rq_bvec,unsigned int nvecs, +unsigned long cnt,struct nfsd_write_dio *write_dio, +struct nfsd_file *nf) +{ +int n_iters =0 ; +struct iov_iter *iters = *iterp; + +/* Setup misaligned start? */ +if (write_dio->start_len) { +iov_iter_bvec (&iters[n_iters],ITER_SOURCE , rq_bvec, nvecs, cnt); + iters[n_iters].count = write_dio->start_len; + iter_is_dio_aligned[n_iters] = false; + ++n_iters; + } + +/* Setup DIO-aligned middle */ +iov_iter_bvec (&iters[n_iters],ITER_SOURCE , rq_bvec, nvecs, cnt); +if (write_dio->start_len) +iov_iter_advance (&iters[n_iters], write_dio->start_len); + iters[n_iters].count -= write_dio->end_len; + iter_is_dio_aligned[n_iters] = +nfsd_iov_iter_aligned_bvec (&iters[n_iters], + nf->nf_dio_mem_align-1 , nf->nf_dio_offset_align-1 ); +if (unlikely (!iter_is_dio_aligned[n_iters])) +return 0 ;/* no DIO-aligned IO possible */ + ++n_iters; + +/* Setup misaligned end? */ +if (write_dio->end_len) { +iov_iter_bvec (&iters[n_iters],ITER_SOURCE , rq_bvec, nvecs, cnt); +iov_iter_advance (&iters[n_iters], + write_dio->start_len + write_dio->middle_len); + iter_is_dio_aligned[n_iters] = false; + ++n_iters; + } + +return n_iters; +} + +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 ) +return host_err; + *cnt = host_err; + +return 0 ; +} + +static int +nfsd_issue_write_dio (struct svc_rqst *rqstp,struct svc_fh *fhp,struct nfsd_file *nf, +loff_t offset,unsigned int nvecs,unsigned long *cnt, +struct kiocb *kiocb,struct nfsd_write_dio *write_dio) +{ +struct file *file = nf->nf_file; +bool iter_is_dio_aligned[3 ]; +struct iov_iter iter_stack[3 ]; +struct iov_iter *iter = iter_stack; +unsigned int n_iters =0 ; +unsigned long in_count = *cnt; +loff_t in_offset = kiocb->ki_pos; +ssize_t host_err; + + n_iters =nfsd_setup_write_dio_iters (&iter, iter_is_dio_aligned, + rqstp->rq_bvec, nvecs, *cnt, write_dio, nf); +if (unlikely (!n_iters)) +return nfsd_buffered_write (rqstp, file, nvecs, cnt, kiocb); + + *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 ) { +/* VFS will return -ENOTBLK if DIO WRITE fails to +* invalidate the page cache. Retry using buffered IO. +*/ +if (unlikely (host_err == -ENOTBLK )) { + kiocb->ki_flags &= ~IOCB_DIRECT ; + *cnt = in_count; + kiocb->ki_pos = in_offset; +return nfsd_buffered_write (rqstp, file, + nvecs, cnt, kiocb); + }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 ; + } +return host_err; + } + *cnt += host_err; +if (host_err < iter[i].count)/* partial write? */ +break ; + } + +return 0 ; +} + +static noinline_for_stack int +nfsd_direct_write (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct nfsd_file *nf,loff_t offset,unsigned int nvecs, +unsigned long *cnt,struct kiocb *kiocb) +{ +struct nfsd_write_dio write_dio; + +/* Any buffered IO issued here will be misaligned, use +* IOCB_SYNC to ensure it has completed before returning. +*/ + kiocb->ki_flags |=IOCB_SYNC ; +/* Check if IOCB_DONTCACHE should be used when issuing buffered IO; +* if so, it will be ignored for any DIO issued here. +*/ +if (nf->nf_file->f_op->fop_flags &FOP_DONTCACHE ) + kiocb->ki_flags |=IOCB_DONTCACHE ; + +if (nfsd_is_write_dio_possible (offset, *cnt, nf, &write_dio)) { +trace_nfsd_write_direct (rqstp, fhp, offset, *cnt); +return nfsd_issue_write_dio (rqstp, fhp, nf, offset, nvecs, + cnt, kiocb, &write_dio); + } + +return nfsd_buffered_write (rqstp, nf->nf_file, nvecs, cnt, kiocb); +} +/** * nfsd_vfs_write - write data to an already-open file * @rqstp: RPC execution context @@ -1281,7 +1478,6 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,struct super_block *sb =file_inode (file)->i_sb;struct kiocb kiocb;struct svc_export *exp; -struct iov_iter iter;errseq_t since;__be32 nfserr;int host_err;@@ -1318,25 +1514,29 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, kiocb.ki_flags |=IOCB_DSYNC ; nvecs =xdr_buf_to_bvec (rqstp->rq_bvec, rqstp->rq_maxpages, payload); -iov_iter_bvec (&iter,ITER_SOURCE , rqstp->rq_bvec, nvecs, *cnt); + since =READ_ONCE (file->f_wb_err);if (verf)nfsd_copy_write_verifier (verf, nn);switch (nfsd_io_cache_write) { -case NFSD_IO_BUFFERED : +case NFSD_IO_DIRECT : + host_err =nfsd_direct_write (rqstp, fhp, nf, offset, + nvecs, cnt, &kiocb);break ;case NFSD_IO_DONTCACHE :if (file->f_op->fop_flags &FOP_DONTCACHE ) kiocb.ki_flags |=IOCB_DONTCACHE ; + fallthrough;/* must call nfsd_buffered_write */ +case NFSD_IO_BUFFERED : + host_err =nfsd_buffered_write (rqstp, file, + nvecs, cnt, &kiocb);break ;
fs/nfsd/vfs.c nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, · line 1542
Quoted 10 lines
fs/nfsd/vfs.c +0
-2} - host_err =vfs_iocb_iter_write (file, &kiocb, &iter);if (host_err <0 ) {commit_reset_write_verifier (nn, rqstp, host_err); goto out_nfserr; } - *cnt = host_err;nfsd_stats_io_write_add (nn, exp, *cnt);fsnotify_modify (file); host_err =filemap_check_wb_err (file->f_mapping, since);
Nice work, Mike!
Chuck Lever
maintainerRe: [PATCH v2] NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
In reply to Mike SnitzerQuoted 16 lines · Mike Snitzer
fs/nfsd/vfs.c +16
-0+ +static noinline_for_stack int +nfsd_direct_write (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct nfsd_file *nf,loff_t offset,unsigned int nvecs, +unsigned long *cnt,struct kiocb *kiocb) +{ +struct nfsd_write_dio write_dio; + +/* Any buffered IO issued here will be misaligned, use +* IOCB_SYNC to ensure it has completed before returning. +*/ + kiocb->ki_flags |=IOCB_SYNC ; +/* Check if IOCB_DONTCACHE should be used when issuing buffered IO; +* if so, it will be ignored for any DIO issued here. +*/ +if (nf->nf_file->f_op->fop_flags &FOP_DONTCACHE )
fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1452
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+ kiocb->ki_flags |=IOCB_DONTCACHE ; + +if (nfsd_is_write_dio_possible (offset, *cnt, nf, &write_dio)) { +trace_nfsd_write_direct (rqstp, fhp, offset, *cnt); +return nfsd_issue_write_dio (rqstp, fhp, nf, offset, nvecs, + cnt, kiocb, &write_dio); + } + +return nfsd_buffered_write (rqstp, nf->nf_file, nvecs, cnt, kiocb); +}
Handful of initial comments:
The current NFSv3 code path, and perhaps NFSv4 too, doesn't support changing a WRITE marked as UNSTABLE to FILE_SYNC. I'm not seeing that rectified in this patch. I have a patch that enables changing the "stable_how" setting, but it's on a system at home. I'll post it in a couple of days and we can build on that.
In nfsd_direct_write we're setting IOCB_SYNC early. When falling back to BUFFERED_IO, that setting is preserved, and the fallback buffered path is now always FILE_SYNC. We should discuss whether the fallback in this case should be always FILE_SYNC or should allow UNSTABLE.
Nit: I'd rather use the normal kernel comment style here, where an initial "/*" appears on a line by itself.
Signature · 1 line
Chuck Lever
Re: [PATCH v2] NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
Hi Chuck,
Quoted 30 lines · Chuck Lever
Quoted 16 lines · Mike Snitzer
fs/nfsd/vfs.c +16
-0+ +static noinline_for_stack int +nfsd_direct_write (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct nfsd_file *nf,loff_t offset,unsigned int nvecs, +unsigned long *cnt,struct kiocb *kiocb) +{ +struct nfsd_write_dio write_dio; + +/* Any buffered IO issued here will be misaligned, use +* IOCB_SYNC to ensure it has completed before returning. +*/ + kiocb->ki_flags |=IOCB_SYNC ; +/* Check if IOCB_DONTCACHE should be used when issuing buffered IO; +* if so, it will be ignored for any DIO issued here. +*/ +if (nf->nf_file->f_op->fop_flags &FOP_DONTCACHE )fs/nfsd/vfs.c static int wait_for_concurrent_writes(struct file *file) · line 1452
Quoted 10 lines
fs/nfsd/vfs.c +10
-0+ kiocb->ki_flags |=IOCB_DONTCACHE ; + +if (nfsd_is_write_dio_possible (offset, *cnt, nf, &write_dio)) { +trace_nfsd_write_direct (rqstp, fhp, offset, *cnt); +return nfsd_issue_write_dio (rqstp, fhp, nf, offset, nvecs, + cnt, kiocb, &write_dio); + } + +return nfsd_buffered_write (rqstp, nf->nf_file, nvecs, cnt, kiocb); +}Handful of initial comments:
Quoted 5 lines
The current NFSv3 code path, and perhaps NFSv4 too, doesn't support changing a WRITE marked as UNSTABLE to
FILE_SYNC. I'm not seeing that rectified in this patch. I have a patch that enables changing the "stable_how" setting, but it's on a system at home. I'll post it in a couple of days and we can build on that.
Just a quick follow-up to see if you might have time to either share your patch and/or rebase NFSD Direct WRITE ontop of it?
Quoted 4 lines
In
nfsd_direct_writewe're settingIOCB_SYNCearly. When falling back toBUFFERED_IO, that setting is preserved, and the fallback buffered path is now alwaysFILE_SYNC. We should discuss whether the fallback in this case should be alwaysFILE_SYNCor should allow UNSTABLE.
Probably makes sense to only set IOCB_SYNC _and_ IOCB_DONTCACHE if nfsd_is_write_dio_possible() returns true. So inverting nfsd_direct_write()'s flow as such:
if (!nfsd_is_write_dio_possible())
return nfsd_buffered_write()
// could push the setting these flags into nfsd_issue_write_dio?
kiocb->ki_flags |= IOCB_SYNC;
if (nf->nf_file->f_op->fop_flags & FOP_DONTCACHE)
kiocb->ki_flags |= IOCB_DONTCACHE;
trace_nfsd_write_direct(rqstp, fhp, offset, *cnt);
return nfsd_issue_write_dio()But the devil is in the details in conjunction with needing to rebase to deal with your change to pass stable_how by reference, etc.
Quoted 2 lines
Nit: I'd rather use the normal kernel comment style here, where an initial "/*" appears on a line by itself.
OK, will use that in general moving forward.
Thanks,
Mike
kernel test robot
build botRe: [PATCH v2] NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
In reply to Mike Snitzerkernel test robot noticed the following build errors:
Hi Mike,
kernel test robot noticed the following build errors:
[auto build test ERROR on next-20251009]
[also build test ERROR on linus/master]
[cannot apply to brauner-vfs/vfs.all v6.17 v6.17-rc7 v6.17-rc6 v6.17] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Mike-Snitzer/NFSD-Implement-NFSD_IO_DIRECT-for-NFS-WRITE/20251010-133404 base: next-20251009 patch link: https://lore.kernel.org/r/aOa0ijW1h-1tynWD%40kernel.org patch subject: [PATCH v2] NFSD: Implement NFSD_IO_DIRECT for NFS WRITE config: sparc-randconfig-002-20251011 (https://download.01.org/0day-ci/archive/20251011/[email protected]/config) compiler: sparc-linux-gcc (GCC) 15.1.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251011/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
fs/nfsd/vfs.c: In function 'nfsd_vfs_write':
>> fs/nfsd/vfs.c:1437:14: error: 'NFSD_IO_DIRECT' undeclared (first use in this function)
1437 | case NFSD_IO_DIRECT:
| ^~~~~~~~~~~~~~
fs/nfsd/vfs.c:1437:14: note: each undeclared identifier is reported only once for each function it appears in
--
fs/nfsd/debugfs.c: In function 'nfsd_io_cache_write_set':
>> fs/nfsd/debugfs.c:109:14: error: 'NFSD_IO_DIRECT' undeclared (first use in this function)
109 | case NFSD_IO_DIRECT:
| ^~~~~~~~~~~~~~
fs/nfsd/debugfs.c:109:14: note: each undeclared identifier is reported only once for each function it appears in
vim +/NFSD_IO_DIRECT +1437 fs/nfsd/vfs.c
1367
1368 /**
1369 * nfsd_vfs_write - write data to an already-open file
1370 * @rqstp: RPC execution context
1371 * @fhp: File handle of file to write into
1372 * @nf: An open file matching @fhp
1373 * @offset: Byte offset of start
1374 * @payload: xdr_buf containing the write payload
1375 * @cnt: IN: number of bytes to write, OUT: number of bytes actually written
1376 * @stable: An NFS stable_how value
1377 * @verf: NFS WRITE verifier
1378 *
1379 * Upon return, caller must invoke fh_put on @fhp.
1380 *
1381 * Return values:
1382 * An nfsstat value in network byte order.
1383 */
1384 __be32
1385 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
1386 struct nfsd_file *nf, loff_t offset,
1387 const struct xdr_buf *payload, unsigned long *cnt,
1388 int stable, __be32 *verf)
1389 {
1390 struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
1391 struct file *file = nf->nf_file;
1392 struct super_block *sb = file_inode(file)->i_sb;
1393 struct kiocb kiocb;
1394 struct svc_export *exp;
1395 errseq_t since;
1396 __be32 nfserr;
1397 int host_err;
1398 unsigned long exp_op_flags = 0;
1399 unsigned int pflags = current->flags;
1400 bool restore_flags = false;
1401 unsigned int nvecs;
1402
1403 trace_nfsd_write_opened(rqstp, fhp, offset, *cnt);
1404
1405 if (sb->s_export_op)
1406 exp_op_flags = sb->s_export_op->flags;
1407
1408 if (test_bit(RQ_LOCAL, &rqstp->rq_flags) &&
1409 !(exp_op_flags & EXPORT_OP_REMOTE_FS)) {
1410 /*
1411 * We want throttling in balance_dirty_pages()
1412 * and shrink_inactive_list() to only consider
1413 * the backingdev we are writing to, so that nfs to
1414 * localhost doesn't cause nfsd to lock up due to all
1415 * the client's dirty pages or its congested queue.
1416 */
1417 current->flags |= PF_LOCAL_THROTTLE;
1418 restore_flags = true;
1419 }
1420
1421 exp = fhp->fh_export;
1422
1423 if (!EX_ISSYNC(exp))
1424 stable = NFS_UNSTABLE;
1425 init_sync_kiocb(&kiocb, file);
1426 kiocb.ki_pos = offset;
1427 if (stable && !fhp->fh_use_wgather)
1428 kiocb.ki_flags |= IOCB_DSYNC;
1429
1430 nvecs = xdr_buf_to_bvec(rqstp->rq_bvec, rqstp->rq_maxpages, payload);
1431
1432 since = READ_ONCE(file->f_wb_err);
1433 if (verf)
1434 nfsd_copy_write_verifier(verf, nn);
1435
1436 switch (nfsd_io_cache_write) {
> 1437 case NFSD_IO_DIRECT: 1438 host_err = nfsd_direct_write(rqstp, fhp, nf, offset,
1439 nvecs, cnt, &kiocb);
1440 break;
1441 case NFSD_IO_DONTCACHE:
1442 if (file->f_op->fop_flags & FOP_DONTCACHE)
1443 kiocb.ki_flags |= IOCB_DONTCACHE;
1444 fallthrough; /* must call nfsd_buffered_write */
1445 case NFSD_IO_BUFFERED:
1446 host_err = nfsd_buffered_write(rqstp, file,
1447 nvecs, cnt, &kiocb);
1448 break;
1449 }
1450 if (host_err < 0) {
1451 commit_reset_write_verifier(nn, rqstp, host_err);
1452 goto out_nfserr;
1453 }
1454 nfsd_stats_io_write_add(nn, exp, *cnt);
1455 fsnotify_modify(file);
1456 host_err = filemap_check_wb_err(file->f_mapping, since);
1457 if (host_err < 0)
1458 goto out_nfserr;
1459
1460 if (stable && fhp->fh_use_wgather) {
1461 host_err = wait_for_concurrent_writes(file);
1462 if (host_err < 0)
1463 commit_reset_write_verifier(nn, rqstp, host_err);
1464 }
1465
1466 out_nfserr:
1467 if (host_err >= 0) {
1468 trace_nfsd_write_io_done(rqstp, fhp, offset, *cnt);
1469 nfserr = nfs_ok;
1470 } else {
1471 trace_nfsd_write_err(rqstp, fhp, offset, host_err);
1472 nfserr = nfserrno(host_err);
1473 }
1474 if (restore_flags)
1475 current_restore_flags(pflags, PF_LOCAL_THROTTLE);
1476 return nfserr;
1477 }
1478
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki