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:
parent
e0848bbb0c
commit
a0962bf8bc
@ -135,6 +135,7 @@ int nfsrv_checksequence(struct nfsrv_descript *, uint32_t, uint32_t *,
|
|||||||
uint32_t *, int, uint32_t *, NFSPROC_T *);
|
uint32_t *, int, uint32_t *, NFSPROC_T *);
|
||||||
int nfsrv_checkreclaimcomplete(struct nfsrv_descript *);
|
int nfsrv_checkreclaimcomplete(struct nfsrv_descript *);
|
||||||
void nfsrv_cache_session(uint8_t *, uint32_t, int, struct mbuf **);
|
void nfsrv_cache_session(uint8_t *, uint32_t, int, struct mbuf **);
|
||||||
|
void nfsrv_freeallbackchannel_xprts(void);
|
||||||
|
|
||||||
/* nfs_nfsdserv.c */
|
/* nfs_nfsdserv.c */
|
||||||
int nfsrvd_access(struct nfsrv_descript *, int,
|
int nfsrvd_access(struct nfsrv_descript *, int,
|
||||||
|
@ -544,6 +544,7 @@ nfsrvd_init(int terminating)
|
|||||||
if (terminating) {
|
if (terminating) {
|
||||||
nfsd_master_proc = NULL;
|
nfsd_master_proc = NULL;
|
||||||
NFSD_UNLOCK();
|
NFSD_UNLOCK();
|
||||||
|
nfsrv_freeallbackchannel_xprts();
|
||||||
svcpool_destroy(nfsrvd_pool);
|
svcpool_destroy(nfsrvd_pool);
|
||||||
nfsrvd_pool = NULL;
|
nfsrvd_pool = NULL;
|
||||||
NFSD_LOCK();
|
NFSD_LOCK();
|
||||||
|
@ -4191,10 +4191,23 @@ nfsrv_docallback(struct nfsclient *clp, int procnum,
|
|||||||
if (!error) {
|
if (!error) {
|
||||||
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
||||||
KASSERT(sep != NULL, ("sep NULL"));
|
KASSERT(sep != NULL, ("sep NULL"));
|
||||||
error = newnfs_request(nd, NULL, clp, &clp->lc_req,
|
if (sep->sess_cbsess.nfsess_xprt != NULL)
|
||||||
NULL, NULL, cred, clp->lc_program,
|
error = newnfs_request(nd, NULL, clp,
|
||||||
clp->lc_req.nr_vers, NULL, 1, NULL,
|
&clp->lc_req, NULL, NULL, cred,
|
||||||
&sep->sess_cbsess);
|
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);
|
nfsrv_freesession(sep, NULL);
|
||||||
} else
|
} else
|
||||||
error = newnfs_request(nd, NULL, clp, &clp->lc_req,
|
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
|
* If this session handles the backchannel, save the nd_xprt for this
|
||||||
* RPC, since this is the one being used.
|
* 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) {
|
(sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) {
|
||||||
savxprt = sep->sess_cbsess.nfsess_xprt;
|
savxprt = sep->sess_cbsess.nfsess_xprt;
|
||||||
SVC_ACQUIRE(nd->nd_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. */
|
nd->nd_xprt->xp_idletimeout = 0; /* Disable timeout. */
|
||||||
sep->sess_cbsess.nfsess_xprt = nd->nd_xprt;
|
sep->sess_cbsess.nfsess_xprt = nd->nd_xprt;
|
||||||
SVC_RELEASE(savxprt);
|
if (savxprt != NULL)
|
||||||
|
SVC_RELEASE(savxprt);
|
||||||
}
|
}
|
||||||
|
|
||||||
*sflagsp = 0;
|
*sflagsp = 0;
|
||||||
@ -6050,3 +6065,29 @@ nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp)
|
|||||||
return (0);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user