/*
* 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.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/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/clnt.h>
#define RPCDBG_FACILITY RPCDBG_SVCDSP
#define svc_serv_is_pooled(serv) ((serv)->sv_function)
/*
* Mode for mapping cpus to pools.
*/
enum {
SVC_POOL_AUTO = -1, /* choose one of the others */
SVC_POOL_GLOBAL, /* no mapping, just a single global pool
* (legacy & UP mode) */
SVC_POOL_PERCPU, /* one pool per cpu */
SVC_POOL_PERNODE /* one pool per numa node */
};
#define SVC_POOL_DEFAULT SVC_POOL_GLOBAL
/*
* Structure for mapping cpus to pools and vice versa.
* Setup once during sunrpc initialisation.
*/
static struct svc_pool_map {
int count; /* How many svc_servs use us */
int mode; /* Note: int not enum to avoid
* warnings about "enumeration value
* not handled in switch" */
unsigned int npools;
unsigned int *pool_to; /* maps pool id to cpu or node */
unsigned int *to_pool; /* maps cpu or node to pool id */
} svc_pool_map = {
.count = 0,
.mode = SVC_POOL_DEFAULT
};
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;
}
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);
}
}
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 (num_online_nodes() > 1) {
/*
* Actually have multiple NUMA nodes,
* so split pools on NUMA node boundaries
*/
return SVC_POOL_PERNODE;
}
node = any_online_node(node_online_map);
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;
}
/*
* 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);
fail:
return -ENOMEM;
}
/*
* Initialise the pool map for SVC_POOL_PERCPU mode.
* Returns number of pools or <0 on error.
*/
static int
svc_pool_map_init_percp