diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 8beb2bc4c328..65179a3946f5 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -35,6 +35,7 @@
 #include <linux/fs_struct.h>
 #include <linux/file.h>
 #include <linux/falloc.h>
+#include <linux/fadvise.h>
 #include <linux/slab.h>
 #include <linux/kthread.h>
 #include <linux/namei.h>
@@ -1995,6 +1996,53 @@ nfsd4_deallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 			       FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE);
 }
 
+static __be32
+nfsd4_io_advise(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+		union nfsd4_op_u *u)
+{
+	struct nfsd4_io_advise *io_advise = &u->io_advise;
+	struct nfsd_file *nf;
+	__be32 status;
+	int advice;
+	int ret;
+
+	status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+					    &io_advise->stateid,
+					    RD_STATE, &nf, NULL);
+	if (status) {
+		dprintk("NFSD: %s: couldn't process stateid!\n", __func__);
+		return status;
+	}
+
+	/*
+	 * If multiple bits are set, select just one using the following
+	 * order of precedence
+	 */
+	if (io_advise->hints & NFS_IO_ADVISE4_NORMAL)
+		advice = POSIX_FADV_NORMAL;
+	else if (io_advise->hints & NFS_IO_ADVISE4_RANDOM)
+		advice = POSIX_FADV_RANDOM;
+	else if (io_advise->hints & NFS_IO_ADVISE4_SEQUENTIAL)
+		advice = POSIX_FADV_SEQUENTIAL;
+	else if (io_advise->hints & NFS_IO_ADVISE4_WILLNEED)
+		advice = POSIX_FADV_WILLNEED;
+	else if (io_advise->hints & NFS_IO_ADVISE4_DONTNEED)
+		advice = POSIX_FADV_DONTNEED;
+	else if (io_advise->hints & NFS_IO_ADVISE4_NOREUSE)
+		advice = POSIX_FADV_NOREUSE;
+	else {
+		status = nfserr_union_notsupp;
+		goto out;
+	}
+
+	ret = vfs_fadvise(nf->nf_file, io_advise->offset, io_advise->count, advice);
+	if (ret < 0)
+		status = nfserrno(ret);
+out:
+	nfsd_file_put(nf);
+	return status;
+}
+
 static __be32
 nfsd4_seek(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	   union nfsd4_op_u *u)
@@ -3096,6 +3144,12 @@ static u32 nfsd4_layoutreturn_rsize(const struct svc_rqst *rqstp,
 #endif /* CONFIG_NFSD_PNFS */
 
 
+static u32 nfsd4_io_advise_rsize(const struct svc_rqst *rqstp,
+				 const struct nfsd4_op *op)
+{
+	return (op_encode_hdr_size + 2) * sizeof(__be32);
+}
+
 static u32 nfsd4_seek_rsize(const struct svc_rqst *rqstp,
 			    const struct nfsd4_op *op)
 {
@@ -3479,6 +3533,11 @@ static const struct nfsd4_operation nfsd4_ops[] = {
 		.op_name = "OP_DEALLOCATE",
 		.op_rsize_bop = nfsd4_only_status_rsize,
 	},
+	[OP_IO_ADVISE] = {
+		.op_func = nfsd4_io_advise,
+		.op_name = "OP_IO_ADVISE",
+		.op_rsize_bop = nfsd4_io_advise_rsize,
+	},
 	[OP_CLONE] = {
 		.op_func = nfsd4_clone,
 		.op_flags = OP_MODIFIES_SOMETHING,
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index bcfeb1a922c0..8814c24047ff 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1882,6 +1882,22 @@ nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
 	return nfs_ok;
 }
 
+static __be32
+nfsd4_decode_io_advise(struct nfsd4_compoundargs *argp,
+		       struct nfsd4_io_advise *io_advise)
+{
+	__be32 status;
+
+	status = nfsd4_decode_stateid4(argp, &io_advise->stateid);
+	if (status)
+		return status;
+	if (xdr_stream_decode_u64(argp->xdr, &io_advise->offset) < 0)
+		return nfserr_bad_xdr;
+	if (xdr_stream_decode_u64(argp->xdr, &io_advise->count) < 0)
+		return nfserr_bad_xdr;
+	return nfsd4_decode_bitmap4(argp, &io_advise->hints, 1);
+}
+
 static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
 				      struct nl4_server *ns)
 {
@@ -2338,7 +2354,7 @@ static const nfsd4_dec nfsd4_dec_ops[] = {
 	[OP_COPY]		= (nfsd4_dec)nfsd4_decode_copy,
 	[OP_COPY_NOTIFY]	= (nfsd4_dec)nfsd4_decode_copy_notify,
 	[OP_DEALLOCATE]		= (nfsd4_dec)nfsd4_decode_fallocate,
-	[OP_IO_ADVISE]		= (nfsd4_dec)nfsd4_decode_notsupp,
+	[OP_IO_ADVISE]		= (nfsd4_dec)nfsd4_decode_io_advise,
 	[OP_LAYOUTERROR]	= (nfsd4_dec)nfsd4_decode_notsupp,
 	[OP_LAYOUTSTATS]	= (nfsd4_dec)nfsd4_decode_notsupp,
 	[OP_OFFLOAD_CANCEL]	= (nfsd4_dec)nfsd4_decode_offload_status,
@@ -4948,6 +4964,13 @@ nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
 	return nfserr;
 }
 
+static __be32
+nfsd4_encode_io_advise(struct nfsd4_compoundres *resp, __be32 nfserr,
+		       struct nfsd4_io_advise *io_advise)
+{
+	return nfsd4_encode_bitmap(resp->xdr, io_advise->hints, 0, 0);
+}
+
 static __be32
 nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
 		  struct nfsd4_seek *seek)
@@ -5282,7 +5305,7 @@ static const nfsd4_enc nfsd4_enc_ops[] = {
 	[OP_COPY]		= (nfsd4_enc)nfsd4_encode_copy,
 	[OP_COPY_NOTIFY]	= (nfsd4_enc)nfsd4_encode_copy_notify,
 	[OP_DEALLOCATE]		= (nfsd4_enc)nfsd4_encode_noop,
-	[OP_IO_ADVISE]		= (nfsd4_enc)nfsd4_encode_noop,
+	[OP_IO_ADVISE]		= (nfsd4_enc)nfsd4_encode_io_advise,
 	[OP_LAYOUTERROR]	= (nfsd4_enc)nfsd4_encode_noop,
 	[OP_LAYOUTSTATS]	= (nfsd4_enc)nfsd4_encode_noop,
 	[OP_OFFLOAD_CANCEL]	= (nfsd4_enc)nfsd4_encode_noop,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 0eb00105d845..9b8ba4d6e3ab 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -518,6 +518,16 @@ struct nfsd4_fallocate {
 	u64		falloc_length;
 };
 
+struct nfsd4_io_advise {
+	/* request */
+	stateid_t	stateid;
+	loff_t		offset;
+	u64		count;
+
+	/* both */
+	u32		hints;
+};
+
 struct nfsd4_clone {
 	/* request */
 	stateid_t	cl_src_stateid;
@@ -688,6 +698,7 @@ struct nfsd4_op {
 		/* NFSv4.2 */
 		struct nfsd4_fallocate		allocate;
 		struct nfsd4_fallocate		deallocate;
+		struct nfsd4_io_advise		io_advise;
 		struct nfsd4_clone		clone;
 		struct nfsd4_copy		copy;
 		struct nfsd4_offload_status	offload_status;
