162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  Copyright (c) 2001 The Regents of the University of Michigan.
362306a36Sopenharmony_ci *  All rights reserved.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Kendrick Smith <kmsmith@umich.edu>
662306a36Sopenharmony_ci *  Andy Adamson <andros@umich.edu>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *  Redistribution and use in source and binary forms, with or without
962306a36Sopenharmony_ci *  modification, are permitted provided that the following conditions
1062306a36Sopenharmony_ci *  are met:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  1. Redistributions of source code must retain the above copyright
1362306a36Sopenharmony_ci *     notice, this list of conditions and the following disclaimer.
1462306a36Sopenharmony_ci *  2. Redistributions in binary form must reproduce the above copyright
1562306a36Sopenharmony_ci *     notice, this list of conditions and the following disclaimer in the
1662306a36Sopenharmony_ci *     documentation and/or other materials provided with the distribution.
1762306a36Sopenharmony_ci *  3. Neither the name of the University nor the names of its
1862306a36Sopenharmony_ci *     contributors may be used to endorse or promote products derived
1962306a36Sopenharmony_ci *     from this software without specific prior written permission.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
2262306a36Sopenharmony_ci *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2362306a36Sopenharmony_ci *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2462306a36Sopenharmony_ci *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2562306a36Sopenharmony_ci *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2662306a36Sopenharmony_ci *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2762306a36Sopenharmony_ci *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2862306a36Sopenharmony_ci *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
2962306a36Sopenharmony_ci *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3062306a36Sopenharmony_ci *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3162306a36Sopenharmony_ci *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h>
3562306a36Sopenharmony_ci#include <linux/sunrpc/xprt.h>
3662306a36Sopenharmony_ci#include <linux/sunrpc/svc_xprt.h>
3762306a36Sopenharmony_ci#include <linux/slab.h>
3862306a36Sopenharmony_ci#include "nfsd.h"
3962306a36Sopenharmony_ci#include "state.h"
4062306a36Sopenharmony_ci#include "netns.h"
4162306a36Sopenharmony_ci#include "trace.h"
4262306a36Sopenharmony_ci#include "xdr4cb.h"
4362306a36Sopenharmony_ci#include "xdr4.h"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define NFSDDBG_FACILITY                NFSDDBG_PROC
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic void nfsd4_mark_cb_fault(struct nfs4_client *, int reason);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define NFSPROC4_CB_NULL 0
5062306a36Sopenharmony_ci#define NFSPROC4_CB_COMPOUND 1
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* Index of predefined Linux callback client operations */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct nfs4_cb_compound_hdr {
5562306a36Sopenharmony_ci	/* args */
5662306a36Sopenharmony_ci	u32		ident;	/* minorversion 0 only */
5762306a36Sopenharmony_ci	u32		nops;
5862306a36Sopenharmony_ci	__be32		*nops_p;
5962306a36Sopenharmony_ci	u32		minorversion;
6062306a36Sopenharmony_ci	/* res */
6162306a36Sopenharmony_ci	int		status;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic __be32 *xdr_encode_empty_array(__be32 *p)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	*p++ = xdr_zero;
6762306a36Sopenharmony_ci	return p;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/*
7162306a36Sopenharmony_ci * Encode/decode NFSv4 CB basic data types
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci * Basic NFSv4 callback data types are defined in section 15 of RFC
7462306a36Sopenharmony_ci * 3530: "Network File System (NFS) version 4 Protocol" and section
7562306a36Sopenharmony_ci * 20 of RFC 5661: "Network File System (NFS) Version 4 Minor Version
7662306a36Sopenharmony_ci * 1 Protocol"
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void encode_uint32(struct xdr_stream *xdr, u32 n)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	WARN_ON_ONCE(xdr_stream_encode_u32(xdr, n) < 0);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void encode_bitmap4(struct xdr_stream *xdr, const __u32 *bitmap,
8562306a36Sopenharmony_ci			   size_t len)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	WARN_ON_ONCE(xdr_stream_encode_uint32_array(xdr, bitmap, len) < 0);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/*
9162306a36Sopenharmony_ci *	nfs_cb_opnum4
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci *	enum nfs_cb_opnum4 {
9462306a36Sopenharmony_ci *		OP_CB_GETATTR		= 3,
9562306a36Sopenharmony_ci *		  ...
9662306a36Sopenharmony_ci *	};
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_cienum nfs_cb_opnum4 {
9962306a36Sopenharmony_ci	OP_CB_GETATTR			= 3,
10062306a36Sopenharmony_ci	OP_CB_RECALL			= 4,
10162306a36Sopenharmony_ci	OP_CB_LAYOUTRECALL		= 5,
10262306a36Sopenharmony_ci	OP_CB_NOTIFY			= 6,
10362306a36Sopenharmony_ci	OP_CB_PUSH_DELEG		= 7,
10462306a36Sopenharmony_ci	OP_CB_RECALL_ANY		= 8,
10562306a36Sopenharmony_ci	OP_CB_RECALLABLE_OBJ_AVAIL	= 9,
10662306a36Sopenharmony_ci	OP_CB_RECALL_SLOT		= 10,
10762306a36Sopenharmony_ci	OP_CB_SEQUENCE			= 11,
10862306a36Sopenharmony_ci	OP_CB_WANTS_CANCELLED		= 12,
10962306a36Sopenharmony_ci	OP_CB_NOTIFY_LOCK		= 13,
11062306a36Sopenharmony_ci	OP_CB_NOTIFY_DEVICEID		= 14,
11162306a36Sopenharmony_ci	OP_CB_OFFLOAD			= 15,
11262306a36Sopenharmony_ci	OP_CB_ILLEGAL			= 10044
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void encode_nfs_cb_opnum4(struct xdr_stream *xdr, enum nfs_cb_opnum4 op)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	__be32 *p;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 4);
12062306a36Sopenharmony_ci	*p = cpu_to_be32(op);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/*
12462306a36Sopenharmony_ci * nfs_fh4
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci *	typedef opaque nfs_fh4<NFS4_FHSIZE>;
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_cistatic void encode_nfs_fh4(struct xdr_stream *xdr, const struct knfsd_fh *fh)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	u32 length = fh->fh_size;
13162306a36Sopenharmony_ci	__be32 *p;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	BUG_ON(length > NFS4_FHSIZE);
13462306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 4 + length);
13562306a36Sopenharmony_ci	xdr_encode_opaque(p, &fh->fh_raw, length);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/*
13962306a36Sopenharmony_ci * stateid4
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci *	struct stateid4 {
14262306a36Sopenharmony_ci *		uint32_t	seqid;
14362306a36Sopenharmony_ci *		opaque		other[12];
14462306a36Sopenharmony_ci *	};
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistatic void encode_stateid4(struct xdr_stream *xdr, const stateid_t *sid)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	__be32 *p;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, NFS4_STATEID_SIZE);
15162306a36Sopenharmony_ci	*p++ = cpu_to_be32(sid->si_generation);
15262306a36Sopenharmony_ci	xdr_encode_opaque_fixed(p, &sid->si_opaque, NFS4_STATEID_OTHER_SIZE);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * sessionid4
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci *	typedef opaque sessionid4[NFS4_SESSIONID_SIZE];
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_cistatic void encode_sessionid4(struct xdr_stream *xdr,
16162306a36Sopenharmony_ci			      const struct nfsd4_session *session)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	__be32 *p;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN);
16662306a36Sopenharmony_ci	xdr_encode_opaque_fixed(p, session->se_sessionid.data,
16762306a36Sopenharmony_ci					NFS4_MAX_SESSIONID_LEN);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/*
17162306a36Sopenharmony_ci * nfsstat4
17262306a36Sopenharmony_ci */
17362306a36Sopenharmony_cistatic const struct {
17462306a36Sopenharmony_ci	int stat;
17562306a36Sopenharmony_ci	int errno;
17662306a36Sopenharmony_ci} nfs_cb_errtbl[] = {
17762306a36Sopenharmony_ci	{ NFS4_OK,		0		},
17862306a36Sopenharmony_ci	{ NFS4ERR_PERM,		-EPERM		},
17962306a36Sopenharmony_ci	{ NFS4ERR_NOENT,	-ENOENT		},
18062306a36Sopenharmony_ci	{ NFS4ERR_IO,		-EIO		},
18162306a36Sopenharmony_ci	{ NFS4ERR_NXIO,		-ENXIO		},
18262306a36Sopenharmony_ci	{ NFS4ERR_ACCESS,	-EACCES		},
18362306a36Sopenharmony_ci	{ NFS4ERR_EXIST,	-EEXIST		},
18462306a36Sopenharmony_ci	{ NFS4ERR_XDEV,		-EXDEV		},
18562306a36Sopenharmony_ci	{ NFS4ERR_NOTDIR,	-ENOTDIR	},
18662306a36Sopenharmony_ci	{ NFS4ERR_ISDIR,	-EISDIR		},
18762306a36Sopenharmony_ci	{ NFS4ERR_INVAL,	-EINVAL		},
18862306a36Sopenharmony_ci	{ NFS4ERR_FBIG,		-EFBIG		},
18962306a36Sopenharmony_ci	{ NFS4ERR_NOSPC,	-ENOSPC		},
19062306a36Sopenharmony_ci	{ NFS4ERR_ROFS,		-EROFS		},
19162306a36Sopenharmony_ci	{ NFS4ERR_MLINK,	-EMLINK		},
19262306a36Sopenharmony_ci	{ NFS4ERR_NAMETOOLONG,	-ENAMETOOLONG	},
19362306a36Sopenharmony_ci	{ NFS4ERR_NOTEMPTY,	-ENOTEMPTY	},
19462306a36Sopenharmony_ci	{ NFS4ERR_DQUOT,	-EDQUOT		},
19562306a36Sopenharmony_ci	{ NFS4ERR_STALE,	-ESTALE		},
19662306a36Sopenharmony_ci	{ NFS4ERR_BADHANDLE,	-EBADHANDLE	},
19762306a36Sopenharmony_ci	{ NFS4ERR_BAD_COOKIE,	-EBADCOOKIE	},
19862306a36Sopenharmony_ci	{ NFS4ERR_NOTSUPP,	-ENOTSUPP	},
19962306a36Sopenharmony_ci	{ NFS4ERR_TOOSMALL,	-ETOOSMALL	},
20062306a36Sopenharmony_ci	{ NFS4ERR_SERVERFAULT,	-ESERVERFAULT	},
20162306a36Sopenharmony_ci	{ NFS4ERR_BADTYPE,	-EBADTYPE	},
20262306a36Sopenharmony_ci	{ NFS4ERR_LOCKED,	-EAGAIN		},
20362306a36Sopenharmony_ci	{ NFS4ERR_RESOURCE,	-EREMOTEIO	},
20462306a36Sopenharmony_ci	{ NFS4ERR_SYMLINK,	-ELOOP		},
20562306a36Sopenharmony_ci	{ NFS4ERR_OP_ILLEGAL,	-EOPNOTSUPP	},
20662306a36Sopenharmony_ci	{ NFS4ERR_DEADLOCK,	-EDEADLK	},
20762306a36Sopenharmony_ci	{ -1,			-EIO		}
20862306a36Sopenharmony_ci};
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci/*
21162306a36Sopenharmony_ci * If we cannot translate the error, the recovery routines should
21262306a36Sopenharmony_ci * handle it.
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * Note: remaining NFSv4 error codes have values > 10000, so should
21562306a36Sopenharmony_ci * not conflict with native Linux error codes.
21662306a36Sopenharmony_ci */
21762306a36Sopenharmony_cistatic int nfs_cb_stat_to_errno(int status)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	int i;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
22262306a36Sopenharmony_ci		if (nfs_cb_errtbl[i].stat == status)
22362306a36Sopenharmony_ci			return nfs_cb_errtbl[i].errno;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	dprintk("NFSD: Unrecognized NFS CB status value: %u\n", status);
22762306a36Sopenharmony_ci	return -status;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int decode_cb_op_status(struct xdr_stream *xdr,
23162306a36Sopenharmony_ci			       enum nfs_cb_opnum4 expected, int *status)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	__be32 *p;
23462306a36Sopenharmony_ci	u32 op;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, 4 + 4);
23762306a36Sopenharmony_ci	if (unlikely(p == NULL))
23862306a36Sopenharmony_ci		goto out_overflow;
23962306a36Sopenharmony_ci	op = be32_to_cpup(p++);
24062306a36Sopenharmony_ci	if (unlikely(op != expected))
24162306a36Sopenharmony_ci		goto out_unexpected;
24262306a36Sopenharmony_ci	*status = nfs_cb_stat_to_errno(be32_to_cpup(p));
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ciout_overflow:
24562306a36Sopenharmony_ci	return -EIO;
24662306a36Sopenharmony_ciout_unexpected:
24762306a36Sopenharmony_ci	dprintk("NFSD: Callback server returned operation %d but "
24862306a36Sopenharmony_ci		"we issued a request for %d\n", op, expected);
24962306a36Sopenharmony_ci	return -EIO;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/*
25362306a36Sopenharmony_ci * CB_COMPOUND4args
25462306a36Sopenharmony_ci *
25562306a36Sopenharmony_ci *	struct CB_COMPOUND4args {
25662306a36Sopenharmony_ci *		utf8str_cs	tag;
25762306a36Sopenharmony_ci *		uint32_t	minorversion;
25862306a36Sopenharmony_ci *		uint32_t	callback_ident;
25962306a36Sopenharmony_ci *		nfs_cb_argop4	argarray<>;
26062306a36Sopenharmony_ci *	};
26162306a36Sopenharmony_ci*/
26262306a36Sopenharmony_cistatic void encode_cb_compound4args(struct xdr_stream *xdr,
26362306a36Sopenharmony_ci				    struct nfs4_cb_compound_hdr *hdr)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	__be32 * p;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
26862306a36Sopenharmony_ci	p = xdr_encode_empty_array(p);		/* empty tag */
26962306a36Sopenharmony_ci	*p++ = cpu_to_be32(hdr->minorversion);
27062306a36Sopenharmony_ci	*p++ = cpu_to_be32(hdr->ident);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	hdr->nops_p = p;
27362306a36Sopenharmony_ci	*p = cpu_to_be32(hdr->nops);		/* argarray element count */
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * Update argarray element count
27862306a36Sopenharmony_ci */
27962306a36Sopenharmony_cistatic void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	BUG_ON(hdr->nops > NFS4_MAX_BACK_CHANNEL_OPS);
28262306a36Sopenharmony_ci	*hdr->nops_p = cpu_to_be32(hdr->nops);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/*
28662306a36Sopenharmony_ci * CB_COMPOUND4res
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci *	struct CB_COMPOUND4res {
28962306a36Sopenharmony_ci *		nfsstat4	status;
29062306a36Sopenharmony_ci *		utf8str_cs	tag;
29162306a36Sopenharmony_ci *		nfs_cb_resop4	resarray<>;
29262306a36Sopenharmony_ci *	};
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistatic int decode_cb_compound4res(struct xdr_stream *xdr,
29562306a36Sopenharmony_ci				  struct nfs4_cb_compound_hdr *hdr)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	u32 length;
29862306a36Sopenharmony_ci	__be32 *p;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, 4 + 4);
30162306a36Sopenharmony_ci	if (unlikely(p == NULL))
30262306a36Sopenharmony_ci		goto out_overflow;
30362306a36Sopenharmony_ci	hdr->status = be32_to_cpup(p++);
30462306a36Sopenharmony_ci	/* Ignore the tag */
30562306a36Sopenharmony_ci	length = be32_to_cpup(p++);
30662306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, length + 4);
30762306a36Sopenharmony_ci	if (unlikely(p == NULL))
30862306a36Sopenharmony_ci		goto out_overflow;
30962306a36Sopenharmony_ci	p += XDR_QUADLEN(length);
31062306a36Sopenharmony_ci	hdr->nops = be32_to_cpup(p);
31162306a36Sopenharmony_ci	return 0;
31262306a36Sopenharmony_ciout_overflow:
31362306a36Sopenharmony_ci	return -EIO;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/*
31762306a36Sopenharmony_ci * CB_RECALL4args
31862306a36Sopenharmony_ci *
31962306a36Sopenharmony_ci *	struct CB_RECALL4args {
32062306a36Sopenharmony_ci *		stateid4	stateid;
32162306a36Sopenharmony_ci *		bool		truncate;
32262306a36Sopenharmony_ci *		nfs_fh4		fh;
32362306a36Sopenharmony_ci *	};
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_cistatic void encode_cb_recall4args(struct xdr_stream *xdr,
32662306a36Sopenharmony_ci				  const struct nfs4_delegation *dp,
32762306a36Sopenharmony_ci				  struct nfs4_cb_compound_hdr *hdr)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	__be32 *p;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	encode_nfs_cb_opnum4(xdr, OP_CB_RECALL);
33262306a36Sopenharmony_ci	encode_stateid4(xdr, &dp->dl_stid.sc_stateid);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 4);
33562306a36Sopenharmony_ci	*p++ = xdr_zero;			/* truncate */
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	encode_nfs_fh4(xdr, &dp->dl_stid.sc_file->fi_fhandle);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	hdr->nops++;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/*
34362306a36Sopenharmony_ci * CB_RECALLANY4args
34462306a36Sopenharmony_ci *
34562306a36Sopenharmony_ci *	struct CB_RECALLANY4args {
34662306a36Sopenharmony_ci *		uint32_t	craa_objects_to_keep;
34762306a36Sopenharmony_ci *		bitmap4		craa_type_mask;
34862306a36Sopenharmony_ci *	};
34962306a36Sopenharmony_ci */
35062306a36Sopenharmony_cistatic void
35162306a36Sopenharmony_ciencode_cb_recallany4args(struct xdr_stream *xdr,
35262306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr *hdr, struct nfsd4_cb_recall_any *ra)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	encode_nfs_cb_opnum4(xdr, OP_CB_RECALL_ANY);
35562306a36Sopenharmony_ci	encode_uint32(xdr, ra->ra_keep);
35662306a36Sopenharmony_ci	encode_bitmap4(xdr, ra->ra_bmval, ARRAY_SIZE(ra->ra_bmval));
35762306a36Sopenharmony_ci	hdr->nops++;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/*
36162306a36Sopenharmony_ci * CB_SEQUENCE4args
36262306a36Sopenharmony_ci *
36362306a36Sopenharmony_ci *	struct CB_SEQUENCE4args {
36462306a36Sopenharmony_ci *		sessionid4		csa_sessionid;
36562306a36Sopenharmony_ci *		sequenceid4		csa_sequenceid;
36662306a36Sopenharmony_ci *		slotid4			csa_slotid;
36762306a36Sopenharmony_ci *		slotid4			csa_highest_slotid;
36862306a36Sopenharmony_ci *		bool			csa_cachethis;
36962306a36Sopenharmony_ci *		referring_call_list4	csa_referring_call_lists<>;
37062306a36Sopenharmony_ci *	};
37162306a36Sopenharmony_ci */
37262306a36Sopenharmony_cistatic void encode_cb_sequence4args(struct xdr_stream *xdr,
37362306a36Sopenharmony_ci				    const struct nfsd4_callback *cb,
37462306a36Sopenharmony_ci				    struct nfs4_cb_compound_hdr *hdr)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
37762306a36Sopenharmony_ci	__be32 *p;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (hdr->minorversion == 0)
38062306a36Sopenharmony_ci		return;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	encode_nfs_cb_opnum4(xdr, OP_CB_SEQUENCE);
38362306a36Sopenharmony_ci	encode_sessionid4(xdr, session);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4 + 4);
38662306a36Sopenharmony_ci	*p++ = cpu_to_be32(session->se_cb_seq_nr);	/* csa_sequenceid */
38762306a36Sopenharmony_ci	*p++ = xdr_zero;			/* csa_slotid */
38862306a36Sopenharmony_ci	*p++ = xdr_zero;			/* csa_highest_slotid */
38962306a36Sopenharmony_ci	*p++ = xdr_zero;			/* csa_cachethis */
39062306a36Sopenharmony_ci	xdr_encode_empty_array(p);		/* csa_referring_call_lists */
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	hdr->nops++;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/*
39662306a36Sopenharmony_ci * CB_SEQUENCE4resok
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci *	struct CB_SEQUENCE4resok {
39962306a36Sopenharmony_ci *		sessionid4	csr_sessionid;
40062306a36Sopenharmony_ci *		sequenceid4	csr_sequenceid;
40162306a36Sopenharmony_ci *		slotid4		csr_slotid;
40262306a36Sopenharmony_ci *		slotid4		csr_highest_slotid;
40362306a36Sopenharmony_ci *		slotid4		csr_target_highest_slotid;
40462306a36Sopenharmony_ci *	};
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci *	union CB_SEQUENCE4res switch (nfsstat4 csr_status) {
40762306a36Sopenharmony_ci *	case NFS4_OK:
40862306a36Sopenharmony_ci *		CB_SEQUENCE4resok	csr_resok4;
40962306a36Sopenharmony_ci *	default:
41062306a36Sopenharmony_ci *		void;
41162306a36Sopenharmony_ci *	};
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * Our current back channel implmentation supports a single backchannel
41462306a36Sopenharmony_ci * with a single slot.
41562306a36Sopenharmony_ci */
41662306a36Sopenharmony_cistatic int decode_cb_sequence4resok(struct xdr_stream *xdr,
41762306a36Sopenharmony_ci				    struct nfsd4_callback *cb)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
42062306a36Sopenharmony_ci	int status = -ESERVERFAULT;
42162306a36Sopenharmony_ci	__be32 *p;
42262306a36Sopenharmony_ci	u32 dummy;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	/*
42562306a36Sopenharmony_ci	 * If the server returns different values for sessionID, slotID or
42662306a36Sopenharmony_ci	 * sequence number, the server is looney tunes.
42762306a36Sopenharmony_ci	 */
42862306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4 + 4 + 4);
42962306a36Sopenharmony_ci	if (unlikely(p == NULL))
43062306a36Sopenharmony_ci		goto out_overflow;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (memcmp(p, session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) {
43362306a36Sopenharmony_ci		dprintk("NFS: %s Invalid session id\n", __func__);
43462306a36Sopenharmony_ci		goto out;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci	p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	dummy = be32_to_cpup(p++);
43962306a36Sopenharmony_ci	if (dummy != session->se_cb_seq_nr) {
44062306a36Sopenharmony_ci		dprintk("NFS: %s Invalid sequence number\n", __func__);
44162306a36Sopenharmony_ci		goto out;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	dummy = be32_to_cpup(p++);
44562306a36Sopenharmony_ci	if (dummy != 0) {
44662306a36Sopenharmony_ci		dprintk("NFS: %s Invalid slotid\n", __func__);
44762306a36Sopenharmony_ci		goto out;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/*
45162306a36Sopenharmony_ci	 * FIXME: process highest slotid and target highest slotid
45262306a36Sopenharmony_ci	 */
45362306a36Sopenharmony_ci	status = 0;
45462306a36Sopenharmony_ciout:
45562306a36Sopenharmony_ci	cb->cb_seq_status = status;
45662306a36Sopenharmony_ci	return status;
45762306a36Sopenharmony_ciout_overflow:
45862306a36Sopenharmony_ci	status = -EIO;
45962306a36Sopenharmony_ci	goto out;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int decode_cb_sequence4res(struct xdr_stream *xdr,
46362306a36Sopenharmony_ci				  struct nfsd4_callback *cb)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	int status;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (cb->cb_clp->cl_minorversion == 0)
46862306a36Sopenharmony_ci		return 0;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &cb->cb_seq_status);
47162306a36Sopenharmony_ci	if (unlikely(status || cb->cb_seq_status))
47262306a36Sopenharmony_ci		return status;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return decode_cb_sequence4resok(xdr, cb);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci/*
47862306a36Sopenharmony_ci * NFSv4.0 and NFSv4.1 XDR encode functions
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * NFSv4.0 callback argument types are defined in section 15 of RFC
48162306a36Sopenharmony_ci * 3530: "Network File System (NFS) version 4 Protocol" and section 20
48262306a36Sopenharmony_ci * of RFC 5661:  "Network File System (NFS) Version 4 Minor Version 1
48362306a36Sopenharmony_ci * Protocol".
48462306a36Sopenharmony_ci */
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/*
48762306a36Sopenharmony_ci * NB: Without this zero space reservation, callbacks over krb5p fail
48862306a36Sopenharmony_ci */
48962306a36Sopenharmony_cistatic void nfs4_xdr_enc_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr,
49062306a36Sopenharmony_ci				 const void *__unused)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	xdr_reserve_space(xdr, 0);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/*
49662306a36Sopenharmony_ci * 20.2. Operation 4: CB_RECALL - Recall a Delegation
49762306a36Sopenharmony_ci */
49862306a36Sopenharmony_cistatic void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
49962306a36Sopenharmony_ci				   const void *data)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	const struct nfsd4_callback *cb = data;
50262306a36Sopenharmony_ci	const struct nfs4_delegation *dp = cb_to_delegation(cb);
50362306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr = {
50462306a36Sopenharmony_ci		.ident = cb->cb_clp->cl_cb_ident,
50562306a36Sopenharmony_ci		.minorversion = cb->cb_clp->cl_minorversion,
50662306a36Sopenharmony_ci	};
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	encode_cb_compound4args(xdr, &hdr);
50962306a36Sopenharmony_ci	encode_cb_sequence4args(xdr, cb, &hdr);
51062306a36Sopenharmony_ci	encode_cb_recall4args(xdr, dp, &hdr);
51162306a36Sopenharmony_ci	encode_cb_nops(&hdr);
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci/*
51562306a36Sopenharmony_ci * 20.6. Operation 8: CB_RECALL_ANY - Keep Any N Recallable Objects
51662306a36Sopenharmony_ci */
51762306a36Sopenharmony_cistatic void
51862306a36Sopenharmony_cinfs4_xdr_enc_cb_recall_any(struct rpc_rqst *req,
51962306a36Sopenharmony_ci		struct xdr_stream *xdr, const void *data)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	const struct nfsd4_callback *cb = data;
52262306a36Sopenharmony_ci	struct nfsd4_cb_recall_any *ra;
52362306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr = {
52462306a36Sopenharmony_ci		.ident = cb->cb_clp->cl_cb_ident,
52562306a36Sopenharmony_ci		.minorversion = cb->cb_clp->cl_minorversion,
52662306a36Sopenharmony_ci	};
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	ra = container_of(cb, struct nfsd4_cb_recall_any, ra_cb);
52962306a36Sopenharmony_ci	encode_cb_compound4args(xdr, &hdr);
53062306a36Sopenharmony_ci	encode_cb_sequence4args(xdr, cb, &hdr);
53162306a36Sopenharmony_ci	encode_cb_recallany4args(xdr, &hdr, ra);
53262306a36Sopenharmony_ci	encode_cb_nops(&hdr);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/*
53662306a36Sopenharmony_ci * NFSv4.0 and NFSv4.1 XDR decode functions
53762306a36Sopenharmony_ci *
53862306a36Sopenharmony_ci * NFSv4.0 callback result types are defined in section 15 of RFC
53962306a36Sopenharmony_ci * 3530: "Network File System (NFS) version 4 Protocol" and section 20
54062306a36Sopenharmony_ci * of RFC 5661:  "Network File System (NFS) Version 4 Minor Version 1
54162306a36Sopenharmony_ci * Protocol".
54262306a36Sopenharmony_ci */
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic int nfs4_xdr_dec_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr,
54562306a36Sopenharmony_ci				void *__unused)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/*
55162306a36Sopenharmony_ci * 20.2. Operation 4: CB_RECALL - Recall a Delegation
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_cistatic int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp,
55462306a36Sopenharmony_ci				  struct xdr_stream *xdr,
55562306a36Sopenharmony_ci				  void *data)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct nfsd4_callback *cb = data;
55862306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr;
55962306a36Sopenharmony_ci	int status;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	status = decode_cb_compound4res(xdr, &hdr);
56262306a36Sopenharmony_ci	if (unlikely(status))
56362306a36Sopenharmony_ci		return status;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	status = decode_cb_sequence4res(xdr, cb);
56662306a36Sopenharmony_ci	if (unlikely(status || cb->cb_seq_status))
56762306a36Sopenharmony_ci		return status;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return decode_cb_op_status(xdr, OP_CB_RECALL, &cb->cb_status);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci/*
57362306a36Sopenharmony_ci * 20.6. Operation 8: CB_RECALL_ANY - Keep Any N Recallable Objects
57462306a36Sopenharmony_ci */
57562306a36Sopenharmony_cistatic int
57662306a36Sopenharmony_cinfs4_xdr_dec_cb_recall_any(struct rpc_rqst *rqstp,
57762306a36Sopenharmony_ci				  struct xdr_stream *xdr,
57862306a36Sopenharmony_ci				  void *data)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct nfsd4_callback *cb = data;
58162306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr;
58262306a36Sopenharmony_ci	int status;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	status = decode_cb_compound4res(xdr, &hdr);
58562306a36Sopenharmony_ci	if (unlikely(status))
58662306a36Sopenharmony_ci		return status;
58762306a36Sopenharmony_ci	status = decode_cb_sequence4res(xdr, cb);
58862306a36Sopenharmony_ci	if (unlikely(status || cb->cb_seq_status))
58962306a36Sopenharmony_ci		return status;
59062306a36Sopenharmony_ci	status =  decode_cb_op_status(xdr, OP_CB_RECALL_ANY, &cb->cb_status);
59162306a36Sopenharmony_ci	return status;
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS
59562306a36Sopenharmony_ci/*
59662306a36Sopenharmony_ci * CB_LAYOUTRECALL4args
59762306a36Sopenharmony_ci *
59862306a36Sopenharmony_ci *	struct layoutrecall_file4 {
59962306a36Sopenharmony_ci *		nfs_fh4         lor_fh;
60062306a36Sopenharmony_ci *		offset4         lor_offset;
60162306a36Sopenharmony_ci *		length4         lor_length;
60262306a36Sopenharmony_ci *		stateid4        lor_stateid;
60362306a36Sopenharmony_ci *	};
60462306a36Sopenharmony_ci *
60562306a36Sopenharmony_ci *	union layoutrecall4 switch(layoutrecall_type4 lor_recalltype) {
60662306a36Sopenharmony_ci *	case LAYOUTRECALL4_FILE:
60762306a36Sopenharmony_ci *		layoutrecall_file4 lor_layout;
60862306a36Sopenharmony_ci *	case LAYOUTRECALL4_FSID:
60962306a36Sopenharmony_ci *		fsid4              lor_fsid;
61062306a36Sopenharmony_ci *	case LAYOUTRECALL4_ALL:
61162306a36Sopenharmony_ci *		void;
61262306a36Sopenharmony_ci *	};
61362306a36Sopenharmony_ci *
61462306a36Sopenharmony_ci *	struct CB_LAYOUTRECALL4args {
61562306a36Sopenharmony_ci *		layouttype4             clora_type;
61662306a36Sopenharmony_ci *		layoutiomode4           clora_iomode;
61762306a36Sopenharmony_ci *		bool                    clora_changed;
61862306a36Sopenharmony_ci *		layoutrecall4           clora_recall;
61962306a36Sopenharmony_ci *	};
62062306a36Sopenharmony_ci */
62162306a36Sopenharmony_cistatic void encode_cb_layout4args(struct xdr_stream *xdr,
62262306a36Sopenharmony_ci				  const struct nfs4_layout_stateid *ls,
62362306a36Sopenharmony_ci				  struct nfs4_cb_compound_hdr *hdr)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	__be32 *p;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	BUG_ON(hdr->minorversion == 0);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 5 * 4);
63062306a36Sopenharmony_ci	*p++ = cpu_to_be32(OP_CB_LAYOUTRECALL);
63162306a36Sopenharmony_ci	*p++ = cpu_to_be32(ls->ls_layout_type);
63262306a36Sopenharmony_ci	*p++ = cpu_to_be32(IOMODE_ANY);
63362306a36Sopenharmony_ci	*p++ = cpu_to_be32(1);
63462306a36Sopenharmony_ci	*p = cpu_to_be32(RETURN_FILE);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	encode_nfs_fh4(xdr, &ls->ls_stid.sc_file->fi_fhandle);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 2 * 8);
63962306a36Sopenharmony_ci	p = xdr_encode_hyper(p, 0);
64062306a36Sopenharmony_ci	xdr_encode_hyper(p, NFS4_MAX_UINT64);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	encode_stateid4(xdr, &ls->ls_recall_sid);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	hdr->nops++;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic void nfs4_xdr_enc_cb_layout(struct rpc_rqst *req,
64862306a36Sopenharmony_ci				   struct xdr_stream *xdr,
64962306a36Sopenharmony_ci				   const void *data)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	const struct nfsd4_callback *cb = data;
65262306a36Sopenharmony_ci	const struct nfs4_layout_stateid *ls =
65362306a36Sopenharmony_ci		container_of(cb, struct nfs4_layout_stateid, ls_recall);
65462306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr = {
65562306a36Sopenharmony_ci		.ident = 0,
65662306a36Sopenharmony_ci		.minorversion = cb->cb_clp->cl_minorversion,
65762306a36Sopenharmony_ci	};
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	encode_cb_compound4args(xdr, &hdr);
66062306a36Sopenharmony_ci	encode_cb_sequence4args(xdr, cb, &hdr);
66162306a36Sopenharmony_ci	encode_cb_layout4args(xdr, ls, &hdr);
66262306a36Sopenharmony_ci	encode_cb_nops(&hdr);
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic int nfs4_xdr_dec_cb_layout(struct rpc_rqst *rqstp,
66662306a36Sopenharmony_ci				  struct xdr_stream *xdr,
66762306a36Sopenharmony_ci				  void *data)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct nfsd4_callback *cb = data;
67062306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr;
67162306a36Sopenharmony_ci	int status;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	status = decode_cb_compound4res(xdr, &hdr);
67462306a36Sopenharmony_ci	if (unlikely(status))
67562306a36Sopenharmony_ci		return status;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	status = decode_cb_sequence4res(xdr, cb);
67862306a36Sopenharmony_ci	if (unlikely(status || cb->cb_seq_status))
67962306a36Sopenharmony_ci		return status;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	return decode_cb_op_status(xdr, OP_CB_LAYOUTRECALL, &cb->cb_status);
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci#endif /* CONFIG_NFSD_PNFS */
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic void encode_stateowner(struct xdr_stream *xdr, struct nfs4_stateowner *so)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	__be32	*p;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 8 + 4 + so->so_owner.len);
69062306a36Sopenharmony_ci	p = xdr_encode_opaque_fixed(p, &so->so_client->cl_clientid, 8);
69162306a36Sopenharmony_ci	xdr_encode_opaque(p, so->so_owner.data, so->so_owner.len);
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic void nfs4_xdr_enc_cb_notify_lock(struct rpc_rqst *req,
69562306a36Sopenharmony_ci					struct xdr_stream *xdr,
69662306a36Sopenharmony_ci					const void *data)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	const struct nfsd4_callback *cb = data;
69962306a36Sopenharmony_ci	const struct nfsd4_blocked_lock *nbl =
70062306a36Sopenharmony_ci		container_of(cb, struct nfsd4_blocked_lock, nbl_cb);
70162306a36Sopenharmony_ci	struct nfs4_lockowner *lo = (struct nfs4_lockowner *)nbl->nbl_lock.fl_owner;
70262306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr = {
70362306a36Sopenharmony_ci		.ident = 0,
70462306a36Sopenharmony_ci		.minorversion = cb->cb_clp->cl_minorversion,
70562306a36Sopenharmony_ci	};
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	__be32 *p;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	BUG_ON(hdr.minorversion == 0);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	encode_cb_compound4args(xdr, &hdr);
71262306a36Sopenharmony_ci	encode_cb_sequence4args(xdr, cb, &hdr);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 4);
71562306a36Sopenharmony_ci	*p = cpu_to_be32(OP_CB_NOTIFY_LOCK);
71662306a36Sopenharmony_ci	encode_nfs_fh4(xdr, &nbl->nbl_fh);
71762306a36Sopenharmony_ci	encode_stateowner(xdr, &lo->lo_owner);
71862306a36Sopenharmony_ci	hdr.nops++;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	encode_cb_nops(&hdr);
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
72462306a36Sopenharmony_ci					struct xdr_stream *xdr,
72562306a36Sopenharmony_ci					void *data)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct nfsd4_callback *cb = data;
72862306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr;
72962306a36Sopenharmony_ci	int status;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	status = decode_cb_compound4res(xdr, &hdr);
73262306a36Sopenharmony_ci	if (unlikely(status))
73362306a36Sopenharmony_ci		return status;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	status = decode_cb_sequence4res(xdr, cb);
73662306a36Sopenharmony_ci	if (unlikely(status || cb->cb_seq_status))
73762306a36Sopenharmony_ci		return status;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	return decode_cb_op_status(xdr, OP_CB_NOTIFY_LOCK, &cb->cb_status);
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci/*
74362306a36Sopenharmony_ci * struct write_response4 {
74462306a36Sopenharmony_ci *	stateid4	wr_callback_id<1>;
74562306a36Sopenharmony_ci *	length4		wr_count;
74662306a36Sopenharmony_ci *	stable_how4	wr_committed;
74762306a36Sopenharmony_ci *	verifier4	wr_writeverf;
74862306a36Sopenharmony_ci * };
74962306a36Sopenharmony_ci * union offload_info4 switch (nfsstat4 coa_status) {
75062306a36Sopenharmony_ci *	case NFS4_OK:
75162306a36Sopenharmony_ci *		write_response4	coa_resok4;
75262306a36Sopenharmony_ci *	default:
75362306a36Sopenharmony_ci *		length4		coa_bytes_copied;
75462306a36Sopenharmony_ci * };
75562306a36Sopenharmony_ci * struct CB_OFFLOAD4args {
75662306a36Sopenharmony_ci *	nfs_fh4		coa_fh;
75762306a36Sopenharmony_ci *	stateid4	coa_stateid;
75862306a36Sopenharmony_ci *	offload_info4	coa_offload_info;
75962306a36Sopenharmony_ci * };
76062306a36Sopenharmony_ci */
76162306a36Sopenharmony_cistatic void encode_offload_info4(struct xdr_stream *xdr,
76262306a36Sopenharmony_ci				 const struct nfsd4_cb_offload *cbo)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	__be32 *p;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 4);
76762306a36Sopenharmony_ci	*p = cbo->co_nfserr;
76862306a36Sopenharmony_ci	switch (cbo->co_nfserr) {
76962306a36Sopenharmony_ci	case nfs_ok:
77062306a36Sopenharmony_ci		p = xdr_reserve_space(xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
77162306a36Sopenharmony_ci		p = xdr_encode_empty_array(p);
77262306a36Sopenharmony_ci		p = xdr_encode_hyper(p, cbo->co_res.wr_bytes_written);
77362306a36Sopenharmony_ci		*p++ = cpu_to_be32(cbo->co_res.wr_stable_how);
77462306a36Sopenharmony_ci		p = xdr_encode_opaque_fixed(p, cbo->co_res.wr_verifier.data,
77562306a36Sopenharmony_ci					    NFS4_VERIFIER_SIZE);
77662306a36Sopenharmony_ci		break;
77762306a36Sopenharmony_ci	default:
77862306a36Sopenharmony_ci		p = xdr_reserve_space(xdr, 8);
77962306a36Sopenharmony_ci		/* We always return success if bytes were written */
78062306a36Sopenharmony_ci		p = xdr_encode_hyper(p, 0);
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistatic void encode_cb_offload4args(struct xdr_stream *xdr,
78562306a36Sopenharmony_ci				   const struct nfsd4_cb_offload *cbo,
78662306a36Sopenharmony_ci				   struct nfs4_cb_compound_hdr *hdr)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	__be32 *p;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 4);
79162306a36Sopenharmony_ci	*p = cpu_to_be32(OP_CB_OFFLOAD);
79262306a36Sopenharmony_ci	encode_nfs_fh4(xdr, &cbo->co_fh);
79362306a36Sopenharmony_ci	encode_stateid4(xdr, &cbo->co_res.cb_stateid);
79462306a36Sopenharmony_ci	encode_offload_info4(xdr, cbo);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	hdr->nops++;
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req,
80062306a36Sopenharmony_ci				    struct xdr_stream *xdr,
80162306a36Sopenharmony_ci				    const void *data)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	const struct nfsd4_callback *cb = data;
80462306a36Sopenharmony_ci	const struct nfsd4_cb_offload *cbo =
80562306a36Sopenharmony_ci		container_of(cb, struct nfsd4_cb_offload, co_cb);
80662306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr = {
80762306a36Sopenharmony_ci		.ident = 0,
80862306a36Sopenharmony_ci		.minorversion = cb->cb_clp->cl_minorversion,
80962306a36Sopenharmony_ci	};
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	encode_cb_compound4args(xdr, &hdr);
81262306a36Sopenharmony_ci	encode_cb_sequence4args(xdr, cb, &hdr);
81362306a36Sopenharmony_ci	encode_cb_offload4args(xdr, cbo, &hdr);
81462306a36Sopenharmony_ci	encode_cb_nops(&hdr);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic int nfs4_xdr_dec_cb_offload(struct rpc_rqst *rqstp,
81862306a36Sopenharmony_ci				   struct xdr_stream *xdr,
81962306a36Sopenharmony_ci				   void *data)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct nfsd4_callback *cb = data;
82262306a36Sopenharmony_ci	struct nfs4_cb_compound_hdr hdr;
82362306a36Sopenharmony_ci	int status;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	status = decode_cb_compound4res(xdr, &hdr);
82662306a36Sopenharmony_ci	if (unlikely(status))
82762306a36Sopenharmony_ci		return status;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	status = decode_cb_sequence4res(xdr, cb);
83062306a36Sopenharmony_ci	if (unlikely(status || cb->cb_seq_status))
83162306a36Sopenharmony_ci		return status;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return decode_cb_op_status(xdr, OP_CB_OFFLOAD, &cb->cb_status);
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci/*
83662306a36Sopenharmony_ci * RPC procedure tables
83762306a36Sopenharmony_ci */
83862306a36Sopenharmony_ci#define PROC(proc, call, argtype, restype)				\
83962306a36Sopenharmony_ci[NFSPROC4_CLNT_##proc] = {						\
84062306a36Sopenharmony_ci	.p_proc    = NFSPROC4_CB_##call,				\
84162306a36Sopenharmony_ci	.p_encode  = nfs4_xdr_enc_##argtype,		\
84262306a36Sopenharmony_ci	.p_decode  = nfs4_xdr_dec_##restype,				\
84362306a36Sopenharmony_ci	.p_arglen  = NFS4_enc_##argtype##_sz,				\
84462306a36Sopenharmony_ci	.p_replen  = NFS4_dec_##restype##_sz,				\
84562306a36Sopenharmony_ci	.p_statidx = NFSPROC4_CB_##call,				\
84662306a36Sopenharmony_ci	.p_name    = #proc,						\
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic const struct rpc_procinfo nfs4_cb_procedures[] = {
85062306a36Sopenharmony_ci	PROC(CB_NULL,	NULL,		cb_null,	cb_null),
85162306a36Sopenharmony_ci	PROC(CB_RECALL,	COMPOUND,	cb_recall,	cb_recall),
85262306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS
85362306a36Sopenharmony_ci	PROC(CB_LAYOUT,	COMPOUND,	cb_layout,	cb_layout),
85462306a36Sopenharmony_ci#endif
85562306a36Sopenharmony_ci	PROC(CB_NOTIFY_LOCK,	COMPOUND,	cb_notify_lock,	cb_notify_lock),
85662306a36Sopenharmony_ci	PROC(CB_OFFLOAD,	COMPOUND,	cb_offload,	cb_offload),
85762306a36Sopenharmony_ci	PROC(CB_RECALL_ANY,	COMPOUND,	cb_recall_any,	cb_recall_any),
85862306a36Sopenharmony_ci};
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cistatic unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)];
86162306a36Sopenharmony_cistatic const struct rpc_version nfs_cb_version4 = {
86262306a36Sopenharmony_ci/*
86362306a36Sopenharmony_ci * Note on the callback rpc program version number: despite language in rfc
86462306a36Sopenharmony_ci * 5661 section 18.36.3 requiring servers to use 4 in this field, the
86562306a36Sopenharmony_ci * official xdr descriptions for both 4.0 and 4.1 specify version 1, and
86662306a36Sopenharmony_ci * in practice that appears to be what implementations use.  The section
86762306a36Sopenharmony_ci * 18.36.3 language is expected to be fixed in an erratum.
86862306a36Sopenharmony_ci */
86962306a36Sopenharmony_ci	.number			= 1,
87062306a36Sopenharmony_ci	.nrprocs		= ARRAY_SIZE(nfs4_cb_procedures),
87162306a36Sopenharmony_ci	.procs			= nfs4_cb_procedures,
87262306a36Sopenharmony_ci	.counts			= nfs4_cb_counts,
87362306a36Sopenharmony_ci};
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic const struct rpc_version *nfs_cb_version[2] = {
87662306a36Sopenharmony_ci	[1] = &nfs_cb_version4,
87762306a36Sopenharmony_ci};
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic const struct rpc_program cb_program;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic struct rpc_stat cb_stats = {
88262306a36Sopenharmony_ci	.program		= &cb_program
88362306a36Sopenharmony_ci};
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci#define NFS4_CALLBACK 0x40000000
88662306a36Sopenharmony_cistatic const struct rpc_program cb_program = {
88762306a36Sopenharmony_ci	.name			= "nfs4_cb",
88862306a36Sopenharmony_ci	.number			= NFS4_CALLBACK,
88962306a36Sopenharmony_ci	.nrvers			= ARRAY_SIZE(nfs_cb_version),
89062306a36Sopenharmony_ci	.version		= nfs_cb_version,
89162306a36Sopenharmony_ci	.stats			= &cb_stats,
89262306a36Sopenharmony_ci	.pipe_dir_name		= "nfsd4_cb",
89362306a36Sopenharmony_ci};
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic int max_cb_time(struct net *net)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	/*
90062306a36Sopenharmony_ci	 * nfsd4_lease is set to at most one hour in __nfsd4_write_time,
90162306a36Sopenharmony_ci	 * so we can use 32-bit math on it. Warn if that assumption
90262306a36Sopenharmony_ci	 * ever stops being true.
90362306a36Sopenharmony_ci	 */
90462306a36Sopenharmony_ci	if (WARN_ON_ONCE(nn->nfsd4_lease > 3600))
90562306a36Sopenharmony_ci		return 360 * HZ;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	return max(((u32)nn->nfsd4_lease)/10, 1u) * HZ;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_cistatic struct workqueue_struct *callback_wq;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic bool nfsd4_queue_cb(struct nfsd4_callback *cb)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	return queue_work(callback_wq, &cb->cb_work);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic void nfsd41_cb_inflight_begin(struct nfs4_client *clp)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	atomic_inc(&clp->cl_cb_inflight);
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic void nfsd41_cb_inflight_end(struct nfs4_client *clp)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (atomic_dec_and_test(&clp->cl_cb_inflight))
92662306a36Sopenharmony_ci		wake_up_var(&clp->cl_cb_inflight);
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic void nfsd41_cb_inflight_wait_complete(struct nfs4_client *clp)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	wait_var_event(&clp->cl_cb_inflight,
93262306a36Sopenharmony_ci			!atomic_read(&clp->cl_cb_inflight));
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic const struct cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc_clnt *client, struct nfsd4_session *ses)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	if (clp->cl_minorversion == 0) {
93862306a36Sopenharmony_ci		client->cl_principal = clp->cl_cred.cr_targ_princ ?
93962306a36Sopenharmony_ci			clp->cl_cred.cr_targ_princ : "nfs";
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		return get_cred(rpc_machine_cred());
94262306a36Sopenharmony_ci	} else {
94362306a36Sopenharmony_ci		struct cred *kcred;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		kcred = prepare_kernel_cred(&init_task);
94662306a36Sopenharmony_ci		if (!kcred)
94762306a36Sopenharmony_ci			return NULL;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci		kcred->fsuid = ses->se_cb_sec.uid;
95062306a36Sopenharmony_ci		kcred->fsgid = ses->se_cb_sec.gid;
95162306a36Sopenharmony_ci		return kcred;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	int maxtime = max_cb_time(clp->net);
95862306a36Sopenharmony_ci	struct rpc_timeout	timeparms = {
95962306a36Sopenharmony_ci		.to_initval	= maxtime,
96062306a36Sopenharmony_ci		.to_retries	= 0,
96162306a36Sopenharmony_ci		.to_maxval	= maxtime,
96262306a36Sopenharmony_ci	};
96362306a36Sopenharmony_ci	struct rpc_create_args args = {
96462306a36Sopenharmony_ci		.net		= clp->net,
96562306a36Sopenharmony_ci		.address	= (struct sockaddr *) &conn->cb_addr,
96662306a36Sopenharmony_ci		.addrsize	= conn->cb_addrlen,
96762306a36Sopenharmony_ci		.saddress	= (struct sockaddr *) &conn->cb_saddr,
96862306a36Sopenharmony_ci		.timeout	= &timeparms,
96962306a36Sopenharmony_ci		.program	= &cb_program,
97062306a36Sopenharmony_ci		.version	= 1,
97162306a36Sopenharmony_ci		.flags		= (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
97262306a36Sopenharmony_ci		.cred		= current_cred(),
97362306a36Sopenharmony_ci	};
97462306a36Sopenharmony_ci	struct rpc_clnt *client;
97562306a36Sopenharmony_ci	const struct cred *cred;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (clp->cl_minorversion == 0) {
97862306a36Sopenharmony_ci		if (!clp->cl_cred.cr_principal &&
97962306a36Sopenharmony_ci		    (clp->cl_cred.cr_flavor >= RPC_AUTH_GSS_KRB5)) {
98062306a36Sopenharmony_ci			trace_nfsd_cb_setup_err(clp, -EINVAL);
98162306a36Sopenharmony_ci			return -EINVAL;
98262306a36Sopenharmony_ci		}
98362306a36Sopenharmony_ci		args.client_name = clp->cl_cred.cr_principal;
98462306a36Sopenharmony_ci		args.prognumber	= conn->cb_prog;
98562306a36Sopenharmony_ci		args.protocol = XPRT_TRANSPORT_TCP;
98662306a36Sopenharmony_ci		args.authflavor = clp->cl_cred.cr_flavor;
98762306a36Sopenharmony_ci		clp->cl_cb_ident = conn->cb_ident;
98862306a36Sopenharmony_ci	} else {
98962306a36Sopenharmony_ci		if (!conn->cb_xprt)
99062306a36Sopenharmony_ci			return -EINVAL;
99162306a36Sopenharmony_ci		clp->cl_cb_session = ses;
99262306a36Sopenharmony_ci		args.bc_xprt = conn->cb_xprt;
99362306a36Sopenharmony_ci		args.prognumber = clp->cl_cb_session->se_cb_prog;
99462306a36Sopenharmony_ci		args.protocol = conn->cb_xprt->xpt_class->xcl_ident |
99562306a36Sopenharmony_ci				XPRT_TRANSPORT_BC;
99662306a36Sopenharmony_ci		args.authflavor = ses->se_cb_sec.flavor;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci	/* Create RPC client */
99962306a36Sopenharmony_ci	client = rpc_create(&args);
100062306a36Sopenharmony_ci	if (IS_ERR(client)) {
100162306a36Sopenharmony_ci		trace_nfsd_cb_setup_err(clp, PTR_ERR(client));
100262306a36Sopenharmony_ci		return PTR_ERR(client);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci	cred = get_backchannel_cred(clp, client, ses);
100562306a36Sopenharmony_ci	if (!cred) {
100662306a36Sopenharmony_ci		trace_nfsd_cb_setup_err(clp, -ENOMEM);
100762306a36Sopenharmony_ci		rpc_shutdown_client(client);
100862306a36Sopenharmony_ci		return -ENOMEM;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (clp->cl_minorversion != 0)
101262306a36Sopenharmony_ci		clp->cl_cb_conn.cb_xprt = conn->cb_xprt;
101362306a36Sopenharmony_ci	clp->cl_cb_client = client;
101462306a36Sopenharmony_ci	clp->cl_cb_cred = cred;
101562306a36Sopenharmony_ci	rcu_read_lock();
101662306a36Sopenharmony_ci	trace_nfsd_cb_setup(clp, rpc_peeraddr2str(client, RPC_DISPLAY_NETID),
101762306a36Sopenharmony_ci			    args.authflavor);
101862306a36Sopenharmony_ci	rcu_read_unlock();
101962306a36Sopenharmony_ci	return 0;
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cistatic void nfsd4_mark_cb_state(struct nfs4_client *clp, int newstate)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	if (clp->cl_cb_state != newstate) {
102562306a36Sopenharmony_ci		clp->cl_cb_state = newstate;
102662306a36Sopenharmony_ci		trace_nfsd_cb_state(clp);
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags))
103362306a36Sopenharmony_ci		return;
103462306a36Sopenharmony_ci	nfsd4_mark_cb_state(clp, NFSD4_CB_DOWN);
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic void nfsd4_mark_cb_fault(struct nfs4_client *clp, int reason)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags))
104062306a36Sopenharmony_ci		return;
104162306a36Sopenharmony_ci	nfsd4_mark_cb_state(clp, NFSD4_CB_FAULT);
104262306a36Sopenharmony_ci}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cistatic void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	if (task->tk_status)
104962306a36Sopenharmony_ci		nfsd4_mark_cb_down(clp, task->tk_status);
105062306a36Sopenharmony_ci	else
105162306a36Sopenharmony_ci		nfsd4_mark_cb_state(clp, NFSD4_CB_UP);
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cistatic void nfsd4_cb_probe_release(void *calldata)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	nfsd41_cb_inflight_end(clp);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic const struct rpc_call_ops nfsd4_cb_probe_ops = {
106362306a36Sopenharmony_ci	/* XXX: release method to ensure we set the cb channel down if
106462306a36Sopenharmony_ci	 * necessary on early failure? */
106562306a36Sopenharmony_ci	.rpc_call_done = nfsd4_cb_probe_done,
106662306a36Sopenharmony_ci	.rpc_release = nfsd4_cb_probe_release,
106762306a36Sopenharmony_ci};
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci/*
107062306a36Sopenharmony_ci * Poke the callback thread to process any updates to the callback
107162306a36Sopenharmony_ci * parameters, and send a null probe.
107262306a36Sopenharmony_ci */
107362306a36Sopenharmony_civoid nfsd4_probe_callback(struct nfs4_client *clp)
107462306a36Sopenharmony_ci{
107562306a36Sopenharmony_ci	trace_nfsd_cb_probe(clp);
107662306a36Sopenharmony_ci	nfsd4_mark_cb_state(clp, NFSD4_CB_UNKNOWN);
107762306a36Sopenharmony_ci	set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags);
107862306a36Sopenharmony_ci	nfsd4_run_cb(&clp->cl_cb_null);
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_civoid nfsd4_probe_callback_sync(struct nfs4_client *clp)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	nfsd4_probe_callback(clp);
108462306a36Sopenharmony_ci	flush_workqueue(callback_wq);
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_civoid nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	nfsd4_mark_cb_state(clp, NFSD4_CB_UNKNOWN);
109062306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
109162306a36Sopenharmony_ci	memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
109262306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci/*
109662306a36Sopenharmony_ci * There's currently a single callback channel slot.
109762306a36Sopenharmony_ci * If the slot is available, then mark it busy.  Otherwise, set the
109862306a36Sopenharmony_ci * thread for sleeping on the callback RPC wait queue.
109962306a36Sopenharmony_ci */
110062306a36Sopenharmony_cistatic bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
110162306a36Sopenharmony_ci{
110262306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (!cb->cb_holds_slot &&
110562306a36Sopenharmony_ci	    test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
110662306a36Sopenharmony_ci		rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
110762306a36Sopenharmony_ci		/* Race breaker */
110862306a36Sopenharmony_ci		if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
110962306a36Sopenharmony_ci			dprintk("%s slot is busy\n", __func__);
111062306a36Sopenharmony_ci			return false;
111162306a36Sopenharmony_ci		}
111262306a36Sopenharmony_ci		rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci	cb->cb_holds_slot = true;
111562306a36Sopenharmony_ci	return true;
111662306a36Sopenharmony_ci}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistatic void nfsd41_cb_release_slot(struct nfsd4_callback *cb)
111962306a36Sopenharmony_ci{
112062306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	if (cb->cb_holds_slot) {
112362306a36Sopenharmony_ci		cb->cb_holds_slot = false;
112462306a36Sopenharmony_ci		clear_bit(0, &clp->cl_cb_slot_busy);
112562306a36Sopenharmony_ci		rpc_wake_up_next(&clp->cl_cb_waitq);
112662306a36Sopenharmony_ci	}
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic void nfsd41_destroy_cb(struct nfsd4_callback *cb)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	nfsd41_cb_release_slot(cb);
113462306a36Sopenharmony_ci	if (cb->cb_ops && cb->cb_ops->release)
113562306a36Sopenharmony_ci		cb->cb_ops->release(cb);
113662306a36Sopenharmony_ci	nfsd41_cb_inflight_end(clp);
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci/*
114062306a36Sopenharmony_ci * TODO: cb_sequence should support referring call lists, cachethis, multiple
114162306a36Sopenharmony_ci * slots, and mark callback channel down on communication errors.
114262306a36Sopenharmony_ci */
114362306a36Sopenharmony_cistatic void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct nfsd4_callback *cb = calldata;
114662306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
114762306a36Sopenharmony_ci	u32 minorversion = clp->cl_minorversion;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	/*
115062306a36Sopenharmony_ci	 * cb_seq_status is only set in decode_cb_sequence4res,
115162306a36Sopenharmony_ci	 * and so will remain 1 if an rpc level failure occurs.
115262306a36Sopenharmony_ci	 */
115362306a36Sopenharmony_ci	cb->cb_seq_status = 1;
115462306a36Sopenharmony_ci	cb->cb_status = 0;
115562306a36Sopenharmony_ci	if (minorversion && !nfsd41_cb_get_slot(cb, task))
115662306a36Sopenharmony_ci		return;
115762306a36Sopenharmony_ci	rpc_call_start(task);
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback *cb)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
116362306a36Sopenharmony_ci	struct nfsd4_session *session = clp->cl_cb_session;
116462306a36Sopenharmony_ci	bool ret = true;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	if (!clp->cl_minorversion) {
116762306a36Sopenharmony_ci		/*
116862306a36Sopenharmony_ci		 * If the backchannel connection was shut down while this
116962306a36Sopenharmony_ci		 * task was queued, we need to resubmit it after setting up
117062306a36Sopenharmony_ci		 * a new backchannel connection.
117162306a36Sopenharmony_ci		 *
117262306a36Sopenharmony_ci		 * Note that if we lost our callback connection permanently
117362306a36Sopenharmony_ci		 * the submission code will error out, so we don't need to
117462306a36Sopenharmony_ci		 * handle that case here.
117562306a36Sopenharmony_ci		 */
117662306a36Sopenharmony_ci		if (RPC_SIGNALLED(task))
117762306a36Sopenharmony_ci			goto need_restart;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci		return true;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (!cb->cb_holds_slot)
118362306a36Sopenharmony_ci		goto need_restart;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	switch (cb->cb_seq_status) {
118662306a36Sopenharmony_ci	case 0:
118762306a36Sopenharmony_ci		/*
118862306a36Sopenharmony_ci		 * No need for lock, access serialized in nfsd4_cb_prepare
118962306a36Sopenharmony_ci		 *
119062306a36Sopenharmony_ci		 * RFC5661 20.9.3
119162306a36Sopenharmony_ci		 * If CB_SEQUENCE returns an error, then the state of the slot
119262306a36Sopenharmony_ci		 * (sequence ID, cached reply) MUST NOT change.
119362306a36Sopenharmony_ci		 */
119462306a36Sopenharmony_ci		++session->se_cb_seq_nr;
119562306a36Sopenharmony_ci		break;
119662306a36Sopenharmony_ci	case -ESERVERFAULT:
119762306a36Sopenharmony_ci		++session->se_cb_seq_nr;
119862306a36Sopenharmony_ci		fallthrough;
119962306a36Sopenharmony_ci	case 1:
120062306a36Sopenharmony_ci	case -NFS4ERR_BADSESSION:
120162306a36Sopenharmony_ci		nfsd4_mark_cb_fault(cb->cb_clp, cb->cb_seq_status);
120262306a36Sopenharmony_ci		ret = false;
120362306a36Sopenharmony_ci		break;
120462306a36Sopenharmony_ci	case -NFS4ERR_DELAY:
120562306a36Sopenharmony_ci		if (!rpc_restart_call(task))
120662306a36Sopenharmony_ci			goto out;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci		rpc_delay(task, 2 * HZ);
120962306a36Sopenharmony_ci		return false;
121062306a36Sopenharmony_ci	case -NFS4ERR_BADSLOT:
121162306a36Sopenharmony_ci		goto retry_nowait;
121262306a36Sopenharmony_ci	case -NFS4ERR_SEQ_MISORDERED:
121362306a36Sopenharmony_ci		if (session->se_cb_seq_nr != 1) {
121462306a36Sopenharmony_ci			session->se_cb_seq_nr = 1;
121562306a36Sopenharmony_ci			goto retry_nowait;
121662306a36Sopenharmony_ci		}
121762306a36Sopenharmony_ci		break;
121862306a36Sopenharmony_ci	default:
121962306a36Sopenharmony_ci		nfsd4_mark_cb_fault(cb->cb_clp, cb->cb_seq_status);
122062306a36Sopenharmony_ci		dprintk("%s: unprocessed error %d\n", __func__,
122162306a36Sopenharmony_ci			cb->cb_seq_status);
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	nfsd41_cb_release_slot(cb);
122562306a36Sopenharmony_ci	dprintk("%s: freed slot, new seqid=%d\n", __func__,
122662306a36Sopenharmony_ci		clp->cl_cb_session->se_cb_seq_nr);
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (RPC_SIGNALLED(task))
122962306a36Sopenharmony_ci		goto need_restart;
123062306a36Sopenharmony_ciout:
123162306a36Sopenharmony_ci	return ret;
123262306a36Sopenharmony_ciretry_nowait:
123362306a36Sopenharmony_ci	if (rpc_restart_call_prepare(task))
123462306a36Sopenharmony_ci		ret = false;
123562306a36Sopenharmony_ci	goto out;
123662306a36Sopenharmony_cineed_restart:
123762306a36Sopenharmony_ci	if (!test_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags)) {
123862306a36Sopenharmony_ci		task->tk_status = 0;
123962306a36Sopenharmony_ci		cb->cb_need_restart = true;
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci	return false;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic void nfsd4_cb_done(struct rpc_task *task, void *calldata)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct nfsd4_callback *cb = calldata;
124762306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	if (!nfsd4_cb_sequence_done(task, cb))
125062306a36Sopenharmony_ci		return;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	if (cb->cb_status) {
125362306a36Sopenharmony_ci		WARN_ON_ONCE(task->tk_status);
125462306a36Sopenharmony_ci		task->tk_status = cb->cb_status;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	switch (cb->cb_ops->done(cb, task)) {
125862306a36Sopenharmony_ci	case 0:
125962306a36Sopenharmony_ci		task->tk_status = 0;
126062306a36Sopenharmony_ci		rpc_restart_call_prepare(task);
126162306a36Sopenharmony_ci		return;
126262306a36Sopenharmony_ci	case 1:
126362306a36Sopenharmony_ci		switch (task->tk_status) {
126462306a36Sopenharmony_ci		case -EIO:
126562306a36Sopenharmony_ci		case -ETIMEDOUT:
126662306a36Sopenharmony_ci		case -EACCES:
126762306a36Sopenharmony_ci			nfsd4_mark_cb_down(clp, task->tk_status);
126862306a36Sopenharmony_ci		}
126962306a36Sopenharmony_ci		break;
127062306a36Sopenharmony_ci	default:
127162306a36Sopenharmony_ci		BUG();
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic void nfsd4_cb_release(void *calldata)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	struct nfsd4_callback *cb = calldata;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	if (cb->cb_need_restart)
128062306a36Sopenharmony_ci		nfsd4_queue_cb(cb);
128162306a36Sopenharmony_ci	else
128262306a36Sopenharmony_ci		nfsd41_destroy_cb(cb);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cistatic const struct rpc_call_ops nfsd4_cb_ops = {
128762306a36Sopenharmony_ci	.rpc_call_prepare = nfsd4_cb_prepare,
128862306a36Sopenharmony_ci	.rpc_call_done = nfsd4_cb_done,
128962306a36Sopenharmony_ci	.rpc_release = nfsd4_cb_release,
129062306a36Sopenharmony_ci};
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ciint nfsd4_create_callback_queue(void)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	callback_wq = alloc_ordered_workqueue("nfsd4_callbacks", 0);
129562306a36Sopenharmony_ci	if (!callback_wq)
129662306a36Sopenharmony_ci		return -ENOMEM;
129762306a36Sopenharmony_ci	return 0;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_civoid nfsd4_destroy_callback_queue(void)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	destroy_workqueue(callback_wq);
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci/* must be called under the state lock */
130662306a36Sopenharmony_civoid nfsd4_shutdown_callback(struct nfs4_client *clp)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	if (clp->cl_cb_state != NFSD4_CB_UNKNOWN)
130962306a36Sopenharmony_ci		trace_nfsd_cb_shutdown(clp);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	set_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags);
131262306a36Sopenharmony_ci	/*
131362306a36Sopenharmony_ci	 * Note this won't actually result in a null callback;
131462306a36Sopenharmony_ci	 * instead, nfsd4_run_cb_null() will detect the killed
131562306a36Sopenharmony_ci	 * client, destroy the rpc client, and stop:
131662306a36Sopenharmony_ci	 */
131762306a36Sopenharmony_ci	nfsd4_run_cb(&clp->cl_cb_null);
131862306a36Sopenharmony_ci	flush_workqueue(callback_wq);
131962306a36Sopenharmony_ci	nfsd41_cb_inflight_wait_complete(clp);
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci/* requires cl_lock: */
132362306a36Sopenharmony_cistatic struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	struct nfsd4_session *s;
132662306a36Sopenharmony_ci	struct nfsd4_conn *c;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	list_for_each_entry(s, &clp->cl_sessions, se_perclnt) {
132962306a36Sopenharmony_ci		list_for_each_entry(c, &s->se_conns, cn_persession) {
133062306a36Sopenharmony_ci			if (c->cn_flags & NFS4_CDFC4_BACK)
133162306a36Sopenharmony_ci				return c;
133262306a36Sopenharmony_ci		}
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci	return NULL;
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci/*
133862306a36Sopenharmony_ci * Note there isn't a lot of locking in this code; instead we depend on
133962306a36Sopenharmony_ci * the fact that it is run from the callback_wq, which won't run two
134062306a36Sopenharmony_ci * work items at once.  So, for example, callback_wq handles all access
134162306a36Sopenharmony_ci * of cl_cb_client and all calls to rpc_create or rpc_shutdown_client.
134262306a36Sopenharmony_ci */
134362306a36Sopenharmony_cistatic void nfsd4_process_cb_update(struct nfsd4_callback *cb)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	struct nfs4_cb_conn conn;
134662306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
134762306a36Sopenharmony_ci	struct nfsd4_session *ses = NULL;
134862306a36Sopenharmony_ci	struct nfsd4_conn *c;
134962306a36Sopenharmony_ci	int err;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/*
135262306a36Sopenharmony_ci	 * This is either an update, or the client dying; in either case,
135362306a36Sopenharmony_ci	 * kill the old client:
135462306a36Sopenharmony_ci	 */
135562306a36Sopenharmony_ci	if (clp->cl_cb_client) {
135662306a36Sopenharmony_ci		rpc_shutdown_client(clp->cl_cb_client);
135762306a36Sopenharmony_ci		clp->cl_cb_client = NULL;
135862306a36Sopenharmony_ci		put_cred(clp->cl_cb_cred);
135962306a36Sopenharmony_ci		clp->cl_cb_cred = NULL;
136062306a36Sopenharmony_ci	}
136162306a36Sopenharmony_ci	if (clp->cl_cb_conn.cb_xprt) {
136262306a36Sopenharmony_ci		svc_xprt_put(clp->cl_cb_conn.cb_xprt);
136362306a36Sopenharmony_ci		clp->cl_cb_conn.cb_xprt = NULL;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci	if (test_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags))
136662306a36Sopenharmony_ci		return;
136762306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
136862306a36Sopenharmony_ci	/*
136962306a36Sopenharmony_ci	 * Only serialized callback code is allowed to clear these
137062306a36Sopenharmony_ci	 * flags; main nfsd code can only set them:
137162306a36Sopenharmony_ci	 */
137262306a36Sopenharmony_ci	BUG_ON(!(clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK));
137362306a36Sopenharmony_ci	clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags);
137462306a36Sopenharmony_ci	memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn));
137562306a36Sopenharmony_ci	c = __nfsd4_find_backchannel(clp);
137662306a36Sopenharmony_ci	if (c) {
137762306a36Sopenharmony_ci		svc_xprt_get(c->cn_xprt);
137862306a36Sopenharmony_ci		conn.cb_xprt = c->cn_xprt;
137962306a36Sopenharmony_ci		ses = c->cn_session;
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	err = setup_callback_client(clp, &conn, ses);
138462306a36Sopenharmony_ci	if (err) {
138562306a36Sopenharmony_ci		nfsd4_mark_cb_down(clp, err);
138662306a36Sopenharmony_ci		if (c)
138762306a36Sopenharmony_ci			svc_xprt_put(c->cn_xprt);
138862306a36Sopenharmony_ci		return;
138962306a36Sopenharmony_ci	}
139062306a36Sopenharmony_ci}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_cistatic void
139362306a36Sopenharmony_cinfsd4_run_cb_work(struct work_struct *work)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	struct nfsd4_callback *cb =
139662306a36Sopenharmony_ci		container_of(work, struct nfsd4_callback, cb_work);
139762306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
139862306a36Sopenharmony_ci	struct rpc_clnt *clnt;
139962306a36Sopenharmony_ci	int flags;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	if (cb->cb_need_restart) {
140262306a36Sopenharmony_ci		cb->cb_need_restart = false;
140362306a36Sopenharmony_ci	} else {
140462306a36Sopenharmony_ci		if (cb->cb_ops && cb->cb_ops->prepare)
140562306a36Sopenharmony_ci			cb->cb_ops->prepare(cb);
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	if (clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK)
140962306a36Sopenharmony_ci		nfsd4_process_cb_update(cb);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	clnt = clp->cl_cb_client;
141262306a36Sopenharmony_ci	if (!clnt) {
141362306a36Sopenharmony_ci		/* Callback channel broken, or client killed; give up: */
141462306a36Sopenharmony_ci		nfsd41_destroy_cb(cb);
141562306a36Sopenharmony_ci		return;
141662306a36Sopenharmony_ci	}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	/*
141962306a36Sopenharmony_ci	 * Don't send probe messages for 4.1 or later.
142062306a36Sopenharmony_ci	 */
142162306a36Sopenharmony_ci	if (!cb->cb_ops && clp->cl_minorversion) {
142262306a36Sopenharmony_ci		nfsd4_mark_cb_state(clp, NFSD4_CB_UP);
142362306a36Sopenharmony_ci		nfsd41_destroy_cb(cb);
142462306a36Sopenharmony_ci		return;
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	cb->cb_msg.rpc_cred = clp->cl_cb_cred;
142862306a36Sopenharmony_ci	flags = clp->cl_minorversion ? RPC_TASK_NOCONNECT : RPC_TASK_SOFTCONN;
142962306a36Sopenharmony_ci	rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | flags,
143062306a36Sopenharmony_ci			cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb);
143162306a36Sopenharmony_ci}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_civoid nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
143462306a36Sopenharmony_ci		const struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	cb->cb_clp = clp;
143762306a36Sopenharmony_ci	cb->cb_msg.rpc_proc = &nfs4_cb_procedures[op];
143862306a36Sopenharmony_ci	cb->cb_msg.rpc_argp = cb;
143962306a36Sopenharmony_ci	cb->cb_msg.rpc_resp = cb;
144062306a36Sopenharmony_ci	cb->cb_ops = ops;
144162306a36Sopenharmony_ci	INIT_WORK(&cb->cb_work, nfsd4_run_cb_work);
144262306a36Sopenharmony_ci	cb->cb_seq_status = 1;
144362306a36Sopenharmony_ci	cb->cb_status = 0;
144462306a36Sopenharmony_ci	cb->cb_need_restart = false;
144562306a36Sopenharmony_ci	cb->cb_holds_slot = false;
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci/**
144962306a36Sopenharmony_ci * nfsd4_run_cb - queue up a callback job to run
145062306a36Sopenharmony_ci * @cb: callback to queue
145162306a36Sopenharmony_ci *
145262306a36Sopenharmony_ci * Kick off a callback to do its thing. Returns false if it was already
145362306a36Sopenharmony_ci * on a queue, true otherwise.
145462306a36Sopenharmony_ci */
145562306a36Sopenharmony_cibool nfsd4_run_cb(struct nfsd4_callback *cb)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	struct nfs4_client *clp = cb->cb_clp;
145862306a36Sopenharmony_ci	bool queued;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	nfsd41_cb_inflight_begin(clp);
146162306a36Sopenharmony_ci	queued = nfsd4_queue_cb(cb);
146262306a36Sopenharmony_ci	if (!queued)
146362306a36Sopenharmony_ci		nfsd41_cb_inflight_end(clp);
146462306a36Sopenharmony_ci	return queued;
146562306a36Sopenharmony_ci}
1466