nfsd4: fix v4 state shutdown error paths
authorJeff Layton <jlayton@redhat.com>
Mon, 19 Jul 2010 20:50:04 +0000 (16:50 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 23 Jul 2010 12:51:22 +0000 (08:51 -0400)
If someone tries to shut down the laundry_wq while it isn't up it'll
cause an oops.

This can happen because write_ports can create a nfsd_svc before we
really start the nfs server, and we may fail before the server is ever
started.

Also make sure state is shutdown on error paths in nfsd_svc().

Use a common global nfsd_up flag instead of nfs4_init, and create common
helper functions for nfsd start/shutdown, as there will be other work
that we want done only when we the number of nfsd threads transitions
between zero and nonzero.

Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs4state.c
fs/nfsd/nfssvc.c

index 182448f..9cc3b78 100644 (file)
@@ -51,7 +51,6 @@ static time_t boot_time;
 static u32 current_ownerid = 1;
 static u32 current_fileid = 1;
 static u32 current_delegid = 1;
-static u32 nfs4_init;
 static stateid_t zerostateid;             /* bits all 0 */
 static stateid_t onestateid;              /* bits all 1 */
 static u64 current_sessionid = 1;
@@ -4071,16 +4070,8 @@ out_free_laundry:
 int
 nfs4_state_start(void)
 {
-       int ret;
-
-       if (nfs4_init)
-               return 0;
        nfsd4_load_reboot_recovery_data();
-       ret = __nfs4_state_start();
-       if (ret)
-               return ret;
-       nfs4_init = 1;
-       return 0;
+       return __nfs4_state_start();
 }
 
 static void
@@ -4115,7 +4106,6 @@ __nfs4_state_shutdown(void)
        }
 
        nfsd4_shutdown_recdir();
-       nfs4_init = 0;
 }
 
 void
index 06b2a26..d7a4d7b 100644 (file)
@@ -180,6 +180,31 @@ int nfsd_nrthreads(void)
        return rv;
 }
 
+static bool nfsd_up = false;
+
+static int nfsd_startup(unsigned short port, int nrservs)
+{
+       int ret;
+
+       ret = nfs4_state_start();
+       nfsd_up = true;
+       return ret;
+}
+
+static void nfsd_shutdown(void)
+{
+       /*
+        * write_ports can create the server without actually starting
+        * any threads--if we get shut down before any threads are
+        * started, then nfsd_last_thread will be run before any of this
+        * other initialization has been done.
+        */
+       if (!nfsd_up)
+               return;
+       nfs4_state_shutdown();
+       nfsd_up = false;
+}
+
 static void nfsd_last_thread(struct svc_serv *serv)
 {
        /* When last nfsd thread exits we need to do some clean-up */
@@ -188,7 +213,7 @@ static void nfsd_last_thread(struct svc_serv *serv)
                lockd_down();
        nfsd_serv = NULL;
        nfsd_racache_shutdown();
-       nfs4_state_shutdown();
+       nfsd_shutdown();
 
        printk(KERN_WARNING "nfsd: last server has exited, flushing export "
                            "cache\n");
@@ -380,6 +405,7 @@ int
 nfsd_svc(unsigned short port, int nrservs)
 {
        int     error;
+       bool    first_thread;
 
        mutex_lock(&nfsd_mutex);
        dprintk("nfsd: creating service\n");
@@ -395,19 +421,23 @@ nfsd_svc(unsigned short port, int nrservs)
        error = nfsd_racache_init(2*nrservs);
        if (error<0)
                goto out;
-       error = nfs4_state_start();
-       if (error)
-               goto out;
+
+       first_thread = (nfsd_serv->sv_nrthreads == 0) && (nrservs != 0);
+
+       if (first_thread) {
+               error = nfsd_startup(port, nrservs);
+               if (error)
+                       goto out;
+       }
 
        nfsd_reset_versions();
 
        error = nfsd_create_serv();
-
        if (error)
-               goto out;
+               goto out_shutdown;
        error = nfsd_init_socks(port);
        if (error)
-               goto failure;
+               goto out_destroy;
 
        error = svc_set_num_threads(nfsd_serv, NULL, nrservs);
        if (error == 0)
@@ -416,9 +446,12 @@ nfsd_svc(unsigned short port, int nrservs)
                 * so subtract 1
                 */
                error = nfsd_serv->sv_nrthreads - 1;
- failure:
+out_destroy:
        svc_destroy(nfsd_serv);         /* Release server */
- out:
+out_shutdown:
+       if (error < 0 && first_thread)
+               nfsd_shutdown();
+out:
        mutex_unlock(&nfsd_mutex);
        return error;
 }