summaryrefslogtreecommitdiffstats
path: root/sys/fs
diff options
context:
space:
mode:
authorrmacklem <rmacklem@FreeBSD.org>2015-12-05 21:38:53 +0000
committerrmacklem <rmacklem@FreeBSD.org>2015-12-05 21:38:53 +0000
commit11ddbfbc5cbb55d74dd49e7d13251e56cb1dff91 (patch)
treed8031cefd74dad50d37ce43a720029b712d391f1 /sys/fs
parent8e2b2c5eb5f3434f37c9d01effd9dcdb733a8a26 (diff)
downloadFreeBSD-src-11ddbfbc5cbb55d74dd49e7d13251e56cb1dff91.zip
FreeBSD-src-11ddbfbc5cbb55d74dd49e7d13251e56cb1dff91.tar.gz
MFC: r291150
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.
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/nfs/nfs_var.h1
-rw-r--r--sys/fs/nfsserver/nfs_nfsdkrpc.c1
-rw-r--r--sys/fs/nfsserver/nfs_nfsdstate.c55
3 files changed, 50 insertions, 7 deletions
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 2abd7e4..d540dc9 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -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,
diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c
index e68a18b..7326038 100644
--- a/sys/fs/nfsserver/nfs_nfsdkrpc.c
+++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c
@@ -547,6 +547,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();
diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c
index c6d9448..37fb3b6 100644
--- a/sys/fs/nfsserver/nfs_nfsdstate.c
+++ b/sys/fs/nfsserver/nfs_nfsdstate.c
@@ -4188,10 +4188,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,
@@ -5776,14 +5789,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;
@@ -6042,3 +6057,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);
+ }
+ }
+ }
+}
+
OpenPOWER on IntegriCloud