svn commit: r291150 - in head/sys/fs: nfs nfsserver

Rick Macklem rmacklem at FreeBSD.org
Sat Nov 21 23:55:48 UTC 2015


Author: rmacklem
Date: Sat Nov 21 23:55:46 2015
New Revision: 291150
URL: https://svnweb.freebsd.org/changeset/base/291150

Log:
  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 at yahoo.com
  PR:		204340
  MFC after:	2 weeks

Modified:
  head/sys/fs/nfs/nfs_var.h
  head/sys/fs/nfsserver/nfs_nfsdkrpc.c
  head/sys/fs/nfsserver/nfs_nfsdstate.c

Modified: head/sys/fs/nfs/nfs_var.h
==============================================================================
--- head/sys/fs/nfs/nfs_var.h	Sat Nov 21 23:30:47 2015	(r291149)
+++ head/sys/fs/nfs/nfs_var.h	Sat Nov 21 23:55:46 2015	(r291150)
@@ -135,6 +135,7 @@ int nfsrv_checksequence(struct nfsrv_des
     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,

Modified: head/sys/fs/nfsserver/nfs_nfsdkrpc.c
==============================================================================
--- head/sys/fs/nfsserver/nfs_nfsdkrpc.c	Sat Nov 21 23:30:47 2015	(r291149)
+++ head/sys/fs/nfsserver/nfs_nfsdkrpc.c	Sat Nov 21 23:55:46 2015	(r291150)
@@ -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();

Modified: head/sys/fs/nfsserver/nfs_nfsdstate.c
==============================================================================
--- head/sys/fs/nfsserver/nfs_nfsdstate.c	Sat Nov 21 23:30:47 2015	(r291149)
+++ head/sys/fs/nfsserver/nfs_nfsdstate.c	Sat Nov 21 23:55:46 2015	(r291150)
@@ -4191,10 +4191,23 @@ nfsrv_docallback(struct nfsclient *clp, 
 	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_descrip
 	 * 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
 	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);
+			}
+		}
+	}
+}
+


More information about the svn-src-all mailing list