When the nfsd threads are terminated, the NFSv4 server state

(opens, locks, etc) is retained, which I believe is correct behaviour.
However, for NFSv4.1, the server also retained a reference to the xprt
(RPC transport socket structure) for the backchannel. This caused
svcpool_destroy() to not call SVC_DESTROY() for the xprt and allowed
a socket upcall to occur after the mutexes in the svcpool were destroyed,
causing a crash.
This patch fixes the code so that the backchannel xprt structure is
dereferenced just before svcpool_destroy() is called, so the code
does do an SVC_DESTROY() on the xprt, which shuts down the socket upcall.

Tested by:	g_amanakis@yahoo.com
PR:		204340
MFC after:	2 weeks
This commit is contained in:
Rick Macklem 2015-11-21 23:55:46 +00:00
parent e0848bbb0c
commit a0962bf8bc
3 changed files with 50 additions and 7 deletions

View File

@ -135,6 +135,7 @@ int nfsrv_checksequence(struct nfsrv_descript *, uint32_t, uint32_t *,
uint32_t *, int, uint32_t *, NFSPROC_T *);
int nfsrv_checkreclaimcomplete(struct nfsrv_descript *);
void nfsrv_cache_session(uint8_t *, uint32_t, int, struct mbuf **);
void nfsrv_freeallbackchannel_xprts(void);
/* nfs_nfsdserv.c */
int nfsrvd_access(struct nfsrv_descript *, int,

View File

@ -544,6 +544,7 @@ nfsrvd_init(int terminating)
if (terminating) {
nfsd_master_proc = NULL;
NFSD_UNLOCK();
nfsrv_freeallbackchannel_xprts();
svcpool_destroy(nfsrvd_pool);
nfsrvd_pool = NULL;
NFSD_LOCK();

View File

@ -4191,10 +4191,23 @@ nfsrv_docallback(struct nfsclient *clp, int procnum,
if (!error) {
if ((nd->nd_flag & ND_NFSV41) != 0) {
KASSERT(sep != NULL, ("sep NULL"));
error = newnfs_request(nd, NULL, clp, &clp->lc_req,
NULL, NULL, cred, clp->lc_program,
clp->lc_req.nr_vers, NULL, 1, NULL,
&sep->sess_cbsess);
if (sep->sess_cbsess.nfsess_xprt != NULL)
error = newnfs_request(nd, NULL, clp,
&clp->lc_req, NULL, NULL, cred,
clp->lc_program, clp->lc_req.nr_vers, NULL,
1, NULL, &sep->sess_cbsess);
else {
/*
* This should probably never occur, but if a
* client somehow does an RPC without a
* SequenceID Op that causes a callback just
* after the nfsd threads have been terminated
* and restared we could conceivably get here
* without a backchannel xprt.
*/
printf("nfsrv_docallback: no xprt\n");
error = ECONNREFUSED;
}
nfsrv_freesession(sep, NULL);
} else
error = newnfs_request(nd, NULL, clp, &clp->lc_req,
@ -5784,14 +5797,16 @@ nfsrv_checksequence(struct nfsrv_descript *nd, uint32_t sequenceid,
* If this session handles the backchannel, save the nd_xprt for this
* RPC, since this is the one being used.
*/
if (sep->sess_cbsess.nfsess_xprt != NULL &&
if (sep->sess_clp->lc_req.nr_client != NULL &&
(sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) {
savxprt = sep->sess_cbsess.nfsess_xprt;
SVC_ACQUIRE(nd->nd_xprt);
nd->nd_xprt->xp_p2 = savxprt->xp_p2;
nd->nd_xprt->xp_p2 =
sep->sess_clp->lc_req.nr_client->cl_private;
nd->nd_xprt->xp_idletimeout = 0; /* Disable timeout. */
sep->sess_cbsess.nfsess_xprt = nd->nd_xprt;
SVC_RELEASE(savxprt);
if (savxprt != NULL)
SVC_RELEASE(savxprt);
}
*sflagsp = 0;
@ -6050,3 +6065,29 @@ nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp)
return (0);
}
/*
* Free up all backchannel xprts. This needs to be done when the nfsd threads
* exit, since those transports will all be going away.
* This is only called after all the nfsd threads are done performing RPCs,
* so locking shouldn't be an issue.
*/
APPLESTATIC void
nfsrv_freeallbackchannel_xprts(void)
{
struct nfsdsession *sep;
struct nfsclient *clp;
SVCXPRT *xprt;
int i;
for (i = 0; i < nfsrv_clienthashsize; i++) {
LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) {
LIST_FOREACH(sep, &clp->lc_session, sess_list) {
xprt = sep->sess_cbsess.nfsess_xprt;
sep->sess_cbsess.nfsess_xprt = NULL;
if (xprt != NULL)
SVC_RELEASE(xprt);
}
}
}
}