2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/net/sunrpc/svc.c
|
|
|
|
*
|
|
|
|
* High-level RPC service routines
|
|
|
|
*
|
|
|
|
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
|
2006-10-02 17:18:01 +08:00
|
|
|
*
|
|
|
|
* Multiple threads pools and NUMAisation
|
|
|
|
* Copyright (c) 2006 Silicon Graphics, Inc.
|
|
|
|
* by Greg Banks <gnb@melbourne.sgi.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/linkage.h>
|
2017-02-09 01:51:30 +08:00
|
|
|
#include <linux/sched/signal.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/net.h>
|
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/mm.h>
|
2006-10-02 17:17:59 +08:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/module.h>
|
2008-06-10 20:40:38 +08:00
|
|
|
#include <linux/kthread.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#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>
|
2009-04-01 21:23:07 +08:00
|
|
|
#include <linux/sunrpc/bc_xprt.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-10-29 02:24:12 +08:00
|
|
|
#include <trace/events/sunrpc.h>
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define RPCDBG_FACILITY RPCDBG_SVCDSP
|
|
|
|
|
2012-01-13 18:02:48 +08:00
|
|
|
static void svc_unregister(const struct svc_serv *serv, struct net *net);
|
2008-08-19 07:34:08 +08:00
|
|
|
|
2015-06-09 03:04:46 +08:00
|
|
|
#define svc_serv_is_pooled(serv) ((serv)->sv_ops->svo_function)
|
2007-03-06 17:42:23 +08:00
|
|
|
|
|
|
|
#define SVC_POOL_DEFAULT SVC_POOL_GLOBAL
|
2006-10-02 17:18:01 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Structure for mapping cpus to pools and vice versa.
|
|
|
|
* Setup once during sunrpc initialisation.
|
|
|
|
*/
|
2015-06-09 03:09:54 +08:00
|
|
|
struct svc_pool_map svc_pool_map = {
|
2007-03-06 17:42:23 +08:00
|
|
|
.mode = SVC_POOL_DEFAULT
|
2006-10-02 17:18:01 +08:00
|
|
|
};
|
2015-06-09 03:09:54 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_pool_map);
|
|
|
|
|
2007-03-06 17:42:23 +08:00
|
|
|
static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */
|
|
|
|
|
|
|
|
static int
|
treewide: Fix function prototypes for module_param_call()
Several function prototypes for the set/get functions defined by
module_param_call() have a slightly wrong argument types. This fixes
those in an effort to clean up the calls when running under type-enforced
compiler instrumentation for CFI. This is the result of running the
following semantic patch:
@match_module_param_call_function@
declarer name module_param_call;
identifier _name, _set_func, _get_func;
expression _arg, _mode;
@@
module_param_call(_name, _set_func, _get_func, _arg, _mode);
@fix_set_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._set_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _set_func(
-_val_type _val
+const char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
@fix_get_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._get_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _get_func(
-_val_type _val
+char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
Two additional by-hand changes are included for places where the above
Coccinelle script didn't notice them:
drivers/platform/x86/thinkpad_acpi.c
fs/lockd/svc.c
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Jessica Yu <jeyu@kernel.org>
2017-10-18 10:04:42 +08:00
|
|
|
param_set_pool_mode(const char *val, const struct kernel_param *kp)
|
2007-03-06 17:42:23 +08:00
|
|
|
{
|
|
|
|
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
|
treewide: Fix function prototypes for module_param_call()
Several function prototypes for the set/get functions defined by
module_param_call() have a slightly wrong argument types. This fixes
those in an effort to clean up the calls when running under type-enforced
compiler instrumentation for CFI. This is the result of running the
following semantic patch:
@match_module_param_call_function@
declarer name module_param_call;
identifier _name, _set_func, _get_func;
expression _arg, _mode;
@@
module_param_call(_name, _set_func, _get_func, _arg, _mode);
@fix_set_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._set_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _set_func(
-_val_type _val
+const char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
@fix_get_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._get_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _get_func(
-_val_type _val
+char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
Two additional by-hand changes are included for places where the above
Coccinelle script didn't notice them:
drivers/platform/x86/thinkpad_acpi.c
fs/lockd/svc.c
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Jessica Yu <jeyu@kernel.org>
2017-10-18 10:04:42 +08:00
|
|
|
param_get_pool_mode(char *buf, const struct kernel_param *kp)
|
2007-03-06 17:42:23 +08:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2006-10-02 17:18:01 +08:00
|
|
|
|
2007-03-06 17:42:23 +08:00
|
|
|
module_param_call(pool_mode, param_set_pool_mode, param_get_pool_mode,
|
|
|
|
&svc_pool_map.mode, 0644);
|
2006-10-02 17:18:01 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Detect best pool mapping mode heuristically,
|
|
|
|
* according to the machine's topology.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
svc_pool_map_choose_mode(void)
|
|
|
|
{
|
|
|
|
unsigned int node;
|
|
|
|
|
2009-06-17 06:32:15 +08:00
|
|
|
if (nr_online_nodes > 1) {
|
2006-10-02 17:18:01 +08:00
|
|
|
/*
|
|
|
|
* Actually have multiple NUMA nodes,
|
|
|
|
* so split pools on NUMA node boundaries
|
|
|
|
*/
|
|
|
|
return SVC_POOL_PERNODE;
|
|
|
|
}
|
|
|
|
|
2010-03-06 05:42:43 +08:00
|
|
|
node = first_online_node;
|
2006-10-02 17:18:01 +08:00
|
|
|
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);
|
2011-12-23 09:22:49 +08:00
|
|
|
m->to_pool = NULL;
|
2006-10-02 17:18:01 +08:00
|
|
|
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_percpu(struct svc_pool_map *m)
|
|
|
|
{
|
2007-02-21 05:57:51 +08:00
|
|
|
unsigned int maxpools = nr_cpu_ids;
|
2006-10-02 17:18:01 +08:00
|
|
|
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) {
|
2014-10-29 16:44:16 +08:00
|
|
|
BUG_ON(pidx >= maxpools);
|
2006-10-02 17:18:01 +08:00
|
|
|
m->to_pool[cpu] = pidx;
|
|
|
|
m->pool_to[pidx] = cpu;
|
|
|
|
pidx++;
|
|
|
|
}
|
|
|
|
/* cpus brought online later all get mapped to pool0, sorry */
|
|
|
|
|
|
|
|
return pidx;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2007-02-21 05:57:51 +08:00
|
|
|
unsigned int maxpools = nr_node_ids;
|
2006-10-02 17:18:01 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2007-03-06 17:42:23 +08: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.
|
2006-10-02 17:18:01 +08:00
|
|
|
*/
|
2015-06-09 03:09:54 +08:00
|
|
|
unsigned int
|
2007-03-06 17:42:23 +08:00
|
|
|
svc_pool_map_get(void)
|
2006-10-02 17:18:01 +08:00
|
|
|
{
|
|
|
|
struct svc_pool_map *m = &svc_pool_map;
|
|
|
|
int npools = -1;
|
|
|
|
|
2007-03-06 17:42:23 +08:00
|
|
|
mutex_lock(&svc_pool_map_mutex);
|
|
|
|
|
|
|
|
if (m->count++) {
|
|
|
|
mutex_unlock(&svc_pool_map_mutex);
|
2006-10-02 17:18:01 +08:00
|
|
|
return m->npools;
|
2007-03-06 17:42:23 +08:00
|
|
|
}
|
2006-10-02 17:18:01 +08:00
|
|
|
|
2007-03-06 17:42:23 +08:00
|
|
|
if (m->mode == SVC_POOL_AUTO)
|
|
|
|
m->mode = svc_pool_map_choose_mode();
|
2006-10-02 17:18:01 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2007-03-06 17:42:23 +08:00
|
|
|
mutex_unlock(&svc_pool_map_mutex);
|
2006-10-02 17:18:01 +08:00
|
|
|
return m->npools;
|
|
|
|
}
|
2015-06-09 03:09:54 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_pool_map_get);
|
2007-03-06 17:42:23 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2015-06-09 03:09:54 +08:00
|
|
|
void
|
2007-03-06 17:42:23 +08:00
|
|
|
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);
|
2011-12-23 09:22:49 +08:00
|
|
|
m->to_pool = NULL;
|
2007-03-06 17:42:23 +08:00
|
|
|
kfree(m->pool_to);
|
2011-12-23 09:22:49 +08:00
|
|
|
m->pool_to = NULL;
|
2007-03-06 17:42:23 +08:00
|
|
|
m->npools = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&svc_pool_map_mutex);
|
|
|
|
}
|
2015-06-09 03:09:54 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_pool_map_put);
|
2007-03-06 17:42:23 +08:00
|
|
|
|
2011-07-29 02:04:09 +08:00
|
|
|
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;
|
|
|
|
}
|
2006-10-02 17:18:01 +08:00
|
|
|
/*
|
2008-06-10 20:40:38 +08:00
|
|
|
* Set the given thread's cpus_allowed mask so that it
|
2006-10-02 17:18:01 +08:00
|
|
|
* will only run on cpus in the given pool.
|
|
|
|
*/
|
2008-06-10 20:40:38 +08:00
|
|
|
static inline void
|
|
|
|
svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx)
|
2006-10-02 17:18:01 +08:00
|
|
|
{
|
|
|
|
struct svc_pool_map *m = &svc_pool_map;
|
2008-06-10 20:40:38 +08:00
|
|
|
unsigned int node = m->pool_to[pidx];
|
2006-10-02 17:18:01 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The caller checks for sv_nrpools > 1, which
|
2007-03-06 17:42:23 +08:00
|
|
|
* implies that we've been initialized.
|
2006-10-02 17:18:01 +08:00
|
|
|
*/
|
2012-10-23 22:43:38 +08:00
|
|
|
WARN_ON_ONCE(m->count == 0);
|
|
|
|
if (m->count == 0)
|
|
|
|
return;
|
2006-10-02 17:18:01 +08:00
|
|
|
|
2008-06-10 20:40:38 +08:00
|
|
|
switch (m->mode) {
|
2006-10-02 17:18:01 +08:00
|
|
|
case SVC_POOL_PERCPU:
|
2008-04-05 09:11:10 +08:00
|
|
|
{
|
2009-03-31 12:05:15 +08:00
|
|
|
set_cpus_allowed_ptr(task, cpumask_of(node));
|
2008-06-10 20:40:38 +08:00
|
|
|
break;
|
2008-04-05 09:11:10 +08:00
|
|
|
}
|
2006-10-02 17:18:01 +08:00
|
|
|
case SVC_POOL_PERNODE:
|
2008-04-05 09:11:10 +08:00
|
|
|
{
|
2009-03-13 12:19:46 +08:00
|
|
|
set_cpus_allowed_ptr(task, cpumask_of_node(node));
|
2008-06-10 20:40:38 +08:00
|
|
|
break;
|
2006-10-02 17:18:01 +08:00
|
|
|
}
|
2008-04-05 09:11:10 +08:00
|
|
|
}
|
2006-10-02 17:18:01 +08: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;
|
|
|
|
|
|
|
|
/*
|
2007-03-06 17:42:23 +08:00
|
|
|
* An uninitialised map happens in a pure client when
|
2006-10-02 17:18:01 +08:00
|
|
|
* lockd is brought up, so silently treat it the
|
|
|
|
* same as SVC_POOL_GLOBAL.
|
|
|
|
*/
|
2007-03-06 17:42:23 +08:00
|
|
|
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;
|
|
|
|
}
|
2006-10-02 17:18:01 +08:00
|
|
|
}
|
|
|
|
return &serv->sv_pools[pidx % serv->sv_nrpools];
|
|
|
|
}
|
|
|
|
|
2012-01-31 19:08:05 +08:00
|
|
|
int svc_rpcb_setup(struct svc_serv *serv, struct net *net)
|
2011-10-25 19:16:58 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2012-01-13 18:02:56 +08:00
|
|
|
err = rpcb_create_local(net);
|
2011-10-25 19:16:58 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* Remove any stale portmap registrations */
|
2012-01-13 18:02:56 +08:00
|
|
|
svc_unregister(serv, net);
|
2011-10-25 19:16:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2012-01-31 19:08:05 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_rpcb_setup);
|
2011-10-25 19:16:58 +08:00
|
|
|
|
2012-01-13 18:03:04 +08:00
|
|
|
void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net)
|
2011-10-25 19:16:58 +08:00
|
|
|
{
|
2012-01-13 18:03:04 +08:00
|
|
|
svc_unregister(serv, net);
|
|
|
|
rpcb_put_local(net);
|
2011-10-25 19:16:58 +08:00
|
|
|
}
|
2011-10-25 19:17:28 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_rpcb_cleanup);
|
2011-10-25 19:16:58 +08:00
|
|
|
|
|
|
|
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;
|
2017-02-25 02:25:22 +08:00
|
|
|
if (!progp->pg_vers[i]->vs_hidden)
|
2011-10-25 19:16:58 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2006-10-02 17:18:01 +08:00
|
|
|
|
2012-05-02 20:08:38 +08:00
|
|
|
int svc_bind(struct svc_serv *serv, struct net *net)
|
|
|
|
{
|
|
|
|
if (!svc_uses_rpcbind(serv))
|
|
|
|
return 0;
|
|
|
|
return svc_rpcb_setup(serv, net);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(svc_bind);
|
|
|
|
|
2016-08-30 08:03:49 +08:00
|
|
|
#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);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void
|
|
|
|
__svc_init_bc(struct svc_serv *serv)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Create an RPC service
|
|
|
|
*/
|
2006-10-02 17:17:59 +08:00
|
|
|
static struct svc_serv *
|
|
|
|
__svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
|
2017-08-02 00:00:06 +08:00
|
|
|
const struct svc_serv_ops *ops)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct svc_serv *serv;
|
2007-10-27 01:32:56 +08:00
|
|
|
unsigned int vers;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int xdrsize;
|
2006-10-02 17:17:58 +08:00
|
|
|
unsigned int i;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-07-22 05:51:30 +08:00
|
|
|
if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return NULL;
|
2005-06-23 01:16:24 +08:00
|
|
|
serv->sv_name = prog->pg_name;
|
2005-04-17 06:20:36 +08:00
|
|
|
serv->sv_program = prog;
|
|
|
|
serv->sv_nrthreads = 1;
|
|
|
|
serv->sv_stats = prog->pg_stats;
|
2006-10-06 15:44:05 +08:00
|
|
|
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);
|
2015-06-09 03:03:32 +08:00
|
|
|
serv->sv_ops = ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
xdrsize = 0;
|
2005-06-23 01:16:24 +08:00
|
|
|
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;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
serv->sv_xdrsize = xdrsize;
|
|
|
|
INIT_LIST_HEAD(&serv->sv_tempsocks);
|
|
|
|
INIT_LIST_HEAD(&serv->sv_permsocks);
|
2017-10-17 08:29:42 +08:00
|
|
|
timer_setup(&serv->sv_temptimer, NULL, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock_init(&serv->sv_lock);
|
|
|
|
|
2016-08-30 08:03:49 +08:00
|
|
|
__svc_init_bc(serv);
|
|
|
|
|
2006-10-02 17:17:59 +08:00
|
|
|
serv->sv_nrpools = npools;
|
2006-10-02 17:17:58 +08:00
|
|
|
serv->sv_pools =
|
2006-12-13 16:34:52 +08:00
|
|
|
kcalloc(serv->sv_nrpools, sizeof(struct svc_pool),
|
2006-10-02 17:17:58 +08:00
|
|
|
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];
|
|
|
|
|
2007-02-01 01:14:08 +08:00
|
|
|
dprintk("svc: initialising pool %u for %s\n",
|
2006-10-02 17:17:58 +08:00
|
|
|
i, serv->sv_name);
|
|
|
|
|
|
|
|
pool->sp_id = i;
|
|
|
|
INIT_LIST_HEAD(&pool->sp_sockets);
|
2006-10-02 17:17:59 +08:00
|
|
|
INIT_LIST_HEAD(&pool->sp_all_threads);
|
2006-10-02 17:17:58 +08:00
|
|
|
spin_lock_init(&pool->sp_lock);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return serv;
|
|
|
|
}
|
|
|
|
|
2006-10-02 17:17:59 +08:00
|
|
|
struct svc_serv *
|
|
|
|
svc_create(struct svc_program *prog, unsigned int bufsize,
|
2017-08-02 00:00:06 +08:00
|
|
|
const struct svc_serv_ops *ops)
|
2006-10-02 17:17:59 +08:00
|
|
|
{
|
2015-06-09 03:03:32 +08:00
|
|
|
return __svc_create(prog, bufsize, /*npools*/1, ops);
|
2006-10-02 17:17:59 +08:00
|
|
|
}
|
2008-12-24 05:30:12 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_create);
|
2006-10-02 17:17:59 +08:00
|
|
|
|
|
|
|
struct svc_serv *
|
|
|
|
svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
|
2017-08-02 00:00:06 +08:00
|
|
|
const struct svc_serv_ops *ops)
|
2006-10-02 17:17:59 +08:00
|
|
|
{
|
|
|
|
struct svc_serv *serv;
|
2007-03-06 17:42:23 +08:00
|
|
|
unsigned int npools = svc_pool_map_get();
|
2006-10-02 17:17:59 +08:00
|
|
|
|
2015-06-09 03:03:32 +08:00
|
|
|
serv = __svc_create(prog, bufsize, npools, ops);
|
2014-11-19 20:51:13 +08:00
|
|
|
if (!serv)
|
|
|
|
goto out_err;
|
2006-10-02 17:17:59 +08:00
|
|
|
return serv;
|
2014-11-19 20:51:13 +08:00
|
|
|
out_err:
|
|
|
|
svc_pool_map_put();
|
|
|
|
return NULL;
|
2006-10-02 17:17:59 +08:00
|
|
|
}
|
2008-12-24 05:30:12 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_create_pooled);
|
2006-10-02 17:17:59 +08:00
|
|
|
|
2012-01-31 18:09:25 +08:00
|
|
|
void svc_shutdown_net(struct svc_serv *serv, struct net *net)
|
|
|
|
{
|
|
|
|
svc_close_net(serv, net);
|
|
|
|
|
2015-06-09 03:03:32 +08:00
|
|
|
if (serv->sv_ops->svo_shutdown)
|
|
|
|
serv->sv_ops->svo_shutdown(serv, net);
|
2012-01-31 18:09:25 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(svc_shutdown_net);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2008-06-10 20:40:35 +08:00
|
|
|
* Destroy an RPC service. Should be called with appropriate locking to
|
|
|
|
* protect the sv_nrthreads, sv_permsocks and sv_tempsocks.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
svc_destroy(struct svc_serv *serv)
|
|
|
|
{
|
2007-02-01 01:14:08 +08:00
|
|
|
dprintk("svc: svc_destroy(%s, %d)\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
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);
|
|
|
|
|
2006-10-02 17:17:54 +08:00
|
|
|
del_timer_sync(&serv->sv_temptimer);
|
2012-01-31 18:09:25 +08:00
|
|
|
|
2012-01-31 18:09:17 +08:00
|
|
|
/*
|
|
|
|
* 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));
|
2007-03-06 17:42:22 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
cache_clean_deferred(serv);
|
|
|
|
|
2007-03-06 17:42:23 +08:00
|
|
|
if (svc_serv_is_pooled(serv))
|
|
|
|
svc_pool_map_put();
|
|
|
|
|
2006-10-02 17:17:58 +08:00
|
|
|
kfree(serv->sv_pools);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(serv);
|
|
|
|
}
|
2008-12-24 05:30:12 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_destroy);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate an RPC server's buffer space.
|
|
|
|
* We allocate pages and place them in rq_argpages.
|
|
|
|
*/
|
|
|
|
static int
|
2011-07-29 02:04:09 +08:00
|
|
|
svc_init_buffer(struct svc_rqst *rqstp, unsigned int size, int node)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-04-15 00:27:52 +08:00
|
|
|
unsigned int pages, arghi;
|
2007-02-10 07:38:13 +08:00
|
|
|
|
2010-01-27 10:24:04 +08:00
|
|
|
/* bc_xprt uses fore channel allocated buffers */
|
|
|
|
if (svc_is_backchannel(rqstp))
|
|
|
|
return 1;
|
|
|
|
|
2006-10-06 15:44:05 +08:00
|
|
|
pages = size / PAGE_SIZE + 1; /* extra page as we hold both request and reply.
|
|
|
|
* We assume one is at most one page
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
arghi = 0;
|
2012-10-23 22:43:41 +08:00
|
|
|
WARN_ON_ONCE(pages > RPCSVC_MAXPAGES);
|
|
|
|
if (pages > RPCSVC_MAXPAGES)
|
|
|
|
pages = RPCSVC_MAXPAGES;
|
2005-04-17 06:20:36 +08:00
|
|
|
while (pages) {
|
2011-07-29 02:04:09 +08:00
|
|
|
struct page *p = alloc_pages_node(node, GFP_KERNEL, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!p)
|
|
|
|
break;
|
2006-10-04 17:15:46 +08:00
|
|
|
rqstp->rq_pages[arghi++] = p;
|
2005-04-17 06:20:36 +08:00
|
|
|
pages--;
|
|
|
|
}
|
2008-04-15 00:27:52 +08:00
|
|
|
return pages == 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release an RPC server buffer
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
svc_release_buffer(struct svc_rqst *rqstp)
|
|
|
|
{
|
2008-04-15 00:27:45 +08:00
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rqstp->rq_pages); i++)
|
2006-10-04 17:15:46 +08:00
|
|
|
if (rqstp->rq_pages[i])
|
|
|
|
put_page(rqstp->rq_pages[i]);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-01-29 23:30:54 +08:00
|
|
|
struct svc_rqst *
|
2015-06-09 03:11:10 +08:00
|
|
|
svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct svc_rqst *rqstp;
|
|
|
|
|
2011-07-29 02:04:09 +08:00
|
|
|
rqstp = kzalloc_node(sizeof(*rqstp), GFP_KERNEL, node);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!rqstp)
|
2015-06-09 03:11:10 +08:00
|
|
|
return rqstp;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
sunrpc: convert to lockless lookup of queued server threads
Testing has shown that the pool->sp_lock can be a bottleneck on a busy
server. Every time data is received on a socket, the server must take
that lock in order to dequeue a thread from the sp_threads list.
Address this problem by eliminating the sp_threads list (which contains
threads that are currently idle) and replacing it with a RQ_BUSY flag in
svc_rqst. This allows us to walk the sp_all_threads list under the
rcu_read_lock and find a suitable thread for the xprt by doing a
test_and_set_bit.
Note that we do still have a potential atomicity problem however with
this approach. We don't want svc_xprt_do_enqueue to set the
rqst->rq_xprt pointer unless a test_and_set_bit of RQ_BUSY returned
zero (which indicates that the thread was idle). But, by the time we
check that, the bit could be flipped by a waking thread.
To address this, we acquire a new per-rqst spinlock (rq_lock) and take
that before doing the test_and_set_bit. If that returns false, then we
can set rq_xprt and drop the spinlock. Then, when the thread wakes up,
it must set the bit under the same spinlock and can trust that if it was
already set then the rq_xprt is also properly set.
With this scheme, the case where we have an idle thread no longer needs
to take the highly contended pool->sp_lock at all, and that removes the
bottleneck.
That still leaves one issue: What of the case where we walk the whole
sp_all_threads list and don't find an idle thread? Because the search is
lockess, it's possible for the queueing to race with a thread that is
going to sleep. To address that, we queue the xprt and then search again.
If we find an idle thread at that point, we can't attach the xprt to it
directly since that might race with a different thread waking up and
finding it. All we can do is wake the idle thread back up and let it
attempt to find the now-queued xprt.
Signed-off-by: Jeff Layton <jlayton@primarydata.com>
Tested-by: Chris Worley <chris.worley@primarydata.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
2014-11-22 03:19:30 +08:00
|
|
|
__set_bit(RQ_BUSY, &rqstp->rq_flags);
|
|
|
|
spin_lock_init(&rqstp->rq_lock);
|
|
|
|
rqstp->rq_server = serv;
|
|
|
|
rqstp->rq_pool = pool;
|
2006-10-02 17:18:01 +08:00
|
|
|
|
2011-07-29 02:04:09 +08:00
|
|
|
rqstp->rq_argp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node);
|
2008-01-29 23:30:54 +08:00
|
|
|
if (!rqstp->rq_argp)
|
2015-06-09 03:11:10 +08:00
|
|
|
goto out_enomem;
|
2008-01-29 23:30:54 +08:00
|
|
|
|
2011-07-29 02:04:09 +08:00
|
|
|
rqstp->rq_resp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node);
|
2008-01-29 23:30:54 +08:00
|
|
|
if (!rqstp->rq_resp)
|
2015-06-09 03:11:10 +08:00
|
|
|
goto out_enomem;
|
2008-01-29 23:30:54 +08:00
|
|
|
|
2011-07-29 02:04:09 +08:00
|
|
|
if (!svc_init_buffer(rqstp, serv->sv_max_mesg, node))
|
2015-06-09 03:11:10 +08:00
|
|
|
goto out_enomem;
|
2008-01-29 23:30:54 +08:00
|
|
|
|
|
|
|
return rqstp;
|
|
|
|
out_enomem:
|
2015-06-09 03:11:10 +08:00
|
|
|
svc_rqst_free(rqstp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
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;
|
2008-01-29 23:30:54 +08:00
|
|
|
}
|
2008-12-24 05:30:12 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_prepare_thread);
|
2008-01-29 23:30:54 +08:00
|
|
|
|
2006-10-02 17:17:59 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2007-02-10 07:38:13 +08:00
|
|
|
return &serv->sv_pools[(*state)++ % serv->sv_nrpools];
|
2006-10-02 17:17:59 +08: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 */
|
2007-02-10 07:38:13 +08:00
|
|
|
for (i = 0; i < serv->sv_nrpools; i++) {
|
|
|
|
pool = &serv->sv_pools[--(*state) % serv->sv_nrpools];
|
2006-10-02 17:17:59 +08:00
|
|
|
spin_lock_bh(&pool->sp_lock);
|
2007-02-10 07:38:13 +08:00
|
|
|
if (!list_empty(&pool->sp_all_threads))
|
|
|
|
goto found_pool;
|
2006-10-02 17:17:59 +08:00
|
|
|
spin_unlock_bh(&pool->sp_lock);
|
2007-02-10 07:38:13 +08:00
|
|
|
}
|
2006-10-02 17:17:59 +08:00
|
|
|
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);
|
2014-11-22 03:19:28 +08:00
|
|
|
set_bit(RQ_VICTIM, &rqstp->rq_flags);
|
|
|
|
list_del_rcu(&rqstp->rq_all);
|
2006-10-02 17:17:59 +08:00
|
|
|
task = rqstp->rq_task;
|
2007-02-10 07:38:13 +08:00
|
|
|
}
|
2006-10-02 17:17:59 +08:00
|
|
|
spin_unlock_bh(&pool->sp_lock);
|
|
|
|
|
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
2017-04-26 23:55:26 +08:00
|
|
|
/* create new threads */
|
|
|
|
static int
|
|
|
|
svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
|
2006-10-02 17:17:59 +08:00
|
|
|
{
|
2008-06-10 20:40:38 +08:00
|
|
|
struct svc_rqst *rqstp;
|
|
|
|
struct task_struct *task;
|
|
|
|
struct svc_pool *chosen_pool;
|
2006-10-02 17:17:59 +08:00
|
|
|
unsigned int state = serv->sv_nrthreads-1;
|
2011-07-29 02:04:09 +08:00
|
|
|
int node;
|
2006-10-02 17:17:59 +08:00
|
|
|
|
2017-04-26 23:55:26 +08:00
|
|
|
do {
|
2006-10-02 17:17:59 +08:00
|
|
|
nrservs--;
|
2008-06-10 20:40:38 +08:00
|
|
|
chosen_pool = choose_pool(serv, pool, &state);
|
|
|
|
|
2011-07-29 02:04:09 +08:00
|
|
|
node = svc_pool_map_get_node(chosen_pool->sp_id);
|
|
|
|
rqstp = svc_prepare_thread(serv, chosen_pool, node);
|
2017-04-26 23:55:26 +08:00
|
|
|
if (IS_ERR(rqstp))
|
|
|
|
return PTR_ERR(rqstp);
|
2008-06-10 20:40:38 +08:00
|
|
|
|
2015-06-09 03:05:56 +08:00
|
|
|
__module_get(serv->sv_ops->svo_module);
|
2015-06-09 03:04:46 +08:00
|
|
|
task = kthread_create_on_node(serv->sv_ops->svo_function, rqstp,
|
2013-07-04 06:04:58 +08:00
|
|
|
node, "%s", serv->sv_name);
|
2008-06-10 20:40:38 +08:00
|
|
|
if (IS_ERR(task)) {
|
2015-06-09 03:05:56 +08:00
|
|
|
module_put(serv->sv_ops->svo_module);
|
2008-06-10 20:40:38 +08:00
|
|
|
svc_exit_thread(rqstp);
|
2017-04-26 23:55:26 +08:00
|
|
|
return PTR_ERR(task);
|
2006-10-02 17:17:59 +08:00
|
|
|
}
|
2008-06-10 20:40:38 +08:00
|
|
|
|
|
|
|
rqstp->rq_task = task;
|
|
|
|
if (serv->sv_nrpools > 1)
|
|
|
|
svc_pool_map_set_cpumask(task, chosen_pool->sp_id);
|
|
|
|
|
|
|
|
svc_sock_update_bufs(serv);
|
|
|
|
wake_up_process(task);
|
2017-04-26 23:55:26 +08:00
|
|
|
} 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;
|
|
|
|
|
2006-10-02 17:17:59 +08:00
|
|
|
/* destroy old threads */
|
2017-04-26 23:55:26 +08:00
|
|
|
do {
|
|
|
|
task = choose_victim(serv, pool, &state);
|
|
|
|
if (task == NULL)
|
|
|
|
break;
|
2008-06-10 20:40:39 +08:00
|
|
|
send_sig(SIGINT, task, 1);
|
2006-10-02 17:17:59 +08:00
|
|
|
nrservs++;
|
2017-04-26 23:55:26 +08:00
|
|
|
} 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);
|
2006-10-02 17:17:59 +08:00
|
|
|
}
|
|
|
|
|
2017-04-26 23:55:26 +08:00
|
|
|
if (nrservs > 0)
|
|
|
|
return svc_start_kthreads(serv, pool, nrservs);
|
|
|
|
if (nrservs < 0)
|
|
|
|
return svc_signal_kthreads(serv, pool, nrservs);
|
|
|
|
return 0;
|
2006-10-02 17:17:59 +08:00
|
|
|
}
|
2008-12-24 05:30:12 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_set_num_threads);
|
2006-10-02 17:17:59 +08:00
|
|
|
|
2017-04-26 23:55:27 +08:00
|
|
|
/* 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);
|
|
|
|
|
2006-10-02 17:17:58 +08:00
|
|
|
/*
|
2015-01-22 21:19:32 +08:00
|
|
|
* Called from a server thread as it's exiting. Caller must hold the "service
|
|
|
|
* mutex" for the service.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void
|
2015-06-09 03:11:10 +08:00
|
|
|
svc_rqst_free(struct svc_rqst *rqstp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
svc_release_buffer(rqstp);
|
2005-11-09 01:41:34 +08:00
|
|
|
kfree(rqstp->rq_resp);
|
|
|
|
kfree(rqstp->rq_argp);
|
|
|
|
kfree(rqstp->rq_auth_data);
|
2015-06-09 03:11:10 +08:00
|
|
|
kfree_rcu(rqstp, rq_rcu_head);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(svc_rqst_free);
|
|
|
|
|
|
|
|
void
|
|
|
|
svc_exit_thread(struct svc_rqst *rqstp)
|
|
|
|
{
|
|
|
|
struct svc_serv *serv = rqstp->rq_server;
|
|
|
|
struct svc_pool *pool = rqstp->rq_pool;
|
2006-10-02 17:17:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&pool->sp_lock);
|
|
|
|
pool->sp_nrthreads--;
|
2014-11-22 03:19:28 +08:00
|
|
|
if (!test_and_set_bit(RQ_VICTIM, &rqstp->rq_flags))
|
|
|
|
list_del_rcu(&rqstp->rq_all);
|
2006-10-02 17:17:58 +08:00
|
|
|
spin_unlock_bh(&pool->sp_lock);
|
|
|
|
|
2015-06-09 03:11:10 +08:00
|
|
|
svc_rqst_free(rqstp);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Release the server */
|
|
|
|
if (serv)
|
|
|
|
svc_destroy(serv);
|
|
|
|
}
|
2008-12-24 05:30:12 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_exit_thread);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
* Register an "inet" protocol family netid with the local
|
|
|
|
* rpcbind daemon via an rpcbind v4 SET request.
|
2008-08-19 07:34:16 +08:00
|
|
|
*
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
* No netconfig infrastructure is available in the kernel, so
|
|
|
|
* we map IP_ protocol numbers to netids by hand.
|
2008-08-19 07:34:16 +08:00
|
|
|
*
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
* Returns zero on success; a negative errno value is returned
|
|
|
|
* if any error occurs.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2012-01-13 18:02:48 +08:00
|
|
|
static int __svc_rpcb_register4(struct net *net, const u32 program,
|
|
|
|
const u32 version,
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
const unsigned short protocol,
|
|
|
|
const unsigned short port)
|
2008-08-19 07:34:16 +08:00
|
|
|
{
|
SUNRPC: Simplify kernel RPC service registration
The kernel registers RPC services with the local portmapper with an
rpcbind SET upcall to the local portmapper. Traditionally, this used
rpcbind v2 (PMAP), but registering RPC services that support IPv6
requires rpcbind v3 or v4.
Since we now want separate PF_INET and PF_INET6 listeners for each
kernel RPC service, svc_register() will do only one of those
registrations at a time.
For PF_INET, it tries an rpcb v4 SET upcall first; if that fails, it
does a legacy portmap SET. This makes it entirely backwards
compatible with legacy user space, but allows a proper v4 SET to be
used if rpcbind is available.
For PF_INET6, it does an rpcb v4 SET upcall. If that fails, it fails
the registration, and thus the transport creation. This let's the
kernel detect if user space is able to support IPv6 RPC services, and
thus whether it should maintain a PF_INET6 listener for each service
at all.
This provides complete backwards compatibilty with legacy user space
that only supports rpcbind v2. The only down-side is that registering
a new kernel RPC service may take an extra exchange with the local
portmapper on legacy systems, but this is an infrequent operation and
is done over UDP (no lingering sockets in TIMEWAIT), so it shouldn't
be consequential.
This patch is part of a series that addresses
http://bugzilla.kernel.org/show_bug.cgi?id=12256
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-03-19 08:47:36 +08:00
|
|
|
const struct sockaddr_in sin = {
|
2008-08-19 07:34:16 +08:00
|
|
|
.sin_family = AF_INET,
|
|
|
|
.sin_addr.s_addr = htonl(INADDR_ANY),
|
|
|
|
.sin_port = htons(port),
|
|
|
|
};
|
SUNRPC: Simplify kernel RPC service registration
The kernel registers RPC services with the local portmapper with an
rpcbind SET upcall to the local portmapper. Traditionally, this used
rpcbind v2 (PMAP), but registering RPC services that support IPv6
requires rpcbind v3 or v4.
Since we now want separate PF_INET and PF_INET6 listeners for each
kernel RPC service, svc_register() will do only one of those
registrations at a time.
For PF_INET, it tries an rpcb v4 SET upcall first; if that fails, it
does a legacy portmap SET. This makes it entirely backwards
compatible with legacy user space, but allows a proper v4 SET to be
used if rpcbind is available.
For PF_INET6, it does an rpcb v4 SET upcall. If that fails, it fails
the registration, and thus the transport creation. This let's the
kernel detect if user space is able to support IPv6 RPC services, and
thus whether it should maintain a PF_INET6 listener for each service
at all.
This provides complete backwards compatibilty with legacy user space
that only supports rpcbind v2. The only down-side is that registering
a new kernel RPC service may take an extra exchange with the local
portmapper on legacy systems, but this is an infrequent operation and
is done over UDP (no lingering sockets in TIMEWAIT), so it shouldn't
be consequential.
This patch is part of a series that addresses
http://bugzilla.kernel.org/show_bug.cgi?id=12256
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-03-19 08:47:36 +08:00
|
|
|
const char *netid;
|
|
|
|
int error;
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
|
|
|
|
switch (protocol) {
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
netid = RPCBIND_NETID_UDP;
|
|
|
|
break;
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
netid = RPCBIND_NETID_TCP;
|
|
|
|
break;
|
|
|
|
default:
|
2009-03-19 08:46:59 +08:00
|
|
|
return -ENOPROTOOPT;
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
}
|
|
|
|
|
2012-01-13 18:02:48 +08:00
|
|
|
error = rpcb_v4_register(net, program, version,
|
SUNRPC: Simplify kernel RPC service registration
The kernel registers RPC services with the local portmapper with an
rpcbind SET upcall to the local portmapper. Traditionally, this used
rpcbind v2 (PMAP), but registering RPC services that support IPv6
requires rpcbind v3 or v4.
Since we now want separate PF_INET and PF_INET6 listeners for each
kernel RPC service, svc_register() will do only one of those
registrations at a time.
For PF_INET, it tries an rpcb v4 SET upcall first; if that fails, it
does a legacy portmap SET. This makes it entirely backwards
compatible with legacy user space, but allows a proper v4 SET to be
used if rpcbind is available.
For PF_INET6, it does an rpcb v4 SET upcall. If that fails, it fails
the registration, and thus the transport creation. This let's the
kernel detect if user space is able to support IPv6 RPC services, and
thus whether it should maintain a PF_INET6 listener for each service
at all.
This provides complete backwards compatibilty with legacy user space
that only supports rpcbind v2. The only down-side is that registering
a new kernel RPC service may take an extra exchange with the local
portmapper on legacy systems, but this is an infrequent operation and
is done over UDP (no lingering sockets in TIMEWAIT), so it shouldn't
be consequential.
This patch is part of a series that addresses
http://bugzilla.kernel.org/show_bug.cgi?id=12256
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-03-19 08:47:36 +08:00
|
|
|
(const struct sockaddr *)&sin, netid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* User space didn't support rpcbind v4, so retry this
|
|
|
|
* registration request with the legacy rpcbind v2 protocol.
|
|
|
|
*/
|
|
|
|
if (error == -EPROTONOSUPPORT)
|
2012-01-13 18:02:48 +08:00
|
|
|
error = rpcb_register(net, program, version, protocol, port);
|
SUNRPC: Simplify kernel RPC service registration
The kernel registers RPC services with the local portmapper with an
rpcbind SET upcall to the local portmapper. Traditionally, this used
rpcbind v2 (PMAP), but registering RPC services that support IPv6
requires rpcbind v3 or v4.
Since we now want separate PF_INET and PF_INET6 listeners for each
kernel RPC service, svc_register() will do only one of those
registrations at a time.
For PF_INET, it tries an rpcb v4 SET upcall first; if that fails, it
does a legacy portmap SET. This makes it entirely backwards
compatible with legacy user space, but allows a proper v4 SET to be
used if rpcbind is available.
For PF_INET6, it does an rpcb v4 SET upcall. If that fails, it fails
the registration, and thus the transport creation. This let's the
kernel detect if user space is able to support IPv6 RPC services, and
thus whether it should maintain a PF_INET6 listener for each service
at all.
This provides complete backwards compatibilty with legacy user space
that only supports rpcbind v2. The only down-side is that registering
a new kernel RPC service may take an extra exchange with the local
portmapper on legacy systems, but this is an infrequent operation and
is done over UDP (no lingering sockets in TIMEWAIT), so it shouldn't
be consequential.
This patch is part of a series that addresses
http://bugzilla.kernel.org/show_bug.cgi?id=12256
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-03-19 08:47:36 +08:00
|
|
|
|
|
|
|
return error;
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
}
|
|
|
|
|
2011-12-10 17:48:31 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
/*
|
|
|
|
* Register an "inet6" protocol family netid with the local
|
|
|
|
* rpcbind daemon via an rpcbind v4 SET request.
|
|
|
|
*
|
|
|
|
* No netconfig infrastructure is available in the kernel, so
|
|
|
|
* we map IP_ protocol numbers to netids by hand.
|
|
|
|
*
|
|
|
|
* Returns zero on success; a negative errno value is returned
|
|
|
|
* if any error occurs.
|
|
|
|
*/
|
2012-01-13 18:02:48 +08:00
|
|
|
static int __svc_rpcb_register6(struct net *net, const u32 program,
|
|
|
|
const u32 version,
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
const unsigned short protocol,
|
|
|
|
const unsigned short port)
|
|
|
|
{
|
SUNRPC: Simplify kernel RPC service registration
The kernel registers RPC services with the local portmapper with an
rpcbind SET upcall to the local portmapper. Traditionally, this used
rpcbind v2 (PMAP), but registering RPC services that support IPv6
requires rpcbind v3 or v4.
Since we now want separate PF_INET and PF_INET6 listeners for each
kernel RPC service, svc_register() will do only one of those
registrations at a time.
For PF_INET, it tries an rpcb v4 SET upcall first; if that fails, it
does a legacy portmap SET. This makes it entirely backwards
compatible with legacy user space, but allows a proper v4 SET to be
used if rpcbind is available.
For PF_INET6, it does an rpcb v4 SET upcall. If that fails, it fails
the registration, and thus the transport creation. This let's the
kernel detect if user space is able to support IPv6 RPC services, and
thus whether it should maintain a PF_INET6 listener for each service
at all.
This provides complete backwards compatibilty with legacy user space
that only supports rpcbind v2. The only down-side is that registering
a new kernel RPC service may take an extra exchange with the local
portmapper on legacy systems, but this is an infrequent operation and
is done over UDP (no lingering sockets in TIMEWAIT), so it shouldn't
be consequential.
This patch is part of a series that addresses
http://bugzilla.kernel.org/show_bug.cgi?id=12256
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-03-19 08:47:36 +08:00
|
|
|
const struct sockaddr_in6 sin6 = {
|
2008-08-19 07:34:16 +08:00
|
|
|
.sin6_family = AF_INET6,
|
|
|
|
.sin6_addr = IN6ADDR_ANY_INIT,
|
|
|
|
.sin6_port = htons(port),
|
|
|
|
};
|
SUNRPC: Simplify kernel RPC service registration
The kernel registers RPC services with the local portmapper with an
rpcbind SET upcall to the local portmapper. Traditionally, this used
rpcbind v2 (PMAP), but registering RPC services that support IPv6
requires rpcbind v3 or v4.
Since we now want separate PF_INET and PF_INET6 listeners for each
kernel RPC service, svc_register() will do only one of those
registrations at a time.
For PF_INET, it tries an rpcb v4 SET upcall first; if that fails, it
does a legacy portmap SET. This makes it entirely backwards
compatible with legacy user space, but allows a proper v4 SET to be
used if rpcbind is available.
For PF_INET6, it does an rpcb v4 SET upcall. If that fails, it fails
the registration, and thus the transport creation. This let's the
kernel detect if user space is able to support IPv6 RPC services, and
thus whether it should maintain a PF_INET6 listener for each service
at all.
This provides complete backwards compatibilty with legacy user space
that only supports rpcbind v2. The only down-side is that registering
a new kernel RPC service may take an extra exchange with the local
portmapper on legacy systems, but this is an infrequent operation and
is done over UDP (no lingering sockets in TIMEWAIT), so it shouldn't
be consequential.
This patch is part of a series that addresses
http://bugzilla.kernel.org/show_bug.cgi?id=12256
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-03-19 08:47:36 +08:00
|
|
|
const char *netid;
|
|
|
|
int error;
|
2008-08-19 07:34:16 +08:00
|
|
|
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
switch (protocol) {
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
netid = RPCBIND_NETID_UDP6;
|
2008-08-19 07:34:16 +08:00
|
|
|
break;
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
case IPPROTO_TCP:
|
2008-08-19 07:34:16 +08:00
|
|
|
netid = RPCBIND_NETID_TCP6;
|
|
|
|
break;
|
|
|
|
default:
|
2009-03-19 08:46:59 +08:00
|
|
|
return -ENOPROTOOPT;
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
}
|
|
|
|
|
2012-01-13 18:02:48 +08:00
|
|
|
error = rpcb_v4_register(net, program, version,
|
SUNRPC: Simplify kernel RPC service registration
The kernel registers RPC services with the local portmapper with an
rpcbind SET upcall to the local portmapper. Traditionally, this used
rpcbind v2 (PMAP), but registering RPC services that support IPv6
requires rpcbind v3 or v4.
Since we now want separate PF_INET and PF_INET6 listeners for each
kernel RPC service, svc_register() will do only one of those
registrations at a time.
For PF_INET, it tries an rpcb v4 SET upcall first; if that fails, it
does a legacy portmap SET. This makes it entirely backwards
compatible with legacy user space, but allows a proper v4 SET to be
used if rpcbind is available.
For PF_INET6, it does an rpcb v4 SET upcall. If that fails, it fails
the registration, and thus the transport creation. This let's the
kernel detect if user space is able to support IPv6 RPC services, and
thus whether it should maintain a PF_INET6 listener for each service
at all.
This provides complete backwards compatibilty with legacy user space
that only supports rpcbind v2. The only down-side is that registering
a new kernel RPC service may take an extra exchange with the local
portmapper on legacy systems, but this is an infrequent operation and
is done over UDP (no lingering sockets in TIMEWAIT), so it shouldn't
be consequential.
This patch is part of a series that addresses
http://bugzilla.kernel.org/show_bug.cgi?id=12256
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-03-19 08:47:36 +08:00
|
|
|
(const struct sockaddr *)&sin6, netid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* User space didn't support rpcbind version 4, so we won't
|
|
|
|
* use a PF_INET6 listener.
|
|
|
|
*/
|
|
|
|
if (error == -EPROTONOSUPPORT)
|
|
|
|
error = -EAFNOSUPPORT;
|
|
|
|
|
|
|
|
return error;
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
}
|
2011-12-10 17:48:31 +08:00
|
|
|
#endif /* IS_ENABLED(CONFIG_IPV6) */
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Register a kernel RPC service via rpcbind version 4.
|
|
|
|
*
|
|
|
|
* Returns zero on success; a negative errno value is returned
|
|
|
|
* if any error occurs.
|
|
|
|
*/
|
2012-01-13 18:02:48 +08:00
|
|
|
static int __svc_register(struct net *net, const char *progname,
|
2009-03-19 08:47:44 +08:00
|
|
|
const u32 program, const u32 version,
|
2009-03-19 08:46:06 +08:00
|
|
|
const int family,
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
const unsigned short protocol,
|
|
|
|
const unsigned short port)
|
|
|
|
{
|
2009-03-19 08:47:44 +08:00
|
|
|
int error = -EAFNOSUPPORT;
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
|
|
|
|
switch (family) {
|
2009-03-19 08:46:06 +08:00
|
|
|
case PF_INET:
|
2012-01-13 18:02:48 +08:00
|
|
|
error = __svc_rpcb_register4(net, program, version,
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
protocol, port);
|
SUNRPC: Simplify kernel RPC service registration
The kernel registers RPC services with the local portmapper with an
rpcbind SET upcall to the local portmapper. Traditionally, this used
rpcbind v2 (PMAP), but registering RPC services that support IPv6
requires rpcbind v3 or v4.
Since we now want separate PF_INET and PF_INET6 listeners for each
kernel RPC service, svc_register() will do only one of those
registrations at a time.
For PF_INET, it tries an rpcb v4 SET upcall first; if that fails, it
does a legacy portmap SET. This makes it entirely backwards
compatible with legacy user space, but allows a proper v4 SET to be
used if rpcbind is available.
For PF_INET6, it does an rpcb v4 SET upcall. If that fails, it fails
the registration, and thus the transport creation. This let's the
kernel detect if user space is able to support IPv6 RPC services, and
thus whether it should maintain a PF_INET6 listener for each service
at all.
This provides complete backwards compatibilty with legacy user space
that only supports rpcbind v2. The only down-side is that registering
a new kernel RPC service may take an extra exchange with the local
portmapper on legacy systems, but this is an infrequent operation and
is done over UDP (no lingering sockets in TIMEWAIT), so it shouldn't
be consequential.
This patch is part of a series that addresses
http://bugzilla.kernel.org/show_bug.cgi?id=12256
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-03-19 08:47:36 +08:00
|
|
|
break;
|
2011-12-10 17:48:31 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
2009-03-19 08:46:06 +08:00
|
|
|
case PF_INET6:
|
2012-01-13 18:02:48 +08:00
|
|
|
error = __svc_rpcb_register6(net, program, version,
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
protocol, port);
|
2011-12-10 17:48:31 +08:00
|
|
|
#endif
|
2008-08-19 07:34:16 +08:00
|
|
|
}
|
|
|
|
|
2009-03-19 08:47:44 +08:00
|
|
|
return error;
|
2008-08-19 07:34:16 +08:00
|
|
|
}
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
|
2008-08-19 07:34:16 +08:00
|
|
|
/**
|
|
|
|
* svc_register - register an RPC service with the local portmapper
|
|
|
|
* @serv: svc_serv struct for the service to register
|
2012-01-13 18:02:48 +08:00
|
|
|
* @net: net namespace for the service to register
|
2009-03-19 08:46:06 +08:00
|
|
|
* @family: protocol family of service's listener socket
|
2008-08-19 07:34:16 +08:00
|
|
|
* @proto: transport protocol number to advertise
|
|
|
|
* @port: port to advertise
|
|
|
|
*
|
2009-03-19 08:46:06 +08:00
|
|
|
* Service is registered for any address in the passed-in protocol family
|
2008-08-19 07:34:16 +08:00
|
|
|
*/
|
2012-01-13 18:02:48 +08:00
|
|
|
int svc_register(const struct svc_serv *serv, struct net *net,
|
|
|
|
const int family, const unsigned short proto,
|
|
|
|
const unsigned short port)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct svc_program *progp;
|
2017-05-12 22:21:37 +08:00
|
|
|
const struct svc_version *vers;
|
2007-10-27 01:32:56 +08:00
|
|
|
unsigned int i;
|
2008-08-19 07:34:00 +08:00
|
|
|
int error = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-10-23 22:43:45 +08:00
|
|
|
WARN_ON_ONCE(proto == 0 && port == 0);
|
|
|
|
if (proto == 0 && port == 0)
|
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-10-04 17:16:05 +08:00
|
|
|
for (progp = serv->sv_program; progp; progp = progp->pg_next) {
|
|
|
|
for (i = 0; i < progp->pg_nvers; i++) {
|
2013-12-31 13:17:20 +08:00
|
|
|
vers = progp->pg_vers[i];
|
|
|
|
if (vers == NULL)
|
2006-10-04 17:16:05 +08:00
|
|
|
continue;
|
|
|
|
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
dprintk("svc: svc_register(%sv%d, %s, %u, %u)%s\n",
|
2006-10-04 17:16:05 +08:00
|
|
|
progp->pg_name,
|
SUNRPC: Register both netids for AF_INET6 servers
TI-RPC is a user-space library of RPC functions that replaces ONC RPC
and allows RPC to operate in the new world of IPv6.
TI-RPC combines the concept of a transport protocol (UDP and TCP)
and a protocol family (PF_INET and PF_INET6) into a single identifier
called a "netid." For example, "udp" means UDP over IPv4, and "udp6"
means UDP over IPv6.
For rpcbind, then, the RPC service tuple that is registered and
advertised is:
[RPC program, RPC version, service address and port, netid]
instead of
[RPC program, RPC version, port, protocol]
Service address is typically ANYADDR, but can be a specific address
of one of the interfaces on a multi-homed host. The third item in
the new tuple is expressed as a universal address.
The current Linux rpcbind implementation registers a netid for both
protocol families when RPCB_SET is done for just the PF_INET6 version
of the netid (ie udp6 or tcp6). So registering "udp6" causes a
registration for "udp" to appear automatically as well.
We've recently determined that this is incorrect behavior. In the
TI-RPC world, "udp6" is not meant to imply that the registered RPC
service handles requests from AF_INET as well, even if the listener
socket does address mapping. "udp" and "udp6" are entirely separate
capabilities, and must be registered separately.
The Linux kernel, unlike TI-RPC, leverages address mapping to allow a
single listener socket to handle requests for both AF_INET and AF_INET6.
This is still OK, but the kernel currently assumes registering "udp6"
will cover "udp" as well. It registers only "udp6" for it's AF_INET6
services, even though they handle both AF_INET and AF_INET6 on the same
port.
So svc_register() actually needs to register both "udp" and "udp6"
explicitly (and likewise for TCP). Until rpcbind is fixed, the
kernel can ignore the return code for the second RPCB_SET call.
Please merge this with commit 15231312:
SUNRPC: Support IPv6 when registering kernel RPC services
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Olaf Kirch <okir@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-09-16 05:27:23 +08:00
|
|
|
i,
|
2006-10-04 17:16:05 +08:00
|
|
|
proto == IPPROTO_UDP? "udp" : "tcp",
|
|
|
|
port,
|
2009-03-19 08:46:06 +08:00
|
|
|
family,
|
2013-12-31 13:17:20 +08:00
|
|
|
vers->vs_hidden ?
|
|
|
|
" (but not telling portmap)" : "");
|
2006-10-04 17:16:05 +08:00
|
|
|
|
2013-12-31 13:17:20 +08:00
|
|
|
if (vers->vs_hidden)
|
2006-10-04 17:16:05 +08:00
|
|
|
continue;
|
|
|
|
|
2017-02-25 02:25:25 +08:00
|
|
|
/*
|
|
|
|
* Don't register a UDP port if we need congestion
|
|
|
|
* control.
|
|
|
|
*/
|
|
|
|
if (vers->vs_need_cong_ctrl && proto == IPPROTO_UDP)
|
|
|
|
continue;
|
|
|
|
|
2012-01-13 18:02:48 +08:00
|
|
|
error = __svc_register(net, progp->pg_name, progp->pg_prog,
|
2009-03-19 08:47:44 +08:00
|
|
|
i, family, proto, port);
|
2013-12-31 13:17:20 +08:00
|
|
|
|
|
|
|
if (vers->vs_rpcb_optnl) {
|
|
|
|
error = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error < 0) {
|
|
|
|
printk(KERN_WARNING "svc: failed to register "
|
|
|
|
"%sv%u RPC service (errno %d).\n",
|
|
|
|
progp->pg_name, i, -error);
|
2006-10-04 17:16:05 +08:00
|
|
|
break;
|
2013-12-31 13:17:20 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-19 07:34:08 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2009-03-19 08:47:29 +08:00
|
|
|
/*
|
|
|
|
* If user space is running rpcbind, it should take the v4 UNSET
|
|
|
|
* and clear everything for this [program, version]. If user space
|
|
|
|
* is running portmap, it will reject the v4 UNSET, but won't have
|
|
|
|
* any "inet6" entries anyway. So a PMAP_UNSET should be sufficient
|
|
|
|
* in this case to clear all existing entries for [program, version].
|
|
|
|
*/
|
2012-01-13 18:02:48 +08:00
|
|
|
static void __svc_unregister(struct net *net, const u32 program, const u32 version,
|
2008-09-25 23:56:57 +08:00
|
|
|
const char *progname)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
2012-01-13 18:02:48 +08:00
|
|
|
error = rpcb_v4_register(net, program, version, NULL, "");
|
2008-09-25 23:56:57 +08:00
|
|
|
|
2009-03-19 08:47:29 +08:00
|
|
|
/*
|
|
|
|
* User space didn't support rpcbind v4, so retry this
|
|
|
|
* request with the legacy rpcbind v2 protocol.
|
|
|
|
*/
|
|
|
|
if (error == -EPROTONOSUPPORT)
|
2012-01-13 18:02:48 +08:00
|
|
|
error = rpcb_register(net, program, version, 0, 0);
|
2008-09-25 23:56:57 +08:00
|
|
|
|
|
|
|
dprintk("svc: %s(%sv%u), error %d\n",
|
|
|
|
__func__, progname, version, error);
|
|
|
|
}
|
|
|
|
|
2008-08-19 07:34:08 +08:00
|
|
|
/*
|
2008-09-25 23:56:57 +08:00
|
|
|
* All netids, bind addresses and ports registered for [program, version]
|
|
|
|
* are removed from the local rpcbind database (if the service is not
|
|
|
|
* hidden) to make way for a new instance of the service.
|
2008-08-19 07:34:08 +08:00
|
|
|
*
|
2008-09-25 23:56:57 +08:00
|
|
|
* The result of unregistration is reported via dprintk for those who want
|
|
|
|
* verification of the result, but is otherwise not important.
|
2008-08-19 07:34:08 +08:00
|
|
|
*/
|
2012-01-13 18:02:48 +08:00
|
|
|
static void svc_unregister(const struct svc_serv *serv, struct net *net)
|
2008-08-19 07:34:08 +08:00
|
|
|
{
|
|
|
|
struct svc_program *progp;
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
clear_thread_flag(TIF_SIGPENDING);
|
|
|
|
|
|
|
|
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)
|
|
|
|
continue;
|
|
|
|
|
2011-05-10 03:22:55 +08:00
|
|
|
dprintk("svc: attempting to unregister %sv%u\n",
|
|
|
|
progp->pg_name, i);
|
2012-01-13 18:02:48 +08:00
|
|
|
__svc_unregister(net, progp->pg_prog, i, progp->pg_name);
|
2008-08-19 07:34:08 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-08-19 07:34:08 +08:00
|
|
|
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
|
|
|
recalc_sigpending();
|
|
|
|
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-08-25 23:09:27 +08:00
|
|
|
/*
|
2012-10-10 06:33:38 +08:00
|
|
|
* dprintk the given error with the address of the client that caused it.
|
2007-08-25 23:09:27 +08:00
|
|
|
*/
|
2014-11-18 05:58:04 +08:00
|
|
|
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
|
2011-11-01 08:11:33 +08:00
|
|
|
static __printf(2, 3)
|
2012-05-14 05:56:26 +08:00
|
|
|
void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)
|
2007-08-25 23:09:27 +08:00
|
|
|
{
|
2012-05-14 05:56:26 +08:00
|
|
|
struct va_format vaf;
|
2007-08-25 23:09:27 +08:00
|
|
|
va_list args;
|
|
|
|
char buf[RPC_MAX_ADDRBUFLEN];
|
|
|
|
|
2012-05-14 05:56:26 +08:00
|
|
|
va_start(args, fmt);
|
2007-08-25 23:09:27 +08:00
|
|
|
|
2012-05-14 05:56:26 +08:00
|
|
|
vaf.fmt = fmt;
|
|
|
|
vaf.va = &args;
|
2007-08-25 23:09:27 +08:00
|
|
|
|
2012-10-10 06:33:38 +08:00
|
|
|
dprintk("svc: %s: %pV", svc_print_addr(rqstp, buf, sizeof(buf)), &vaf);
|
2007-08-25 23:09:27 +08:00
|
|
|
|
2012-05-14 05:56:26 +08:00
|
|
|
va_end(args);
|
2007-08-25 23:09:27 +08:00
|
|
|
}
|
2013-01-09 05:22:15 +08:00
|
|
|
#else
|
|
|
|
static __printf(2,3) void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) {}
|
|
|
|
#endif
|
2007-08-25 23:09:27 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2009-04-01 21:23:06 +08:00
|
|
|
* Common routine for processing the RPC request.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2009-04-01 21:23:06 +08:00
|
|
|
static int
|
|
|
|
svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct svc_program *progp;
|
2017-05-12 22:21:37 +08:00
|
|
|
const struct svc_version *versp = NULL; /* compiler food */
|
2017-05-12 22:11:49 +08:00
|
|
|
const struct svc_procedure *procp = NULL;
|
2006-10-02 17:17:50 +08:00
|
|
|
struct svc_serv *serv = rqstp->rq_server;
|
2006-09-27 13:29:38 +08:00
|
|
|
__be32 *statp;
|
2009-04-01 21:23:06 +08:00
|
|
|
u32 prog, vers, proc;
|
2006-09-27 13:29:38 +08:00
|
|
|
__be32 auth_stat, rpc_stat;
|
2005-04-17 06:20:36 +08:00
|
|
|
int auth_res;
|
2006-10-04 17:16:08 +08:00
|
|
|
__be32 *reply_statp;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
rpc_stat = rpc_success;
|
|
|
|
|
|
|
|
if (argv->iov_len < 6*4)
|
|
|
|
goto err_short_len;
|
|
|
|
|
2017-06-20 02:00:40 +08:00
|
|
|
/* Will be turned off by GSS integrity and privacy services */
|
2014-11-19 20:51:18 +08:00
|
|
|
set_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
|
2009-04-03 13:27:32 +08:00
|
|
|
/* Will be turned off only when NFSv4 Sessions are used */
|
2014-11-19 20:51:16 +08:00
|
|
|
set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
|
2014-11-19 20:51:17 +08:00
|
|
|
clear_bit(RQ_DROPME, &rqstp->rq_flags);
|
2007-12-31 11:07:29 +08:00
|
|
|
|
|
|
|
/* Setup reply header */
|
|
|
|
rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
svc_putu32(resv, rqstp->rq_xid);
|
|
|
|
|
2006-09-27 13:28:46 +08:00
|
|
|
vers = svc_getnl(argv);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* First words of reply: */
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, 1); /* REPLY */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (vers != 2) /* RPC version number */
|
|
|
|
goto err_bad_rpc;
|
|
|
|
|
|
|
|
/* Save position in case we later decide to reject: */
|
2006-10-04 17:16:08 +08:00
|
|
|
reply_statp = resv->iov_base + resv->iov_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, 0); /* ACCEPT */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-27 13:28:46 +08:00
|
|
|
rqstp->rq_prog = prog = svc_getnl(argv); /* program number */
|
|
|
|
rqstp->rq_vers = vers = svc_getnl(argv); /* version number */
|
|
|
|
rqstp->rq_proc = proc = svc_getnl(argv); /* procedure number */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-07 17:00:27 +08:00
|
|
|
for (progp = serv->sv_program; progp; progp = progp->pg_next)
|
|
|
|
if (prog == progp->pg_prog)
|
|
|
|
break;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Decode auth data, and add verifier to reply buffer.
|
|
|
|
* We do this before anything else in order to get a decent
|
|
|
|
* auth verifier.
|
|
|
|
*/
|
|
|
|
auth_res = svc_authenticate(rqstp, &auth_stat);
|
|
|
|
/* Also give the program a chance to reject this call: */
|
2005-11-07 17:00:27 +08:00
|
|
|
if (auth_res == SVC_OK && progp) {
|
2005-04-17 06:20:36 +08:00
|
|
|
auth_stat = rpc_autherr_badcred;
|
|
|
|
auth_res = progp->pg_authenticate(rqstp);
|
|
|
|
}
|
|
|
|
switch (auth_res) {
|
|
|
|
case SVC_OK:
|
|
|
|
break;
|
|
|
|
case SVC_GARBAGE:
|
2008-02-20 07:56:56 +08:00
|
|
|
goto err_garbage;
|
2005-04-17 06:20:36 +08:00
|
|
|
case SVC_SYSERR:
|
|
|
|
rpc_stat = rpc_system_err;
|
|
|
|
goto err_bad;
|
|
|
|
case SVC_DENIED:
|
|
|
|
goto err_bad_auth;
|
2010-08-12 15:04:07 +08:00
|
|
|
case SVC_CLOSE:
|
2016-11-30 00:04:34 +08:00
|
|
|
goto close;
|
2005-04-17 06:20:36 +08:00
|
|
|
case SVC_DROP:
|
|
|
|
goto dropit;
|
|
|
|
case SVC_COMPLETE:
|
|
|
|
goto sendit;
|
|
|
|
}
|
2005-11-07 17:00:27 +08:00
|
|
|
|
2005-06-23 01:16:24 +08:00
|
|
|
if (progp == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto err_bad_prog;
|
|
|
|
|
|
|
|
if (vers >= progp->pg_nvers ||
|
|
|
|
!(versp = progp->pg_vers[vers]))
|
|
|
|
goto err_bad_vers;
|
|
|
|
|
2017-02-25 02:25:24 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
procp = versp->vs_proc + proc;
|
|
|
|
if (proc >= versp->vs_nproc || !procp->pc_func)
|
|
|
|
goto err_bad_proc;
|
|
|
|
rqstp->rq_procinfo = procp;
|
|
|
|
|
|
|
|
/* Syntactic check complete */
|
|
|
|
serv->sv_stats->rpccnt++;
|
2018-03-27 22:51:22 +08:00
|
|
|
trace_svc_process(rqstp, progp->pg_name);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Build the reply header. */
|
|
|
|
statp = resv->iov_base +resv->iov_len;
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, RPC_SUCCESS);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Bump per-procedure stats counter */
|
2017-05-09 05:40:27 +08:00
|
|
|
versp->vs_count[proc]++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Initialize storage for argp and resp */
|
|
|
|
memset(rqstp->rq_argp, 0, procp->pc_argsize);
|
|
|
|
memset(rqstp->rq_resp, 0, procp->pc_ressize);
|
|
|
|
|
2007-02-10 07:38:13 +08:00
|
|
|
/* un-reserve some of the out-queue now that we have a
|
2005-04-17 06:20:36 +08:00
|
|
|
* better idea of reply size
|
|
|
|
*/
|
|
|
|
if (procp->pc_xdrressize)
|
2007-05-09 17:34:50 +08:00
|
|
|
svc_reserve_auth(rqstp, procp->pc_xdrressize<<2);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Call the function that processes the request. */
|
|
|
|
if (!versp->vs_dispatch) {
|
2017-05-09 01:01:48 +08:00
|
|
|
/*
|
|
|
|
* Decode arguments
|
|
|
|
* XXX: why do we ignore the return value?
|
|
|
|
*/
|
|
|
|
if (procp->pc_decode &&
|
|
|
|
!procp->pc_decode(rqstp, argv->iov_base))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto err_garbage;
|
|
|
|
|
2017-05-08 23:35:49 +08:00
|
|
|
*statp = procp->pc_func(rqstp);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Encode reply */
|
2016-06-30 01:55:14 +08:00
|
|
|
if (*statp == rpc_drop_reply ||
|
|
|
|
test_bit(RQ_DROPME, &rqstp->rq_flags)) {
|
2006-10-17 15:10:18 +08:00
|
|
|
if (procp->pc_release)
|
2017-05-09 00:48:24 +08:00
|
|
|
procp->pc_release(rqstp);
|
2006-10-17 15:10:18 +08:00
|
|
|
goto dropit;
|
|
|
|
}
|
2016-06-30 01:55:22 +08:00
|
|
|
if (*statp == rpc_autherr_badcred) {
|
|
|
|
if (procp->pc_release)
|
2017-05-09 00:48:24 +08:00
|
|
|
procp->pc_release(rqstp);
|
2016-06-30 01:55:22 +08:00
|
|
|
goto err_bad_auth;
|
|
|
|
}
|
2017-05-09 01:42:02 +08:00
|
|
|
if (*statp == rpc_success && procp->pc_encode &&
|
|
|
|
!procp->pc_encode(rqstp, resv->iov_base + resv->iov_len)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
dprintk("svc: failed to encode reply\n");
|
|
|
|
/* serv->sv_stats->rpcsystemerr++; */
|
|
|
|
*statp = rpc_system_err;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dprintk("svc: calling dispatcher\n");
|
|
|
|
if (!versp->vs_dispatch(rqstp, statp)) {
|
|
|
|
/* Release reply info */
|
|
|
|
if (procp->pc_release)
|
2017-05-09 00:48:24 +08:00
|
|
|
procp->pc_release(rqstp);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto dropit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check RPC status result */
|
|
|
|
if (*statp != rpc_success)
|
|
|
|
resv->iov_len = ((void*)statp) - resv->iov_base + 4;
|
|
|
|
|
|
|
|
/* Release reply info */
|
|
|
|
if (procp->pc_release)
|
2017-05-09 00:48:24 +08:00
|
|
|
procp->pc_release(rqstp);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (procp->pc_encode == NULL)
|
|
|
|
goto dropit;
|
|
|
|
|
|
|
|
sendit:
|
|
|
|
if (svc_authorise(rqstp))
|
2016-11-30 00:04:34 +08:00
|
|
|
goto close;
|
2009-04-01 21:23:06 +08:00
|
|
|
return 1; /* Caller can now send it */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dropit:
|
|
|
|
svc_authorise(rqstp); /* doesn't hurt to call this twice */
|
|
|
|
dprintk("svc: svc_process dropit\n");
|
|
|
|
return 0;
|
|
|
|
|
2016-11-30 00:04:34 +08:00
|
|
|
close:
|
|
|
|
if (test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
|
|
|
|
svc_close_xprt(rqstp->rq_xprt);
|
|
|
|
dprintk("svc: svc_process close\n");
|
|
|
|
return 0;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err_short_len:
|
2017-02-28 06:30:02 +08:00
|
|
|
svc_printk(rqstp, "short len %zd, dropping request\n",
|
2007-08-25 23:09:27 +08:00
|
|
|
argv->iov_len);
|
2016-11-30 00:04:34 +08:00
|
|
|
goto close;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
err_bad_rpc:
|
|
|
|
serv->sv_stats->rpcbadfmt++;
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, 1); /* REJECT */
|
|
|
|
svc_putnl(resv, 0); /* RPC_MISMATCH */
|
|
|
|
svc_putnl(resv, 2); /* Only RPCv2 supported */
|
|
|
|
svc_putnl(resv, 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto sendit;
|
|
|
|
|
|
|
|
err_bad_auth:
|
|
|
|
dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat));
|
|
|
|
serv->sv_stats->rpcbadauth++;
|
|
|
|
/* Restore write pointer to location of accept status: */
|
2006-10-04 17:16:08 +08:00
|
|
|
xdr_ressize_check(rqstp, reply_statp);
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, 1); /* REJECT */
|
|
|
|
svc_putnl(resv, 1); /* AUTH_ERROR */
|
|
|
|
svc_putnl(resv, ntohl(auth_stat)); /* status */
|
2005-04-17 06:20:36 +08:00
|
|
|
goto sendit;
|
|
|
|
|
|
|
|
err_bad_prog:
|
2005-06-23 01:16:24 +08:00
|
|
|
dprintk("svc: unknown program %d\n", prog);
|
2005-04-17 06:20:36 +08:00
|
|
|
serv->sv_stats->rpcbadfmt++;
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, RPC_PROG_UNAVAIL);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto sendit;
|
|
|
|
|
|
|
|
err_bad_vers:
|
2007-08-25 23:09:27 +08:00
|
|
|
svc_printk(rqstp, "unknown version (%d for prog %d, %s)\n",
|
2007-01-30 05:19:52 +08:00
|
|
|
vers, prog, progp->pg_name);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
serv->sv_stats->rpcbadfmt++;
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, RPC_PROG_MISMATCH);
|
|
|
|
svc_putnl(resv, progp->pg_lovers);
|
|
|
|
svc_putnl(resv, progp->pg_hivers);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto sendit;
|
|
|
|
|
|
|
|
err_bad_proc:
|
2007-08-25 23:09:27 +08:00
|
|
|
svc_printk(rqstp, "unknown procedure (%d)\n", proc);
|
2007-01-30 05:19:52 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
serv->sv_stats->rpcbadfmt++;
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, RPC_PROC_UNAVAIL);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto sendit;
|
|
|
|
|
|
|
|
err_garbage:
|
2007-08-25 23:09:27 +08:00
|
|
|
svc_printk(rqstp, "failed to decode args\n");
|
2007-01-30 05:19:52 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
rpc_stat = rpc_garbage_args;
|
|
|
|
err_bad:
|
|
|
|
serv->sv_stats->rpcbadfmt++;
|
2006-09-27 13:28:46 +08:00
|
|
|
svc_putnl(resv, ntohl(rpc_stat));
|
2005-04-17 06:20:36 +08:00
|
|
|
goto sendit;
|
|
|
|
}
|
2006-10-04 17:15:47 +08:00
|
|
|
|
2009-04-01 21:23:06 +08:00
|
|
|
/*
|
|
|
|
* Process the RPC request.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
svc_process(struct svc_rqst *rqstp)
|
|
|
|
{
|
|
|
|
struct kvec *argv = &rqstp->rq_arg.head[0];
|
|
|
|
struct kvec *resv = &rqstp->rq_res.head[0];
|
|
|
|
struct svc_serv *serv = rqstp->rq_server;
|
|
|
|
u32 dir;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup response xdr_buf.
|
|
|
|
* Initially it has just one page
|
|
|
|
*/
|
2012-12-11 07:01:37 +08:00
|
|
|
rqstp->rq_next_page = &rqstp->rq_respages[1];
|
2009-04-01 21:23:06 +08:00
|
|
|
resv->iov_base = page_address(rqstp->rq_respages[0]);
|
|
|
|
resv->iov_len = 0;
|
|
|
|
rqstp->rq_res.pages = rqstp->rq_respages + 1;
|
|
|
|
rqstp->rq_res.len = 0;
|
|
|
|
rqstp->rq_res.page_base = 0;
|
|
|
|
rqstp->rq_res.page_len = 0;
|
|
|
|
rqstp->rq_res.buflen = PAGE_SIZE;
|
|
|
|
rqstp->rq_res.tail[0].iov_base = NULL;
|
|
|
|
rqstp->rq_res.tail[0].iov_len = 0;
|
|
|
|
|
|
|
|
dir = svc_getnl(argv);
|
|
|
|
if (dir != 0) {
|
|
|
|
/* direction != CALL */
|
|
|
|
svc_printk(rqstp, "bad direction %d, dropping request\n", dir);
|
|
|
|
serv->sv_stats->rpcbadfmt++;
|
2014-10-29 02:24:12 +08:00
|
|
|
goto out_drop;
|
2009-04-01 21:23:06 +08:00
|
|
|
}
|
|
|
|
|
2011-01-06 10:04:24 +08:00
|
|
|
/* Returns 1 for send, 0 for drop */
|
2018-03-27 22:51:22 +08:00
|
|
|
if (likely(svc_process_common(rqstp, argv, resv)))
|
|
|
|
return svc_send(rqstp);
|
2014-10-29 02:24:12 +08:00
|
|
|
|
|
|
|
out_drop:
|
|
|
|
svc_drop(rqstp);
|
|
|
|
return 0;
|
2009-04-01 21:23:06 +08:00
|
|
|
}
|
2015-05-27 01:48:57 +08:00
|
|
|
EXPORT_SYMBOL_GPL(svc_process);
|
2009-04-01 21:23:06 +08:00
|
|
|
|
2011-07-14 07:20:49 +08:00
|
|
|
#if defined(CONFIG_SUNRPC_BACKCHANNEL)
|
2009-04-01 21:23:07 +08:00
|
|
|
/*
|
|
|
|
* Process a backchannel RPC request that arrived over an existing
|
|
|
|
* outbound connection
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
|
|
|
|
struct svc_rqst *rqstp)
|
|
|
|
{
|
|
|
|
struct kvec *argv = &rqstp->rq_arg.head[0];
|
|
|
|
struct kvec *resv = &rqstp->rq_res.head[0];
|
2015-05-12 02:04:50 +08:00
|
|
|
struct rpc_task *task;
|
2015-06-05 03:37:10 +08:00
|
|
|
int proc_error;
|
2015-05-12 02:04:50 +08:00
|
|
|
int error;
|
|
|
|
|
|
|
|
dprintk("svc: %s(%p)\n", __func__, req);
|
2009-04-01 21:23:07 +08:00
|
|
|
|
|
|
|
/* Build the svc_rqst used by the common processing routine */
|
2011-01-06 10:04:35 +08:00
|
|
|
rqstp->rq_xprt = serv->sv_bc_xprt;
|
2009-04-01 21:23:07 +08:00
|
|
|
rqstp->rq_xid = req->rq_xid;
|
|
|
|
rqstp->rq_prot = req->rq_xprt->prot;
|
|
|
|
rqstp->rq_server = serv;
|
|
|
|
|
|
|
|
rqstp->rq_addrlen = sizeof(req->rq_xprt->addr);
|
|
|
|
memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen);
|
|
|
|
memcpy(&rqstp->rq_arg, &req->rq_rcv_buf, sizeof(rqstp->rq_arg));
|
|
|
|
memcpy(&rqstp->rq_res, &req->rq_snd_buf, sizeof(rqstp->rq_res));
|
2015-12-08 04:52:23 +08:00
|
|
|
|
|
|
|
/* Adjust the argument buffer length */
|
2015-11-20 22:55:30 +08:00
|
|
|
rqstp->rq_arg.len = req->rq_private_buf.len;
|
2015-12-08 04:52:23 +08:00
|
|
|
if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
|
|
|
|
rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len;
|
|
|
|
rqstp->rq_arg.page_len = 0;
|
|
|
|
} else if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len +
|
|
|
|
rqstp->rq_arg.page_len)
|
|
|
|
rqstp->rq_arg.page_len = rqstp->rq_arg.len -
|
|
|
|
rqstp->rq_arg.head[0].iov_len;
|
|
|
|
else
|
|
|
|
rqstp->rq_arg.len = rqstp->rq_arg.head[0].iov_len +
|
|
|
|
rqstp->rq_arg.page_len;
|
2009-04-01 21:23:07 +08:00
|
|
|
|
|
|
|
/* reset result send buffer "put" position */
|
|
|
|
resv->iov_len = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip the next two words because they've already been
|
2015-05-12 02:04:50 +08:00
|
|
|
* processed in the transport
|
2009-04-01 21:23:07 +08:00
|
|
|
*/
|
|
|
|
svc_getu32(argv); /* XID */
|
|
|
|
svc_getnl(argv); /* CALLDIR */
|
|
|
|
|
2015-05-12 02:04:50 +08:00
|
|
|
/* Parse and execute the bc call */
|
2015-06-05 03:37:10 +08:00
|
|
|
proc_error = svc_process_common(rqstp, argv, resv);
|
|
|
|
|
|
|
|
atomic_inc(&req->rq_xprt->bc_free_slots);
|
|
|
|
if (!proc_error) {
|
2015-05-12 02:04:50 +08:00
|
|
|
/* Processing error: drop the request */
|
2012-06-01 03:26:38 +08:00
|
|
|
xprt_free_bc_request(req);
|
2011-01-06 10:04:24 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-05-12 02:04:50 +08:00
|
|
|
|
|
|
|
/* Finally, send the reply synchronously */
|
|
|
|
memcpy(&req->rq_snd_buf, &rqstp->rq_res, sizeof(req->rq_snd_buf));
|
2015-06-02 10:59:08 +08:00
|
|
|
task = rpc_run_bc_task(req);
|
2015-05-12 02:04:50 +08:00
|
|
|
if (IS_ERR(task)) {
|
|
|
|
error = PTR_ERR(task);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN_ON_ONCE(atomic_read(&task->tk_count) != 1);
|
|
|
|
error = task->tk_status;
|
|
|
|
rpc_put_task(task);
|
|
|
|
|
|
|
|
out:
|
|
|
|
dprintk("svc: %s(), error=%d\n", __func__, error);
|
|
|
|
return error;
|
2009-04-01 21:23:07 +08:00
|
|
|
}
|
2011-07-14 07:24:15 +08:00
|
|
|
EXPORT_SYMBOL_GPL(bc_svc_process);
|
2011-07-14 07:20:49 +08:00
|
|
|
#endif /* CONFIG_SUNRPC_BACKCHANNEL */
|
2009-04-01 21:23:07 +08:00
|
|
|
|
2006-10-04 17:15:47 +08:00
|
|
|
/*
|
|
|
|
* Return (transport-specific) limit on the rpc payload.
|
|
|
|
*/
|
|
|
|
u32 svc_max_payload(const struct svc_rqst *rqstp)
|
|
|
|
{
|
2007-12-31 11:07:21 +08:00
|
|
|
u32 max = rqstp->rq_xprt->xpt_class->xcl_max_payload;
|
2006-10-04 17:15:47 +08:00
|
|
|
|
2006-10-06 15:44:05 +08:00
|
|
|
if (rqstp->rq_server->sv_max_payload < max)
|
|
|
|
max = rqstp->rq_server->sv_max_payload;
|
2006-10-04 17:15:47 +08:00
|
|
|
return max;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(svc_max_payload);
|
2018-03-27 22:54:07 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* svc_fill_write_vector - Construct data argument for VFS write call
|
|
|
|
* @rqstp: svc_rqst to operate on
|
|
|
|
* @first: buffer containing first section of write payload
|
|
|
|
* @total: total number of bytes of write payload
|
|
|
|
*
|
|
|
|
* Returns the number of elements populated in the data argument array.
|
|
|
|
*/
|
|
|
|
unsigned int svc_fill_write_vector(struct svc_rqst *rqstp, struct kvec *first,
|
|
|
|
size_t total)
|
|
|
|
{
|
|
|
|
struct kvec *vec = rqstp->rq_vec;
|
|
|
|
struct page **pages;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/* Some types of transport can present the write payload
|
|
|
|
* entirely in rq_arg.pages. In this case, @first is empty.
|
|
|
|
*/
|
|
|
|
i = 0;
|
|
|
|
if (first->iov_len) {
|
|
|
|
vec[i].iov_base = first->iov_base;
|
|
|
|
vec[i].iov_len = min_t(size_t, total, first->iov_len);
|
|
|
|
total -= vec[i].iov_len;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN_ON_ONCE(rqstp->rq_arg.page_base != 0);
|
|
|
|
pages = rqstp->rq_arg.pages;
|
|
|
|
while (total) {
|
|
|
|
vec[i].iov_base = page_address(*pages);
|
|
|
|
vec[i].iov_len = min_t(size_t, total, PAGE_SIZE);
|
|
|
|
total -= vec[i].iov_len;
|
|
|
|
++i;
|
|
|
|
|
|
|
|
++pages;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN_ON_ONCE(i > ARRAY_SIZE(rqstp->rq_vec));
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(svc_fill_write_vector);
|
2018-03-27 22:54:21 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* svc_fill_symlink_pathname - Construct pathname argument for VFS symlink call
|
|
|
|
* @rqstp: svc_rqst to operate on
|
|
|
|
* @first: buffer containing first section of pathname
|
|
|
|
* @total: total length of the pathname argument
|
|
|
|
*
|
|
|
|
* Returns pointer to a NUL-terminated string, or an ERR_PTR. The buffer is
|
|
|
|
* released automatically when @rqstp is recycled.
|
|
|
|
*/
|
|
|
|
char *svc_fill_symlink_pathname(struct svc_rqst *rqstp, struct kvec *first,
|
|
|
|
size_t total)
|
|
|
|
{
|
|
|
|
struct xdr_buf *arg = &rqstp->rq_arg;
|
|
|
|
struct page **pages;
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
/* VFS API demands a NUL-terminated pathname. This function
|
|
|
|
* uses a page from @rqstp as the pathname buffer, to enable
|
|
|
|
* direct placement. Thus the total buffer size is PAGE_SIZE.
|
|
|
|
* Space in this buffer for NUL-termination requires that we
|
|
|
|
* cap the size of the returned symlink pathname just a
|
|
|
|
* little early.
|
|
|
|
*/
|
|
|
|
if (total > PAGE_SIZE - 1)
|
|
|
|
return ERR_PTR(-ENAMETOOLONG);
|
|
|
|
|
|
|
|
/* Some types of transport can present the pathname entirely
|
|
|
|
* in rq_arg.pages. If not, then copy the pathname into one
|
|
|
|
* page.
|
|
|
|
*/
|
|
|
|
pages = arg->pages;
|
|
|
|
WARN_ON_ONCE(arg->page_base != 0);
|
|
|
|
if (first->iov_base == 0) {
|
|
|
|
result = page_address(*pages);
|
|
|
|
result[total] = '\0';
|
|
|
|
} else {
|
|
|
|
size_t len, remaining;
|
|
|
|
char *dst;
|
|
|
|
|
|
|
|
result = page_address(*(rqstp->rq_next_page++));
|
|
|
|
dst = result;
|
|
|
|
remaining = total;
|
|
|
|
|
|
|
|
len = min_t(size_t, total, first->iov_len);
|
|
|
|
memcpy(dst, first->iov_base, len);
|
|
|
|
dst += len;
|
|
|
|
remaining -= len;
|
|
|
|
|
|
|
|
/* No more than one page left */
|
|
|
|
if (remaining) {
|
|
|
|
len = min_t(size_t, remaining, PAGE_SIZE);
|
|
|
|
memcpy(dst, page_address(*pages), len);
|
|
|
|
dst += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
*dst = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanity check: we don't allow the pathname argument to
|
|
|
|
* contain a NUL byte.
|
|
|
|
*/
|
|
|
|
if (strlen(result) != total)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(svc_fill_symlink_pathname);
|