2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/fs/nfsd/nfssvc.c
|
|
|
|
*
|
|
|
|
* Central processing for nfsd.
|
|
|
|
*
|
|
|
|
* Authors: Olaf Kirch (okir@monad.swb.de)
|
|
|
|
*
|
|
|
|
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
Detach sched.h from mm.h
First thing mm.h does is including sched.h solely for can_do_mlock() inline
function which has "current" dereference inside. By dealing with can_do_mlock()
mm.h can be detached from sched.h which is good. See below, why.
This patch
a) removes unconditional inclusion of sched.h from mm.h
b) makes can_do_mlock() normal function in mm/mlock.c
c) exports can_do_mlock() to not break compilation
d) adds sched.h inclusions back to files that were getting it indirectly.
e) adds less bloated headers to some files (asm/signal.h, jiffies.h) that were
getting them indirectly
Net result is:
a) mm.h users would get less code to open, read, preprocess, parse, ... if
they don't need sched.h
b) sched.h stops being dependency for significant number of files:
on x86_64 allmodconfig touching sched.h results in recompile of 4083 files,
after patch it's only 3744 (-8.3%).
Cross-compile tested on
all arm defconfigs, all mips defconfigs, all powerpc defconfigs,
alpha alpha-up
arm
i386 i386-up i386-defconfig i386-allnoconfig
ia64 ia64-up
m68k
mips
parisc parisc-up
powerpc powerpc-up
s390 s390-up
sparc sparc-up
sparc64 sparc64-up
um-x86_64
x86_64 x86_64-up x86_64-defconfig x86_64-allnoconfig
as well as my two usual configs.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-21 05:22:52 +08:00
|
|
|
#include <linux/sched.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/nfs.h>
|
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/uio.h>
|
|
|
|
#include <linux/unistd.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/smp_lock.h>
|
2007-07-17 19:03:35 +08:00
|
|
|
#include <linux/freezer.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/fs_struct.h>
|
2008-06-10 20:40:38 +08:00
|
|
|
#include <linux/kthread.h>
|
2009-04-03 13:28:18 +08:00
|
|
|
#include <linux/swap.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/sunrpc/types.h>
|
|
|
|
#include <linux/sunrpc/stats.h>
|
|
|
|
#include <linux/sunrpc/svc.h>
|
|
|
|
#include <linux/sunrpc/svcsock.h>
|
|
|
|
#include <linux/sunrpc/cache.h>
|
|
|
|
#include <linux/nfsd/nfsd.h>
|
|
|
|
#include <linux/nfsd/stats.h>
|
|
|
|
#include <linux/nfsd/cache.h>
|
2005-11-07 17:00:25 +08:00
|
|
|
#include <linux/nfsd/syscall.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/lockd/bind.h>
|
2005-06-23 01:16:26 +08:00
|
|
|
#include <linux/nfsacl.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define NFSDDBG_FACILITY NFSDDBG_SVC
|
|
|
|
|
|
|
|
extern struct svc_program nfsd_program;
|
2008-06-10 20:40:38 +08:00
|
|
|
static int nfsd(void *vrqstp);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct timeval nfssvc_boot;
|
|
|
|
|
2008-06-10 20:40:35 +08:00
|
|
|
/*
|
|
|
|
* nfsd_mutex protects nfsd_serv -- both the pointer itself and the members
|
|
|
|
* of the svc_serv struct. In particular, ->sv_nrthreads but also to some
|
|
|
|
* extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt
|
|
|
|
*
|
|
|
|
* If (out side the lock) nfsd_serv is non-NULL, then it must point to a
|
|
|
|
* properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number
|
|
|
|
* of nfsd threads must exist and each must listed in ->sp_all_threads in each
|
|
|
|
* entry of ->sv_pools[].
|
|
|
|
*
|
|
|
|
* Transitions of the thread count between zero and non-zero are of particular
|
|
|
|
* interest since the svc_serv needs to be created and initialized at that
|
|
|
|
* point, or freed.
|
2008-06-10 20:40:36 +08:00
|
|
|
*
|
|
|
|
* Finally, the nfsd_mutex also protects some of the global variables that are
|
|
|
|
* accessed when nfsd starts and that are settable via the write_* routines in
|
|
|
|
* nfsctl.c. In particular:
|
|
|
|
*
|
|
|
|
* user_recovery_dirname
|
|
|
|
* user_lease_time
|
|
|
|
* nfsd_versions
|
2008-06-10 20:40:35 +08:00
|
|
|
*/
|
|
|
|
DEFINE_MUTEX(nfsd_mutex);
|
|
|
|
struct svc_serv *nfsd_serv;
|
|
|
|
|
2009-06-25 03:37:45 +08:00
|
|
|
/*
|
|
|
|
* nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used.
|
|
|
|
* nfsd_drc_max_pages limits the total amount of memory available for
|
|
|
|
* version 4.1 DRC caches.
|
|
|
|
* nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
|
|
|
|
*/
|
|
|
|
spinlock_t nfsd_drc_lock;
|
|
|
|
unsigned int nfsd_drc_max_pages;
|
|
|
|
unsigned int nfsd_drc_pages_used;
|
|
|
|
|
2006-02-01 19:04:34 +08:00
|
|
|
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
|
|
|
static struct svc_stat nfsd_acl_svcstats;
|
|
|
|
static struct svc_version * nfsd_acl_version[] = {
|
|
|
|
[2] = &nfsd_acl_version2,
|
|
|
|
[3] = &nfsd_acl_version3,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define NFSD_ACL_MINVERS 2
|
2006-03-24 19:15:34 +08:00
|
|
|
#define NFSD_ACL_NRVERS ARRAY_SIZE(nfsd_acl_version)
|
2006-02-01 19:04:34 +08:00
|
|
|
static struct svc_version *nfsd_acl_versions[NFSD_ACL_NRVERS];
|
|
|
|
|
|
|
|
static struct svc_program nfsd_acl_program = {
|
|
|
|
.pg_prog = NFS_ACL_PROGRAM,
|
|
|
|
.pg_nvers = NFSD_ACL_NRVERS,
|
|
|
|
.pg_vers = nfsd_acl_versions,
|
2007-01-26 16:56:58 +08:00
|
|
|
.pg_name = "nfsacl",
|
2006-02-01 19:04:34 +08:00
|
|
|
.pg_class = "nfsd",
|
|
|
|
.pg_stats = &nfsd_acl_svcstats,
|
|
|
|
.pg_authenticate = &svc_set_client,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct svc_stat nfsd_acl_svcstats = {
|
|
|
|
.program = &nfsd_acl_program,
|
|
|
|
};
|
|
|
|
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
|
|
|
|
|
2005-11-07 17:00:25 +08:00
|
|
|
static struct svc_version * nfsd_version[] = {
|
|
|
|
[2] = &nfsd_version2,
|
|
|
|
#if defined(CONFIG_NFSD_V3)
|
|
|
|
[3] = &nfsd_version3,
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_NFSD_V4)
|
|
|
|
[4] = &nfsd_version4,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#define NFSD_MINVERS 2
|
2006-03-24 19:15:34 +08:00
|
|
|
#define NFSD_NRVERS ARRAY_SIZE(nfsd_version)
|
2005-11-07 17:00:25 +08:00
|
|
|
static struct svc_version *nfsd_versions[NFSD_NRVERS];
|
|
|
|
|
|
|
|
struct svc_program nfsd_program = {
|
2006-02-01 19:04:34 +08:00
|
|
|
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
|
|
|
.pg_next = &nfsd_acl_program,
|
|
|
|
#endif
|
2005-11-07 17:00:25 +08:00
|
|
|
.pg_prog = NFS_PROGRAM, /* program number */
|
|
|
|
.pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */
|
|
|
|
.pg_vers = nfsd_versions, /* version table */
|
|
|
|
.pg_name = "nfsd", /* program name */
|
|
|
|
.pg_class = "nfsd", /* authentication class */
|
|
|
|
.pg_stats = &nfsd_svcstats, /* version table */
|
|
|
|
.pg_authenticate = &svc_set_client, /* export authentication */
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2009-04-03 13:28:59 +08:00
|
|
|
u32 nfsd_supported_minorversion;
|
|
|
|
|
2006-10-02 17:17:46 +08:00
|
|
|
int nfsd_vers(int vers, enum vers_op change)
|
|
|
|
{
|
|
|
|
if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS)
|
|
|
|
return -1;
|
|
|
|
switch(change) {
|
|
|
|
case NFSD_SET:
|
|
|
|
nfsd_versions[vers] = nfsd_version[vers];
|
|
|
|
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
|
|
|
if (vers < NFSD_ACL_NRVERS)
|
2007-01-26 16:56:58 +08:00
|
|
|
nfsd_acl_versions[vers] = nfsd_acl_version[vers];
|
2006-10-02 17:17:46 +08:00
|
|
|
#endif
|
2007-01-26 16:56:58 +08:00
|
|
|
break;
|
2006-10-02 17:17:46 +08:00
|
|
|
case NFSD_CLEAR:
|
|
|
|
nfsd_versions[vers] = NULL;
|
|
|
|
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
|
|
|
if (vers < NFSD_ACL_NRVERS)
|
2007-01-26 16:56:58 +08:00
|
|
|
nfsd_acl_versions[vers] = NULL;
|
2006-10-02 17:17:46 +08:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case NFSD_TEST:
|
|
|
|
return nfsd_versions[vers] != NULL;
|
|
|
|
case NFSD_AVAIL:
|
|
|
|
return nfsd_version[vers] != NULL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2009-04-03 13:28:59 +08:00
|
|
|
|
|
|
|
int nfsd_minorversion(u32 minorversion, enum vers_op change)
|
|
|
|
{
|
|
|
|
if (minorversion > NFSD_SUPPORTED_MINOR_VERSION)
|
|
|
|
return -1;
|
|
|
|
switch(change) {
|
|
|
|
case NFSD_SET:
|
|
|
|
nfsd_supported_minorversion = minorversion;
|
|
|
|
break;
|
|
|
|
case NFSD_CLEAR:
|
|
|
|
if (minorversion == 0)
|
|
|
|
return -1;
|
|
|
|
nfsd_supported_minorversion = minorversion - 1;
|
|
|
|
break;
|
|
|
|
case NFSD_TEST:
|
|
|
|
return minorversion <= nfsd_supported_minorversion;
|
|
|
|
case NFSD_AVAIL:
|
|
|
|
return minorversion <= NFSD_SUPPORTED_MINOR_VERSION;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Maximum number of nfsd processes
|
|
|
|
*/
|
|
|
|
#define NFSD_MAXSERVS 8192
|
|
|
|
|
|
|
|
int nfsd_nrthreads(void)
|
|
|
|
{
|
2008-06-12 11:38:42 +08:00
|
|
|
int rv = 0;
|
|
|
|
mutex_lock(&nfsd_mutex);
|
|
|
|
if (nfsd_serv)
|
|
|
|
rv = nfsd_serv->sv_nrthreads;
|
|
|
|
mutex_unlock(&nfsd_mutex);
|
|
|
|
return rv;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-10-02 17:17:44 +08:00
|
|
|
static void nfsd_last_thread(struct svc_serv *serv)
|
|
|
|
{
|
|
|
|
/* When last nfsd thread exits we need to do some clean-up */
|
2007-12-31 11:07:53 +08:00
|
|
|
struct svc_xprt *xprt;
|
|
|
|
list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list)
|
2006-10-02 17:17:45 +08:00
|
|
|
lockd_down();
|
2006-10-02 17:17:44 +08:00
|
|
|
nfsd_serv = NULL;
|
|
|
|
nfsd_racache_shutdown();
|
|
|
|
nfs4_state_shutdown();
|
|
|
|
|
2008-06-10 20:40:37 +08:00
|
|
|
printk(KERN_WARNING "nfsd: last server has exited, flushing export "
|
|
|
|
"cache\n");
|
|
|
|
nfsd_export_flush();
|
2006-10-02 17:17:44 +08:00
|
|
|
}
|
2006-10-02 17:17:46 +08:00
|
|
|
|
|
|
|
void nfsd_reset_versions(void)
|
|
|
|
{
|
|
|
|
int found_one = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) {
|
|
|
|
if (nfsd_program.pg_vers[i])
|
|
|
|
found_one = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found_one) {
|
|
|
|
for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++)
|
|
|
|
nfsd_program.pg_vers[i] = nfsd_version[i];
|
|
|
|
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
|
|
|
for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++)
|
|
|
|
nfsd_acl_program.pg_vers[i] =
|
|
|
|
nfsd_acl_version[i];
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-03 13:28:18 +08:00
|
|
|
/*
|
|
|
|
* Each session guarantees a negotiated per slot memory cache for replies
|
|
|
|
* which in turn consumes memory beyond the v2/v3/v4.0 server. A dedicated
|
|
|
|
* NFSv4.1 server might want to use more memory for a DRC than a machine
|
|
|
|
* with mutiple services.
|
|
|
|
*
|
|
|
|
* Impose a hard limit on the number of pages for the DRC which varies
|
|
|
|
* according to the machines free pages. This is of course only a default.
|
|
|
|
*
|
|
|
|
* For now this is a #defined shift which could be under admin control
|
|
|
|
* in the future.
|
|
|
|
*/
|
|
|
|
static void set_max_drc(void)
|
|
|
|
{
|
2009-04-06 17:00:36 +08:00
|
|
|
/* The percent of nr_free_buffer_pages used by the V4.1 server DRC */
|
|
|
|
#define NFSD_DRC_SIZE_SHIFT 7
|
2009-06-25 03:37:45 +08:00
|
|
|
nfsd_drc_max_pages = nr_free_buffer_pages()
|
2009-04-03 13:28:18 +08:00
|
|
|
>> NFSD_DRC_SIZE_SHIFT;
|
2009-06-25 03:37:45 +08:00
|
|
|
nfsd_drc_pages_used = 0;
|
|
|
|
spin_lock_init(&nfsd_drc_lock);
|
|
|
|
dprintk("%s nfsd_drc_max_pages %u\n", __func__,
|
|
|
|
nfsd_drc_max_pages);
|
2009-04-03 13:28:18 +08:00
|
|
|
}
|
2008-06-10 20:40:35 +08:00
|
|
|
|
2006-10-02 17:17:48 +08:00
|
|
|
int nfsd_create_serv(void)
|
2006-10-02 17:17:46 +08:00
|
|
|
{
|
|
|
|
int err = 0;
|
2008-06-10 20:40:35 +08:00
|
|
|
|
|
|
|
WARN_ON(!mutex_is_locked(&nfsd_mutex));
|
2006-10-02 17:17:46 +08:00
|
|
|
if (nfsd_serv) {
|
2006-10-02 17:17:58 +08:00
|
|
|
svc_get(nfsd_serv);
|
2006-10-02 17:17:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2006-10-04 17:15:48 +08:00
|
|
|
if (nfsd_max_blksize == 0) {
|
|
|
|
/* choose a suitable default */
|
|
|
|
struct sysinfo i;
|
|
|
|
si_meminfo(&i);
|
|
|
|
/* Aim for 1/4096 of memory per thread
|
|
|
|
* This gives 1MB on 4Gig machines
|
|
|
|
* But only uses 32K on 128M machines.
|
|
|
|
* Bottom out at 8K on 32M and smaller.
|
|
|
|
* Of course, this is only a default.
|
|
|
|
*/
|
|
|
|
nfsd_max_blksize = NFSSVC_MAXBLKSIZE;
|
2006-10-04 17:16:15 +08:00
|
|
|
i.totalram <<= PAGE_SHIFT - 12;
|
2006-10-04 17:15:48 +08:00
|
|
|
while (nfsd_max_blksize > i.totalram &&
|
|
|
|
nfsd_max_blksize >= 8*1024*2)
|
|
|
|
nfsd_max_blksize /= 2;
|
|
|
|
}
|
2006-10-02 17:17:46 +08:00
|
|
|
|
2008-06-10 20:40:37 +08:00
|
|
|
nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
|
2008-06-10 20:40:39 +08:00
|
|
|
nfsd_last_thread, nfsd, THIS_MODULE);
|
2006-10-02 17:17:46 +08:00
|
|
|
if (nfsd_serv == NULL)
|
|
|
|
err = -ENOMEM;
|
2009-04-03 13:28:18 +08:00
|
|
|
else
|
|
|
|
set_max_drc();
|
2008-06-10 20:40:35 +08:00
|
|
|
|
2006-10-02 17:17:46 +08:00
|
|
|
do_gettimeofday(&nfssvc_boot); /* record boot time */
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfsd_init_socks(int port)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
if (!list_empty(&nfsd_serv->sv_permsocks))
|
|
|
|
return 0;
|
|
|
|
|
2009-03-19 08:46:21 +08:00
|
|
|
error = svc_create_xprt(nfsd_serv, "udp", PF_INET, port,
|
2007-02-12 16:53:29 +08:00
|
|
|
SVC_SOCK_DEFAULTS);
|
2006-10-02 17:17:46 +08:00
|
|
|
if (error < 0)
|
|
|
|
return error;
|
|
|
|
|
2008-10-23 01:12:36 +08:00
|
|
|
error = lockd_up();
|
|
|
|
if (error < 0)
|
|
|
|
return error;
|
|
|
|
|
2009-03-19 08:46:21 +08:00
|
|
|
error = svc_create_xprt(nfsd_serv, "tcp", PF_INET, port,
|
2007-02-12 16:53:29 +08:00
|
|
|
SVC_SOCK_DEFAULTS);
|
2006-10-02 17:17:46 +08:00
|
|
|
if (error < 0)
|
|
|
|
return error;
|
2008-10-04 05:15:30 +08:00
|
|
|
|
|
|
|
error = lockd_up();
|
|
|
|
if (error < 0)
|
|
|
|
return error;
|
|
|
|
|
2006-10-02 17:17:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-10-02 17:18:02 +08:00
|
|
|
int nfsd_nrpools(void)
|
|
|
|
{
|
|
|
|
if (nfsd_serv == NULL)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return nfsd_serv->sv_nrpools;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfsd_get_nrthreads(int n, int *nthreads)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (nfsd_serv != NULL) {
|
|
|
|
for (i = 0; i < nfsd_serv->sv_nrpools && i < n; i++)
|
|
|
|
nthreads[i] = nfsd_serv->sv_pools[i].sp_nrthreads;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfsd_set_nrthreads(int n, int *nthreads)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int tot = 0;
|
|
|
|
int err = 0;
|
|
|
|
|
2008-06-10 20:40:35 +08:00
|
|
|
WARN_ON(!mutex_is_locked(&nfsd_mutex));
|
|
|
|
|
2006-10-02 17:18:02 +08:00
|
|
|
if (nfsd_serv == NULL || n <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (n > nfsd_serv->sv_nrpools)
|
|
|
|
n = nfsd_serv->sv_nrpools;
|
|
|
|
|
|
|
|
/* enforce a global maximum number of threads */
|
|
|
|
tot = 0;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (nthreads[i] > NFSD_MAXSERVS)
|
|
|
|
nthreads[i] = NFSD_MAXSERVS;
|
|
|
|
tot += nthreads[i];
|
|
|
|
}
|
|
|
|
if (tot > NFSD_MAXSERVS) {
|
|
|
|
/* total too large: scale down requested numbers */
|
|
|
|
for (i = 0; i < n && tot > 0; i++) {
|
|
|
|
int new = nthreads[i] * NFSD_MAXSERVS / tot;
|
|
|
|
tot -= (nthreads[i] - new);
|
|
|
|
nthreads[i] = new;
|
|
|
|
}
|
|
|
|
for (i = 0; i < n && tot > 0; i++) {
|
|
|
|
nthreads[i]--;
|
|
|
|
tot--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There must always be a thread in pool 0; the admin
|
|
|
|
* can't shut down NFS completely using pool_threads.
|
|
|
|
*/
|
|
|
|
if (nthreads[0] == 0)
|
|
|
|
nthreads[0] = 1;
|
|
|
|
|
|
|
|
/* apply the new numbers */
|
|
|
|
svc_get(nfsd_serv);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i],
|
|
|
|
nthreads[i]);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
svc_destroy(nfsd_serv);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int
|
|
|
|
nfsd_svc(unsigned short port, int nrservs)
|
|
|
|
{
|
|
|
|
int error;
|
2008-06-10 20:40:35 +08:00
|
|
|
|
|
|
|
mutex_lock(&nfsd_mutex);
|
2006-10-02 17:17:46 +08:00
|
|
|
dprintk("nfsd: creating service\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
if (nrservs <= 0)
|
|
|
|
nrservs = 0;
|
|
|
|
if (nrservs > NFSD_MAXSERVS)
|
|
|
|
nrservs = NFSD_MAXSERVS;
|
2009-06-16 09:03:20 +08:00
|
|
|
error = 0;
|
|
|
|
if (nrservs == 0 && nfsd_serv == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Readahead param cache - will no-op if it already exists */
|
|
|
|
error = nfsd_racache_init(2*nrservs);
|
|
|
|
if (error<0)
|
|
|
|
goto out;
|
2007-08-02 03:30:59 +08:00
|
|
|
nfs4_state_start();
|
2006-10-02 17:17:46 +08:00
|
|
|
|
|
|
|
nfsd_reset_versions();
|
|
|
|
|
|
|
|
error = nfsd_create_serv();
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
error = nfsd_init_socks(port);
|
|
|
|
if (error)
|
|
|
|
goto failure;
|
|
|
|
|
2006-10-02 17:18:00 +08:00
|
|
|
error = svc_set_num_threads(nfsd_serv, NULL, nrservs);
|
nfsd: don't take nfsd_mutex twice when setting number of threads.
Currently when we write a number to 'threads' in nfsdfs,
we take the nfsd_mutex, update the number of threads, then take the
mutex again to read the number of threads.
Mostly this isn't a big deal. However if we are write '0', and
portmap happens to be dead, then we can get unpredictable behaviour.
If the nfsd threads all got killed quickly and the last thread is
waiting for portmap to respond, then the second time we take the mutex
we will block waiting for the last thread.
However if the nfsd threads didn't die quite that fast, then there
will be no contention when we try to take the mutex again.
Unpredictability isn't fun, and waiting for the last thread to exit is
pointless, so avoid taking the lock twice.
To achieve this, get nfsd_svc return a non-negative number of active
threads when not returning a negative error.
Signed-off-by: NeilBrown <neilb@suse.de>
2009-06-16 09:03:07 +08:00
|
|
|
if (error == 0)
|
|
|
|
/* We are holding a reference to nfsd_serv which
|
|
|
|
* we don't want to count in the return value,
|
|
|
|
* so subtract 1
|
|
|
|
*/
|
|
|
|
error = nfsd_serv->sv_nrthreads - 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
failure:
|
|
|
|
svc_destroy(nfsd_serv); /* Release server */
|
|
|
|
out:
|
2008-06-10 20:40:35 +08:00
|
|
|
mutex_unlock(&nfsd_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the NFS server kernel thread
|
|
|
|
*/
|
2008-06-10 20:40:38 +08:00
|
|
|
static int
|
|
|
|
nfsd(void *vrqstp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-10 20:40:38 +08:00
|
|
|
struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp;
|
|
|
|
int err, preverr = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Lock module and set up kernel thread */
|
2008-06-10 20:40:35 +08:00
|
|
|
mutex_lock(&nfsd_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-06-10 20:40:38 +08:00
|
|
|
/* At this point, the thread shares current->fs
|
2005-04-17 06:20:36 +08:00
|
|
|
* with the init process. We need to create files with a
|
|
|
|
* umask of 0 instead of init's umask. */
|
2009-03-30 07:00:13 +08:00
|
|
|
if (unshare_fs_struct() < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk("Unable to start nfsd thread: out of memory\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2009-03-30 07:00:13 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
current->fs->umask = 0;
|
|
|
|
|
2008-06-10 20:40:38 +08:00
|
|
|
/*
|
|
|
|
* thread is spawned with all signals set to SIG_IGN, re-enable
|
2008-07-01 02:09:46 +08:00
|
|
|
* the ones that will bring down the thread
|
2008-06-10 20:40:38 +08:00
|
|
|
*/
|
2008-07-01 02:09:46 +08:00
|
|
|
allow_signal(SIGKILL);
|
|
|
|
allow_signal(SIGHUP);
|
|
|
|
allow_signal(SIGINT);
|
|
|
|
allow_signal(SIGQUIT);
|
2008-06-10 20:40:35 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
nfsdstats.th_cnt++;
|
2008-06-10 20:40:35 +08:00
|
|
|
mutex_unlock(&nfsd_mutex);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* We want less throttling in balance_dirty_pages() so that nfs to
|
|
|
|
* localhost doesn't cause nfsd to lock up due to all the client's
|
|
|
|
* dirty pages.
|
|
|
|
*/
|
|
|
|
current->flags |= PF_LESS_THROTTLE;
|
2007-07-17 19:03:35 +08:00
|
|
|
set_freezable();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The main request loop
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
/*
|
|
|
|
* Find a socket with data available and call its
|
|
|
|
* recvfrom routine.
|
|
|
|
*/
|
2006-10-02 17:17:50 +08:00
|
|
|
while ((err = svc_recv(rqstp, 60*60*HZ)) == -EAGAIN)
|
2005-04-17 06:20:36 +08:00
|
|
|
;
|
2008-06-10 20:40:38 +08:00
|
|
|
if (err == -EINTR)
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2008-06-10 20:40:38 +08:00
|
|
|
else if (err < 0) {
|
|
|
|
if (err != preverr) {
|
|
|
|
printk(KERN_WARNING "%s: unexpected error "
|
|
|
|
"from svc_recv (%d)\n", __func__, -err);
|
|
|
|
preverr = err;
|
|
|
|
}
|
|
|
|
schedule_timeout_uninterruptible(HZ);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Lock the export hash tables for reading. */
|
|
|
|
exp_readlock();
|
|
|
|
|
2006-10-02 17:17:50 +08:00
|
|
|
svc_process(rqstp);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Unlock export hash tables */
|
|
|
|
exp_readunlock();
|
|
|
|
}
|
|
|
|
|
2006-10-02 17:17:45 +08:00
|
|
|
/* Clear signals before calling svc_exit_thread() */
|
2005-04-17 06:26:37 +08:00
|
|
|
flush_signals(current);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-06-10 20:40:35 +08:00
|
|
|
mutex_lock(&nfsd_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
nfsdstats.th_cnt --;
|
|
|
|
|
|
|
|
out:
|
|
|
|
/* Release the thread */
|
|
|
|
svc_exit_thread(rqstp);
|
|
|
|
|
|
|
|
/* Release module */
|
2008-06-10 20:40:35 +08:00
|
|
|
mutex_unlock(&nfsd_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
module_put_and_exit(0);
|
2008-06-10 20:40:38 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-07-17 19:04:48 +08:00
|
|
|
static __be32 map_new_errors(u32 vers, __be32 nfserr)
|
|
|
|
{
|
|
|
|
if (nfserr == nfserr_jukebox && vers == 2)
|
|
|
|
return nfserr_dropit;
|
|
|
|
if (nfserr == nfserr_wrongsec && vers < 4)
|
|
|
|
return nfserr_acces;
|
|
|
|
return nfserr;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int
|
2006-10-20 14:29:02 +08:00
|
|
|
nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct svc_procedure *proc;
|
|
|
|
kxdrproc_t xdr;
|
2006-10-20 14:28:55 +08:00
|
|
|
__be32 nfserr;
|
|
|
|
__be32 *nfserrp;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dprintk("nfsd_dispatch: vers %d proc %d\n",
|
|
|
|
rqstp->rq_vers, rqstp->rq_proc);
|
|
|
|
proc = rqstp->rq_procinfo;
|
|
|
|
|
|
|
|
/* Check whether we have this call in the cache. */
|
|
|
|
switch (nfsd_cache_lookup(rqstp, proc->pc_cachetype)) {
|
|
|
|
case RC_INTR:
|
|
|
|
case RC_DROPIT:
|
|
|
|
return 0;
|
|
|
|
case RC_REPLY:
|
|
|
|
return 1;
|
|
|
|
case RC_DOIT:;
|
|
|
|
/* do it */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode arguments */
|
|
|
|
xdr = proc->pc_decode;
|
2006-10-20 14:28:55 +08:00
|
|
|
if (xdr && !xdr(rqstp, (__be32*)rqstp->rq_arg.head[0].iov_base,
|
2005-04-17 06:20:36 +08:00
|
|
|
rqstp->rq_argp)) {
|
|
|
|
dprintk("nfsd: failed to decode arguments!\n");
|
|
|
|
nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
|
|
|
|
*statp = rpc_garbage_args;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* need to grab the location to store the status, as
|
|
|
|
* nfsv4 does some encoding while processing
|
|
|
|
*/
|
|
|
|
nfserrp = rqstp->rq_res.head[0].iov_base
|
|
|
|
+ rqstp->rq_res.head[0].iov_len;
|
2006-10-20 14:28:55 +08:00
|
|
|
rqstp->rq_res.head[0].iov_len += sizeof(__be32);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-04-03 13:28:15 +08:00
|
|
|
/* NFSv4.1 DRC requires statp */
|
|
|
|
if (rqstp->rq_vers == 4)
|
|
|
|
nfsd4_set_statp(rqstp, statp);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Now call the procedure handler, and encode NFS status. */
|
|
|
|
nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
|
2007-07-17 19:04:48 +08:00
|
|
|
nfserr = map_new_errors(rqstp->rq_vers, nfserr);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (nfserr == nfserr_dropit) {
|
2007-06-23 05:26:32 +08:00
|
|
|
dprintk("nfsd: Dropping request; may be revisited later\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rqstp->rq_proc != 0)
|
|
|
|
*nfserrp++ = nfserr;
|
|
|
|
|
|
|
|
/* Encode result.
|
|
|
|
* For NFSv2, additional info is never returned in case of an error.
|
|
|
|
*/
|
|
|
|
if (!(nfserr && rqstp->rq_vers == 2)) {
|
|
|
|
xdr = proc->pc_encode;
|
|
|
|
if (xdr && !xdr(rqstp, nfserrp,
|
|
|
|
rqstp->rq_resp)) {
|
|
|
|
/* Failed to encode result. Release cache entry */
|
|
|
|
dprintk("nfsd: failed to encode result!\n");
|
|
|
|
nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
|
|
|
|
*statp = rpc_system_err;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store reply in cache. */
|
|
|
|
nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1);
|
|
|
|
return 1;
|
|
|
|
}
|
2009-01-13 18:26:36 +08:00
|
|
|
|
|
|
|
int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
if (nfsd_serv == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
return svc_pool_stats_open(nfsd_serv, file);
|
|
|
|
}
|