--- sys/fs/nfs/nfsport.h.sav 2021-04-08 14:44:23.331512000 -0700 +++ sys/fs/nfs/nfsport.h 2021-04-08 14:50:19.337092000 -0700 @@ -357,10 +357,13 @@ #define NFSPROC_OPENLAYGET 54 #define NFSPROC_CREATELAYGET 55 +/* BindConnectionToSession, done by the krpc for a new connection. */ +#define NFSPROC_BINDCONNTOSESS 56 + /* * Must be defined as one higher than the last NFSv4.1 Proc# above. */ -#define NFSV41_NPROCS 56 +#define NFSV41_NPROCS 57 #endif /* NFS_V3NPROCS */ @@ -389,7 +392,7 @@ struct nfsstatsv1 { uint64_t readlink_bios; uint64_t biocache_readdirs; uint64_t readdir_bios; - uint64_t rpccnt[NFSV41_NPROCS + 13]; + uint64_t rpccnt[NFSV41_NPROCS + 12]; uint64_t rpcretries; uint64_t srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS]; uint64_t srvrpc_errs; --- sys/fs/nfs/nfsproto.h.sav 2021-04-08 14:50:36.325656000 -0700 +++ sys/fs/nfs/nfsproto.h 2021-04-08 14:51:35.235980000 -0700 @@ -359,10 +359,13 @@ #define NFSPROC_OPENLAYGET 54 #define NFSPROC_CREATELAYGET 55 +/* BindConnectionToSession, done by the krpc for a new connection. */ +#define NFSPROC_BINDCONNTOSESS 56 + /* * Must be defined as one higher than the last NFSv4.1 Proc# above. */ -#define NFSV41_NPROCS 56 +#define NFSV41_NPROCS 57 #endif /* NFS_V3NPROCS */ --- sys/fs/nfs/nfs_commonsubs.c.sav 2021-04-08 14:52:36.933768000 -0700 +++ sys/fs/nfs/nfs_commonsubs.c 2021-04-08 16:56:27.955021000 -0700 @@ -192,7 +192,7 @@ static struct nfsrv_lughash *nfsgroupnamehash; */ static int nfs_bigreply[NFSV41_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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; /* local functions */ static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep); @@ -267,6 +267,7 @@ static struct { { NFSV4OP_COMMIT, 1, "CommitDS", 8, }, { NFSV4OP_OPEN, 3, "OpenLayoutGet", 13, }, { NFSV4OP_OPEN, 8, "CreateLayGet", 12, }, + { NFSV4OP_BINDCONNTOSESS, 1, "BindConSess", 11, }, }; /* @@ -275,7 +276,7 @@ static struct { static int nfs_bigrequest[NFSV41_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 }; /* --- sys/fs/nfs/nfscl.h.sav 2021-04-08 15:05:27.154268000 -0700 +++ sys/fs/nfs/nfscl.h 2021-04-08 15:06:57.802860000 -0700 @@ -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 */ --- sys/fs/nfs/nfs_var.h.sav 2021-04-08 15:05:43.243693000 -0700 +++ sys/fs/nfs/nfs_var.h 2021-04-08 15:06:25.758858000 -0700 @@ -535,6 +535,7 @@ int nfscl_doiods(vnode_t, struct uio *, int *, int *, int nfscl_findlayoutforio(struct nfscllayout *, uint64_t, uint32_t, struct nfsclflayout **); void nfscl_freenfsclds(struct nfsclds *); +void nfsrpc_bindconnsess(CLIENT *, void *, struct ucred *); /* nfs_clstate.c */ int nfscl_open(vnode_t, u_int8_t *, int, u_int32_t, int, --- sys/fs/nfsclient/nfs_clrpcops.c.sav 2021-04-08 15:14:26.058756000 -0700 +++ sys/fs/nfsclient/nfs_clrpcops.c 2021-04-10 16:23:06.269087000 -0700 @@ -924,6 +924,8 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclcli struct nfsclds *dsp; struct in6_addr a6; struct nfsclsession *tsep; + struct rpc_reconupcall recon; + struct nfscl_reconarg *rcp; if (nfsboottime.tv_sec == 0) NFSSETBOOTTIME(nfsboottime); @@ -942,6 +944,23 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclcli &nmp->nm_sockreq, dsp->nfsclds_sess.nfsess_sequenceid, 1, cred, p); 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 @@ -5854,6 +5873,7 @@ nfsm_copym(struct mbuf *m, int off, int xfer) return (m2); } + /* * Find a file layout that will handle the first bytes of the requested * range and return the information from it needed to the I/O operation. @@ -7678,5 +7698,76 @@ out: *islockedp = 1; } return (laystat); +} + +/* + * 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); } --- sys/rpc/clnt.h.sav 2021-04-08 15:21:58.584734000 -0700 +++ sys/rpc/clnt.h 2021-04-08 15:24:19.660301000 -0700 @@ -357,6 +357,12 @@ enum clnt_stat clnt_call_private(CLIENT *, struct rpc_ #define CLSET_PRIVPORT 27 /* set privileged source port flag */ #define CLGET_PRIVPORT 28 /* get privileged source port flag */ #define CLSET_BACKCHANNEL 29 /* set backchannel for socket */ +/* Structure used as the argument for CLSET_RECONUPCALL. */ +struct rpc_reconupcall { + void (*call)(CLIENT *, void *, struct ucred *); + void *arg; +}; +#define CLSET_RECONUPCALL 30 /* Reconnect upcall */ #endif --- sys/rpc/krpc.h.sav 2021-04-08 15:22:10.346699000 -0700 +++ sys/rpc/krpc.h 2021-04-08 15:26:15.497461000 -0700 @@ -78,6 +78,9 @@ struct rc_data { CLIENT* rc_client; /* underlying RPC client */ struct rpc_err rc_err; void *rc_backchannel; + void (*rc_reconcall)(CLIENT *, void *, + struct ucred *); /* reconection upcall */ + void *rc_reconarg; /* upcall arg */ }; struct ct_data { --- sys/rpc/clnt_rc.c.sav 2021-04-08 15:22:25.663647000 -0700 +++ sys/rpc/clnt_rc.c 2021-04-08 15:42:21.700777000 -0700 @@ -108,6 +108,8 @@ clnt_reconnect_create( rc->rc_closed = FALSE; rc->rc_ucred = crdup(curthread->td_ucred); rc->rc_client = NULL; + rc->rc_reconcall = NULL; + rc->rc_reconarg = NULL; cl->cl_refs = 1; cl->cl_ops = &clnt_reconnect_ops; @@ -193,6 +195,9 @@ clnt_reconnect_connect(CLIENT *cl) newclient = clnt_vc_create(so, (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers, rc->rc_sendsz, rc->rc_recvsz, rc->rc_intr); + if (newclient != NULL && rc->rc_reconcall != NULL) + (*rc->rc_reconcall)(newclient, rc->rc_reconarg, + rc->rc_ucred); } td->td_ucred = oldcred; @@ -385,6 +390,7 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void { struct rc_data *rc = (struct rc_data *)cl->cl_private; SVCXPRT *xprt; + struct rpc_reconupcall *upcp; if (info == NULL) { return (FALSE); @@ -472,6 +478,12 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void rc->rc_backchannel = info; break; + case CLSET_RECONUPCALL: + upcp = (struct rpc_reconupcall *)info; + rc->rc_reconcall = upcp->call; + rc->rc_reconarg = upcp->arg; + break; + default: return (FALSE); } @@ -514,11 +526,14 @@ 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_reconarg, 0); mem_free(rc, sizeof(*rc)); mem_free(cl, sizeof (CLIENT)); }