]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - fs/nfsd/nfssvc.c
nfsd4: session needs room for following op to error out
[linux-3.10.git] / fs / nfsd / nfssvc.c
index 9fd8496d5b84e3475988839c11c55821ca570f94..262df5ccbf59db0c4fd516a30fa721945b55f600 100644 (file)
 #include "nfsd.h"
 #include "cache.h"
 #include "vfs.h"
+#include "netns.h"
 
 #define NFSDDBG_FACILITY       NFSDDBG_SVC
 
 extern struct svc_program      nfsd_program;
 static int                     nfsd(void *vrqstp);
-struct timeval                 nfssvc_boot;
 
 /*
- * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members
+ * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members
  * of the svc_serv struct. In particular, ->sv_nrthreads but also to some
  * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt
  *
- * If (out side the lock) nfsd_serv is non-NULL, then it must point to a
+ * If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a
  * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number
  * of nfsd threads must exist and each must listed in ->sp_all_threads in each
  * entry of ->sv_pools[].
@@ -51,7 +51,6 @@ struct timeval                        nfssvc_boot;
  *     nfsd_versions
  */
 DEFINE_MUTEX(nfsd_mutex);
-struct svc_serv                *nfsd_serv;
 
 /*
  * nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used.
@@ -60,8 +59,8 @@ struct svc_serv               *nfsd_serv;
  * nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
  */
 spinlock_t     nfsd_drc_lock;
-unsigned int   nfsd_drc_max_mem;
-unsigned int   nfsd_drc_mem_used;
+unsigned long  nfsd_drc_max_mem;
+unsigned long  nfsd_drc_mem_used;
 
 #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
 static struct svc_stat nfsd_acl_svcstats;
@@ -172,12 +171,14 @@ int nfsd_minorversion(u32 minorversion, enum vers_op change)
  */
 #define        NFSD_MAXSERVS           8192
 
-int nfsd_nrthreads(void)
+int nfsd_nrthreads(struct net *net)
 {
        int rv = 0;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
        mutex_lock(&nfsd_mutex);
-       if (nfsd_serv)
-               rv = nfsd_serv->sv_nrthreads;
+       if (nn->nfsd_serv)
+               rv = nn->nfsd_serv->sv_nrthreads;
        mutex_unlock(&nfsd_mutex);
        return rv;
 }
@@ -185,15 +186,17 @@ int nfsd_nrthreads(void)
 static int nfsd_init_socks(struct net *net)
 {
        int error;
-       if (!list_empty(&nfsd_serv->sv_permsocks))
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+       if (!list_empty(&nn->nfsd_serv->sv_permsocks))
                return 0;
 
-       error = svc_create_xprt(nfsd_serv, "udp", net, PF_INET, NFS_PORT,
+       error = svc_create_xprt(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT,
                                        SVC_SOCK_DEFAULTS);
        if (error < 0)
                return error;
 
-       error = svc_create_xprt(nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
+       error = svc_create_xprt(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
                                        SVC_SOCK_DEFAULTS);
        if (error < 0)
                return error;
@@ -201,15 +204,15 @@ static int nfsd_init_socks(struct net *net)
        return 0;
 }
 
-static bool nfsd_up = false;
+static int nfsd_users = 0;
 
-static int nfsd_startup(int nrservs)
+static int nfsd_startup_generic(int nrservs)
 {
        int ret;
-       struct net *net = &init_net;
 
-       if (nfsd_up)
+       if (nfsd_users++)
                return 0;
+
        /*
         * Readahead param cache - will no-op if it already exists.
         * (Note therefore results will be suboptimal if number of
@@ -218,34 +221,69 @@ static int nfsd_startup(int nrservs)
        ret = nfsd_racache_init(2*nrservs);
        if (ret)
                return ret;
-       ret = nfsd_init_socks(net);
+       ret = nfs4_state_start();
        if (ret)
                goto out_racache;
-       ret = lockd_up(net);
+       return 0;
+
+out_racache:
+       nfsd_racache_shutdown();
+       return ret;
+}
+
+static void nfsd_shutdown_generic(void)
+{
+       if (--nfsd_users)
+               return;
+
+       nfs4_state_shutdown();
+       nfsd_racache_shutdown();
+}
+
+static int nfsd_startup_net(int nrservs, struct net *net)
+{
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+       int ret;
+
+       if (nn->nfsd_net_up)
+               return 0;
+
+       ret = nfsd_startup_generic(nrservs);
        if (ret)
-               goto out_racache;
-       ret = nfs4_state_start();
+               return ret;
+       ret = nfsd_init_socks(net);
        if (ret)
-               goto out_lockd;
-
+               goto out_socks;
+       ret = lockd_up(net);
+       if (ret)
+               goto out_socks;
        ret = nfs4_state_start_net(net);
        if (ret)
-               goto out_net_state;
+               goto out_lockd;
 
-       nfsd_up = true;
+       nn->nfsd_net_up = true;
        return 0;
-out_net_state:
-       nfs4_state_shutdown();
+
 out_lockd:
-       lockd_down(&init_net);
-out_racache:
-       nfsd_racache_shutdown();
+       lockd_down(net);
+out_socks:
+       nfsd_shutdown_generic();
        return ret;
 }
 
-static void nfsd_shutdown(void)
+static void nfsd_shutdown_net(struct net *net)
+{
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+       nfs4_state_shutdown_net(net);
+       lockd_down(net);
+       nn->nfsd_net_up = false;
+       nfsd_shutdown_generic();
+}
+
+static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
 {
-       struct net *net = &init_net;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
        /*
         * write_ports can create the server without actually starting
@@ -253,18 +291,9 @@ static void nfsd_shutdown(void)
         * started, then nfsd_last_thread will be run before any of this
         * other initialization has been done.
         */
-       if (!nfsd_up)
+       if (!nn->nfsd_net_up)
                return;
-       nfs4_state_shutdown_net(net);
-       nfs4_state_shutdown();
-       lockd_down(net);
-       nfsd_racache_shutdown();
-       nfsd_up = false;
-}
-
-static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
-{
-       nfsd_shutdown();
+       nfsd_shutdown_net(net);
 
        svc_rpcb_cleanup(serv, net);
 
@@ -313,7 +342,7 @@ static void set_max_drc(void)
                                        >> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
        nfsd_drc_mem_used = 0;
        spin_lock_init(&nfsd_drc_lock);
-       dprintk("%s nfsd_drc_max_mem %u \n", __func__, nfsd_drc_max_mem);
+       dprintk("%s nfsd_drc_max_mem %lu \n", __func__, nfsd_drc_max_mem);
 }
 
 static int nfsd_get_default_max_blksize(void)
@@ -337,69 +366,84 @@ static int nfsd_get_default_max_blksize(void)
        return ret;
 }
 
-int nfsd_create_serv(void)
+int nfsd_create_serv(struct net *net)
 {
        int error;
-       struct net *net = &init_net;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
        WARN_ON(!mutex_is_locked(&nfsd_mutex));
-       if (nfsd_serv) {
-               svc_get(nfsd_serv);
+       if (nn->nfsd_serv) {
+               svc_get(nn->nfsd_serv);
                return 0;
        }
        if (nfsd_max_blksize == 0)
                nfsd_max_blksize = nfsd_get_default_max_blksize();
        nfsd_reset_versions();
-       nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
+       nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
                                      nfsd_last_thread, nfsd, THIS_MODULE);
-       if (nfsd_serv == NULL)
+       if (nn->nfsd_serv == NULL)
                return -ENOMEM;
 
-       error = svc_bind(nfsd_serv, net);
+       error = svc_bind(nn->nfsd_serv, net);
        if (error < 0) {
-               svc_destroy(nfsd_serv);
+               svc_destroy(nn->nfsd_serv);
                return error;
        }
 
        set_max_drc();
-       do_gettimeofday(&nfssvc_boot);          /* record boot time */
+       do_gettimeofday(&nn->nfssvc_boot);              /* record boot time */
        return 0;
 }
 
-int nfsd_nrpools(void)
+int nfsd_nrpools(struct net *net)
 {
-       if (nfsd_serv == NULL)
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+       if (nn->nfsd_serv == NULL)
                return 0;
        else
-               return nfsd_serv->sv_nrpools;
+               return nn->nfsd_serv->sv_nrpools;
 }
 
-int nfsd_get_nrthreads(int n, int *nthreads)
+int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
 {
        int i = 0;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
-       if (nfsd_serv != NULL) {
-               for (i = 0; i < nfsd_serv->sv_nrpools && i < n; i++)
-                       nthreads[i] = nfsd_serv->sv_pools[i].sp_nrthreads;
+       if (nn->nfsd_serv != NULL) {
+               for (i = 0; i < nn->nfsd_serv->sv_nrpools && i < n; i++)
+                       nthreads[i] = nn->nfsd_serv->sv_pools[i].sp_nrthreads;
        }
 
        return 0;
 }
 
-int nfsd_set_nrthreads(int n, int *nthreads)
+void nfsd_destroy(struct net *net)
+{
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+       int destroy = (nn->nfsd_serv->sv_nrthreads == 1);
+
+       if (destroy)
+               svc_shutdown_net(nn->nfsd_serv, net);
+       svc_destroy(nn->nfsd_serv);
+       if (destroy)
+               nn->nfsd_serv = NULL;
+}
+
+int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
 {
        int i = 0;
        int tot = 0;
        int err = 0;
-       struct net *net = &init_net;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
        WARN_ON(!mutex_is_locked(&nfsd_mutex));
 
-       if (nfsd_serv == NULL || n <= 0)
+       if (nn->nfsd_serv == NULL || n <= 0)
                return 0;
 
-       if (n > nfsd_serv->sv_nrpools)
-               n = nfsd_serv->sv_nrpools;
+       if (n > nn->nfsd_serv->sv_nrpools)
+               n = nn->nfsd_serv->sv_nrpools;
 
        /* enforce a global maximum number of threads */
        tot = 0;
@@ -429,9 +473,9 @@ int nfsd_set_nrthreads(int n, int *nthreads)
                nthreads[0] = 1;
 
        /* apply the new numbers */
-       svc_get(nfsd_serv);
+       svc_get(nn->nfsd_serv);
        for (i = 0; i < n; i++) {
-               err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i],
+               err = svc_set_num_threads(nn->nfsd_serv, &nn->nfsd_serv->sv_pools[i],
                                          nthreads[i]);
                if (err)
                        break;
@@ -446,11 +490,11 @@ int nfsd_set_nrthreads(int n, int *nthreads)
  * this is the first time nrservs is nonzero.
  */
 int
-nfsd_svc(int nrservs)
+nfsd_svc(int nrservs, struct net *net)
 {
        int     error;
        bool    nfsd_up_before;
-       struct net *net = &init_net;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
        mutex_lock(&nfsd_mutex);
        dprintk("nfsd: creating service\n");
@@ -459,29 +503,29 @@ nfsd_svc(int nrservs)
        if (nrservs > NFSD_MAXSERVS)
                nrservs = NFSD_MAXSERVS;
        error = 0;
-       if (nrservs == 0 && nfsd_serv == NULL)
+       if (nrservs == 0 && nn->nfsd_serv == NULL)
                goto out;
 
-       error = nfsd_create_serv();
+       error = nfsd_create_serv(net);
        if (error)
                goto out;
 
-       nfsd_up_before = nfsd_up;
+       nfsd_up_before = nn->nfsd_net_up;
 
-       error = nfsd_startup(nrservs);
+       error = nfsd_startup_net(nrservs, net);
        if (error)
                goto out_destroy;
-       error = svc_set_num_threads(nfsd_serv, NULL, nrservs);
+       error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs);
        if (error)
                goto out_shutdown;
-       /* We are holding a reference to nfsd_serv which
+       /* We are holding a reference to nn->nfsd_serv which
         * we don't want to count in the return value,
         * so subtract 1
         */
-       error = nfsd_serv->sv_nrthreads - 1;
+       error = nn->nfsd_serv->sv_nrthreads - 1;
 out_shutdown:
        if (error < 0 && !nfsd_up_before)
-               nfsd_shutdown();
+               nfsd_shutdown_net(net);
 out_destroy:
        nfsd_destroy(net);              /* Release server */
 out:
@@ -497,6 +541,8 @@ static int
 nfsd(void *vrqstp)
 {
        struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp;
+       struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list);
+       struct net *net = perm_sock->xpt_net;
        int err;
 
        /* Lock module and set up kernel thread */
@@ -561,7 +607,7 @@ out:
        /* Release the thread */
        svc_exit_thread(rqstp);
 
-       nfsd_destroy(&init_net);
+       nfsd_destroy(net);
 
        /* Release module */
        mutex_unlock(&nfsd_mutex);
@@ -606,7 +652,6 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
 
        /* Check whether we have this call in the cache. */
        switch (nfsd_cache_lookup(rqstp)) {
-       case RC_INTR:
        case RC_DROPIT:
                return 0;
        case RC_REPLY:
@@ -657,14 +702,16 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
 int nfsd_pool_stats_open(struct inode *inode, struct file *file)
 {
        int ret;
+       struct nfsd_net *nn = net_generic(inode->i_sb->s_fs_info, nfsd_net_id);
+
        mutex_lock(&nfsd_mutex);
-       if (nfsd_serv == NULL) {
+       if (nn->nfsd_serv == NULL) {
                mutex_unlock(&nfsd_mutex);
                return -ENODEV;
        }
        /* bump up the psudo refcount while traversing */
-       svc_get(nfsd_serv);
-       ret = svc_pool_stats_open(nfsd_serv, file);
+       svc_get(nn->nfsd_serv);
+       ret = svc_pool_stats_open(nn->nfsd_serv, file);
        mutex_unlock(&nfsd_mutex);
        return ret;
 }
@@ -672,7 +719,7 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)
 int nfsd_pool_stats_release(struct inode *inode, struct file *file)
 {
        int ret = seq_release(inode, file);
-       struct net *net = &init_net;
+       struct net *net = inode->i_sb->s_fs_info;
 
        mutex_lock(&nfsd_mutex);
        /* this function really, really should have been called svc_put() */