diff mbox

[v6,01/10] NFSD CB_OFFLOAD xdr

Message ID 20171024174752.74910-2-kolga@netapp.com (mailing list archive)
State New, archived
Headers show

Commit Message

Olga Kornievskaia Oct. 24, 2017, 5:47 p.m. UTC
Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
---
 fs/nfsd/nfs4callback.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nfsd/state.h        |  1 +
 fs/nfsd/xdr4.h         |  6 ++++
 fs/nfsd/xdr4cb.h       | 10 ++++++
 4 files changed, 114 insertions(+)

Comments

J. Bruce Fields Jan. 25, 2018, 4:43 p.m. UTC | #1
On Tue, Oct 24, 2017 at 01:47:43PM -0400, Olga Kornievskaia wrote:
> Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
> ---
>  fs/nfsd/nfs4callback.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/nfsd/state.h        |  1 +
>  fs/nfsd/xdr4.h         |  6 ++++
>  fs/nfsd/xdr4cb.h       | 10 ++++++
>  4 files changed, 114 insertions(+)
> 
> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
> index 49b0a9e..d12d914 100644
> --- a/fs/nfsd/nfs4callback.c
> +++ b/fs/nfsd/nfs4callback.c
> @@ -39,6 +39,7 @@
>  #include "state.h"
>  #include "netns.h"
>  #include "xdr4cb.h"
> +#include "xdr4.h"
>  
>  #define NFSDDBG_FACILITY                NFSDDBG_PROC
>  
> @@ -105,6 +106,7 @@ enum nfs_cb_opnum4 {
>  	OP_CB_WANTS_CANCELLED		= 12,
>  	OP_CB_NOTIFY_LOCK		= 13,
>  	OP_CB_NOTIFY_DEVICEID		= 14,
> +	OP_CB_OFFLOAD			= 15,
>  	OP_CB_ILLEGAL			= 10044
>  };
>  
> @@ -683,6 +685,100 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
>  }
>  
>  /*
> + * struct write_response4 {
> + *	stateid4	wr_callback_id<1>;
> + *	length4		wr_count;
> + *	stable_how4	wr_committed;
> + *	verifier4	wr_writeverf;
> + * };
> + * union offload_info4 switch (nfsstat4 coa_status) {
> + *	case NFS4_OK:
> + *		write_response4	coa_resok4;
> + *	default:
> + *	length4		coa_bytes_copied;
> + * };
> + * struct CB_OFFLOAD4args {
> + *	nfs_fh4		coa_fh;
> + *	stateid4	coa_stateid;
> + *	offload_info4	coa_offload_info;
> + * };
> + */
> +static void encode_offload_info4(struct xdr_stream *xdr,
> +				 __be32 nfserr,
> +				 const struct nfsd4_copy *cp)
> +{
> +	__be32 *p;
> +
> +	p = xdr_reserve_space(xdr, 4);
> +	*p++ = nfserr;
> +	if (!nfserr) {
> +		p = xdr_reserve_space(xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
> +		p = xdr_encode_empty_array(p);
> +		p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written);
> +		*p++ = cpu_to_be32(cp->cp_res.wr_stable_how);
> +		p = xdr_encode_opaque_fixed(p, cp->cp_res.wr_verifier.data,
> +					    NFS4_VERIFIER_SIZE);
> +	} else {
> +		p = xdr_reserve_space(xdr, 8);
> +		p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written);

Nit: since we're not using this any more, may as well just make that:

		/* We always return success if bytes were written: */
		p = xdr_encode_hyper(p, 0);

--b.

> +	}
> +}
> +
> +static void encode_cb_offload4args(struct xdr_stream *xdr,
> +				   __be32 nfserr,
> +				   const struct knfsd_fh *fh,
> +				   const struct nfsd4_copy *cp,
> +				   struct nfs4_cb_compound_hdr *hdr)
> +{
> +	__be32 *p;
> +
> +	p = xdr_reserve_space(xdr, 4);
> +	*p++ = cpu_to_be32(OP_CB_OFFLOAD);
> +	encode_nfs_fh4(xdr, fh);
> +	encode_stateid4(xdr, &cp->cp_res.cb_stateid);
> +	encode_offload_info4(xdr, nfserr, cp);
> +
> +	hdr->nops++;
> +}
> +
> +static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req,
> +				    struct xdr_stream *xdr,
> +				    const void *data)
> +{
> +	const struct nfsd4_callback *cb = data;
> +	const struct nfsd4_copy *cp =
> +		container_of(cb, struct nfsd4_copy, cp_cb);
> +	struct nfs4_cb_compound_hdr hdr = {
> +		.ident = 0,
> +		.minorversion = cb->cb_clp->cl_minorversion,
> +	};
> +
> +	encode_cb_compound4args(xdr, &hdr);
> +	encode_cb_sequence4args(xdr, cb, &hdr);
> +	encode_cb_offload4args(xdr, cp->nfserr, &cp->fh, cp, &hdr);
> +	encode_cb_nops(&hdr);
> +}
> +
> +static int nfs4_xdr_dec_cb_offload(struct rpc_rqst *rqstp,
> +				   struct xdr_stream *xdr,
> +				   void *data)
> +{
> +	struct nfsd4_callback *cb = data;
> +	struct nfs4_cb_compound_hdr hdr;
> +	int status;
> +
> +	status = decode_cb_compound4res(xdr, &hdr);
> +	if (unlikely(status))
> +		return status;
> +
> +	if (cb) {
> +		status = decode_cb_sequence4res(xdr, cb);
> +		if (unlikely(status || cb->cb_seq_status))
> +			return status;
> +	}
> +	return decode_cb_op_status(xdr, OP_CB_OFFLOAD, &cb->cb_status);
> +}
> +/*
>   * RPC procedure tables
>   */
>  #define PROC(proc, call, argtype, restype)				\
> @@ -703,6 +799,7 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
>  	PROC(CB_LAYOUT,	COMPOUND,	cb_layout,	cb_layout),
>  #endif
>  	PROC(CB_NOTIFY_LOCK,	COMPOUND,	cb_notify_lock,	cb_notify_lock),
> +	PROC(CB_OFFLOAD,	COMPOUND,	cb_offload,	cb_offload),
>  };
>  
>  static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)];
> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> index 005c911..f8b0210 100644
> --- a/fs/nfsd/state.h
> +++ b/fs/nfsd/state.h
> @@ -570,6 +570,7 @@ enum nfsd4_cb_op {
>  	NFSPROC4_CLNT_CB_NULL = 0,
>  	NFSPROC4_CLNT_CB_RECALL,
>  	NFSPROC4_CLNT_CB_LAYOUT,
> +	NFSPROC4_CLNT_CB_OFFLOAD,
>  	NFSPROC4_CLNT_CB_SEQUENCE,
>  	NFSPROC4_CLNT_CB_NOTIFY_LOCK,
>  };
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index 1e4edbf..4ac2676 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -507,6 +507,7 @@ struct nfsd42_write_res {
>  	u64			wr_bytes_written;
>  	u32			wr_stable_how;
>  	nfs4_verifier		wr_verifier;
> +	stateid_t		cb_stateid;
>  };
>  
>  struct nfsd4_copy {
> @@ -523,6 +524,11 @@ struct nfsd4_copy {
>  
>  	/* response */
>  	struct nfsd42_write_res	cp_res;
> +
> +	/* for cb_offload */
> +	struct nfsd4_callback	cp_cb;
> +	__be32			nfserr;
> +	struct knfsd_fh		fh;
>  };
>  
>  struct nfsd4_seek {
> diff --git a/fs/nfsd/xdr4cb.h b/fs/nfsd/xdr4cb.h
> index 49b719d..7e39913 100644
> --- a/fs/nfsd/xdr4cb.h
> +++ b/fs/nfsd/xdr4cb.h
> @@ -37,3 +37,13 @@
>  #define NFS4_dec_cb_notify_lock_sz	(cb_compound_dec_hdr_sz  +      \
>  					cb_sequence_dec_sz +            \
>  					op_dec_sz)
> +#define enc_cb_offload_info_sz		(1 + 1 + 2 + 1 +		\
> +					XDR_QUADLEN(NFS4_VERIFIER_SIZE))
> +#define NFS4_enc_cb_offload_sz		(cb_compound_enc_hdr_sz +       \
> +					cb_sequence_enc_sz +            \
> +					enc_nfs4_fh_sz +		\
> +					enc_stateid_sz +		\
> +					enc_cb_offload_info_sz)
> +#define NFS4_dec_cb_offload_sz		(cb_compound_dec_hdr_sz  +      \
> +					cb_sequence_dec_sz +            \
> +					op_dec_sz)
> -- 
> 1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Olga Kornievskaia Jan. 26, 2018, 3:16 p.m. UTC | #2
On Thu, Jan 25, 2018 at 11:43 AM, J. Bruce Fields <bfields@fieldses.org> wrote:
> On Tue, Oct 24, 2017 at 01:47:43PM -0400, Olga Kornievskaia wrote:
>> Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
>> ---
>>  fs/nfsd/nfs4callback.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  fs/nfsd/state.h        |  1 +
>>  fs/nfsd/xdr4.h         |  6 ++++
>>  fs/nfsd/xdr4cb.h       | 10 ++++++
>>  4 files changed, 114 insertions(+)
>>
>> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
>> index 49b0a9e..d12d914 100644
>> --- a/fs/nfsd/nfs4callback.c
>> +++ b/fs/nfsd/nfs4callback.c
>> @@ -39,6 +39,7 @@
>>  #include "state.h"
>>  #include "netns.h"
>>  #include "xdr4cb.h"
>> +#include "xdr4.h"
>>
>>  #define NFSDDBG_FACILITY                NFSDDBG_PROC
>>
>> @@ -105,6 +106,7 @@ enum nfs_cb_opnum4 {
>>       OP_CB_WANTS_CANCELLED           = 12,
>>       OP_CB_NOTIFY_LOCK               = 13,
>>       OP_CB_NOTIFY_DEVICEID           = 14,
>> +     OP_CB_OFFLOAD                   = 15,
>>       OP_CB_ILLEGAL                   = 10044
>>  };
>>
>> @@ -683,6 +685,100 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
>>  }
>>
>>  /*
>> + * struct write_response4 {
>> + *   stateid4        wr_callback_id<1>;
>> + *   length4         wr_count;
>> + *   stable_how4     wr_committed;
>> + *   verifier4       wr_writeverf;
>> + * };
>> + * union offload_info4 switch (nfsstat4 coa_status) {
>> + *   case NFS4_OK:
>> + *           write_response4 coa_resok4;
>> + *   default:
>> + *   length4         coa_bytes_copied;
>> + * };
>> + * struct CB_OFFLOAD4args {
>> + *   nfs_fh4         coa_fh;
>> + *   stateid4        coa_stateid;
>> + *   offload_info4   coa_offload_info;
>> + * };
>> + */
>> +static void encode_offload_info4(struct xdr_stream *xdr,
>> +                              __be32 nfserr,
>> +                              const struct nfsd4_copy *cp)
>> +{
>> +     __be32 *p;
>> +
>> +     p = xdr_reserve_space(xdr, 4);
>> +     *p++ = nfserr;
>> +     if (!nfserr) {
>> +             p = xdr_reserve_space(xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
>> +             p = xdr_encode_empty_array(p);
>> +             p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written);
>> +             *p++ = cpu_to_be32(cp->cp_res.wr_stable_how);
>> +             p = xdr_encode_opaque_fixed(p, cp->cp_res.wr_verifier.data,
>> +                                         NFS4_VERIFIER_SIZE);
>> +     } else {
>> +             p = xdr_reserve_space(xdr, 8);
>> +             p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written);
>
> Nit: since we're not using this any more, may as well just make that:
>
>                 /* We always return success if bytes were written: */
>                 p = xdr_encode_hyper(p, 0);

Sure I can do this.

>
> --b.
>
>> +     }
>> +}
>> +
>> +static void encode_cb_offload4args(struct xdr_stream *xdr,
>> +                                __be32 nfserr,
>> +                                const struct knfsd_fh *fh,
>> +                                const struct nfsd4_copy *cp,
>> +                                struct nfs4_cb_compound_hdr *hdr)
>> +{
>> +     __be32 *p;
>> +
>> +     p = xdr_reserve_space(xdr, 4);
>> +     *p++ = cpu_to_be32(OP_CB_OFFLOAD);
>> +     encode_nfs_fh4(xdr, fh);
>> +     encode_stateid4(xdr, &cp->cp_res.cb_stateid);
>> +     encode_offload_info4(xdr, nfserr, cp);
>> +
>> +     hdr->nops++;
>> +}
>> +
>> +static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req,
>> +                                 struct xdr_stream *xdr,
>> +                                 const void *data)
>> +{
>> +     const struct nfsd4_callback *cb = data;
>> +     const struct nfsd4_copy *cp =
>> +             container_of(cb, struct nfsd4_copy, cp_cb);
>> +     struct nfs4_cb_compound_hdr hdr = {
>> +             .ident = 0,
>> +             .minorversion = cb->cb_clp->cl_minorversion,
>> +     };
>> +
>> +     encode_cb_compound4args(xdr, &hdr);
>> +     encode_cb_sequence4args(xdr, cb, &hdr);
>> +     encode_cb_offload4args(xdr, cp->nfserr, &cp->fh, cp, &hdr);
>> +     encode_cb_nops(&hdr);
>> +}
>> +
>> +static int nfs4_xdr_dec_cb_offload(struct rpc_rqst *rqstp,
>> +                                struct xdr_stream *xdr,
>> +                                void *data)
>> +{
>> +     struct nfsd4_callback *cb = data;
>> +     struct nfs4_cb_compound_hdr hdr;
>> +     int status;
>> +
>> +     status = decode_cb_compound4res(xdr, &hdr);
>> +     if (unlikely(status))
>> +             return status;
>> +
>> +     if (cb) {
>> +             status = decode_cb_sequence4res(xdr, cb);
>> +             if (unlikely(status || cb->cb_seq_status))
>> +                     return status;
>> +     }
>> +     return decode_cb_op_status(xdr, OP_CB_OFFLOAD, &cb->cb_status);
>> +}
>> +/*
>>   * RPC procedure tables
>>   */
>>  #define PROC(proc, call, argtype, restype)                           \
>> @@ -703,6 +799,7 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
>>       PROC(CB_LAYOUT, COMPOUND,       cb_layout,      cb_layout),
>>  #endif
>>       PROC(CB_NOTIFY_LOCK,    COMPOUND,       cb_notify_lock, cb_notify_lock),
>> +     PROC(CB_OFFLOAD,        COMPOUND,       cb_offload,     cb_offload),
>>  };
>>
>>  static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)];
>> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
>> index 005c911..f8b0210 100644
>> --- a/fs/nfsd/state.h
>> +++ b/fs/nfsd/state.h
>> @@ -570,6 +570,7 @@ enum nfsd4_cb_op {
>>       NFSPROC4_CLNT_CB_NULL = 0,
>>       NFSPROC4_CLNT_CB_RECALL,
>>       NFSPROC4_CLNT_CB_LAYOUT,
>> +     NFSPROC4_CLNT_CB_OFFLOAD,
>>       NFSPROC4_CLNT_CB_SEQUENCE,
>>       NFSPROC4_CLNT_CB_NOTIFY_LOCK,
>>  };
>> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
>> index 1e4edbf..4ac2676 100644
>> --- a/fs/nfsd/xdr4.h
>> +++ b/fs/nfsd/xdr4.h
>> @@ -507,6 +507,7 @@ struct nfsd42_write_res {
>>       u64                     wr_bytes_written;
>>       u32                     wr_stable_how;
>>       nfs4_verifier           wr_verifier;
>> +     stateid_t               cb_stateid;
>>  };
>>
>>  struct nfsd4_copy {
>> @@ -523,6 +524,11 @@ struct nfsd4_copy {
>>
>>       /* response */
>>       struct nfsd42_write_res cp_res;
>> +
>> +     /* for cb_offload */
>> +     struct nfsd4_callback   cp_cb;
>> +     __be32                  nfserr;
>> +     struct knfsd_fh         fh;
>>  };
>>
>>  struct nfsd4_seek {
>> diff --git a/fs/nfsd/xdr4cb.h b/fs/nfsd/xdr4cb.h
>> index 49b719d..7e39913 100644
>> --- a/fs/nfsd/xdr4cb.h
>> +++ b/fs/nfsd/xdr4cb.h
>> @@ -37,3 +37,13 @@
>>  #define NFS4_dec_cb_notify_lock_sz   (cb_compound_dec_hdr_sz  +      \
>>                                       cb_sequence_dec_sz +            \
>>                                       op_dec_sz)
>> +#define enc_cb_offload_info_sz               (1 + 1 + 2 + 1 +                \
>> +                                     XDR_QUADLEN(NFS4_VERIFIER_SIZE))
>> +#define NFS4_enc_cb_offload_sz               (cb_compound_enc_hdr_sz +       \
>> +                                     cb_sequence_enc_sz +            \
>> +                                     enc_nfs4_fh_sz +                \
>> +                                     enc_stateid_sz +                \
>> +                                     enc_cb_offload_info_sz)
>> +#define NFS4_dec_cb_offload_sz               (cb_compound_dec_hdr_sz  +      \
>> +                                     cb_sequence_dec_sz +            \
>> +                                     op_dec_sz)
>> --
>> 1.8.3.1
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 49b0a9e..d12d914 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -39,6 +39,7 @@ 
 #include "state.h"
 #include "netns.h"
 #include "xdr4cb.h"
+#include "xdr4.h"
 
 #define NFSDDBG_FACILITY                NFSDDBG_PROC
 
@@ -105,6 +106,7 @@  enum nfs_cb_opnum4 {
 	OP_CB_WANTS_CANCELLED		= 12,
 	OP_CB_NOTIFY_LOCK		= 13,
 	OP_CB_NOTIFY_DEVICEID		= 14,
+	OP_CB_OFFLOAD			= 15,
 	OP_CB_ILLEGAL			= 10044
 };
 
@@ -683,6 +685,100 @@  static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
 }
 
 /*
+ * struct write_response4 {
+ *	stateid4	wr_callback_id<1>;
+ *	length4		wr_count;
+ *	stable_how4	wr_committed;
+ *	verifier4	wr_writeverf;
+ * };
+ * union offload_info4 switch (nfsstat4 coa_status) {
+ *	case NFS4_OK:
+ *		write_response4	coa_resok4;
+ *	default:
+ *	length4		coa_bytes_copied;
+ * };
+ * struct CB_OFFLOAD4args {
+ *	nfs_fh4		coa_fh;
+ *	stateid4	coa_stateid;
+ *	offload_info4	coa_offload_info;
+ * };
+ */
+static void encode_offload_info4(struct xdr_stream *xdr,
+				 __be32 nfserr,
+				 const struct nfsd4_copy *cp)
+{
+	__be32 *p;
+
+	p = xdr_reserve_space(xdr, 4);
+	*p++ = nfserr;
+	if (!nfserr) {
+		p = xdr_reserve_space(xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
+		p = xdr_encode_empty_array(p);
+		p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written);
+		*p++ = cpu_to_be32(cp->cp_res.wr_stable_how);
+		p = xdr_encode_opaque_fixed(p, cp->cp_res.wr_verifier.data,
+					    NFS4_VERIFIER_SIZE);
+	} else {
+		p = xdr_reserve_space(xdr, 8);
+		p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written);
+	}
+}
+
+static void encode_cb_offload4args(struct xdr_stream *xdr,
+				   __be32 nfserr,
+				   const struct knfsd_fh *fh,
+				   const struct nfsd4_copy *cp,
+				   struct nfs4_cb_compound_hdr *hdr)
+{
+	__be32 *p;
+
+	p = xdr_reserve_space(xdr, 4);
+	*p++ = cpu_to_be32(OP_CB_OFFLOAD);
+	encode_nfs_fh4(xdr, fh);
+	encode_stateid4(xdr, &cp->cp_res.cb_stateid);
+	encode_offload_info4(xdr, nfserr, cp);
+
+	hdr->nops++;
+}
+
+static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req,
+				    struct xdr_stream *xdr,
+				    const void *data)
+{
+	const struct nfsd4_callback *cb = data;
+	const struct nfsd4_copy *cp =
+		container_of(cb, struct nfsd4_copy, cp_cb);
+	struct nfs4_cb_compound_hdr hdr = {
+		.ident = 0,
+		.minorversion = cb->cb_clp->cl_minorversion,
+	};
+
+	encode_cb_compound4args(xdr, &hdr);
+	encode_cb_sequence4args(xdr, cb, &hdr);
+	encode_cb_offload4args(xdr, cp->nfserr, &cp->fh, cp, &hdr);
+	encode_cb_nops(&hdr);
+}
+
+static int nfs4_xdr_dec_cb_offload(struct rpc_rqst *rqstp,
+				   struct xdr_stream *xdr,
+				   void *data)
+{
+	struct nfsd4_callback *cb = data;
+	struct nfs4_cb_compound_hdr hdr;
+	int status;
+
+	status = decode_cb_compound4res(xdr, &hdr);
+	if (unlikely(status))
+		return status;
+
+	if (cb) {
+		status = decode_cb_sequence4res(xdr, cb);
+		if (unlikely(status || cb->cb_seq_status))
+			return status;
+	}
+	return decode_cb_op_status(xdr, OP_CB_OFFLOAD, &cb->cb_status);
+}
+/*
  * RPC procedure tables
  */
 #define PROC(proc, call, argtype, restype)				\
@@ -703,6 +799,7 @@  static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
 	PROC(CB_LAYOUT,	COMPOUND,	cb_layout,	cb_layout),
 #endif
 	PROC(CB_NOTIFY_LOCK,	COMPOUND,	cb_notify_lock,	cb_notify_lock),
+	PROC(CB_OFFLOAD,	COMPOUND,	cb_offload,	cb_offload),
 };
 
 static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)];
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 005c911..f8b0210 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -570,6 +570,7 @@  enum nfsd4_cb_op {
 	NFSPROC4_CLNT_CB_NULL = 0,
 	NFSPROC4_CLNT_CB_RECALL,
 	NFSPROC4_CLNT_CB_LAYOUT,
+	NFSPROC4_CLNT_CB_OFFLOAD,
 	NFSPROC4_CLNT_CB_SEQUENCE,
 	NFSPROC4_CLNT_CB_NOTIFY_LOCK,
 };
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 1e4edbf..4ac2676 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -507,6 +507,7 @@  struct nfsd42_write_res {
 	u64			wr_bytes_written;
 	u32			wr_stable_how;
 	nfs4_verifier		wr_verifier;
+	stateid_t		cb_stateid;
 };
 
 struct nfsd4_copy {
@@ -523,6 +524,11 @@  struct nfsd4_copy {
 
 	/* response */
 	struct nfsd42_write_res	cp_res;
+
+	/* for cb_offload */
+	struct nfsd4_callback	cp_cb;
+	__be32			nfserr;
+	struct knfsd_fh		fh;
 };
 
 struct nfsd4_seek {
diff --git a/fs/nfsd/xdr4cb.h b/fs/nfsd/xdr4cb.h
index 49b719d..7e39913 100644
--- a/fs/nfsd/xdr4cb.h
+++ b/fs/nfsd/xdr4cb.h
@@ -37,3 +37,13 @@ 
 #define NFS4_dec_cb_notify_lock_sz	(cb_compound_dec_hdr_sz  +      \
 					cb_sequence_dec_sz +            \
 					op_dec_sz)
+#define enc_cb_offload_info_sz		(1 + 1 + 2 + 1 +		\
+					XDR_QUADLEN(NFS4_VERIFIER_SIZE))
+#define NFS4_enc_cb_offload_sz		(cb_compound_enc_hdr_sz +       \
+					cb_sequence_enc_sz +            \
+					enc_nfs4_fh_sz +		\
+					enc_stateid_sz +		\
+					enc_cb_offload_info_sz)
+#define NFS4_dec_cb_offload_sz		(cb_compound_dec_hdr_sz  +      \
+					cb_sequence_dec_sz +            \
+					op_dec_sz)