Release 4.11 net/sunrpc/svc.c
/*
* linux/net/sunrpc/svc.c
*
* High-level RPC service routines
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*
* Multiple threads pools and NUMAisation
* Copyright (c) 2006 Silicon Graphics, Inc.
* by Greg Banks <gnb@melbourne.sgi.com>
*/
#include <linux/linkage.h>
#include <linux/sched/signal.h>
#include <linux/errno.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/bc_xprt.h>
#include <trace/events/sunrpc.h>
#define RPCDBG_FACILITY RPCDBG_SVCDSP
static void svc_unregister(const struct svc_serv *serv, struct net *net);
#define svc_serv_is_pooled(serv) ((serv)->sv_ops->svo_function)
#define SVC_POOL_DEFAULT SVC_POOL_GLOBAL
/*
* Structure for mapping cpus to pools and vice versa.
* Setup once during sunrpc initialisation.
*/
struct svc_pool_map svc_pool_map = {
.mode = SVC_POOL_DEFAULT
};
EXPORT_SYMBOL_GPL(svc_pool_map);
static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */
static int
param_set_pool_mode(const char *val, struct kernel_param *kp)
{
int *ip = (int *)kp->arg;
struct svc_pool_map *m = &svc_pool_map;
int err;
mutex_lock(&svc_pool_map_mutex);
err = -EBUSY;
if (m->count)
goto out;
err = 0;
if (!strncmp(val, "auto", 4))
*ip = SVC_POOL_AUTO;
else if (!strncmp(val, "global", 6))
*ip = SVC_POOL_GLOBAL;
else if (!strncmp(val, "percpu", 6))
*ip = SVC_POOL_PERCPU;
else if (!strncmp(val, "pernode", 7))
*ip = SVC_POOL_PERNODE;
else
err = -EINVAL;
out:
mutex_unlock(&svc_pool_map_mutex);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 151 | 100.00% | 1 | 100.00% |
Total | 151 | 100.00% | 1 | 100.00% |
static int
param_get_pool_mode(char *buf, struct kernel_param *kp)
{
int *ip = (int *)kp->arg;
switch (*ip)
{
case SVC_POOL_AUTO:
return strlcpy(buf, "auto", 20);
case SVC_POOL_GLOBAL:
return strlcpy(buf, "global", 20);
case SVC_POOL_PERCPU:
return strlcpy(buf, "percpu", 20);
case SVC_POOL_PERNODE:
return strlcpy(buf, "pernode", 20);
default:
return sprintf(buf, "%d", *ip);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 98 | 100.00% | 1 | 100.00% |
Total | 98 | 100.00% | 1 | 100.00% |
module_param_call(pool_mode, param_set_pool_mode, param_get_pool_mode,
&svc_pool_map.mode, 0644);
/*
* Detect best pool mapping mode heuristically,
* according to the machine's topology.
*/
static int
svc_pool_map_choose_mode(void)
{
unsigned int node;
if (nr_online_nodes > 1) {
/*
* Actually have multiple NUMA nodes,
* so split pools on NUMA node boundaries
*/
return SVC_POOL_PERNODE;
}
node = first_online_node;
if (nr_cpus_node(node) > 2) {
/*
* Non-trivial SMP, or CONFIG_NUMA on
* non-NUMA hardware, e.g. with a generic
* x86_64 kernel on Xeons. In this case we
* want to divide the pools on cpu boundaries.
*/
return SVC_POOL_PERCPU;
}
/* default: one global pool */
return SVC_POOL_GLOBAL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 45 | 95.74% | 1 | 33.33% |
H Hartley Sweeten | 1 | 2.13% | 1 | 33.33% |
Christoph Lameter | 1 | 2.13% | 1 | 33.33% |
Total | 47 | 100.00% | 3 | 100.00% |
/*
* Allocate the to_pool[] and pool_to[] arrays.
* Returns 0 on success or an errno.
*/
static int
svc_pool_map_alloc_arrays(struct svc_pool_map *m, unsigned int maxpools)
{
m->to_pool = kcalloc(maxpools, sizeof(unsigned int), GFP_KERNEL);
if (!m->to_pool)
goto fail;
m->pool_to = kcalloc(maxpools, sizeof(unsigned int), GFP_KERNEL);
if (!m->pool_to)
goto fail_free;
return 0;
fail_free:
kfree(m->to_pool);
m->to_pool = NULL;
fail:
return -ENOMEM;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 87 | 93.55% | 1 | 50.00% |
J. Bruce Fields | 6 | 6.45% | 1 | 50.00% |
Total | 93 | 100.00% | 2 | 100.00% |
/*
* Initialise the pool map for SVC_POOL_PERCPU mode.
* Returns number of pools or <0 on error.
*/
static int
svc_pool_map_init_percpu(struct svc_pool_map *m)
{
unsigned int maxpools = nr_cpu_ids;
unsigned int pidx = 0;
unsigned int cpu;
int err;
err = svc_pool_map_alloc_arrays(m, maxpools);
if (err)
return err;
for_each_online_cpu(cpu) {
BUG_ON(pidx >= maxpools);
m->to_pool[cpu] = pidx;
m->pool_to[pidx] = cpu;
pidx++;
}
/* cpus brought online later all get mapped to pool0, sorry */
return pidx;
}Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 82 | 97.62% | 1 | 33.33% |
Christoph Lameter | 1 | 1.19% | 1 | 33.33% |
Dan Carpenter | 1 | 1.19% | 1 | 33.33% |
Total | 84 | 100.00% | 3 | 100.00% |
;
/*
* Initialise the pool map for SVC_POOL_PERNODE mode.
* Returns number of pools or <0 on error.
*/
static int
svc_pool_map_init_pernode(struct svc_pool_map *m)
{
unsigned int maxpools = nr_node_ids;
unsigned int pidx = 0;
unsigned int node;
int err;
err = svc_pool_map_alloc_arrays(m, maxpools);
if (err)
return err;
for_each_node_with_cpus(node) {
/* some architectures (e.g. SN2) have cpuless nodes */
BUG_ON(pidx > maxpools);
m->to_pool[node] = pidx;
m->pool_to[pidx] = node;
pidx++;
}
/* nodes brought online later all get mapped to pool0, sorry */
return pidx;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 84 | 98.82% | 1 | 50.00% |
Christoph Lameter | 1 | 1.18% | 1 | 50.00% |
Total | 85 | 100.00% | 2 | 100.00% |
/*
* Add a reference to the global map of cpus to pools (and
* vice versa). Initialise the map if we're the first user.
* Returns the number of pools.
*/
unsigned int
svc_pool_map_get(void)
{
struct svc_pool_map *m = &svc_pool_map;
int npools = -1;
mutex_lock(&svc_pool_map_mutex);
if (m->count++) {
mutex_unlock(&svc_pool_map_mutex);
return m->npools;
}
if (m->mode == SVC_POOL_AUTO)
m->mode = svc_pool_map_choose_mode();
switch (m->mode) {
case SVC_POOL_PERCPU:
npools = svc_pool_map_init_percpu(m);
break;
case SVC_POOL_PERNODE:
npools = svc_pool_map_init_pernode(m);
break;
}
if (npools < 0) {
/* default, or memory allocation failure */
npools = 1;
m->mode = SVC_POOL_GLOBAL;
}
m->npools = npools;
mutex_unlock(&svc_pool_map_mutex);
return m->npools;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 129 | 100.00% | 2 | 100.00% |
Total | 129 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL_GPL(svc_pool_map_get);
/*
* Drop a reference to the global map of cpus to pools.
* When the last reference is dropped, the map data is
* freed; this allows the sysadmin to change the pool
* mode using the pool_mode module option without
* rebooting or re-loading sunrpc.ko.
*/
void
svc_pool_map_put(void)
{
struct svc_pool_map *m = &svc_pool_map;
mutex_lock(&svc_pool_map_mutex);
if (!--m->count) {
kfree(m->to_pool);
m->to_pool = NULL;
kfree(m->pool_to);
m->pool_to = NULL;
m->npools = 0;
}
mutex_unlock(&svc_pool_map_mutex);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 57 | 82.61% | 1 | 50.00% |
J. Bruce Fields | 12 | 17.39% | 1 | 50.00% |
Total | 69 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL_GPL(svc_pool_map_put);
static int svc_pool_map_get_node(unsigned int pidx)
{
const struct svc_pool_map *m = &svc_pool_map;
if (m->count) {
if (m->mode == SVC_POOL_PERCPU)
return cpu_to_node(m->pool_to[pidx]);
if (m->mode == SVC_POOL_PERNODE)
return m->pool_to[pidx];
}
return NUMA_NO_NODE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric Dumazet | 65 | 100.00% | 1 | 100.00% |
Total | 65 | 100.00% | 1 | 100.00% |
/*
* Set the given thread's cpus_allowed mask so that it
* will only run on cpus in the given pool.
*/
static inline void
svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx)
{
struct svc_pool_map *m = &svc_pool_map;
unsigned int node = m->pool_to[pidx];
/*
* The caller checks for sv_nrpools > 1, which
* implies that we've been initialized.
*/
WARN_ON_ONCE(m->count == 0);
if (m->count == 0)
return;
switch (m->mode) {
case SVC_POOL_PERCPU:
{
set_cpus_allowed_ptr(task, cpumask_of(node));
break;
}
case SVC_POOL_PERNODE:
{
set_cpus_allowed_ptr(task, cpumask_of_node(node));
break;
}
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 49 | 52.13% | 2 | 22.22% |
Jeff Layton | 20 | 21.28% | 1 | 11.11% |
Weston Andros Adamson | 10 | 10.64% | 1 | 11.11% |
Mike Travis | 9 | 9.57% | 2 | 22.22% |
Rusty Russell | 5 | 5.32% | 2 | 22.22% |
Ingo Molnar | 1 | 1.06% | 1 | 11.11% |
Total | 94 | 100.00% | 9 | 100.00% |
/*
* Use the mapping mode to choose a pool for a given CPU.
* Used when enqueueing an incoming RPC. Always returns
* a non-NULL pool pointer.
*/
struct svc_pool *
svc_pool_for_cpu(struct svc_serv *serv, int cpu)
{
struct svc_pool_map *m = &svc_pool_map;
unsigned int pidx = 0;
/*
* An uninitialised map happens in a pure client when
* lockd is brought up, so silently treat it the
* same as SVC_POOL_GLOBAL.
*/
if (svc_serv_is_pooled(serv)) {
switch (m->mode) {
case SVC_POOL_PERCPU:
pidx = m->to_pool[cpu];
break;
case SVC_POOL_PERNODE:
pidx = m->to_pool[cpu_to_node(cpu)];
break;
}
}
return &serv->sv_pools[pidx % serv->sv_nrpools];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 89 | 100.00% | 2 | 100.00% |
Total | 89 | 100.00% | 2 | 100.00% |
int svc_rpcb_setup(struct svc_serv *serv, struct net *net)
{
int err;
err = rpcb_create_local(net);
if (err)
return err;
/* Remove any stale portmap registrations */
svc_unregister(serv, net);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Stanislav Kinsbursky | 43 | 100.00% | 4 | 100.00% |
Total | 43 | 100.00% | 4 | 100.00% |
EXPORT_SYMBOL_GPL(svc_rpcb_setup);
void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net)
{
svc_unregister(serv, net);
rpcb_put_local(net);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Stanislav Kinsbursky | 27 | 100.00% | 4 | 100.00% |
Total | 27 | 100.00% | 4 | 100.00% |
EXPORT_SYMBOL_GPL(svc_rpcb_cleanup);
static int svc_uses_rpcbind(struct svc_serv *serv)
{
struct svc_program *progp;
unsigned int i;
for (progp = serv->sv_program; progp; progp = progp->pg_next) {
for (i = 0; i < progp->pg_nvers; i++) {
if (progp->pg_vers[i] == NULL)
continue;
if (!progp->pg_vers[i]->vs_hidden)
return 1;
}
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Stanislav Kinsbursky | 84 | 98.82% | 1 | 50.00% |
Jeff Layton | 1 | 1.18% | 1 | 50.00% |
Total | 85 | 100.00% | 2 | 100.00% |
int svc_bind(struct svc_serv *serv, struct net *net)
{
if (!svc_uses_rpcbind(serv))
return 0;
return svc_rpcb_setup(serv, net);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Stanislav Kinsbursky | 34 | 100.00% | 1 | 100.00% |
Total | 34 | 100.00% | 1 | 100.00% |
EXPORT_SYMBOL_GPL(svc_bind);
#if defined(CONFIG_SUNRPC_BACKCHANNEL)
static void
__svc_init_bc(struct svc_serv *serv)
{
INIT_LIST_HEAD(&serv->sv_cb_list);
spin_lock_init(&serv->sv_cb_lock);
init_waitqueue_head(&serv->sv_cb_waitq);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Trond Myklebust | 35 | 100.00% | 1 | 100.00% |
Total | 35 | 100.00% | 1 | 100.00% |
#else
static void
__svc_init_bc(struct svc_serv *serv)
{
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Trond Myklebust | 10 | 100.00% | 1 | 100.00% |
Total | 10 | 100.00% | 1 | 100.00% |
#endif
/*
* Create an RPC service
*/
static struct svc_serv *
__svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
struct svc_serv_ops *ops)
{
struct svc_serv *serv;
unsigned int vers;
unsigned int xdrsize;
unsigned int i;
if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL)))
return NULL;
serv->sv_name = prog->pg_name;
serv->sv_program = prog;
serv->sv_nrthreads = 1;
serv->sv_stats = prog->pg_stats;
if (bufsize > RPCSVC_MAXPAYLOAD)
bufsize = RPCSVC_MAXPAYLOAD;
serv->sv_max_payload = bufsize? bufsize : 4096;
serv->sv_max_mesg = roundup(serv->sv_max_payload + PAGE_SIZE, PAGE_SIZE);
serv->sv_ops = ops;
xdrsize = 0;
while (prog) {
prog->pg_lovers = prog->pg_nvers-1;
for (vers=0; vers<prog->pg_nvers ; vers++)
if (prog->pg_vers[vers]) {
prog->pg_hivers = vers;
if (prog->pg_lovers > vers)
prog->pg_lovers = vers;
if (prog->pg_vers[vers]->vs_xdrsize > xdrsize)
xdrsize = prog->pg_vers[vers]->vs_xdrsize;
}
prog = prog->pg_next;
}
serv->sv_xdrsize = xdrsize;
INIT_LIST_HEAD(&serv->sv_tempsocks);
INIT_LIST_HEAD(&serv->sv_permsocks);
init_timer(&serv->sv_temptimer);
spin_lock_init(&serv->sv_lock);
__svc_init_bc(serv);
serv->sv_nrpools = npools;
serv->sv_pools =
kcalloc(serv->sv_nrpools, sizeof(struct svc_pool),
GFP_KERNEL);
if (!serv->sv_pools) {
kfree(serv);
return NULL;
}
for (i = 0; i < serv->sv_nrpools; i++) {
struct svc_pool *pool = &serv->sv_pools[i];
dprintk("svc: initialising pool %u for %s\n",
i, serv->sv_name);
pool->sp_id = i;
INIT_LIST_HEAD(&pool->sp_sockets);
INIT_LIST_HEAD(&pool->sp_all_threads);
spin_lock_init(&pool->sp_lock);
}
return serv;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Neil Brown | 145 | 37.47% | 5 | 26.32% |
Greg Banks | 117 | 30.23% | 3 | 15.79% |
Linus Torvalds (pre-git) | 83 | 21.45% | 2 | 10.53% |
Andreas Gruenbacher | 24 | 6.20% | 1 | 5.26% |
Trond Myklebust | 5 | 1.29% | 1 | 5.26% |
Robert P. J. Day | 4 | 1.03% | 1 | 5.26% |
Jeff Layton | 4 | 1.03% | 1 | 5.26% |
Stanislav Kinsbursky | 2 | 0.52% | 2 | 10.53% |
Chuck Lever | 2 | 0.52% | 2 | 10.53% |
Panagiotis Issaris | 1 | 0.26% | 1 | 5.26% |
Total | 387 | 100.00% | 19 | 100.00% |
struct svc_serv *
svc_create(struct svc_program *prog, unsigned int bufsize,
struct svc_serv_ops *ops)
{
return __svc_create(prog, bufsize, /*npools*/1, ops);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 31 | 91.18% | 1 | 50.00% |
Jeff Layton | 3 | 8.82% | 1 | 50.00% |
Total | 34 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL_GPL(svc_create);
struct svc_serv *
svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
struct svc_serv_ops *ops)
{
struct svc_serv *serv;
unsigned int npools = svc_pool_map_get();
serv = __svc_create(prog, bufsize, npools, ops);
if (!serv)
goto out_err;
return serv;
out_err:
svc_pool_map_put();
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 50 | 76.92% | 3 | 60.00% |
Jeff Layton | 15 | 23.08% | 2 | 40.00% |
Total | 65 | 100.00% | 5 | 100.00% |
EXPORT_SYMBOL_GPL(svc_create_pooled);
void svc_shutdown_net(struct svc_serv *serv, struct net *net)
{
svc_close_net(serv, net);
if (serv->sv_ops->svo_shutdown)
serv->sv_ops->svo_shutdown(serv, net);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Stanislav Kinsbursky | 35 | 85.37% | 1 | 50.00% |
Jeff Layton | 6 | 14.63% | 1 | 50.00% |
Total | 41 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL_GPL(svc_shutdown_net);
/*
* Destroy an RPC service. Should be called with appropriate locking to
* protect the sv_nrthreads, sv_permsocks and sv_tempsocks.
*/
void
svc_destroy(struct svc_serv *serv)
{
dprintk("svc: svc_destroy(%s, %d)\n",
serv->sv_program->pg_name,
serv->sv_nrthreads);
if (serv->sv_nrthreads) {
if (--(serv->sv_nrthreads) != 0) {
svc_sock_update_bufs(serv);
return;
}
} else
printk("svc_destroy: no threads for serv=%p!\n", serv);
del_timer_sync(&serv->sv_temptimer);
/*
* The last user is gone and thus all sockets have to be destroyed to
* the point. Check this.
*/
BUG_ON(!list_empty(&serv->sv_permsocks));
BUG_ON(!list_empty(&serv->sv_tempsocks));
cache_clean_deferred(serv);
if (svc_serv_is_pooled(serv))
svc_pool_map_put();
kfree(serv->sv_pools);
kfree(serv);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 57 | 47.50% | 2 | 20.00% |
Greg Banks | 25 | 20.83% | 3 | 30.00% |
Stanislav Kinsbursky | 23 | 19.17% | 1 | 10.00% |
Neil Brown | 14 | 11.67% | 3 | 30.00% |
Chuck Lever | 1 | 0.83% | 1 | 10.00% |
Total | 120 | 100.00% | 10 | 100.00% |
EXPORT_SYMBOL_GPL(svc_destroy);
/*
* Allocate an RPC server's buffer space.
* We allocate pages and place them in rq_argpages.
*/
static int
svc_init_buffer(struct svc_rqst *rqstp, unsigned int size, int node)
{
unsigned int pages, arghi;
/* bc_xprt uses fore channel allocated buffers */
if (svc_is_backchannel(rqstp))
return 1;
pages = size / PAGE_SIZE + 1; /* extra page as we hold both request and reply.
* We assume one is at most one page
*/
arghi = 0;
WARN_ON_ONCE(pages > RPCSVC_MAXPAGES);
if (pages > RPCSVC_MAXPAGES)
pages = RPCSVC_MAXPAGES;
while (pages) {
struct page *p = alloc_pages_node(node, GFP_KERNEL, 0);
if (!p)
break;
rqstp->rq_pages[arghi++] = p;
pages--;
}
return pages == 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Neil Brown | 48 | 44.04% | 4 | 40.00% |
Linus Torvalds (pre-git) | 25 | 22.94% | 1 | 10.00% |
Weston Andros Adamson | 11 | 10.09% | 1 | 10.00% |
Andy Adamson | 11 | 10.09% | 1 | 10.00% |
Eric Dumazet | 8 | 7.34% | 1 | 10.00% |
Chuck Lever | 4 | 3.67% | 1 | 10.00% |
Kris Katterjohn | 2 | 1.83% | 1 | 10.00% |
Total | 109 | 100.00% | 10 | 100.00% |
/*
* Release an RPC server buffer
*/
static void
svc_release_buffer(struct svc_rqst *rqstp)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(rqstp->rq_pages); i++)
if (rqstp->rq_pages[i])
put_page(rqstp->rq_pages[i]);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Neil Brown | 42 | 80.77% | 3 | 60.00% |
Linus Torvalds (pre-git) | 9 | 17.31% | 1 | 20.00% |
Chuck Lever | 1 | 1.92% | 1 | 20.00% |
Total | 52 | 100.00% | 5 | 100.00% |
struct svc_rqst *
svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node)
{
struct svc_rqst *rqstp;
rqstp = kzalloc_node(sizeof(*rqstp), GFP_KERNEL, node);
if (!rqstp)
return rqstp;
__set_bit(RQ_BUSY, &rqstp->rq_flags);
spin_lock_init(&rqstp->rq_lock);
rqstp->rq_server = serv;
rqstp->rq_pool = pool;
rqstp->rq_argp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node);
if (!rqstp->rq_argp)
goto out_enomem;
rqstp->rq_resp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node);
if (!rqstp->rq_resp)
goto out_enomem;
if (!svc_init_buffer(rqstp, serv->sv_max_mesg, node))
goto out_enomem;
return rqstp;
out_enomem:
svc_rqst_free(rqstp);
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jeff Layton | 74 | 46.84% | 3 | 37.50% |
Linus Torvalds (pre-git) | 61 | 38.61% | 2 | 25.00% |
Eric Dumazet | 14 | 8.86% | 1 | 12.50% |
Greg Banks | 8 | 5.06% | 1 | 12.50% |
Neil Brown | 1 | 0.63% | 1 | 12.50% |
Total | 158 | 100.00% | 8 | 100.00% |
EXPORT_SYMBOL_GPL(svc_rqst_alloc);
struct svc_rqst *
svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
{
struct svc_rqst *rqstp;
rqstp = svc_rqst_alloc(serv, pool, node);
if (!rqstp)
return ERR_PTR(-ENOMEM);
serv->sv_nrthreads++;
spin_lock_bh(&pool->sp_lock);
pool->sp_nrthreads++;
list_add_rcu(&rqstp->rq_all, &pool->sp_all_threads);
spin_unlock_bh(&pool->sp_lock);
return rqstp;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jeff Layton | 88 | 97.78% | 2 | 66.67% |
Greg Banks | 2 | 2.22% | 1 | 33.33% |
Total | 90 | 100.00% | 3 | 100.00% |
EXPORT_SYMBOL_GPL(svc_prepare_thread);
/*
* Choose a pool in which to create a new thread, for svc_set_num_threads
*/
static inline struct svc_pool *
choose_pool(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
{
if (pool != NULL)
return pool;
return &serv->sv_pools[(*state)++ % serv->sv_nrpools];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 45 | 90.00% | 1 | 50.00% |
Linus Torvalds (pre-git) | 5 | 10.00% | 1 | 50.00% |
Total | 50 | 100.00% | 2 | 100.00% |
/*
* Choose a thread to kill, for svc_set_num_threads
*/
static inline struct task_struct *
choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
{
unsigned int i;
struct task_struct *task = NULL;
if (pool != NULL) {
spin_lock_bh(&pool->sp_lock);
} else {
/* choose a pool in round-robin fashion */
for (i = 0; i < serv->sv_nrpools; i++) {
pool = &serv->sv_pools[--(*state) % serv->sv_nrpools];
spin_lock_bh(&pool->sp_lock);
if (!list_empty(&pool->sp_all_threads))
goto found_pool;
spin_unlock_bh(&pool->sp_lock);
}
return NULL;
}
found_pool:
if (!list_empty(&pool->sp_all_threads)) {
struct svc_rqst *rqstp;
/*
* Remove from the pool->sp_all_threads list
* so we don't try to kill it again.
*/
rqstp = list_entry(pool->sp_all_threads.next, struct svc_rqst, rq_all);
set_bit(RQ_VICTIM, &rqstp->rq_flags);
list_del_rcu(&rqstp->rq_all);
task = rqstp->rq_task;
}
spin_unlock_bh(&pool->sp_lock);
return task;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Banks | 176 | 90.26% | 2 | 50.00% |
Jeff Layton | 11 | 5.64% | 1 | 25.00% |
Linus Torvalds (pre-git) | 8 | 4.10% | 1 | 25.00% |
Total | 195 | 100.00% | 4 | 100.00% |
/*
* 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)
{
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) {
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;
}
__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;
}
rqstp