Adds support for using the NFS v4.2 operation IO_ADVISE to
send client I/O access pattern hints to the server.
There is a one-to-one correspondence between the advice
(POSIX_FADV_*) given in fadvise64() and the bitmap mask sent
to the server. Any other advice given results in just
calling generic_fadvise().
The bitmap mask given by the server reply is just ignored
since the fadvise64() returns 0 on success. If server
replies with more than one bitmap word, only the first word
is stored on the nfs42_io_advise_res struct and all other
words are ignored.
Signed-off-by: Jorge Mora <redacted>
---
fs/nfs/nfs42.h | 1 +
fs/nfs/nfs42proc.c | 67 +++++++++++++++++++++++++++++++
fs/nfs/nfs42xdr.c | 84
+++++++++++++++++++++++++++++++++++++++
fs/nfs/nfs4file.c | 37 +++++++++++++++++
fs/nfs/nfs4proc.c | 1 +
fs/nfs/nfs4xdr.c | 1 +
include/linux/nfs4.h | 1 +
include/linux/nfs_fs_sb.h | 1 +
include/linux/nfs_xdr.h | 28 +++++++++++++
9 files changed, 221 insertions(+)
diff --git a/fs/nfs/nfs42.h b/fs/nfs/nfs42.h
index 0fe5aacbcfdf..c3e9fcd54c72 100644
--- a/fs/nfs/nfs42.h
+++ b/fs/nfs/nfs42.h
@@ -20,6 +20,7 @@ int nfs42_proc_allocate(struct file *, loff_t,
loff_t);
ssize_t nfs42_proc_copy(struct file *, loff_t, struct file *,
loff_t, size_t,
struct nl4_server *, nfs4_stateid *, bool);
int nfs42_proc_deallocate(struct file *, loff_t, loff_t);
+int nfs42_proc_io_advise(struct file *, loff_t, u64, u32);
loff_t nfs42_proc_llseek(struct file *, loff_t, int);
int nfs42_proc_layoutstats_generic(struct nfs_server *,
struct nfs42_layoutstat_data *);
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index ecb428512fe1..af40f705386a 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -172,6 +172,73 @@ int nfs42_proc_deallocate(struct file *filep,
loff_t offset, loff_t len)
return err;
}
+static loff_t _nfs42_proc_io_advise(struct file *filep,
+ struct nfs_lock_context *lock, loff_t offset,
+ u64 count, u32 hints)
+{
+ struct inode *inode = file_inode(filep);
+ struct nfs42_io_advise_args args = {
+ .fh = NFS_FH(inode),
+ .offset = offset,
+ .count = count,
+ .hints = hints,
+ };
+ struct nfs42_io_advise_res res;
+ struct rpc_message msg = {
+ .rpc_proc =
&nfs4_procedures[NFSPROC4_CLNT_IO_ADVISE],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ };
+ struct nfs_server *server = NFS_SERVER(inode);
+ int status;
+
+ if (!nfs_server_capable(inode, NFS_CAP_IO_ADVISE))
+ return -ENOTSUPP;
+
+ status = nfs4_set_rw_stateid(&args.stateid, lock-quoted
open_context,
+ lock, FMODE_READ);
+ if (status) {
+ if (status == -EAGAIN)
+ status = -NFS4ERR_BAD_STATEID;
+ return status;
+ }
+
+ status = nfs4_call_sync(server->client, server, &msg,
+ &args.seq_args, &res.seq_res, 0);
+ if (status == -ENOTSUPP)
+ server->caps &= ~NFS_CAP_IO_ADVISE;
+ return status;
+}
+
+int nfs42_proc_io_advise(struct file *filep, loff_t offset, u64
count, u32 hints)
+{
+ struct nfs_server *server = NFS_SERVER(file_inode(filep));
+ struct nfs4_exception exception = { };
+ struct nfs_lock_context *lock;
+ loff_t err;
+
+ lock = nfs_get_lock_context(nfs_file_open_context(filep));
+ if (IS_ERR(lock))
+ return PTR_ERR(lock);
+
+ exception.inode = file_inode(filep);
+ exception.state = lock->open_context->state;
+
+ do {
+ err = _nfs42_proc_io_advise(filep, lock, offset,
count, hints);
+ if (err >= 0)
+ break;
+ if (err == -ENOTSUPP) {
+ err = -EOPNOTSUPP;
+ break;
+ }
+ err = nfs4_handle_exception(server, err, &exception);
+ } while (exception.retry);
+
+ nfs_put_lock_context(lock);
+ return err;
+}
+
static int handle_async_copy(struct nfs42_copy_res *res,
struct nfs_server *dst_server,
struct nfs_server *src_server,diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c
index d80ee88ca996..1e30729b0ed5 100644
--- a/fs/nfs/nfs42xdr.c
+++ b/fs/nfs/nfs42xdr.c
#define encode_deallocate_maxsz (op_encode_hdr_maxsz
+ \
encode_fallocate_maxsz)
#define decode_deallocate_maxsz (op_decode_hdr_maxsz)
+#define nfs42_io_advise_bitmap_maxsz 2 /* bitmap length and hints
mask */
+#define encode_io_advise_maxsz (op_encode_hdr_maxsz + \
+ encode_stateid_maxsz + \
+ 2 /* offset */ + \
+ 2 /* count */ + \
+ nfs42_io_advise_bitmap_maxsz
/* hints */)
+#define decode_io_advise_maxsz (op_decode_hdr_maxsz + \
+ nfs42_io_advise_bitmap_maxsz
/* hints */)
#define encode_read_plus_maxsz (op_encode_hdr_maxsz + \
encode_stateid_maxsz + 3)
#define NFS42_READ_PLUS_DATA_SEGMENT_SIZE \
decode_putfh_maxsz + \
decode_deallocate_maxsz + \
decode_getattr_maxsz)
+#define NFS4_enc_io_advise_sz (compound_encode_hdr_maxsz +
\
+ encode_sequence_maxsz + \
+ encode_putfh_maxsz + \
+ encode_io_advise_maxsz)
+#define NFS4_dec_io_advise_sz (compound_decode_hdr_maxsz +
\
+ decode_sequence_maxsz + \
+ decode_putfh_maxsz + \
+ decode_io_advise_maxsz)
#define NFS4_enc_read_plus_sz (compound_encode_hdr_maxsz +
\
encode_sequence_maxsz + \
encode_putfh_maxsz + \
@@ -342,6 +358,17 @@ static void encode_deallocate(struct xdr_stream
*xdr,
encode_fallocate(xdr, args);
}
+static void encode_io_advise(struct xdr_stream *xdr,
+ const struct nfs42_io_advise_args *args,
+ struct compound_hdr *hdr)
+{
+ encode_op_hdr(xdr, OP_IO_ADVISE, decode_io_advise_maxsz,
hdr);
+ encode_nfs4_stateid(xdr, &args->stateid);
+ encode_uint64(xdr, args->offset);
+ encode_uint64(xdr, args->count);
+ xdr_encode_bitmap4(xdr, &args->hints, 1);
+}
+
static void encode_read_plus(struct xdr_stream *xdr,
const struct nfs_pgio_args *args,
struct compound_hdr *hdr)@@ -764,6 +791,25 @@ static void nfs4_xdr_enc_deallocate(struct
rpc_rqst *req,
encode_nops(&hdr);
}
+/*
+ * Encode IO_ADVISE request
+ */
+static void nfs4_xdr_enc_io_advise(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const void *data)
+{
+ const struct nfs42_io_advise_args *args = data;
+ struct compound_hdr hdr = {
+ .minorversion = nfs4_xdr_minorversion(&args-quoted
seq_args),
+ };
+
+ encode_compound_hdr(xdr, req, &hdr);
+ encode_sequence(xdr, &args->seq_args, &hdr);
+ encode_putfh(xdr, args->fh, &hdr);
+ encode_io_advise(xdr, args, &hdr);
+ encode_nops(&hdr);
+}
+
/*
* Encode READ_PLUS request
*/
@@ -1034,6 +1080,19 @@ static int decode_deallocate(struct xdr_stream
*xdr, struct nfs42_falloc_res *re
return decode_op_hdr(xdr, OP_DEALLOCATE);
}
+static int decode_io_advise(struct xdr_stream *xdr, struct
nfs42_io_advise_res *res)
+{
+ int status;
+
+ status = decode_op_hdr(xdr, OP_IO_ADVISE);
+ if (status)
+ return status;
+
+ if (unlikely(decode_bitmap4(xdr, &res->hints, 1) < 0))
+ return -EIO;
+ return 0;
+}
+
struct read_plus_segment {
enum data_content4 type;
uint64_t offset;@@ -1337,6 +1396,31 @@ static int nfs4_xdr_dec_deallocate(struct
rpc_rqst *rqstp,
return status;
}
+/*
+ * Decode IO_ADVISE response
+ */
+static int nfs4_xdr_dec_io_advise(struct rpc_rqst *rqstp,
+ struct xdr_stream *xdr,
+ void *data)
+{
+ struct nfs42_io_advise_res *res = data;
+ struct compound_hdr hdr;
+ int status;
+
+ status = decode_compound_hdr(xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_sequence(xdr, &res->seq_res, rqstp);
+ if (status)
+ goto out;
+ status = decode_putfh(xdr);
+ if (status)
+ goto out;
+ status = decode_io_advise(xdr, res);
+out:
+ return status;
+}
+
/*
* Decode READ_PLUS request
*/diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 2563ed8580f3..5f6e4b5f1559 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/falloc.h>
+#include <linux/fadvise.h>
#include <linux/mount.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_ssc.h>
@@ -236,6 +237,41 @@ static long nfs42_fallocate(struct file *filep,
int mode, loff_t offset, loff_t
return nfs42_proc_allocate(filep, offset, len);
}
+static int nfs42_file_fadvise(struct file *filep, loff_t start,
loff_t len, int advice)
+{
+ int ret;
+ unsigned int hints = 0;
+
+ switch (advice) {
+ case POSIX_FADV_NORMAL:
+ hints = NFS_IO_ADVISE4_NORMAL;
+ break;
+ case POSIX_FADV_RANDOM:
+ hints = NFS_IO_ADVISE4_RANDOM;
+ break;
+ case POSIX_FADV_SEQUENTIAL:
+ hints = NFS_IO_ADVISE4_SEQUENTIAL;
+ break;
+ case POSIX_FADV_WILLNEED:
+ hints = NFS_IO_ADVISE4_WILLNEED;
+ break;
+ case POSIX_FADV_DONTNEED:
+ hints = NFS_IO_ADVISE4_DONTNEED;
+ break;
+ case POSIX_FADV_NOREUSE:
+ hints = NFS_IO_ADVISE4_NOREUSE;
+ break;
+ default:
+ goto out;
+ }
+
+ ret = nfs42_proc_io_advise(filep, start, len, hints);
+ if (ret < 0)
+ return ret;