]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/sunrpc/svc.c
sunrpc: mark all struct svc_version instances as const
[linux.git] / net / sunrpc / svc.c
index 75f290bddca1bbfd5c8a9db9bab1ac3d231f9ada..45b4f2d2e3bdfa226dc6bb484f5e1741e7d6515f 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 #include <linux/linkage.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
 #include <linux/errno.h>
 #include <linux/net.h>
 #include <linux/in.h>
@@ -385,7 +385,7 @@ static int svc_uses_rpcbind(struct svc_serv *serv)
                for (i = 0; i < progp->pg_nvers; i++) {
                        if (progp->pg_vers[i] == NULL)
                                continue;
-                       if (progp->pg_vers[i]->vs_hidden == 0)
+                       if (!progp->pg_vers[i]->vs_hidden)
                                return 1;
                }
        }
@@ -702,59 +702,32 @@ choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
        return task;
 }
 
-/*
- * Create or destroy enough new threads to make the number
- * of threads the given number.  If `pool' is non-NULL, applies
- * only to threads in that pool, otherwise round-robins between
- * all pools.  Caller must ensure that mutual exclusion between this and
- * server startup or shutdown.
- *
- * Destroying threads relies on the service threads filling in
- * rqstp->rq_task, which only the nfs ones do.  Assumes the serv
- * has been created using svc_create_pooled().
- *
- * Based on code that used to be in nfsd_svc() but tweaked
- * to be pool-aware.
- */
-int
-svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+/* create new threads */
+static int
+svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
 {
        struct svc_rqst *rqstp;
        struct task_struct *task;
        struct svc_pool *chosen_pool;
-       int error = 0;
        unsigned int state = serv->sv_nrthreads-1;
        int node;
 
-       if (pool == NULL) {
-               /* The -1 assumes caller has done a svc_get() */
-               nrservs -= (serv->sv_nrthreads-1);
-       } else {
-               spin_lock_bh(&pool->sp_lock);
-               nrservs -= pool->sp_nrthreads;
-               spin_unlock_bh(&pool->sp_lock);
-       }
-
-       /* create new threads */
-       while (nrservs > 0) {
+       do {
                nrservs--;
                chosen_pool = choose_pool(serv, pool, &state);
 
                node = svc_pool_map_get_node(chosen_pool->sp_id);
                rqstp = svc_prepare_thread(serv, chosen_pool, node);
-               if (IS_ERR(rqstp)) {
-                       error = PTR_ERR(rqstp);
-                       break;
-               }
+               if (IS_ERR(rqstp))
+                       return PTR_ERR(rqstp);
 
                __module_get(serv->sv_ops->svo_module);
                task = kthread_create_on_node(serv->sv_ops->svo_function, rqstp,
                                              node, "%s", serv->sv_name);
                if (IS_ERR(task)) {
-                       error = PTR_ERR(task);
                        module_put(serv->sv_ops->svo_module);
                        svc_exit_thread(rqstp);
-                       break;
+                       return PTR_ERR(task);
                }
 
                rqstp->rq_task = task;
@@ -763,18 +736,103 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
 
                svc_sock_update_bufs(serv);
                wake_up_process(task);
-       }
+       } while (nrservs > 0);
+
+       return 0;
+}
+
+
+/* destroy old threads */
+static int
+svc_signal_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+       struct task_struct *task;
+       unsigned int state = serv->sv_nrthreads-1;
+
        /* destroy old threads */
-       while (nrservs < 0 &&
-              (task = choose_victim(serv, pool, &state)) != NULL) {
+       do {
+               task = choose_victim(serv, pool, &state);
+               if (task == NULL)
+                       break;
                send_sig(SIGINT, task, 1);
                nrservs++;
+       } while (nrservs < 0);
+
+       return 0;
+}
+
+/*
+ * Create or destroy enough new threads to make the number
+ * of threads the given number.  If `pool' is non-NULL, applies
+ * only to threads in that pool, otherwise round-robins between
+ * all pools.  Caller must ensure that mutual exclusion between this and
+ * server startup or shutdown.
+ *
+ * Destroying threads relies on the service threads filling in
+ * rqstp->rq_task, which only the nfs ones do.  Assumes the serv
+ * has been created using svc_create_pooled().
+ *
+ * Based on code that used to be in nfsd_svc() but tweaked
+ * to be pool-aware.
+ */
+int
+svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+       if (pool == NULL) {
+               /* The -1 assumes caller has done a svc_get() */
+               nrservs -= (serv->sv_nrthreads-1);
+       } else {
+               spin_lock_bh(&pool->sp_lock);
+               nrservs -= pool->sp_nrthreads;
+               spin_unlock_bh(&pool->sp_lock);
        }
 
-       return error;
+       if (nrservs > 0)
+               return svc_start_kthreads(serv, pool, nrservs);
+       if (nrservs < 0)
+               return svc_signal_kthreads(serv, pool, nrservs);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(svc_set_num_threads);
 
+/* destroy old threads */
+static int
+svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+       struct task_struct *task;
+       unsigned int state = serv->sv_nrthreads-1;
+
+       /* destroy old threads */
+       do {
+               task = choose_victim(serv, pool, &state);
+               if (task == NULL)
+                       break;
+               kthread_stop(task);
+               nrservs++;
+       } while (nrservs < 0);
+       return 0;
+}
+
+int
+svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+       if (pool == NULL) {
+               /* The -1 assumes caller has done a svc_get() */
+               nrservs -= (serv->sv_nrthreads-1);
+       } else {
+               spin_lock_bh(&pool->sp_lock);
+               nrservs -= pool->sp_nrthreads;
+               spin_unlock_bh(&pool->sp_lock);
+       }
+
+       if (nrservs > 0)
+               return svc_start_kthreads(serv, pool, nrservs);
+       if (nrservs < 0)
+               return svc_stop_kthreads(serv, pool, nrservs);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(svc_set_num_threads_sync);
+
 /*
  * Called from a server thread as it's exiting. Caller must hold the "service
  * mutex" for the service.
@@ -950,7 +1008,7 @@ int svc_register(const struct svc_serv *serv, struct net *net,
                 const unsigned short port)
 {
        struct svc_program      *progp;
-       struct svc_version      *vers;
+       const struct svc_version *vers;
        unsigned int            i;
        int                     error = 0;
 
@@ -976,6 +1034,13 @@ int svc_register(const struct svc_serv *serv, struct net *net,
                        if (vers->vs_hidden)
                                continue;
 
+                       /*
+                        * Don't register a UDP port if we need congestion
+                        * control.
+                        */
+                       if (vers->vs_need_cong_ctrl && proto == IPPROTO_UDP)
+                               continue;
+
                        error = __svc_register(net, progp->pg_name, progp->pg_prog,
                                                i, family, proto, port);
 
@@ -1086,10 +1151,9 @@ static int
 svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 {
        struct svc_program      *progp;
-       struct svc_version      *versp = NULL;  /* compiler food */
-       struct svc_procedure    *procp = NULL;
+       const struct svc_version *versp = NULL; /* compiler food */
+       const struct svc_procedure *procp = NULL;
        struct svc_serv         *serv = rqstp->rq_server;
-       kxdrproc_t              xdr;
        __be32                  *statp;
        u32                     prog, vers, proc;
        __be32                  auth_stat, rpc_stat;
@@ -1169,6 +1233,21 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
          !(versp = progp->pg_vers[vers]))
                goto err_bad_vers;
 
+       /*
+        * Some protocol versions (namely NFSv4) require some form of
+        * congestion control.  (See RFC 7530 section 3.1 paragraph 2)
+        * In other words, UDP is not allowed. We mark those when setting
+        * up the svc_xprt, and verify that here.
+        *
+        * The spec is not very clear about what error should be returned
+        * when someone tries to access a server that is listening on UDP
+        * for lower versions. RPC_PROG_MISMATCH seems to be the closest
+        * fit.
+        */
+       if (versp->vs_need_cong_ctrl &&
+           !test_bit(XPT_CONG_CTRL, &rqstp->rq_xprt->xpt_flags))
+               goto err_bad_vers;
+
        procp = versp->vs_proc + proc;
        if (proc >= versp->vs_nproc || !procp->pc_func)
                goto err_bad_proc;
@@ -1182,7 +1261,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
        svc_putnl(resv, RPC_SUCCESS);
 
        /* Bump per-procedure stats counter */
-       procp->pc_count++;
+       versp->vs_count[proc]++;
 
        /* Initialize storage for argp and resp */
        memset(rqstp->rq_argp, 0, procp->pc_argsize);
@@ -1196,28 +1275,30 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 
        /* Call the function that processes the request. */
        if (!versp->vs_dispatch) {
-               /* Decode arguments */
-               xdr = procp->pc_decode;
-               if (xdr && !xdr(rqstp, argv->iov_base, rqstp->rq_argp))
+               /*
+                * Decode arguments
+                * XXX: why do we ignore the return value?
+                */
+               if (procp->pc_decode &&
+                   !procp->pc_decode(rqstp, argv->iov_base))
                        goto err_garbage;
 
-               *statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
+               *statp = procp->pc_func(rqstp);
 
                /* Encode reply */
                if (*statp == rpc_drop_reply ||
                    test_bit(RQ_DROPME, &rqstp->rq_flags)) {
                        if (procp->pc_release)
-                               procp->pc_release(rqstp, NULL, rqstp->rq_resp);
+                               procp->pc_release(rqstp);
                        goto dropit;
                }
                if (*statp == rpc_autherr_badcred) {
                        if (procp->pc_release)
-                               procp->pc_release(rqstp, NULL, rqstp->rq_resp);
+                               procp->pc_release(rqstp);
                        goto err_bad_auth;
                }
-               if (*statp == rpc_success &&
-                   (xdr = procp->pc_encode) &&
-                   !xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) {
+               if (*statp == rpc_success && procp->pc_encode &&
+                   !procp->pc_encode(rqstp, resv->iov_base + resv->iov_len)) {
                        dprintk("svc: failed to encode reply\n");
                        /* serv->sv_stats->rpcsystemerr++; */
                        *statp = rpc_system_err;
@@ -1227,7 +1308,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
                if (!versp->vs_dispatch(rqstp, statp)) {
                        /* Release reply info */
                        if (procp->pc_release)
-                               procp->pc_release(rqstp, NULL, rqstp->rq_resp);
+                               procp->pc_release(rqstp);
                        goto dropit;
                }
        }
@@ -1238,7 +1319,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 
        /* Release reply info */
        if (procp->pc_release)
-               procp->pc_release(rqstp, NULL, rqstp->rq_resp);
+               procp->pc_release(rqstp);
 
        if (procp->pc_encode == NULL)
                goto dropit;
@@ -1260,7 +1341,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
        return 0;
 
 err_short_len:
-       svc_printk(rqstp, "short len %Zd, dropping request\n",
+       svc_printk(rqstp, "short len %zd, dropping request\n",
                        argv->iov_len);
        goto close;