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