diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c index 43bb396d9cfd..4afa4c2d9ab4 100644 --- a/sys/fs/nfs/nfs_commonsubs.c +++ b/sys/fs/nfs/nfs_commonsubs.c @@ -218,7 +218,7 @@ static struct nfsrv_lughash *nfsgroupnamehash; static int nfs_bigreply[NFSV42_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 1 }; + 1, 0, 0, 1, 0 }; /* local functions */ static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep); @@ -301,6 +301,7 @@ static struct { { NFSV4OP_SETXATTR, 2, "Setxattr", 8, }, { NFSV4OP_REMOVEXATTR, 2, "Rmxattr", 7, }, { NFSV4OP_LISTXATTRS, 2, "Listxattr", 9, }, + { NFSV4OP_BINDCONNTOSESS, 1, "BindConSess", 11, }, }; /* @@ -309,7 +310,7 @@ static struct { static int nfs_bigrequest[NFSV42_NPROCS] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }; /* diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index aba5c5124e72..0297b52015f8 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -561,6 +561,7 @@ int nfsrpc_listextattr(vnode_t, uint64_t *, struct uio *, size_t *, bool *, struct nfsvattr *, int *, struct ucred *, NFSPROC_T *); int nfsrpc_rmextattr(vnode_t, const char *, struct nfsvattr *, int *, struct ucred *, NFSPROC_T *); +void nfsrpc_bindconnsess(CLIENT *, void *, struct ucred *); /* nfs_clstate.c */ int nfscl_open(vnode_t, u_int8_t *, int, u_int32_t, int, diff --git a/sys/fs/nfs/nfscl.h b/sys/fs/nfs/nfscl.h index 52da0af6fa51..3d7afaf68432 100644 --- a/sys/fs/nfs/nfscl.h +++ b/sys/fs/nfs/nfscl.h @@ -81,4 +81,9 @@ struct nfsv4node { printf(__VA_ARGS__); \ } while (0) +struct nfscl_reconarg { + int minorvers; + uint8_t sessionid[NFSX_V4SESSIONID]; +}; + #endif /* _NFS_NFSCL_H */ diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h index 255c9a47ebdf..6777dc72f6a3 100644 --- a/sys/fs/nfs/nfsport.h +++ b/sys/fs/nfs/nfsport.h @@ -412,10 +412,13 @@ #define NFSPROC_RMEXTATTR 63 #define NFSPROC_LISTEXTATTR 64 +/* BindConnectionToSession, done by the krpc for a new connection. */ +#define NFSPROC_BINDCONNTOSESS 65 + /* * Must be defined as one higher than the last NFSv4.2 Proc# above. */ -#define NFSV42_NPROCS 65 +#define NFSV42_NPROCS 66 #endif /* NFS_V3NPROCS */ @@ -444,7 +447,7 @@ struct nfsstatsv1 { uint64_t readlink_bios; uint64_t biocache_readdirs; uint64_t readdir_bios; - uint64_t rpccnt[NFSV42_NPROCS + 15]; + uint64_t rpccnt[NFSV42_NPROCS + 14]; uint64_t rpcretries; uint64_t srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS + 15]; uint64_t reserved_0; @@ -509,7 +512,7 @@ struct nfsstatsov1 { uint64_t readlink_bios; uint64_t biocache_readdirs; uint64_t readdir_bios; - uint64_t rpccnt[NFSV42_NPROCS + 4]; + uint64_t rpccnt[NFSV42_NPROCS + 3]; uint64_t rpcretries; uint64_t srvrpccnt[NFSV42_PURENOPS + NFSV4OP_FAKENOPS]; uint64_t reserved_0; diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h index c123152a7cb7..236d8c14ff24 100644 --- a/sys/fs/nfs/nfsproto.h +++ b/sys/fs/nfs/nfsproto.h @@ -394,10 +394,13 @@ #define NFSPROC_RMEXTATTR 63 #define NFSPROC_LISTEXTATTR 64 +/* BindConnectionToSession, done by the krpc for a new connection. */ +#define NFSPROC_BINDCONNTOSESS 65 + /* * Must be defined as one higher than the last NFSv4.2 Proc# above. */ -#define NFSV42_NPROCS 65 +#define NFSV42_NPROCS 66 #endif /* NFS_V3NPROCS */ diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c index 7cd793502476..ed95173b732b 100644 --- a/sys/fs/nfsclient/nfs_clrpcops.c +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -946,6 +946,8 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim, struct nfsclds *dsp, *odsp; struct in6_addr a6; struct nfsclsession *tsep; + struct rpc_reconupcall recon; + struct nfscl_reconarg *rcp; if (nfsboottime.tv_sec == 0) NFSSETBOOTTIME(nfsboottime); @@ -1019,6 +1021,23 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim, NFSCL_DEBUG(1, "aft createsess=%d\n", error); } if (error == 0) { + /* + * If the session supports a backchannel, set up + * the BindConnectionToSession call in the krpc + * so that it is done on a reconnection. + */ + if (nfscl_enablecallb != 0 && nfs_numnfscbd > 0) { + rcp = mem_alloc(sizeof(*rcp)); + rcp->minorvers = nmp->nm_minorvers; + memcpy(rcp->sessionid, + dsp->nfsclds_sess.nfsess_sessionid, + NFSX_V4SESSIONID); + recon.call = nfsrpc_bindconnsess; + recon.arg = rcp; + CLNT_CONTROL(nmp->nm_client, CLSET_RECONUPCALL, + &recon); + } + NFSLOCKMNT(nmp); /* * The old sessions cannot be safely free'd @@ -8709,3 +8728,74 @@ nfsm_split(struct mbuf *mp, uint64_t xfer) m->m_next = NULL; return (m2); } + +/* + * Do the NFSv4.1 Bind Connection to Session. + * Called from the reconnect layer of the krpc (sys/rpc/clnt_rc.c). + */ +void +nfsrpc_bindconnsess(CLIENT *cl, void *arg, struct ucred *cr) +{ + struct nfscl_reconarg *rcp = (struct nfscl_reconarg *)arg; + uint32_t res, *tl; + struct nfsrv_descript nfsd; + struct nfsrv_descript *nd = &nfsd; + struct rpc_callextra ext; + struct timeval utimeout; + enum clnt_stat stat; + int error; + + nfscl_reqstart(nd, NFSPROC_BINDCONNTOSESS, NULL, NULL, 0, NULL, NULL, + NFS_VER4, rcp->minorvers); + NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED); + memcpy(tl, rcp->sessionid, NFSX_V4SESSIONID); + tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; + *tl++ = txdr_unsigned(NFSCDFC4_FORE_OR_BOTH); + *tl = newnfs_false; + + memset(&ext, 0, sizeof(ext)); + utimeout.tv_sec = 30; + utimeout.tv_usec = 0; + ext.rc_auth = authunix_create(cr); + nd->nd_mrep = NULL; + stat = CLNT_CALL_MBUF(cl, &ext, NFSV4PROC_COMPOUND, nd->nd_mreq, + &nd->nd_mrep, utimeout); + AUTH_DESTROY(ext.rc_auth); + if (stat != RPC_SUCCESS) { + printf("nfsrpc_bindconnsess: call failed stat=%d\n", stat); + return; + } + if (nd->nd_mrep == NULL) { + printf("nfsrpc_bindconnsess: no reply args\n"); + return; + } + error = 0; + newnfs_realign(&nd->nd_mrep, M_WAITOK); + nd->nd_md = nd->nd_mrep; + nd->nd_dpos = mtod(nd->nd_md, char *); + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + nd->nd_repstat = fxdr_unsigned(uint32_t, *tl++); + if (nd->nd_repstat == NFSERR_OK) { + res = fxdr_unsigned(uint32_t, *tl); + if (res > 0 && (error = nfsm_advance(nd, NFSM_RNDUP(res), + -1)) != 0) + goto nfsmout; + NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + + 4 * NFSX_UNSIGNED); + tl += 3; + if (!NFSBCMP(tl, rcp->sessionid, NFSX_V4SESSIONID)) { + tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; + res = fxdr_unsigned(uint32_t, *tl); + if (res != NFSCDFS4_BOTH) + printf("nfsrpc_bindconnsess: did not " + "return FS4_BOTH\n"); + } else + printf("nfsrpc_bindconnsess: not same " + "sessionid\n"); + } else if (nd->nd_repstat != NFSERR_BADSESSION) + printf("nfsrpc_bindconnsess: returned %d\n", nd->nd_repstat); +nfsmout: + if (error != 0) + printf("nfsrpc_bindconnsess: reply bad xdr\n"); + m_freem(nd->nd_mrep); +} diff --git a/sys/rpc/clnt.h b/sys/rpc/clnt.h index f4cc78b1c3b6..6f8f898ca918 100644 --- a/sys/rpc/clnt.h +++ b/sys/rpc/clnt.h @@ -360,6 +360,12 @@ enum clnt_stat clnt_call_private(CLIENT *, struct rpc_callextra *, rpcproc_t, #define CLSET_TLS 30 /* set TLS for socket */ #define CLSET_BLOCKRCV 31 /* Temporarily block reception */ #define CLSET_TLSCERTNAME 32 /* TLS certificate file name */ +/* Structure used as the argument for CLSET_RECONUPCALL. */ +struct rpc_reconupcall { + void (*call)(CLIENT *, void *, struct ucred *); + void *arg; +}; +#define CLSET_RECONUPCALL 33 /* Reconnect upcall */ #endif diff --git a/sys/rpc/clnt_rc.c b/sys/rpc/clnt_rc.c index 8c204989d0ea..ae3b2985a891 100644 --- a/sys/rpc/clnt_rc.c +++ b/sys/rpc/clnt_rc.c @@ -111,6 +111,8 @@ clnt_reconnect_create( rc->rc_client = NULL; rc->rc_tls = false; rc->rc_tlscertname = NULL; + rc->rc_reconcall = NULL; + rc->rc_reconarg = NULL; cl->cl_refs = 1; cl->cl_ops = &clnt_reconnect_ops; @@ -213,6 +215,9 @@ clnt_reconnect_connect(CLIENT *cl) goto out; } } + if (newclient != NULL && rc->rc_reconcall != NULL) + (*rc->rc_reconcall)(newclient, rc->rc_reconarg, + rc->rc_ucred); } td->td_ucred = oldcred; @@ -408,6 +413,7 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info) struct rc_data *rc = (struct rc_data *)cl->cl_private; SVCXPRT *xprt; size_t slen; + struct rpc_reconupcall *upcp; if (info == NULL) { return (FALSE); @@ -513,6 +519,12 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info) strlcpy(rc->rc_tlscertname, info, slen); break; + case CLSET_RECONUPCALL: + upcp = (struct rpc_reconupcall *)info; + rc->rc_reconcall = upcp->call; + rc->rc_reconarg = upcp->arg; + break; + default: return (FALSE); } @@ -555,12 +567,15 @@ clnt_reconnect_destroy(CLIENT *cl) CLNT_DESTROY(rc->rc_client); if (rc->rc_backchannel) { xprt = (SVCXPRT *)rc->rc_backchannel; + KASSERT(xprt->xp_socket == NULL, + ("clnt_reconnect_destroy: xp_socket not NULL")); xprt_unregister(xprt); SVC_RELEASE(xprt); } crfree(rc->rc_ucred); mtx_destroy(&rc->rc_lock); mem_free(rc->rc_tlscertname, 0); /* 0 ok, since arg. ignored. */ + mem_free(rc->rc_reconarg, 0); mem_free(rc, sizeof(*rc)); mem_free(cl, sizeof (CLIENT)); } diff --git a/sys/rpc/krpc.h b/sys/rpc/krpc.h index 77facdcf16cc..48df782e481c 100644 --- a/sys/rpc/krpc.h +++ b/sys/rpc/krpc.h @@ -81,6 +81,9 @@ struct rc_data { void *rc_backchannel; bool rc_tls; /* Enable TLS on connection */ char *rc_tlscertname; + void (*rc_reconcall)(CLIENT *, void *, + struct ucred *); /* reconection upcall */ + void *rc_reconarg; /* upcall arg */ }; /* Bits for ct_rcvstate. */