Author: rmacklem
Date: Sat Dec  5 21:38:53 2015
New Revision: 291869
URL: https://svnweb.freebsd.org/changeset/base/291869

Log:
  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.

Modified:
  stable/10/sys/fs/nfs/nfs_var.h
  stable/10/sys/fs/nfsserver/nfs_nfsdkrpc.c
  stable/10/sys/fs/nfsserver/nfs_nfsdstate.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/fs/nfs/nfs_var.h
==============================================================================
--- stable/10/sys/fs/nfs/nfs_var.h      Sat Dec  5 21:38:04 2015        
(r291868)
+++ stable/10/sys/fs/nfs/nfs_var.h      Sat Dec  5 21:38:53 2015        
(r291869)
@@ -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: stable/10/sys/fs/nfsserver/nfs_nfsdkrpc.c
==============================================================================
--- stable/10/sys/fs/nfsserver/nfs_nfsdkrpc.c   Sat Dec  5 21:38:04 2015        
(r291868)
+++ stable/10/sys/fs/nfsserver/nfs_nfsdkrpc.c   Sat Dec  5 21:38:53 2015        
(r291869)
@@ -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();

Modified: stable/10/sys/fs/nfsserver/nfs_nfsdstate.c
==============================================================================
--- stable/10/sys/fs/nfsserver/nfs_nfsdstate.c  Sat Dec  5 21:38:04 2015        
(r291868)
+++ stable/10/sys/fs/nfsserver/nfs_nfsdstate.c  Sat Dec  5 21:38:53 2015        
(r291869)
@@ -4188,10 +4188,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,
@@ -5776,14 +5789,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;
@@ -6042,3 +6057,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);
+                       }
+               }
+       }
+}
+
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to